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