]>
Commit | Line | Data |
---|---|---|
365e5b34 | 1 | |
e1ac4723 | 2 | /* |
a46d2c0e | 3 | * $Id: cbdata.cc,v 1.55 2003/03/04 01:40:25 robertc 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 | ||
38 | /* | |
39 | * These routines manage a set of registered callback data pointers. | |
40 | * One of the easiest ways to make Squid coredump is to issue a | |
41 | * callback to for some data structure which has previously been | |
42 | * freed. With these routines, we register (add) callback data | |
43 | * pointers, lock them just before registering the callback function, | |
44 | * validate them before issuing the callback, and then free them | |
45 | * when finished. | |
e1ac4723 | 46 | */ |
a2172245 | 47 | |
48 | #include "squid.h" | |
e6ccf245 | 49 | #include "Store.h" |
e48ed433 | 50 | #if CBDATA_DEBUG |
51 | #include "Stack.h" | |
52 | #endif | |
53 | #include "Generic.h" | |
a2172245 | 54 | |
8b9a2d90 | 55 | static int cbdataCount = 0; |
fa80a8ef | 56 | #if CBDATA_DEBUG |
57 | dlink_list cbdataEntries; | |
58 | #endif | |
8b9a2d90 | 59 | |
e48ed433 | 60 | #if CBDATA_DEBUG |
62e76326 | 61 | |
62 | class CBDataCall | |
63 | { | |
64 | ||
e48ed433 | 65 | public: |
66 | CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine){} | |
62e76326 | 67 | |
e48ed433 | 68 | char const *label; |
69 | char const *file; | |
70 | int line; | |
71 | }; | |
62e76326 | 72 | |
e48ed433 | 73 | #endif |
74 | ||
62e76326 | 75 | typedef struct _cbdata |
76 | { | |
e48ed433 | 77 | #if CBDATA_DEBUG |
78 | void dump(StoreEntry *)const; | |
79 | #endif | |
62e76326 | 80 | |
e48ed433 | 81 | void deleteSelf(); |
3015e83a | 82 | int valid; |
83 | int locks; | |
72711e31 | 84 | int type; |
4e3f712d | 85 | #if CBDATA_DEBUG |
62e76326 | 86 | |
e48ed433 | 87 | void addHistory(char const *label, char const *file, int line) const |
62e76326 | 88 | { |
89 | if (calls->count > 100) | |
90 | return; | |
91 | ||
e48ed433 | 92 | stackPush (calls, new CBDataCall(label, file, line)); |
62e76326 | 93 | } |
94 | ||
fa80a8ef | 95 | dlink_node link; |
4e3f712d | 96 | const char *file; |
97 | int line; | |
e48ed433 | 98 | Stack *calls; |
4e3f712d | 99 | #endif |
62e76326 | 100 | |
fa80a8ef | 101 | long y; /* cookie used while debugging */ |
28c60158 | 102 | union { |
62e76326 | 103 | void *pointer; |
104 | double double_float; | |
105 | int integer; | |
28c60158 | 106 | } data; |
62e76326 | 107 | } |
108 | ||
109 | cbdata; | |
a2172245 | 110 | |
db1cd23c | 111 | static OBJH cbdataDump; |
e48ed433 | 112 | #ifdef CBDATA_DEBUG |
113 | static OBJH cbdataDumpHistory; | |
114 | #endif | |
a2172245 | 115 | |
62e76326 | 116 | struct CBDataIndex |
117 | { | |
72711e31 | 118 | MemPool *pool; |
119 | FREE *free_func; | |
62e76326 | 120 | } |
121 | ||
122 | *cbdata_index = NULL; | |
28c60158 | 123 | int cbdata_types = 0; |
124 | ||
125 | #define OFFSET_OF(type, member) ((int)(char *)&((type *)0L)->member) | |
e6ccf245 | 126 | #define CBDATA_COOKIE (long)0xDEADBEEF |
fa80a8ef | 127 | #define CBDATA_CHECK(c) assert(c->y == ((long)c ^ CBDATA_COOKIE)) |
28c60158 | 128 | |
e48ed433 | 129 | void |
130 | _cbdata::deleteSelf() | |
131 | { | |
132 | #if CBDATA_DEBUG | |
133 | CBDataCall *aCall; | |
62e76326 | 134 | |
e48ed433 | 135 | while ((aCall = (CBDataCall *)stackPop(calls))) |
62e76326 | 136 | delete aCall; |
137 | ||
e48ed433 | 138 | stackDestroy(calls); |
62e76326 | 139 | |
e48ed433 | 140 | #endif |
62e76326 | 141 | |
e48ed433 | 142 | FREE *free_func = cbdata_index[type].free_func; |
62e76326 | 143 | |
e48ed433 | 144 | if (free_func) |
62e76326 | 145 | free_func(&data); |
146 | ||
e48ed433 | 147 | memPoolFree(cbdata_index[type].pool, this); |
148 | } | |
149 | ||
fa80a8ef | 150 | static void |
151 | cbdataInternalInitType(cbdata_type type, const char *name, int size, FREE * free_func) | |
a2172245 | 152 | { |
28c60158 | 153 | char *label; |
62e76326 | 154 | |
28c60158 | 155 | if (type >= cbdata_types) { |
62e76326 | 156 | cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index)); |
157 | memset(&cbdata_index[cbdata_types], 0, | |
158 | (type + 1 - cbdata_types) * sizeof(*cbdata_index)); | |
159 | cbdata_types = type + 1; | |
28c60158 | 160 | } |
62e76326 | 161 | |
72711e31 | 162 | if (cbdata_index[type].pool) |
62e76326 | 163 | return; |
164 | ||
e6ccf245 | 165 | label = (char *)xmalloc(strlen(name) + 20); |
62e76326 | 166 | |
28c60158 | 167 | snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type); |
62e76326 | 168 | |
28c60158 | 169 | assert(OFFSET_OF(cbdata, data) == (sizeof(cbdata) - sizeof(((cbdata *) NULL)->data))); |
62e76326 | 170 | |
72711e31 | 171 | cbdata_index[type].pool = memPoolCreate(label, size + OFFSET_OF(cbdata, data)); |
62e76326 | 172 | |
72711e31 | 173 | cbdata_index[type].free_func = free_func; |
a2172245 | 174 | } |
175 | ||
28c60158 | 176 | cbdata_type |
fa80a8ef | 177 | cbdataInternalAddType(cbdata_type type, const char *name, int size, FREE * free_func) |
a2172245 | 178 | { |
28c60158 | 179 | if (type) |
62e76326 | 180 | return type; |
181 | ||
e6ccf245 | 182 | type = (cbdata_type)cbdata_types; |
62e76326 | 183 | |
fa80a8ef | 184 | cbdataInternalInitType(type, name, size, free_func); |
62e76326 | 185 | |
28c60158 | 186 | return type; |
a2172245 | 187 | } |
188 | ||
a2172245 | 189 | void |
190 | cbdataInit(void) | |
191 | { | |
3015e83a | 192 | debug(45, 3) ("cbdataInit\n"); |
22f3fd98 | 193 | cachemgrRegister("cbdata", |
62e76326 | 194 | "Callback Data Registry Contents", |
195 | cbdataDump, 0, 1); | |
e48ed433 | 196 | #if CBDATA_DEBUG |
62e76326 | 197 | |
198 | cachemgrRegister("cbdatahistory", | |
199 | "Detailed call history for all current cbdata contents", | |
200 | cbdataDumpHistory, 0, 1); | |
e48ed433 | 201 | #endif |
fa80a8ef | 202 | #define CREATE_CBDATA(type) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), NULL) |
203 | #define CREATE_CBDATA_FREE(type, free_func) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), free_func) | |
204 | /* XXX | |
205 | * most of these should be moved out to their respective module. | |
206 | */ | |
28c60158 | 207 | CREATE_CBDATA(ErrorState); |
208 | CREATE_CBDATA(FwdState); | |
209 | CREATE_CBDATA(generic_cbdata); | |
210 | CREATE_CBDATA(helper); | |
211 | CREATE_CBDATA(helper_server); | |
94439e4e | 212 | CREATE_CBDATA(statefulhelper); |
213 | CREATE_CBDATA(helper_stateful_server); | |
72711e31 | 214 | CREATE_CBDATA_FREE(peer, peerDestroy); |
28c60158 | 215 | CREATE_CBDATA(ps_state); |
216 | CREATE_CBDATA(RemovalPolicy); | |
217 | CREATE_CBDATA(RemovalPolicyWalker); | |
218 | CREATE_CBDATA(RemovalPurgeWalker); | |
a2172245 | 219 | } |
220 | ||
28c60158 | 221 | void * |
4e3f712d | 222 | #if CBDATA_DEBUG |
72711e31 | 223 | cbdataInternalAllocDbg(cbdata_type type, const char *file, int line) |
4e3f712d | 224 | #else |
72711e31 | 225 | cbdataInternalAlloc(cbdata_type type) |
4e3f712d | 226 | #endif |
a2172245 | 227 | { |
28c60158 | 228 | cbdata *p; |
229 | assert(type > 0 && type < cbdata_types); | |
e6ccf245 | 230 | p = (cbdata *)memPoolAlloc(cbdata_index[type].pool); |
28c60158 | 231 | p->type = type; |
28c60158 | 232 | p->valid = 1; |
233 | p->locks = 0; | |
fa80a8ef | 234 | p->y = (long) p ^ CBDATA_COOKIE; |
8b9a2d90 | 235 | cbdataCount++; |
fa80a8ef | 236 | #if CBDATA_DEBUG |
62e76326 | 237 | |
e48ed433 | 238 | p->file = file; |
239 | p->line = line; | |
240 | p->calls = stackCreate(); | |
241 | p->addHistory("Alloc", file, line); | |
fa80a8ef | 242 | dlinkAdd(p, &p->link, &cbdataEntries); |
159be25d | 243 | debug(45, 3) ("cbdataAlloc: %p %s:%d\n", &p->data, file, line); |
fa80a8ef | 244 | #endif |
62e76326 | 245 | |
28c60158 | 246 | return (void *) &p->data; |
b8a2718d | 247 | } |
248 | ||
c29316a4 | 249 | void * |
fa80a8ef | 250 | #if CBDATA_DEBUG |
251 | cbdataInternalFreeDbg(void *p, const char *file, int line) | |
252 | #else | |
72711e31 | 253 | cbdataInternalFree(void *p) |
fa80a8ef | 254 | #endif |
a2172245 | 255 | { |
28c60158 | 256 | cbdata *c; |
159be25d | 257 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
fa80a8ef | 258 | #if CBDATA_DEBUG |
62e76326 | 259 | |
fa80a8ef | 260 | debug(45, 3) ("cbdataFree: %p %s:%d\n", p, file, line); |
261 | #else | |
62e76326 | 262 | |
3015e83a | 263 | debug(45, 3) ("cbdataFree: %p\n", p); |
fa80a8ef | 264 | #endif |
62e76326 | 265 | |
fa80a8ef | 266 | CBDATA_CHECK(c); |
3015e83a | 267 | c->valid = 0; |
e48ed433 | 268 | #if CBDATA_DEBUG |
62e76326 | 269 | |
e48ed433 | 270 | c->addHistory("Free", file, line); |
271 | #endif | |
62e76326 | 272 | |
3015e83a | 273 | if (c->locks) { |
62e76326 | 274 | debug(45, 3) ("cbdataFree: %p has %d locks, not freeing\n", |
275 | p, c->locks); | |
276 | return NULL; | |
3015e83a | 277 | } |
62e76326 | 278 | |
28c60158 | 279 | cbdataCount--; |
280 | debug(45, 3) ("cbdataFree: Freeing %p\n", p); | |
fa80a8ef | 281 | #if CBDATA_DEBUG |
62e76326 | 282 | |
fa80a8ef | 283 | dlinkDelete(&c->link, &cbdataEntries); |
284 | #endif | |
62e76326 | 285 | |
e48ed433 | 286 | c->deleteSelf(); |
c29316a4 | 287 | return NULL; |
72711e31 | 288 | } |
289 | ||
a2172245 | 290 | void |
b7e6c377 | 291 | #if CBDATA_DEBUG |
fa80a8ef | 292 | cbdataInternalLockDbg(const void *p, const char *file, int line) |
b7e6c377 | 293 | #else |
fa80a8ef | 294 | cbdataInternalLock(const void *p) |
b7e6c377 | 295 | #endif |
a2172245 | 296 | { |
3015e83a | 297 | cbdata *c; |
62e76326 | 298 | |
3015e83a | 299 | if (p == NULL) |
62e76326 | 300 | return; |
301 | ||
28c60158 | 302 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
62e76326 | 303 | |
b7e6c377 | 304 | #if CBDATA_DEBUG |
62e76326 | 305 | |
fa80a8ef | 306 | debug(45, 3) ("cbdataLock: %p=%d %s:%d\n", p, c ? c->locks + 1 : -1, file, line); |
62e76326 | 307 | |
e48ed433 | 308 | c->addHistory("Reference", file, line); |
62e76326 | 309 | |
fa80a8ef | 310 | #else |
62e76326 | 311 | |
fa80a8ef | 312 | debug(45, 3) ("cbdataLock: %p=%d\n", p, c ? c->locks + 1 : -1); |
62e76326 | 313 | |
b7e6c377 | 314 | #endif |
62e76326 | 315 | |
fa80a8ef | 316 | CBDATA_CHECK(c); |
62e76326 | 317 | |
59e12f93 | 318 | assert(c->locks < 65535); |
62e76326 | 319 | |
fa80a8ef | 320 | c->locks++; |
a2172245 | 321 | } |
322 | ||
323 | void | |
b7e6c377 | 324 | #if CBDATA_DEBUG |
fa80a8ef | 325 | cbdataInternalUnlockDbg(const void *p, const char *file, int line) |
b7e6c377 | 326 | #else |
fa80a8ef | 327 | cbdataInternalUnlock(const void *p) |
b7e6c377 | 328 | #endif |
a2172245 | 329 | { |
3015e83a | 330 | cbdata *c; |
62e76326 | 331 | |
3015e83a | 332 | if (p == NULL) |
62e76326 | 333 | return; |
334 | ||
28c60158 | 335 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
62e76326 | 336 | |
fa80a8ef | 337 | #if CBDATA_DEBUG |
62e76326 | 338 | |
fa80a8ef | 339 | debug(45, 3) ("cbdataUnlock: %p=%d %s:%d\n", p, c ? c->locks - 1 : -1, file, line); |
62e76326 | 340 | |
e48ed433 | 341 | c->addHistory("Dereference", file, line); |
62e76326 | 342 | |
fa80a8ef | 343 | #else |
62e76326 | 344 | |
fa80a8ef | 345 | debug(45, 3) ("cbdataUnlock: %p=%d\n", p, c ? c->locks - 1 : -1); |
62e76326 | 346 | |
fa80a8ef | 347 | #endif |
62e76326 | 348 | |
fa80a8ef | 349 | CBDATA_CHECK(c); |
62e76326 | 350 | |
3015e83a | 351 | assert(c != NULL); |
62e76326 | 352 | |
3015e83a | 353 | assert(c->locks > 0); |
62e76326 | 354 | |
3015e83a | 355 | c->locks--; |
62e76326 | 356 | |
aab9676e | 357 | if (c->valid || c->locks) |
62e76326 | 358 | return; |
359 | ||
28c60158 | 360 | cbdataCount--; |
62e76326 | 361 | |
28c60158 | 362 | debug(45, 3) ("cbdataUnlock: Freeing %p\n", p); |
62e76326 | 363 | |
fa80a8ef | 364 | #if CBDATA_DEBUG |
62e76326 | 365 | |
fa80a8ef | 366 | dlinkDelete(&c->link, &cbdataEntries); |
62e76326 | 367 | |
fa80a8ef | 368 | #endif |
62e76326 | 369 | |
e48ed433 | 370 | c->deleteSelf(); |
a2172245 | 371 | } |
372 | ||
373 | int | |
fa80a8ef | 374 | cbdataReferenceValid(const void *p) |
a2172245 | 375 | { |
3015e83a | 376 | cbdata *c; |
62e76326 | 377 | |
3015e83a | 378 | if (p == NULL) |
62e76326 | 379 | return 1; /* A NULL pointer cannot become invalid */ |
380 | ||
fa80a8ef | 381 | debug(45, 3) ("cbdataReferenceValid: %p\n", p); |
62e76326 | 382 | |
28c60158 | 383 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
62e76326 | 384 | |
fa80a8ef | 385 | CBDATA_CHECK(c); |
62e76326 | 386 | |
3015e83a | 387 | assert(c->locks > 0); |
62e76326 | 388 | |
3015e83a | 389 | return c->valid; |
a2172245 | 390 | } |
391 | ||
fa80a8ef | 392 | int |
393 | #if CBDATA_DEBUG | |
394 | cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line) | |
395 | #else | |
396 | cbdataInternalReferenceDoneValid(void **pp, void **tp) | |
397 | #endif | |
398 | { | |
399 | void *p = (void *) *pp; | |
400 | int valid = cbdataReferenceValid(p); | |
401 | *pp = NULL; | |
402 | #if CBDATA_DEBUG | |
62e76326 | 403 | |
fa80a8ef | 404 | cbdataInternalUnlockDbg(p, file, line); |
405 | #else | |
62e76326 | 406 | |
fa80a8ef | 407 | cbdataInternalUnlock(p); |
408 | #endif | |
62e76326 | 409 | |
fa80a8ef | 410 | if (valid) { |
62e76326 | 411 | *tp = p; |
412 | return 1; | |
fa80a8ef | 413 | } else { |
62e76326 | 414 | *tp = NULL; |
415 | return 0; | |
fa80a8ef | 416 | } |
417 | } | |
418 | ||
e48ed433 | 419 | #if CBDATA_DEBUG |
420 | void | |
421 | _cbdata::dump(StoreEntry *sentry) const | |
422 | { | |
423 | storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' : | |
62e76326 | 424 | '!', &data, type, locks, file, line); |
e48ed433 | 425 | } |
426 | ||
427 | struct CBDataDumper : public unary_function<_cbdata, void> | |
428 | { | |
429 | CBDataDumper(StoreEntry *anEntry):where(anEntry){} | |
62e76326 | 430 | |
431 | void operator()(_cbdata const &x) | |
432 | { | |
433 | x.dump(where); | |
e48ed433 | 434 | } |
62e76326 | 435 | |
e48ed433 | 436 | StoreEntry *where; |
437 | }; | |
62e76326 | 438 | |
e48ed433 | 439 | #endif |
fa80a8ef | 440 | |
db1cd23c | 441 | static void |
3015e83a | 442 | cbdataDump(StoreEntry * sentry) |
a2172245 | 443 | { |
8b9a2d90 | 444 | storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount); |
fa80a8ef | 445 | #if CBDATA_DEBUG |
62e76326 | 446 | |
fa80a8ef | 447 | storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n"); |
e48ed433 | 448 | CBDataDumper dumper(sentry); |
449 | for_each (cbdataEntries, dumper); | |
fa80a8ef | 450 | storeAppendPrintf(sentry, "\n"); |
451 | storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n"); | |
62e76326 | 452 | |
e48ed433 | 453 | for (int i = 1; i < cbdata_types; i++) { |
62e76326 | 454 | MemPool *pool = cbdata_index[i].pool; |
455 | ||
456 | if (pool) { | |
457 | int obj_size = pool->obj_size - OFFSET_OF(cbdata, data); | |
458 | storeAppendPrintf(sentry, "%s\t%d\t%d\t%d\n", pool->label + 7, obj_size, pool->meter.inuse.level, obj_size * pool->meter.inuse.level); | |
459 | } | |
fa80a8ef | 460 | } |
62e76326 | 461 | |
fa80a8ef | 462 | #else |
463 | storeAppendPrintf(sentry, "detailed allocation information only available when compiled with CBDATA_DEBUG\n"); | |
62e76326 | 464 | |
fa80a8ef | 465 | #endif |
62e76326 | 466 | |
fa80a8ef | 467 | storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n"); |
a2172245 | 468 | } |
e48ed433 | 469 | |
470 | #if CBDATA_DEBUG | |
471 | ||
472 | template <class T> | |
473 | T& for_each(Stack const &collection, T& visitor) | |
474 | { | |
732735ed | 475 | for (size_t index = 0; index < collection.count; ++index) |
62e76326 | 476 | visitor(*(typename T::argument_type const *)collection.items[index]); |
477 | ||
e48ed433 | 478 | return visitor; |
479 | }; | |
480 | ||
481 | struct CBDataCallDumper : public unary_function<CBDataCall, void> | |
482 | { | |
483 | CBDataCallDumper (StoreEntry *anEntry):where(anEntry){} | |
62e76326 | 484 | |
485 | void operator()(CBDataCall const &x) | |
486 | { | |
487 | storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line); | |
e48ed433 | 488 | } |
62e76326 | 489 | |
e48ed433 | 490 | StoreEntry *where; |
491 | }; | |
492 | ||
493 | struct CBDataHistoryDumper : public CBDataDumper | |
494 | { | |
495 | CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry){} | |
62e76326 | 496 | |
497 | void operator()(_cbdata const &x) | |
498 | { | |
499 | CBDataDumper::operator()(x); | |
500 | storeAppendPrintf(where, "\n"); | |
501 | storeAppendPrintf(where, "Action\tFile\tLine\n"); | |
502 | for_each (*x.calls,callDumper); | |
503 | storeAppendPrintf(where, "\n"); | |
e48ed433 | 504 | } |
62e76326 | 505 | |
e48ed433 | 506 | StoreEntry *where; |
507 | CBDataCallDumper callDumper; | |
508 | }; | |
509 | ||
510 | void | |
511 | cbdataDumpHistory(StoreEntry *sentry) | |
512 | { | |
513 | storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount); | |
514 | storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n"); | |
515 | CBDataHistoryDumper dumper(sentry); | |
516 | for_each (cbdataEntries, dumper); | |
517 | } | |
62e76326 | 518 | |
e48ed433 | 519 | #endif |