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