]> git.ipfire.org Git - thirdparty/squid.git/blame - src/cbdata.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[thirdparty/squid.git] / src / cbdata.cc
CommitLineData
365e5b34 1
e1ac4723 2/*
262a0e14 3 * $Id$
e1ac4723 4 *
5 * DEBUG: section 45 Callback Data Registry
28c60158 6 * ORIGINAL AUTHOR: Duane Wessels
7 * Modified by Moez Mahfoudh (08/12/2000)
e48ed433 8 * History added by Robert Collins (2002-10-25)
e1ac4723 9 *
2b6662ba 10 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 11 * ----------------------------------------------------------
e1ac4723 12 *
2b6662ba 13 * Squid is the result of efforts by numerous individuals from
14 * the Internet community; see the CONTRIBUTORS file for full
15 * details. Many organizations have provided support for Squid's
16 * development; see the SPONSORS file for full details. Squid is
17 * Copyrighted (C) 2001 by the Regents of the University of
18 * California; see the COPYRIGHT file for full details. Squid
19 * incorporates software developed and/or copyrighted by other
20 * sources; see the CREDITS file for full details.
e1ac4723 21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26ac0430 26 *
e1ac4723 27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
26ac0430 31 *
e1ac4723 32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
cbdec147 34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 35 *
e1ac4723 36 */
37
63be0a78 38/**
39 \defgroup CBDATAInternal Callback Data Allocator Internals
40 \ingroup CBDATAAPI
41 *
e1ac4723 42 * These routines manage a set of registered callback data pointers.
63be0a78 43 * One of the easiest ways to make Squid coredump is to issue a
e1ac4723 44 * callback to for some data structure which has previously been
45 * freed. With these routines, we register (add) callback data
46 * pointers, lock them just before registering the callback function,
47 * validate them before issuing the callback, and then free them
48 * when finished.
e1ac4723 49 */
a2172245 50
f7f3304a 51#include "squid.h"
aa839030 52#include "cbdata.h"
8822ebee 53#include "mgr/Registration.h"
e6ccf245 54#include "Store.h"
9a0a18de 55#if USE_CBDATA_DEBUG
e48ed433 56#include "Stack.h"
57#endif
58#include "Generic.h"
a2172245 59
b4bab919 60#if WITH_VALGRIND
61#define HASHED_CBDATA 1
62#endif
63
8b9a2d90 64static int cbdataCount = 0;
9a0a18de 65#if USE_CBDATA_DEBUG
fa80a8ef 66dlink_list cbdataEntries;
67#endif
8b9a2d90 68
9a0a18de 69#if USE_CBDATA_DEBUG
62e76326 70
71class CBDataCall
72{
73
e48ed433 74public:
26ac0430 75 CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine) {}
62e76326 76
e48ed433 77 char const *label;
78 char const *file;
79 int line;
80};
62e76326 81
e48ed433 82#endif
83
63be0a78 84/// \ingroup CBDATAInternal
43ae1d95 85#define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
86
63be0a78 87/// \ingroup CBDATAInternal
43ae1d95 88class cbdata
62e76326 89{
63be0a78 90 /** \todo examine making cbdata templated on this - so we get type
b4bab919 91 * safe access to data - RBC 20030902 */
43ae1d95 92public:
b4bab919 93#if HASHED_CBDATA
94 hash_link hash; // Must be first
95#endif
96
9a0a18de 97#if USE_CBDATA_DEBUG
43ae1d95 98
e48ed433 99 void dump(StoreEntry *)const;
100#endif
62e76326 101
b4bab919 102#if !HASHED_CBDATA
f668fcda 103 void *operator new(size_t size, void *where);
e13b11a5 104 void operator delete(void *where, void *where2);
b4bab919 105#else
106 MEMPROXY_CLASS(cndata);
107#endif
f668fcda 108
109 ~cbdata();
3015e83a 110 int valid;
3ad2eed0 111 int32_t locks;
f668fcda 112 cbdata_type type;
9a0a18de 113#if USE_CBDATA_DEBUG
62e76326 114
7ddfeb80 115 void addHistory(char const *label, char const *aFile, int aLine) {
91caca83 116 if (calls.size() > 1000)
62e76326 117 return;
118
7ddfeb80 119 calls.push_back(new CBDataCall(label, aFile, aLine));
62e76326 120 }
121
fa80a8ef 122 dlink_node link;
4e3f712d 123 const char *file;
124 int line;
91caca83 125 Stack<CBDataCall*> calls;
4e3f712d 126#endif
62e76326 127
43ae1d95 128 /* cookie used while debugging */
129 long cookie;
7ddfeb80 130 void check(int aLine) const {assert(cookie == ((long)this ^ Cookie));}
b4bab919 131 static const long Cookie;
43ae1d95 132
b4bab919 133#if !HASHED_CBDATA
43ae1d95 134 size_t dataSize() const { return sizeof(data);}
43ae1d95 135 static long MakeOffset();
136 static const long Offset;
b4bab919 137 /* MUST be the last per-instance member */
138 void *data;
139#endif
140
43ae1d95 141};
142
bf5113eb 143const long cbdata::Cookie((long)0xDEADBEEF);
b4bab919 144#if !HASHED_CBDATA
43ae1d95 145const long cbdata::Offset(MakeOffset());
146
f668fcda 147void *
148cbdata::operator new(size_t size, void *where)
149{
150 // assert (size == sizeof(cbdata));
151 return where;
152}
153
63be0a78 154/**
155 * Only ever invoked when placement new throws
156 * an exception. Used to prevent an incorrect
157 * free.
158 */
f668fcda 159void
160cbdata::operator delete(void *where, void *where2)
cfdb8f88
AJ
161{
162 ; // empty.
f668fcda 163}
164
43ae1d95 165long
166cbdata::MakeOffset()
167{
168 cbdata *zero = (cbdata *)0L;
169 void **dataOffset = &zero->data;
170 return (long)dataOffset;
171}
b4bab919 172#else
cfdb8f88 173MEMPROXY_CLASS_INLINE(cbdata);
b4bab919 174#endif
a2172245 175
db1cd23c 176static OBJH cbdataDump;
9a0a18de 177#if USE_CBDATA_DEBUG
e48ed433 178static OBJH cbdataDumpHistory;
179#endif
a2172245 180
63be0a78 181/// \ingroup CBDATAInternal
26ac0430 182struct CBDataIndex {
b4bab919 183 MemAllocator *pool;
72711e31 184 FREE *free_func;
62e76326 185}
62e76326 186*cbdata_index = NULL;
63be0a78 187
188/// \ingroup CBDATAInternal
28c60158 189int cbdata_types = 0;
190
b4bab919 191#if HASHED_CBDATA
192static hash_table *cbdata_htable = NULL;
193
194static int
195cbdata_cmp(const void *p1, const void *p2)
196{
197 return (char *) p1 - (char *) p2;
198}
199
200static unsigned int
201cbdata_hash(const void *p, unsigned int mod)
202{
203 return ((unsigned long) p >> 8) % mod;
204}
205#endif
206
207
f668fcda 208cbdata::~cbdata()
e48ed433 209{
9a0a18de 210#if USE_CBDATA_DEBUG
e48ed433 211 CBDataCall *aCall;
62e76326 212
91caca83 213 while ((aCall = calls.pop()))
62e76326 214 delete aCall;
215
e48ed433 216#endif
62e76326 217
e48ed433 218 FREE *free_func = cbdata_index[type].free_func;
62e76326 219
b4bab919 220#if HASHED_CBDATA
221 void *p = hash.key;
222#else
223 void *p = &data;
224#endif
225
e48ed433 226 if (free_func)
b4bab919 227 free_func(p);
e48ed433 228}
229
fa80a8ef 230static void
231cbdataInternalInitType(cbdata_type type, const char *name, int size, FREE * free_func)
a2172245 232{
28c60158 233 char *label;
aa839030 234 assert (type == cbdata_types + 1);
62e76326 235
aa839030 236 cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
237 memset(&cbdata_index[type], 0, sizeof(*cbdata_index));
238 cbdata_types = type;
62e76326 239
e6ccf245 240 label = (char *)xmalloc(strlen(name) + 20);
62e76326 241
28c60158 242 snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type);
62e76326 243
b4bab919 244#if !HASHED_CBDATA
43ae1d95 245 assert((size_t)cbdata::Offset == (sizeof(cbdata) - ((cbdata *)NULL)->dataSize()));
b4bab919 246 size += cbdata::Offset;
247#endif
62e76326 248
04eb0689 249 cbdata_index[type].pool = memPoolCreate(label, size);
62e76326 250
72711e31 251 cbdata_index[type].free_func = free_func;
b4bab919 252
253#if HASHED_CBDATA
254 if (!cbdata_htable)
26ac0430 255 cbdata_htable = hash_create(cbdata_cmp, 1 << 12, cbdata_hash);
b4bab919 256#endif
a2172245 257}
258
28c60158 259cbdata_type
fa80a8ef 260cbdataInternalAddType(cbdata_type type, const char *name, int size, FREE * free_func)
a2172245 261{
28c60158 262 if (type)
62e76326 263 return type;
264
aa839030 265 type = (cbdata_type)(cbdata_types + 1);
62e76326 266
fa80a8ef 267 cbdataInternalInitType(type, name, size, free_func);
62e76326 268
28c60158 269 return type;
a2172245 270}
271
62ee09ca 272void
dbeed9ea 273cbdataRegisterWithCacheManager(void)
62ee09ca 274{
8822ebee 275 Mgr::RegisterAction("cbdata",
d9fc6862
A
276 "Callback Data Registry Contents",
277 cbdataDump, 0, 1);
9a0a18de 278#if USE_CBDATA_DEBUG
62ee09ca 279
8822ebee 280 Mgr::RegisterAction("cbdatahistory",
d9fc6862
A
281 "Detailed call history for all current cbdata contents",
282 cbdataDumpHistory, 0, 1);
62ee09ca 283#endif
284}
285
28c60158 286void *
9a0a18de 287#if USE_CBDATA_DEBUG
72711e31 288cbdataInternalAllocDbg(cbdata_type type, const char *file, int line)
4e3f712d 289#else
72711e31 290cbdataInternalAlloc(cbdata_type type)
4e3f712d 291#endif
a2172245 292{
b4bab919 293 cbdata *c;
294 void *p;
aa839030 295 assert(type > 0 && type <= cbdata_types);
296 /* placement new: the pool alloc gives us cbdata + user type memory space
297 * and we init it with cbdata at the start of it
298 */
b4bab919 299#if HASHED_CBDATA
300 c = new cbdata;
301 p = cbdata_index[type].pool->alloc();
302 c->hash.key = p;
303 hash_join(cbdata_htable, &c->hash);
304#else
305 c = new (cbdata_index[type].pool->alloc()) cbdata;
306 p = (void *)&c->data;
307#endif
f668fcda 308
b4bab919 309 c->type = type;
310 c->valid = 1;
311 c->locks = 0;
312 c->cookie = (long) c ^ cbdata::Cookie;
8b9a2d90 313 cbdataCount++;
9a0a18de 314#if USE_CBDATA_DEBUG
62e76326 315
b4bab919 316 c->file = file;
317 c->line = line;
318 c->calls = Stack<CBDataCall *> ();
319 c->addHistory("Alloc", file, line);
320 dlinkAdd(c, &c->link, &cbdataEntries);
bf8fe701 321 debugs(45, 3, "cbdataAlloc: " << p << " " << file << ":" << line);
fa80a8ef 322#endif
62e76326 323
b4bab919 324 return p;
b8a2718d 325}
326
c29316a4 327void *
9a0a18de 328#if USE_CBDATA_DEBUG
fa80a8ef 329cbdataInternalFreeDbg(void *p, const char *file, int line)
330#else
72711e31 331cbdataInternalFree(void *p)
fa80a8ef 332#endif
a2172245 333{
28c60158 334 cbdata *c;
b4bab919 335#if HASHED_CBDATA
336 c = (cbdata *) hash_lookup(cbdata_htable, p);
337#else
43ae1d95 338 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 339#endif
9a0a18de 340#if USE_CBDATA_DEBUG
62e76326 341
bf8fe701 342 debugs(45, 3, "cbdataFree: " << p << " " << file << ":" << line);
fa80a8ef 343#else
62e76326 344
bf8fe701 345 debugs(45, 9, "cbdataFree: " << p);
fa80a8ef 346#endif
62e76326 347
06a5ae20 348 c->check(__LINE__);
a2fb868f 349 assert(c->valid);
3015e83a 350 c->valid = 0;
9a0a18de 351#if USE_CBDATA_DEBUG
62e76326 352
e48ed433 353 c->addHistory("Free", file, line);
354#endif
62e76326 355
3015e83a 356 if (c->locks) {
bf8fe701 357 debugs(45, 9, "cbdataFree: " << p << " has " << c->locks << " locks, not freeing");
62e76326 358 return NULL;
3015e83a 359 }
62e76326 360
28c60158 361 cbdataCount--;
bf8fe701 362 debugs(45, 9, "cbdataFree: Freeing " << p);
9a0a18de 363#if USE_CBDATA_DEBUG
62e76326 364
fa80a8ef 365 dlinkDelete(&c->link, &cbdataEntries);
366#endif
62e76326 367
f668fcda 368 /* This is ugly. But: operator delete doesn't get
26ac0430 369 * the type parameter, so we can't use that
f668fcda 370 * to free the memory.
371 * So, we free it ourselves.
26ac0430 372 * Note that this means a non-placement
f668fcda 373 * new would be a seriously bad idea.
374 * Lastly, if we where a templated class,
375 * we could use the normal delete operator
376 * and it would Just Work. RBC 20030902
377 */
b4bab919 378 cbdata_type theType = c->type;
379#if HASHED_CBDATA
380 hash_remove_link(cbdata_htable, &c->hash);
381 delete c;
dc47f531 382 cbdata_index[theType].pool->freeOne((void *)p);
b4bab919 383#else
384 c->cbdata::~cbdata();
dc47f531 385 cbdata_index[theType].pool->freeOne(c);
b4bab919 386#endif
c29316a4 387 return NULL;
72711e31 388}
389
a2172245 390void
9a0a18de 391#if USE_CBDATA_DEBUG
fa80a8ef 392cbdataInternalLockDbg(const void *p, const char *file, int line)
b7e6c377 393#else
fa80a8ef 394cbdataInternalLock(const void *p)
b7e6c377 395#endif
a2172245 396{
3015e83a 397 cbdata *c;
62e76326 398
3015e83a 399 if (p == NULL)
62e76326 400 return;
401
b4bab919 402#if HASHED_CBDATA
403 c = (cbdata *) hash_lookup(cbdata_htable, p);
404#else
43ae1d95 405 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 406#endif
62e76326 407
9a0a18de 408#if USE_CBDATA_DEBUG
62e76326 409
bf8fe701 410 debugs(45, 3, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1) << " " << file << ":" << line);
62e76326 411
e48ed433 412 c->addHistory("Reference", file, line);
62e76326 413
fa80a8ef 414#else
62e76326 415
bf8fe701 416 debugs(45, 9, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1));
62e76326 417
b7e6c377 418#endif
62e76326 419
06a5ae20 420 c->check(__LINE__);
62e76326 421
3ad2eed0 422 assert(c->locks < INT_MAX);
62e76326 423
fa80a8ef 424 c->locks++;
a2172245 425}
426
427void
9a0a18de 428#if USE_CBDATA_DEBUG
fa80a8ef 429cbdataInternalUnlockDbg(const void *p, const char *file, int line)
b7e6c377 430#else
fa80a8ef 431cbdataInternalUnlock(const void *p)
b7e6c377 432#endif
a2172245 433{
3015e83a 434 cbdata *c;
62e76326 435
3015e83a 436 if (p == NULL)
62e76326 437 return;
438
b4bab919 439#if HASHED_CBDATA
440 c = (cbdata *) hash_lookup(cbdata_htable, p);
441#else
43ae1d95 442 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 443#endif
62e76326 444
9a0a18de 445#if USE_CBDATA_DEBUG
62e76326 446
bf8fe701 447 debugs(45, 3, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1) << " " << file << ":" << line);
62e76326 448
e48ed433 449 c->addHistory("Dereference", file, line);
62e76326 450
fa80a8ef 451#else
62e76326 452
bf8fe701 453 debugs(45, 9, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1));
62e76326 454
fa80a8ef 455#endif
62e76326 456
06a5ae20 457 c->check(__LINE__);
62e76326 458
3015e83a 459 assert(c != NULL);
62e76326 460
3015e83a 461 assert(c->locks > 0);
62e76326 462
3015e83a 463 c->locks--;
62e76326 464
aab9676e 465 if (c->valid || c->locks)
62e76326 466 return;
467
28c60158 468 cbdataCount--;
62e76326 469
bf8fe701 470 debugs(45, 9, "cbdataUnlock: Freeing " << p);
62e76326 471
9a0a18de 472#if USE_CBDATA_DEBUG
62e76326 473
fa80a8ef 474 dlinkDelete(&c->link, &cbdataEntries);
62e76326 475
fa80a8ef 476#endif
62e76326 477
f668fcda 478 /* This is ugly. But: operator delete doesn't get
26ac0430 479 * the type parameter, so we can't use that
f668fcda 480 * to free the memory.
481 * So, we free it ourselves.
26ac0430 482 * Note that this means a non-placement
f668fcda 483 * new would be a seriously bad idea.
484 * Lastly, if we where a templated class,
485 * we could use the normal delete operator
486 * and it would Just Work. RBC 20030902
487 */
b4bab919 488 cbdata_type theType = c->type;
489#if HASHED_CBDATA
490 hash_remove_link(cbdata_htable, &c->hash);
491 delete c;
dc47f531 492 cbdata_index[theType].pool->freeOne((void *)p);
b4bab919 493#else
494 c->cbdata::~cbdata();
dc47f531 495 cbdata_index[theType].pool->freeOne(c);
b4bab919 496#endif
a2172245 497}
498
499int
fa80a8ef 500cbdataReferenceValid(const void *p)
a2172245 501{
3015e83a 502 cbdata *c;
62e76326 503
3015e83a 504 if (p == NULL)
62e76326 505 return 1; /* A NULL pointer cannot become invalid */
506
bf8fe701 507 debugs(45, 9, "cbdataReferenceValid: " << p);
62e76326 508
b4bab919 509#if HASHED_CBDATA
510 c = (cbdata *) hash_lookup(cbdata_htable, p);
511#else
43ae1d95 512 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 513#endif
62e76326 514
06a5ae20 515 c->check(__LINE__);
62e76326 516
3015e83a 517 assert(c->locks > 0);
62e76326 518
3015e83a 519 return c->valid;
a2172245 520}
521
fa80a8ef 522int
9a0a18de 523#if USE_CBDATA_DEBUG
fa80a8ef 524cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line)
525#else
526cbdataInternalReferenceDoneValid(void **pp, void **tp)
527#endif
528{
529 void *p = (void *) *pp;
530 int valid = cbdataReferenceValid(p);
531 *pp = NULL;
9a0a18de 532#if USE_CBDATA_DEBUG
62e76326 533
fa80a8ef 534 cbdataInternalUnlockDbg(p, file, line);
535#else
62e76326 536
fa80a8ef 537 cbdataInternalUnlock(p);
538#endif
62e76326 539
fa80a8ef 540 if (valid) {
62e76326 541 *tp = p;
542 return 1;
fa80a8ef 543 } else {
62e76326 544 *tp = NULL;
545 return 0;
fa80a8ef 546 }
547}
548
9a0a18de 549#if USE_CBDATA_DEBUG
e48ed433 550void
43ae1d95 551cbdata::dump(StoreEntry *sentry) const
e48ed433 552{
b4bab919 553#if HASHED_CBDATA
c692f6b0 554 void *p = (void *)hash.key;
b4bab919 555#else
c692f6b0 556 void *p = (void *)&data;
b4bab919 557#endif
e48ed433 558 storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' :
b4bab919 559 '!', p, type, locks, file, line);
e48ed433 560}
561
26ac0430
AJ
562struct CBDataDumper : public unary_function<cbdata, void> {
563 CBDataDumper(StoreEntry *anEntry):where(anEntry) {}
62e76326 564
26ac0430 565 void operator()(cbdata const &x) {
62e76326 566 x.dump(where);
e48ed433 567 }
62e76326 568
e48ed433 569 StoreEntry *where;
570};
62e76326 571
e48ed433 572#endif
fa80a8ef 573
db1cd23c 574static void
3015e83a 575cbdataDump(StoreEntry * sentry)
a2172245 576{
8b9a2d90 577 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
9a0a18de 578#if USE_CBDATA_DEBUG
62e76326 579
fa80a8ef 580 storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
e48ed433 581 CBDataDumper dumper(sentry);
582 for_each (cbdataEntries, dumper);
fa80a8ef 583 storeAppendPrintf(sentry, "\n");
584 storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n");
62e76326 585
e48ed433 586 for (int i = 1; i < cbdata_types; i++) {
b4bab919 587 MemAllocator *pool = cbdata_index[i].pool;
62e76326 588
589 if (pool) {
b4bab919 590#if HASHED_CBDATA
591 int obj_size = pool->objectSize();
592#else
b001e822 593 int obj_size = pool->objectSize() - cbdata::Offset;
b4bab919 594#endif
06a0702b 595 storeAppendPrintf(sentry, "%s\t%d\t%ld\t%ld\n", pool->objectType() + 7, obj_size, (long int)pool->getMeter().inuse.level, (long int)obj_size * pool->getMeter().inuse.level);
62e76326 596 }
fa80a8ef 597 }
62e76326 598
fa80a8ef 599#else
9a0a18de 600 storeAppendPrintf(sentry, "detailed allocation information only available when compiled with --enable-debug-cbdata\n");
62e76326 601
fa80a8ef 602#endif
62e76326 603
fa80a8ef 604 storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
a2172245 605}
e48ed433 606
aa839030 607CBDATA_CLASS_INIT(generic_cbdata);
608
9a0a18de 609#if USE_CBDATA_DEBUG
e48ed433 610
26ac0430
AJ
611struct CBDataCallDumper : public unary_function<CBDataCall, void> {
612 CBDataCallDumper (StoreEntry *anEntry):where(anEntry) {}
62e76326 613
26ac0430 614 void operator()(CBDataCall const &x) {
62e76326 615 storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line);
e48ed433 616 }
62e76326 617
e48ed433 618 StoreEntry *where;
619};
620
26ac0430
AJ
621struct CBDataHistoryDumper : public CBDataDumper {
622 CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry) {}
62e76326 623
26ac0430 624 void operator()(cbdata const &x) {
62e76326 625 CBDataDumper::operator()(x);
626 storeAppendPrintf(where, "\n");
627 storeAppendPrintf(where, "Action\tFile\tLine\n");
91caca83 628 for_each (x.calls,callDumper);
62e76326 629 storeAppendPrintf(where, "\n");
e48ed433 630 }
62e76326 631
e48ed433 632 StoreEntry *where;
633 CBDataCallDumper callDumper;
634};
635
636void
637cbdataDumpHistory(StoreEntry *sentry)
638{
639 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
640 storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
641 CBDataHistoryDumper dumper(sentry);
642 for_each (cbdataEntries, dumper);
643}
62e76326 644
e48ed433 645#endif