]>
Commit | Line | Data |
---|---|---|
e1ac4723 | 1 | /* |
77b1029d | 2 | * Copyright (C) 1996-2020 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
e1ac4723 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 45 Callback Data Registry */ |
10 | ||
f7f3304a | 11 | #include "squid.h" |
aa839030 | 12 | #include "cbdata.h" |
e2849af8 | 13 | #include "Generic.h" |
f327361e | 14 | #include "mem/Pool.h" |
8822ebee | 15 | #include "mgr/Registration.h" |
e6ccf245 | 16 | #include "Store.h" |
a2172245 | 17 | |
20148bf2 AJ |
18 | #include <climits> |
19 | ||
85b067e3 | 20 | #if USE_CBDATA_DEBUG |
85b067e3 | 21 | #include <algorithm> |
074d6a40 | 22 | #include <vector> |
85b067e3 | 23 | #endif |
582c2af2 | 24 | |
b4bab919 | 25 | #if WITH_VALGRIND |
fa4f735f | 26 | #include <map> |
b4bab919 | 27 | #endif |
28 | ||
20148bf2 | 29 | static int cbdataCount = 0; |
9a0a18de | 30 | #if USE_CBDATA_DEBUG |
fa80a8ef | 31 | dlink_list cbdataEntries; |
32 | #endif | |
8b9a2d90 | 33 | |
9a0a18de | 34 | #if USE_CBDATA_DEBUG |
62e76326 | 35 | |
36 | class CBDataCall | |
37 | { | |
38 | ||
e48ed433 | 39 | public: |
26ac0430 | 40 | CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine) {} |
62e76326 | 41 | |
e48ed433 | 42 | char const *label; |
43 | char const *file; | |
44 | int line; | |
45 | }; | |
62e76326 | 46 | |
e48ed433 | 47 | #endif |
48 | ||
43ae1d95 | 49 | #define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER)) |
50 | ||
ffc6d4e9 AJ |
51 | /** |
52 | * Manage a set of registered callback data pointers. | |
53 | * One of the easiest ways to make Squid coredump is to issue a | |
54 | * callback to for some data structure which has previously been | |
55 | * freed. With this class, we register (add) callback data | |
56 | * pointers, lock them just before registering the callback function, | |
57 | * validate them before issuing the callback, and then free them | |
58 | * when finished. | |
59 | */ | |
20148bf2 | 60 | class cbdata |
62e76326 | 61 | { |
fa4f735f | 62 | #if !WITH_VALGRIND |
741c2986 | 63 | public: |
ced8def3 AJ |
64 | void *operator new(size_t, void *where) {return where;} |
65 | /** | |
66 | * Only ever invoked when placement new throws | |
67 | * an exception. Used to prevent an incorrect free. | |
68 | */ | |
69 | void operator delete(void *, void *) {} | |
741c2986 | 70 | #else |
c16af8fd | 71 | MEMPROXY_CLASS(cbdata); |
741c2986 AJ |
72 | #endif |
73 | ||
63be0a78 | 74 | /** \todo examine making cbdata templated on this - so we get type |
b4bab919 | 75 | * safe access to data - RBC 20030902 */ |
43ae1d95 | 76 | public: |
9a0a18de | 77 | #if USE_CBDATA_DEBUG |
43ae1d95 | 78 | |
e48ed433 | 79 | void dump(StoreEntry *)const; |
80 | #endif | |
fa4f735f AJ |
81 | cbdata() : |
82 | valid(0), | |
20148bf2 | 83 | locks(0), |
cc8c4af2 | 84 | type(CBDATA_UNKNOWN), |
fa4f735f AJ |
85 | #if USE_CBDATA_DEBUG |
86 | file(NULL), | |
87 | line(0), | |
88 | #endif | |
f1d03995 AJ |
89 | cookie(0), |
90 | data(NULL) | |
fa4f735f | 91 | {} |
f668fcda | 92 | ~cbdata(); |
fa4f735f | 93 | |
3015e83a | 94 | int valid; |
20148bf2 | 95 | int32_t locks; |
f668fcda | 96 | cbdata_type type; |
9a0a18de | 97 | #if USE_CBDATA_DEBUG |
62e76326 | 98 | |
7ddfeb80 | 99 | void addHistory(char const *label, char const *aFile, int aLine) { |
91caca83 | 100 | if (calls.size() > 1000) |
62e76326 | 101 | return; |
102 | ||
85b067e3 | 103 | calls.push_back(new CBDataCall(label, aFile, aLine)); |
62e76326 | 104 | } |
105 | ||
fa80a8ef | 106 | dlink_node link; |
4e3f712d | 107 | const char *file; |
108 | int line; | |
85b067e3 | 109 | std::vector<CBDataCall*> calls; // used as a stack with random access operator |
4e3f712d | 110 | #endif |
62e76326 | 111 | |
43ae1d95 | 112 | /* cookie used while debugging */ |
113 | long cookie; | |
ced8def3 | 114 | void check(int) const {assert(cookie == ((long)this ^ Cookie));} |
b4bab919 | 115 | static const long Cookie; |
43ae1d95 | 116 | |
fa4f735f | 117 | #if !WITH_VALGRIND |
43ae1d95 | 118 | size_t dataSize() const { return sizeof(data);} |
43ae1d95 | 119 | static long MakeOffset(); |
120 | static const long Offset; | |
fa4f735f | 121 | #endif |
b4bab919 | 122 | /* MUST be the last per-instance member */ |
123 | void *data; | |
43ae1d95 | 124 | }; |
125 | ||
bf5113eb | 126 | const long cbdata::Cookie((long)0xDEADBEEF); |
fa4f735f | 127 | #if !WITH_VALGRIND |
43ae1d95 | 128 | const long cbdata::Offset(MakeOffset()); |
129 | ||
130 | long | |
131 | cbdata::MakeOffset() | |
132 | { | |
133 | cbdata *zero = (cbdata *)0L; | |
134 | void **dataOffset = &zero->data; | |
135 | return (long)dataOffset; | |
136 | } | |
b4bab919 | 137 | #endif |
a2172245 | 138 | |
db1cd23c | 139 | static OBJH cbdataDump; |
9a0a18de | 140 | #if USE_CBDATA_DEBUG |
e48ed433 | 141 | static OBJH cbdataDumpHistory; |
142 | #endif | |
a2172245 | 143 | |
26ac0430 | 144 | struct CBDataIndex { |
b4bab919 | 145 | MemAllocator *pool; |
62e76326 | 146 | } |
62e76326 | 147 | *cbdata_index = NULL; |
63be0a78 | 148 | |
28c60158 | 149 | int cbdata_types = 0; |
150 | ||
fa4f735f AJ |
151 | #if WITH_VALGRIND |
152 | static std::map<const void *, cbdata *> cbdata_htable; | |
b4bab919 | 153 | #endif |
154 | ||
f668fcda | 155 | cbdata::~cbdata() |
e48ed433 | 156 | { |
9a0a18de | 157 | #if USE_CBDATA_DEBUG |
62e76326 | 158 | |
3aa53107 | 159 | while (!calls.empty()) { |
85b067e3 FC |
160 | delete calls.back(); |
161 | calls.pop_back(); | |
3aa53107 | 162 | } |
62e76326 | 163 | |
e48ed433 | 164 | #endif |
e57b674b AR |
165 | |
166 | #if WITH_VALGRIND | |
167 | void *p = data; | |
168 | #else | |
169 | void *p = this; | |
170 | #endif | |
171 | cbdata_index[type].pool->freeOne(p); | |
e48ed433 | 172 | } |
173 | ||
fa80a8ef | 174 | static void |
5545e2f4 | 175 | cbdataInternalInitType(cbdata_type type, const char *name, int size) |
a2172245 | 176 | { |
28c60158 | 177 | char *label; |
aa839030 | 178 | assert (type == cbdata_types + 1); |
62e76326 | 179 | |
aa839030 | 180 | cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index)); |
181 | memset(&cbdata_index[type], 0, sizeof(*cbdata_index)); | |
182 | cbdata_types = type; | |
62e76326 | 183 | |
e6ccf245 | 184 | label = (char *)xmalloc(strlen(name) + 20); |
62e76326 | 185 | |
28c60158 | 186 | snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type); |
62e76326 | 187 | |
fa4f735f | 188 | #if !WITH_VALGRIND |
43ae1d95 | 189 | assert((size_t)cbdata::Offset == (sizeof(cbdata) - ((cbdata *)NULL)->dataSize())); |
b4bab919 | 190 | size += cbdata::Offset; |
191 | #endif | |
62e76326 | 192 | |
04eb0689 | 193 | cbdata_index[type].pool = memPoolCreate(label, size); |
a2172245 | 194 | } |
195 | ||
28c60158 | 196 | cbdata_type |
5545e2f4 | 197 | cbdataInternalAddType(cbdata_type type, const char *name, int size) |
a2172245 | 198 | { |
28c60158 | 199 | if (type) |
62e76326 | 200 | return type; |
201 | ||
aa839030 | 202 | type = (cbdata_type)(cbdata_types + 1); |
62e76326 | 203 | |
5545e2f4 | 204 | cbdataInternalInitType(type, name, size); |
62e76326 | 205 | |
28c60158 | 206 | return type; |
a2172245 | 207 | } |
208 | ||
62ee09ca | 209 | void |
dbeed9ea | 210 | cbdataRegisterWithCacheManager(void) |
62ee09ca | 211 | { |
8822ebee | 212 | Mgr::RegisterAction("cbdata", |
d9fc6862 A |
213 | "Callback Data Registry Contents", |
214 | cbdataDump, 0, 1); | |
9a0a18de | 215 | #if USE_CBDATA_DEBUG |
62ee09ca | 216 | |
8822ebee | 217 | Mgr::RegisterAction("cbdatahistory", |
d9fc6862 A |
218 | "Detailed call history for all current cbdata contents", |
219 | cbdataDumpHistory, 0, 1); | |
62ee09ca | 220 | #endif |
221 | } | |
222 | ||
28c60158 | 223 | void * |
5c2f68b7 | 224 | cbdataInternalAlloc(cbdata_type type, const char *file, int line) |
a2172245 | 225 | { |
b4bab919 | 226 | cbdata *c; |
227 | void *p; | |
aa839030 | 228 | assert(type > 0 && type <= cbdata_types); |
229 | /* placement new: the pool alloc gives us cbdata + user type memory space | |
230 | * and we init it with cbdata at the start of it | |
231 | */ | |
fa4f735f | 232 | #if WITH_VALGRIND |
b4bab919 | 233 | c = new cbdata; |
234 | p = cbdata_index[type].pool->alloc(); | |
fa4f735f AJ |
235 | c->data = p; |
236 | cbdata_htable.emplace(p,c); | |
b4bab919 | 237 | #else |
238 | c = new (cbdata_index[type].pool->alloc()) cbdata; | |
239 | p = (void *)&c->data; | |
240 | #endif | |
f668fcda | 241 | |
b4bab919 | 242 | c->type = type; |
243 | c->valid = 1; | |
20148bf2 | 244 | c->locks = 0; |
b4bab919 | 245 | c->cookie = (long) c ^ cbdata::Cookie; |
d7ae3534 | 246 | ++cbdataCount; |
9a0a18de | 247 | #if USE_CBDATA_DEBUG |
62e76326 | 248 | |
b4bab919 | 249 | c->file = file; |
250 | c->line = line; | |
85b067e3 | 251 | c->calls = std::vector<CBDataCall *> (); |
b4bab919 | 252 | c->addHistory("Alloc", file, line); |
253 | dlinkAdd(c, &c->link, &cbdataEntries); | |
0796f998 | 254 | debugs(45, 3, "Allocating " << p << " " << file << ":" << line); |
c4eab974 | 255 | #else |
0796f998 | 256 | debugs(45, 9, "Allocating " << p); |
fa80a8ef | 257 | #endif |
62e76326 | 258 | |
b4bab919 | 259 | return p; |
b8a2718d | 260 | } |
261 | ||
50e2f4c4 AJ |
262 | void |
263 | cbdataRealFree(cbdata *c, const char *file, const int line) | |
a2172245 | 264 | { |
e57b674b AR |
265 | #if WITH_VALGRIND |
266 | void *p = c->data; | |
267 | #else | |
268 | void *p = (void *)&c->data; | |
269 | #endif | |
62e76326 | 270 | |
5e263176 | 271 | --cbdataCount; |
9a0a18de | 272 | #if USE_CBDATA_DEBUG |
e57b674b | 273 | debugs(45, 3, "Freeing " << p << ' ' << file << ':' << line); |
fa80a8ef | 274 | dlinkDelete(&c->link, &cbdataEntries); |
e57b674b AR |
275 | #else |
276 | debugs(45, 9, "Freeing " << p); | |
fa80a8ef | 277 | #endif |
62e76326 | 278 | |
fa4f735f | 279 | #if WITH_VALGRIND |
e57b674b | 280 | cbdata_htable.erase(p); |
08650a87 AJ |
281 | delete c; |
282 | #else | |
f668fcda | 283 | /* This is ugly. But: operator delete doesn't get |
26ac0430 | 284 | * the type parameter, so we can't use that |
f668fcda | 285 | * to free the memory. |
286 | * So, we free it ourselves. | |
26ac0430 | 287 | * Note that this means a non-placement |
f668fcda | 288 | * new would be a seriously bad idea. |
289 | * Lastly, if we where a templated class, | |
290 | * we could use the normal delete operator | |
291 | * and it would Just Work. RBC 20030902 | |
292 | */ | |
b4bab919 | 293 | c->cbdata::~cbdata(); |
08650a87 | 294 | #endif |
50e2f4c4 AJ |
295 | } |
296 | ||
297 | void * | |
298 | cbdataInternalFree(void *p, const char *file, int line) | |
299 | { | |
300 | cbdata *c; | |
fa4f735f AJ |
301 | #if WITH_VALGRIND |
302 | c = cbdata_htable.at(p); | |
50e2f4c4 AJ |
303 | #else |
304 | c = (cbdata *) (((char *) p) - cbdata::Offset); | |
305 | #endif | |
306 | #if USE_CBDATA_DEBUG | |
307 | debugs(45, 3, p << " " << file << ":" << line); | |
308 | #else | |
309 | debugs(45, 9, p); | |
310 | #endif | |
311 | ||
312 | c->check(__LINE__); | |
313 | assert(c->valid); | |
314 | c->valid = 0; | |
315 | #if USE_CBDATA_DEBUG | |
316 | ||
317 | c->addHistory("Free", file, line); | |
318 | #endif | |
319 | ||
20148bf2 AJ |
320 | if (c->locks) { |
321 | debugs(45, 9, p << " has " << c->locks << " locks, not freeing"); | |
50e2f4c4 AJ |
322 | return NULL; |
323 | } | |
324 | ||
325 | cbdataRealFree(c, file, line); | |
c29316a4 | 326 | return NULL; |
72711e31 | 327 | } |
328 | ||
a2172245 | 329 | void |
9a0a18de | 330 | #if USE_CBDATA_DEBUG |
fa80a8ef | 331 | cbdataInternalLockDbg(const void *p, const char *file, int line) |
b7e6c377 | 332 | #else |
fa80a8ef | 333 | cbdataInternalLock(const void *p) |
b7e6c377 | 334 | #endif |
a2172245 | 335 | { |
3015e83a | 336 | cbdata *c; |
62e76326 | 337 | |
3015e83a | 338 | if (p == NULL) |
62e76326 | 339 | return; |
340 | ||
fa4f735f AJ |
341 | #if WITH_VALGRIND |
342 | c = cbdata_htable.at(p); | |
b4bab919 | 343 | #else |
43ae1d95 | 344 | c = (cbdata *) (((char *) p) - cbdata::Offset); |
b4bab919 | 345 | #endif |
62e76326 | 346 | |
9a0a18de | 347 | #if USE_CBDATA_DEBUG |
20148bf2 | 348 | debugs(45, 3, p << "=" << (c ? c->locks + 1 : -1) << " " << file << ":" << line); |
e48ed433 | 349 | c->addHistory("Reference", file, line); |
fa80a8ef | 350 | #else |
20148bf2 | 351 | debugs(45, 9, p << "=" << (c ? c->locks + 1 : -1)); |
b7e6c377 | 352 | #endif |
62e76326 | 353 | |
06a5ae20 | 354 | c->check(__LINE__); |
62e76326 | 355 | |
20148bf2 AJ |
356 | assert(c->locks < INT_MAX); |
357 | ||
358 | ++ c->locks; | |
a2172245 | 359 | } |
360 | ||
361 | void | |
9a0a18de | 362 | #if USE_CBDATA_DEBUG |
fa80a8ef | 363 | cbdataInternalUnlockDbg(const void *p, const char *file, int line) |
b7e6c377 | 364 | #else |
fa80a8ef | 365 | cbdataInternalUnlock(const void *p) |
b7e6c377 | 366 | #endif |
a2172245 | 367 | { |
3015e83a | 368 | cbdata *c; |
62e76326 | 369 | |
3015e83a | 370 | if (p == NULL) |
62e76326 | 371 | return; |
372 | ||
fa4f735f AJ |
373 | #if WITH_VALGRIND |
374 | c = cbdata_htable.at(p); | |
b4bab919 | 375 | #else |
43ae1d95 | 376 | c = (cbdata *) (((char *) p) - cbdata::Offset); |
b4bab919 | 377 | #endif |
62e76326 | 378 | |
9a0a18de | 379 | #if USE_CBDATA_DEBUG |
20148bf2 | 380 | debugs(45, 3, p << "=" << (c ? c->locks - 1 : -1) << " " << file << ":" << line); |
e48ed433 | 381 | c->addHistory("Dereference", file, line); |
fa80a8ef | 382 | #else |
20148bf2 | 383 | debugs(45, 9, p << "=" << (c ? c->locks - 1 : -1)); |
fa80a8ef | 384 | #endif |
62e76326 | 385 | |
06a5ae20 | 386 | c->check(__LINE__); |
62e76326 | 387 | |
3015e83a | 388 | assert(c != NULL); |
62e76326 | 389 | |
20148bf2 AJ |
390 | assert(c->locks > 0); |
391 | ||
392 | -- c->locks; | |
62e76326 | 393 | |
20148bf2 | 394 | if (c->locks) |
62e76326 | 395 | return; |
396 | ||
bc40792d AJ |
397 | if (c->valid) { |
398 | #if USE_CBDATA_DEBUG | |
cfd798c9 | 399 | debugs(45, 3, "CBDATA valid with no references ... cbdata=" << p << " " << file << ":" << line); |
bc40792d AJ |
400 | #endif |
401 | return; | |
402 | } | |
403 | ||
fbbbe714 | 404 | #if USE_CBDATA_DEBUG |
50e2f4c4 | 405 | cbdataRealFree(c, file, line); |
fbbbe714 AJ |
406 | #else |
407 | cbdataRealFree(c, NULL, 0); | |
408 | #endif | |
a2172245 | 409 | } |
410 | ||
411 | int | |
fa80a8ef | 412 | cbdataReferenceValid(const void *p) |
a2172245 | 413 | { |
3015e83a | 414 | cbdata *c; |
62e76326 | 415 | |
3015e83a | 416 | if (p == NULL) |
f53969cc | 417 | return 1; /* A NULL pointer cannot become invalid */ |
62e76326 | 418 | |
0796f998 | 419 | debugs(45, 9, p); |
62e76326 | 420 | |
fa4f735f AJ |
421 | #if WITH_VALGRIND |
422 | c = cbdata_htable.at(p); | |
b4bab919 | 423 | #else |
43ae1d95 | 424 | c = (cbdata *) (((char *) p) - cbdata::Offset); |
b4bab919 | 425 | #endif |
62e76326 | 426 | |
06a5ae20 | 427 | c->check(__LINE__); |
62e76326 | 428 | |
20148bf2 | 429 | assert(c->locks > 0); |
62e76326 | 430 | |
3015e83a | 431 | return c->valid; |
a2172245 | 432 | } |
433 | ||
fa80a8ef | 434 | int |
9a0a18de | 435 | #if USE_CBDATA_DEBUG |
fa80a8ef | 436 | cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line) |
437 | #else | |
438 | cbdataInternalReferenceDoneValid(void **pp, void **tp) | |
439 | #endif | |
440 | { | |
441 | void *p = (void *) *pp; | |
442 | int valid = cbdataReferenceValid(p); | |
443 | *pp = NULL; | |
9a0a18de | 444 | #if USE_CBDATA_DEBUG |
62e76326 | 445 | |
fa80a8ef | 446 | cbdataInternalUnlockDbg(p, file, line); |
447 | #else | |
62e76326 | 448 | |
fa80a8ef | 449 | cbdataInternalUnlock(p); |
450 | #endif | |
62e76326 | 451 | |
fa80a8ef | 452 | if (valid) { |
62e76326 | 453 | *tp = p; |
454 | return 1; | |
fa80a8ef | 455 | } else { |
62e76326 | 456 | *tp = NULL; |
457 | return 0; | |
fa80a8ef | 458 | } |
459 | } | |
460 | ||
9a0a18de | 461 | #if USE_CBDATA_DEBUG |
e48ed433 | 462 | void |
43ae1d95 | 463 | cbdata::dump(StoreEntry *sentry) const |
e48ed433 | 464 | { |
fa4f735f AJ |
465 | #if WITH_VALGRIND |
466 | void *p = data; | |
b4bab919 | 467 | #else |
c692f6b0 | 468 | void *p = (void *)&data; |
b4bab919 | 469 | #endif |
20148bf2 AJ |
470 | storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' : |
471 | '!', p, type, locks, file, line); | |
e48ed433 | 472 | } |
473 | ||
26ac0430 AJ |
474 | struct CBDataDumper : public unary_function<cbdata, void> { |
475 | CBDataDumper(StoreEntry *anEntry):where(anEntry) {} | |
62e76326 | 476 | |
26ac0430 | 477 | void operator()(cbdata const &x) { |
62e76326 | 478 | x.dump(where); |
e48ed433 | 479 | } |
62e76326 | 480 | |
e48ed433 | 481 | StoreEntry *where; |
482 | }; | |
62e76326 | 483 | |
e48ed433 | 484 | #endif |
fa80a8ef | 485 | |
db1cd23c | 486 | static void |
3015e83a | 487 | cbdataDump(StoreEntry * sentry) |
a2172245 | 488 | { |
20148bf2 | 489 | storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount); |
9a0a18de | 490 | #if USE_CBDATA_DEBUG |
62e76326 | 491 | |
fa80a8ef | 492 | storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n"); |
e48ed433 | 493 | CBDataDumper dumper(sentry); |
494 | for_each (cbdataEntries, dumper); | |
fa80a8ef | 495 | storeAppendPrintf(sentry, "\n"); |
496 | storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n"); | |
62e76326 | 497 | |
d7ae3534 | 498 | for (int i = 1; i < cbdata_types; ++i) { |
b4bab919 | 499 | MemAllocator *pool = cbdata_index[i].pool; |
62e76326 | 500 | |
501 | if (pool) { | |
fa4f735f | 502 | #if WITH_VALGRIND |
b4bab919 | 503 | int obj_size = pool->objectSize(); |
504 | #else | |
b001e822 | 505 | int obj_size = pool->objectSize() - cbdata::Offset; |
b4bab919 | 506 | #endif |
9663db1c | 507 | storeAppendPrintf(sentry, "%s\t%d\t%ld\t%ld\n", pool->objectType() + 7, obj_size, (long int)pool->getMeter().inuse.currentLevel(), (long int)obj_size * pool->getMeter().inuse.currentLevel()); |
62e76326 | 508 | } |
fa80a8ef | 509 | } |
62e76326 | 510 | |
fa80a8ef | 511 | #else |
9a0a18de | 512 | storeAppendPrintf(sentry, "detailed allocation information only available when compiled with --enable-debug-cbdata\n"); |
62e76326 | 513 | |
fa80a8ef | 514 | #endif |
62e76326 | 515 | |
fa80a8ef | 516 | storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n"); |
a2172245 | 517 | } |
e48ed433 | 518 | |
fd9c47d1 AR |
519 | CallbackData & |
520 | CallbackData::operator =(const CallbackData &other) | |
521 | { | |
522 | if (data_ != other.data_) { // assignment to self and no-op assignments | |
523 | auto old = data_; | |
524 | data_ = cbdataReference(other.data_); | |
525 | cbdataReferenceDone(old); | |
526 | } | |
527 | return *this; | |
528 | } | |
529 | ||
aa839030 | 530 | CBDATA_CLASS_INIT(generic_cbdata); |
531 | ||
9a0a18de | 532 | #if USE_CBDATA_DEBUG |
e48ed433 | 533 | |
26ac0430 AJ |
534 | struct CBDataCallDumper : public unary_function<CBDataCall, void> { |
535 | CBDataCallDumper (StoreEntry *anEntry):where(anEntry) {} | |
62e76326 | 536 | |
85b067e3 FC |
537 | void operator()(CBDataCall * const &x) { |
538 | storeAppendPrintf(where, "%s\t%s\t%d\n", x->label, x->file, x->line); | |
e48ed433 | 539 | } |
62e76326 | 540 | |
e48ed433 | 541 | StoreEntry *where; |
542 | }; | |
543 | ||
26ac0430 AJ |
544 | struct CBDataHistoryDumper : public CBDataDumper { |
545 | CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry) {} | |
62e76326 | 546 | |
26ac0430 | 547 | void operator()(cbdata const &x) { |
62e76326 | 548 | CBDataDumper::operator()(x); |
549 | storeAppendPrintf(where, "\n"); | |
550 | storeAppendPrintf(where, "Action\tFile\tLine\n"); | |
85b067e3 | 551 | std::for_each (x.calls.begin(), x.calls.end(), callDumper); |
62e76326 | 552 | storeAppendPrintf(where, "\n"); |
e48ed433 | 553 | } |
62e76326 | 554 | |
e48ed433 | 555 | StoreEntry *where; |
556 | CBDataCallDumper callDumper; | |
557 | }; | |
558 | ||
559 | void | |
560 | cbdataDumpHistory(StoreEntry *sentry) | |
561 | { | |
20148bf2 | 562 | storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount); |
e48ed433 | 563 | storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n"); |
564 | CBDataHistoryDumper dumper(sentry); | |
565 | for_each (cbdataEntries, dumper); | |
566 | } | |
62e76326 | 567 | |
e48ed433 | 568 | #endif |
f53969cc | 569 |