]> git.ipfire.org Git - thirdparty/squid.git/blame - src/cbdata.cc
Updates for running on squid-cache.org
[thirdparty/squid.git] / src / cbdata.cc
CommitLineData
365e5b34 1
e1ac4723 2/*
63be0a78 3 * $Id: cbdata.cc,v 1.77 2008/02/26 21:49:34 amosjeffries Exp $
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.
26 *
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.
31 *
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
aa839030 51#include "cbdata.h"
62ee09ca 52#include "CacheManager.h"
e6ccf245 53#include "Store.h"
e48ed433 54#if CBDATA_DEBUG
55#include "Stack.h"
56#endif
57#include "Generic.h"
a2172245 58
b4bab919 59#if WITH_VALGRIND
60#define HASHED_CBDATA 1
61#endif
62
8b9a2d90 63static int cbdataCount = 0;
fa80a8ef 64#if CBDATA_DEBUG
65dlink_list cbdataEntries;
66#endif
8b9a2d90 67
e48ed433 68#if CBDATA_DEBUG
62e76326 69
70class CBDataCall
71{
72
e48ed433 73public:
74 CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine){}
62e76326 75
e48ed433 76 char const *label;
77 char const *file;
78 int line;
79};
62e76326 80
e48ed433 81#endif
82
63be0a78 83/// \ingroup CBDATAInternal
43ae1d95 84#define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
85
63be0a78 86/// \ingroup CBDATAInternal
43ae1d95 87class cbdata
62e76326 88{
63be0a78 89 /** \todo examine making cbdata templated on this - so we get type
b4bab919 90 * safe access to data - RBC 20030902 */
43ae1d95 91public:
b4bab919 92#if HASHED_CBDATA
93 hash_link hash; // Must be first
94#endif
95
e48ed433 96#if CBDATA_DEBUG
43ae1d95 97
e48ed433 98 void dump(StoreEntry *)const;
99#endif
62e76326 100
b4bab919 101#if !HASHED_CBDATA
f668fcda 102 void *operator new(size_t size, void *where);
e13b11a5 103 void operator delete(void *where, void *where2);
b4bab919 104#else
105 MEMPROXY_CLASS(cndata);
106#endif
f668fcda 107
108 ~cbdata();
3015e83a 109 int valid;
110 int locks;
f668fcda 111 cbdata_type type;
4e3f712d 112#if CBDATA_DEBUG
62e76326 113
91caca83 114 void addHistory(char const *label, char const *file, int line)
62e76326 115 {
91caca83 116 if (calls.size() > 1000)
62e76326 117 return;
118
91caca83 119 calls.push_back(new CBDataCall(label, file, line));
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;
b4bab919 130 void check(int line) const {assert(cookie == ((long)this ^ Cookie));}
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;
e48ed433 177#ifdef CBDATA_DEBUG
178static OBJH cbdataDumpHistory;
179#endif
a2172245 180
63be0a78 181/// \ingroup CBDATAInternal
62e76326 182struct CBDataIndex
183{
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
208
f668fcda 209cbdata::~cbdata()
e48ed433 210{
211#if CBDATA_DEBUG
212 CBDataCall *aCall;
62e76326 213
91caca83 214 while ((aCall = calls.pop()))
62e76326 215 delete aCall;
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)
256 cbdata_htable = hash_create(cbdata_cmp, 1 << 12, cbdata_hash);
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{
a08f1b5a
FC
276 CacheManager *manager=CacheManager::GetInstance();
277 manager->registerAction("cbdata",
278 "Callback Data Registry Contents",
279 cbdataDump, 0, 1);
62ee09ca 280#if CBDATA_DEBUG
281
a08f1b5a
FC
282 manager->registerAction("cbdatahistory",
283 "Detailed call history for all current cbdata contents",
284 cbdataDumpHistory, 0, 1);
62ee09ca 285#endif
286}
287
28c60158 288void *
4e3f712d 289#if CBDATA_DEBUG
72711e31 290cbdataInternalAllocDbg(cbdata_type type, const char *file, int line)
4e3f712d 291#else
72711e31 292cbdataInternalAlloc(cbdata_type type)
4e3f712d 293#endif
a2172245 294{
b4bab919 295 cbdata *c;
296 void *p;
aa839030 297 assert(type > 0 && type <= cbdata_types);
298 /* placement new: the pool alloc gives us cbdata + user type memory space
299 * and we init it with cbdata at the start of it
300 */
b4bab919 301#if HASHED_CBDATA
302 c = new cbdata;
303 p = cbdata_index[type].pool->alloc();
304 c->hash.key = p;
305 hash_join(cbdata_htable, &c->hash);
306#else
307 c = new (cbdata_index[type].pool->alloc()) cbdata;
308 p = (void *)&c->data;
309#endif
f668fcda 310
b4bab919 311 c->type = type;
312 c->valid = 1;
313 c->locks = 0;
314 c->cookie = (long) c ^ cbdata::Cookie;
8b9a2d90 315 cbdataCount++;
fa80a8ef 316#if CBDATA_DEBUG
62e76326 317
b4bab919 318 c->file = file;
319 c->line = line;
320 c->calls = Stack<CBDataCall *> ();
321 c->addHistory("Alloc", file, line);
322 dlinkAdd(c, &c->link, &cbdataEntries);
bf8fe701 323 debugs(45, 3, "cbdataAlloc: " << p << " " << file << ":" << line);
fa80a8ef 324#endif
62e76326 325
b4bab919 326 return p;
b8a2718d 327}
328
c29316a4 329void *
fa80a8ef 330#if CBDATA_DEBUG
331cbdataInternalFreeDbg(void *p, const char *file, int line)
332#else
72711e31 333cbdataInternalFree(void *p)
fa80a8ef 334#endif
a2172245 335{
28c60158 336 cbdata *c;
b4bab919 337#if HASHED_CBDATA
338 c = (cbdata *) hash_lookup(cbdata_htable, p);
339#else
43ae1d95 340 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 341#endif
fa80a8ef 342#if CBDATA_DEBUG
62e76326 343
bf8fe701 344 debugs(45, 3, "cbdataFree: " << p << " " << file << ":" << line);
fa80a8ef 345#else
62e76326 346
bf8fe701 347 debugs(45, 9, "cbdataFree: " << p);
fa80a8ef 348#endif
62e76326 349
06a5ae20 350 c->check(__LINE__);
a2fb868f 351 assert(c->valid);
3015e83a 352 c->valid = 0;
e48ed433 353#if CBDATA_DEBUG
62e76326 354
e48ed433 355 c->addHistory("Free", file, line);
356#endif
62e76326 357
3015e83a 358 if (c->locks) {
bf8fe701 359 debugs(45, 9, "cbdataFree: " << p << " has " << c->locks << " locks, not freeing");
62e76326 360 return NULL;
3015e83a 361 }
62e76326 362
28c60158 363 cbdataCount--;
bf8fe701 364 debugs(45, 9, "cbdataFree: Freeing " << p);
fa80a8ef 365#if CBDATA_DEBUG
62e76326 366
fa80a8ef 367 dlinkDelete(&c->link, &cbdataEntries);
368#endif
62e76326 369
f668fcda 370 /* This is ugly. But: operator delete doesn't get
371 * the type parameter, so we can't use that
372 * to free the memory.
373 * So, we free it ourselves.
374 * Note that this means a non-placement
375 * new would be a seriously bad idea.
376 * Lastly, if we where a templated class,
377 * we could use the normal delete operator
378 * and it would Just Work. RBC 20030902
379 */
b4bab919 380 cbdata_type theType = c->type;
381#if HASHED_CBDATA
382 hash_remove_link(cbdata_htable, &c->hash);
383 delete c;
384 cbdata_index[theType].pool->free((void *)p);
385#else
386 c->cbdata::~cbdata();
b001e822 387 cbdata_index[theType].pool->free(c);
b4bab919 388#endif
c29316a4 389 return NULL;
72711e31 390}
391
a2172245 392void
b7e6c377 393#if CBDATA_DEBUG
fa80a8ef 394cbdataInternalLockDbg(const void *p, const char *file, int line)
b7e6c377 395#else
fa80a8ef 396cbdataInternalLock(const void *p)
b7e6c377 397#endif
a2172245 398{
3015e83a 399 cbdata *c;
62e76326 400
3015e83a 401 if (p == NULL)
62e76326 402 return;
403
b4bab919 404#if HASHED_CBDATA
405 c = (cbdata *) hash_lookup(cbdata_htable, p);
406#else
43ae1d95 407 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 408#endif
62e76326 409
b7e6c377 410#if CBDATA_DEBUG
62e76326 411
bf8fe701 412 debugs(45, 3, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1) << " " << file << ":" << line);
62e76326 413
e48ed433 414 c->addHistory("Reference", file, line);
62e76326 415
fa80a8ef 416#else
62e76326 417
bf8fe701 418 debugs(45, 9, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1));
62e76326 419
b7e6c377 420#endif
62e76326 421
06a5ae20 422 c->check(__LINE__);
62e76326 423
e5130cd3 424 assert(c->locks < 65535);
62e76326 425
fa80a8ef 426 c->locks++;
a2172245 427}
428
429void
b7e6c377 430#if CBDATA_DEBUG
fa80a8ef 431cbdataInternalUnlockDbg(const void *p, const char *file, int line)
b7e6c377 432#else
fa80a8ef 433cbdataInternalUnlock(const void *p)
b7e6c377 434#endif
a2172245 435{
3015e83a 436 cbdata *c;
62e76326 437
3015e83a 438 if (p == NULL)
62e76326 439 return;
440
b4bab919 441#if HASHED_CBDATA
442 c = (cbdata *) hash_lookup(cbdata_htable, p);
443#else
43ae1d95 444 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 445#endif
62e76326 446
fa80a8ef 447#if CBDATA_DEBUG
62e76326 448
bf8fe701 449 debugs(45, 3, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1) << " " << file << ":" << line);
62e76326 450
e48ed433 451 c->addHistory("Dereference", file, line);
62e76326 452
fa80a8ef 453#else
62e76326 454
bf8fe701 455 debugs(45, 9, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1));
62e76326 456
fa80a8ef 457#endif
62e76326 458
06a5ae20 459 c->check(__LINE__);
62e76326 460
3015e83a 461 assert(c != NULL);
62e76326 462
3015e83a 463 assert(c->locks > 0);
62e76326 464
3015e83a 465 c->locks--;
62e76326 466
aab9676e 467 if (c->valid || c->locks)
62e76326 468 return;
469
28c60158 470 cbdataCount--;
62e76326 471
bf8fe701 472 debugs(45, 9, "cbdataUnlock: Freeing " << p);
62e76326 473
fa80a8ef 474#if CBDATA_DEBUG
62e76326 475
fa80a8ef 476 dlinkDelete(&c->link, &cbdataEntries);
62e76326 477
fa80a8ef 478#endif
62e76326 479
f668fcda 480 /* This is ugly. But: operator delete doesn't get
481 * the type parameter, so we can't use that
482 * to free the memory.
483 * So, we free it ourselves.
484 * Note that this means a non-placement
485 * new would be a seriously bad idea.
486 * Lastly, if we where a templated class,
487 * we could use the normal delete operator
488 * and it would Just Work. RBC 20030902
489 */
b4bab919 490 cbdata_type theType = c->type;
491#if HASHED_CBDATA
492 hash_remove_link(cbdata_htable, &c->hash);
493 delete c;
494 cbdata_index[theType].pool->free((void *)p);
495#else
496 c->cbdata::~cbdata();
b001e822 497 cbdata_index[theType].pool->free(c);
b4bab919 498#endif
a2172245 499}
500
501int
fa80a8ef 502cbdataReferenceValid(const void *p)
a2172245 503{
3015e83a 504 cbdata *c;
62e76326 505
3015e83a 506 if (p == NULL)
62e76326 507 return 1; /* A NULL pointer cannot become invalid */
508
bf8fe701 509 debugs(45, 9, "cbdataReferenceValid: " << p);
62e76326 510
b4bab919 511#if HASHED_CBDATA
512 c = (cbdata *) hash_lookup(cbdata_htable, p);
513#else
43ae1d95 514 c = (cbdata *) (((char *) p) - cbdata::Offset);
b4bab919 515#endif
62e76326 516
06a5ae20 517 c->check(__LINE__);
62e76326 518
3015e83a 519 assert(c->locks > 0);
62e76326 520
3015e83a 521 return c->valid;
a2172245 522}
523
fa80a8ef 524int
525#if CBDATA_DEBUG
526cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line)
527#else
528cbdataInternalReferenceDoneValid(void **pp, void **tp)
529#endif
530{
531 void *p = (void *) *pp;
532 int valid = cbdataReferenceValid(p);
533 *pp = NULL;
534#if CBDATA_DEBUG
62e76326 535
fa80a8ef 536 cbdataInternalUnlockDbg(p, file, line);
537#else
62e76326 538
fa80a8ef 539 cbdataInternalUnlock(p);
540#endif
62e76326 541
fa80a8ef 542 if (valid) {
62e76326 543 *tp = p;
544 return 1;
fa80a8ef 545 } else {
62e76326 546 *tp = NULL;
547 return 0;
fa80a8ef 548 }
549}
550
e48ed433 551#if CBDATA_DEBUG
552void
43ae1d95 553cbdata::dump(StoreEntry *sentry) const
e48ed433 554{
b4bab919 555#if HASHED_CBDATA
c692f6b0 556 void *p = (void *)hash.key;
b4bab919 557#else
c692f6b0 558 void *p = (void *)&data;
b4bab919 559#endif
e48ed433 560 storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' :
b4bab919 561 '!', p, type, locks, file, line);
e48ed433 562}
563
43ae1d95 564struct CBDataDumper : public unary_function<cbdata, void>
e48ed433 565{
566 CBDataDumper(StoreEntry *anEntry):where(anEntry){}
62e76326 567
43ae1d95 568 void operator()(cbdata const &x)
62e76326 569 {
570 x.dump(where);
e48ed433 571 }
62e76326 572
e48ed433 573 StoreEntry *where;
574};
62e76326 575
e48ed433 576#endif
fa80a8ef 577
db1cd23c 578static void
3015e83a 579cbdataDump(StoreEntry * sentry)
a2172245 580{
8b9a2d90 581 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
fa80a8ef 582#if CBDATA_DEBUG
62e76326 583
fa80a8ef 584 storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
e48ed433 585 CBDataDumper dumper(sentry);
586 for_each (cbdataEntries, dumper);
fa80a8ef 587 storeAppendPrintf(sentry, "\n");
588 storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n");
62e76326 589
e48ed433 590 for (int i = 1; i < cbdata_types; i++) {
b4bab919 591 MemAllocator *pool = cbdata_index[i].pool;
62e76326 592
593 if (pool) {
b4bab919 594#if HASHED_CBDATA
595 int obj_size = pool->objectSize();
596#else
b001e822 597 int obj_size = pool->objectSize() - cbdata::Offset;
b4bab919 598#endif
06a0702b 599 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 600 }
fa80a8ef 601 }
62e76326 602
fa80a8ef 603#else
604 storeAppendPrintf(sentry, "detailed allocation information only available when compiled with CBDATA_DEBUG\n");
62e76326 605
fa80a8ef 606#endif
62e76326 607
fa80a8ef 608 storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
a2172245 609}
e48ed433 610
aa839030 611CBDATA_CLASS_INIT(generic_cbdata);
612
e48ed433 613#if CBDATA_DEBUG
614
e48ed433 615struct CBDataCallDumper : public unary_function<CBDataCall, void>
616{
617 CBDataCallDumper (StoreEntry *anEntry):where(anEntry){}
62e76326 618
619 void operator()(CBDataCall const &x)
620 {
621 storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line);
e48ed433 622 }
62e76326 623
e48ed433 624 StoreEntry *where;
625};
626
627struct CBDataHistoryDumper : public CBDataDumper
628{
629 CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry){}
62e76326 630
43ae1d95 631 void operator()(cbdata const &x)
62e76326 632 {
633 CBDataDumper::operator()(x);
634 storeAppendPrintf(where, "\n");
635 storeAppendPrintf(where, "Action\tFile\tLine\n");
91caca83 636 for_each (x.calls,callDumper);
62e76326 637 storeAppendPrintf(where, "\n");
e48ed433 638 }
62e76326 639
e48ed433 640 StoreEntry *where;
641 CBDataCallDumper callDumper;
642};
643
644void
645cbdataDumpHistory(StoreEntry *sentry)
646{
647 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
648 storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
649 CBDataHistoryDumper dumper(sentry);
650 for_each (cbdataEntries, dumper);
651}
62e76326 652
e48ed433 653#endif