]> git.ipfire.org Git - thirdparty/squid.git/blame - src/cbdata.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / cbdata.cc
CommitLineData
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 29static int cbdataCount = 0;
9a0a18de 30#if USE_CBDATA_DEBUG
fa80a8ef 31dlink_list cbdataEntries;
32#endif
8b9a2d90 33
9a0a18de 34#if USE_CBDATA_DEBUG
62e76326 35
36class CBDataCall
37{
38
e48ed433 39public:
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 60class cbdata
62e76326 61{
fa4f735f 62#if !WITH_VALGRIND
741c2986 63public:
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 76public:
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 126const long cbdata::Cookie((long)0xDEADBEEF);
fa4f735f 127#if !WITH_VALGRIND
43ae1d95 128const long cbdata::Offset(MakeOffset());
129
130long
131cbdata::MakeOffset()
132{
133 cbdata *zero = (cbdata *)0L;
134 void **dataOffset = &zero->data;
135 return (long)dataOffset;
136}
b4bab919 137#endif
a2172245 138
db1cd23c 139static OBJH cbdataDump;
9a0a18de 140#if USE_CBDATA_DEBUG
e48ed433 141static OBJH cbdataDumpHistory;
142#endif
a2172245 143
26ac0430 144struct CBDataIndex {
b4bab919 145 MemAllocator *pool;
62e76326 146}
62e76326 147*cbdata_index = NULL;
63be0a78 148
28c60158 149int cbdata_types = 0;
150
fa4f735f
AJ
151#if WITH_VALGRIND
152static std::map<const void *, cbdata *> cbdata_htable;
b4bab919 153#endif
154
f668fcda 155cbdata::~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 174static void
5545e2f4 175cbdataInternalInitType(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 196cbdata_type
5545e2f4 197cbdataInternalAddType(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 209void
dbeed9ea 210cbdataRegisterWithCacheManager(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 223void *
5c2f68b7 224cbdataInternalAlloc(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
262void
263cbdataRealFree(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
297void *
298cbdataInternalFree(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 329void
9a0a18de 330#if USE_CBDATA_DEBUG
fa80a8ef 331cbdataInternalLockDbg(const void *p, const char *file, int line)
b7e6c377 332#else
fa80a8ef 333cbdataInternalLock(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
361void
9a0a18de 362#if USE_CBDATA_DEBUG
fa80a8ef 363cbdataInternalUnlockDbg(const void *p, const char *file, int line)
b7e6c377 364#else
fa80a8ef 365cbdataInternalUnlock(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
411int
fa80a8ef 412cbdataReferenceValid(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 434int
9a0a18de 435#if USE_CBDATA_DEBUG
fa80a8ef 436cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line)
437#else
438cbdataInternalReferenceDoneValid(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 462void
43ae1d95 463cbdata::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
474struct 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 486static void
3015e83a 487cbdataDump(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
519CallbackData &
520CallbackData::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 530CBDATA_CLASS_INIT(generic_cbdata);
531
9a0a18de 532#if USE_CBDATA_DEBUG
e48ed433 533
26ac0430
AJ
534struct 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
544struct 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
559void
560cbdataDumpHistory(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