3
* $Id: cbdata.cc,v 1.75 2006/09/03 21:05:20 hno Exp $
5
* DEBUG: section 45 Callback Data Registry
6
* ORIGINAL AUTHOR: Duane Wessels
7
* Modified by Moez Mahfoudh (08/12/2000)
8
* History added by Robert Collins (2002-10-25)
10
* SQUID Web Proxy Cache http://www.squid-cache.org/
11
* ----------------------------------------------------------
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.
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.
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.
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
34
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
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
49
#include "CacheManager.h"
57
#define HASHED_CBDATA 1
60
static int cbdataCount = 0;
62
dlink_list cbdataEntries;
71
CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine){}
80
#define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
84
/* TODO: examine making cbdata templated on this - so we get type
85
* safe access to data - RBC 20030902 */
88
hash_link hash; // Must be first
93
void dump(StoreEntry *)const;
97
void *operator new(size_t size, void *where);
98
void operator delete(void *where, void *where2);
100
MEMPROXY_CLASS(cndata);
109
void addHistory(char const *label, char const *file, int line)
111
if (calls.size() > 1000)
114
calls.push_back(new CBDataCall(label, file, line));
120
Stack<CBDataCall*> calls;
123
/* cookie used while debugging */
125
void check(int line) const {assert(cookie == ((long)this ^ Cookie));}
126
static const long Cookie;
129
size_t dataSize() const { return sizeof(data);}
130
static long MakeOffset();
131
static const long Offset;
132
/* MUST be the last per-instance member */
138
const long cbdata::Cookie((long)0xDEADBEEF);
140
const long cbdata::Offset(MakeOffset());
143
cbdata::operator new(size_t size, void *where)
145
// assert (size == sizeof(cbdata));
150
cbdata::operator delete(void *where, void *where2)
152
/* Only ever invoked when placement new throws
153
* an exception. Used to prevent an incorrect
161
cbdata *zero = (cbdata *)0L;
162
void **dataOffset = &zero->data;
163
return (long)dataOffset;
166
MEMPROXY_CLASS_INLINE(cbdata)
169
static OBJH cbdataDump;
171
static OBJH cbdataDumpHistory;
180
*cbdata_index = NULL;
181
int cbdata_types = 0;
184
static hash_table *cbdata_htable = NULL;
187
cbdata_cmp(const void *p1, const void *p2)
189
return (char *) p1 - (char *) p2;
193
cbdata_hash(const void *p, unsigned int mod)
195
return ((unsigned long) p >> 8) % mod;
205
while ((aCall = calls.pop()))
210
FREE *free_func = cbdata_index[type].free_func;
223
cbdataInternalInitType(cbdata_type type, const char *name, int size, FREE * free_func)
226
assert (type == cbdata_types + 1);
228
cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
229
memset(&cbdata_index[type], 0, sizeof(*cbdata_index));
232
label = (char *)xmalloc(strlen(name) + 20);
234
snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type);
237
assert((size_t)cbdata::Offset == (sizeof(cbdata) - ((cbdata *)NULL)->dataSize()));
238
size += cbdata::Offset;
241
cbdata_index[type].pool = memPoolCreate(label, size);
243
cbdata_index[type].free_func = free_func;
247
cbdata_htable = hash_create(cbdata_cmp, 1 << 12, cbdata_hash);
252
cbdataInternalAddType(cbdata_type type, const char *name, int size, FREE * free_func)
257
type = (cbdata_type)(cbdata_types + 1);
259
cbdataInternalInitType(type, name, size, free_func);
265
cbdataRegisterWithCacheManager(CacheManager & manager)
267
manager.registerAction("cbdata",
268
"Callback Data Registry Contents",
272
manager.registerAction("cbdatahistory",
273
"Detailed call history for all current cbdata contents",
274
cbdataDumpHistory, 0, 1);
280
cbdataInternalAllocDbg(cbdata_type type, const char *file, int line)
282
cbdataInternalAlloc(cbdata_type type)
287
assert(type > 0 && type <= cbdata_types);
288
/* placement new: the pool alloc gives us cbdata + user type memory space
289
* and we init it with cbdata at the start of it
293
p = cbdata_index[type].pool->alloc();
295
hash_join(cbdata_htable, &c->hash);
297
c = new (cbdata_index[type].pool->alloc()) cbdata;
298
p = (void *)&c->data;
304
c->cookie = (long) c ^ cbdata::Cookie;
310
c->calls = Stack<CBDataCall *> ();
311
c->addHistory("Alloc", file, line);
312
dlinkAdd(c, &c->link, &cbdataEntries);
313
debug(45, 3) ("cbdataAlloc: %p %s:%d\n", p, file, line);
321
cbdataInternalFreeDbg(void *p, const char *file, int line)
323
cbdataInternalFree(void *p)
328
c = (cbdata *) hash_lookup(cbdata_htable, p);
330
c = (cbdata *) (((char *) p) - cbdata::Offset);
334
debug(45, 3) ("cbdataFree: %p %s:%d\n", p, file, line);
337
debug(45, 9) ("cbdataFree: %p\n", p);
345
c->addHistory("Free", file, line);
349
debug(45, 9) ("cbdataFree: %p has %d locks, not freeing\n",
355
debug(45, 9) ("cbdataFree: Freeing %p\n", p);
358
dlinkDelete(&c->link, &cbdataEntries);
361
/* This is ugly. But: operator delete doesn't get
362
* the type parameter, so we can't use that
363
* to free the memory.
364
* So, we free it ourselves.
365
* Note that this means a non-placement
366
* new would be a seriously bad idea.
367
* Lastly, if we where a templated class,
368
* we could use the normal delete operator
369
* and it would Just Work. RBC 20030902
371
cbdata_type theType = c->type;
373
hash_remove_link(cbdata_htable, &c->hash);
375
cbdata_index[theType].pool->free((void *)p);
377
c->cbdata::~cbdata();
378
cbdata_index[theType].pool->free(c);
385
cbdataInternalLockDbg(const void *p, const char *file, int line)
387
cbdataInternalLock(const void *p)
396
c = (cbdata *) hash_lookup(cbdata_htable, p);
398
c = (cbdata *) (((char *) p) - cbdata::Offset);
403
debug(45, 3) ("cbdataLock: %p=%d %s:%d\n", p, c ? c->locks + 1 : -1, file, line);
405
c->addHistory("Reference", file, line);
409
debug(45, 9) ("cbdataLock: %p=%d\n", p, c ? c->locks + 1 : -1);
415
assert(c->locks < 65535);
422
cbdataInternalUnlockDbg(const void *p, const char *file, int line)
424
cbdataInternalUnlock(const void *p)
433
c = (cbdata *) hash_lookup(cbdata_htable, p);
435
c = (cbdata *) (((char *) p) - cbdata::Offset);
440
debug(45, 3) ("cbdataUnlock: %p=%d %s:%d\n", p, c ? c->locks - 1 : -1, file, line);
442
c->addHistory("Dereference", file, line);
446
debug(45, 9) ("cbdataUnlock: %p=%d\n", p, c ? c->locks - 1 : -1);
454
assert(c->locks > 0);
458
if (c->valid || c->locks)
463
debug(45, 9) ("cbdataUnlock: Freeing %p\n", p);
467
dlinkDelete(&c->link, &cbdataEntries);
471
/* This is ugly. But: operator delete doesn't get
472
* the type parameter, so we can't use that
473
* to free the memory.
474
* So, we free it ourselves.
475
* Note that this means a non-placement
476
* new would be a seriously bad idea.
477
* Lastly, if we where a templated class,
478
* we could use the normal delete operator
479
* and it would Just Work. RBC 20030902
481
cbdata_type theType = c->type;
483
hash_remove_link(cbdata_htable, &c->hash);
485
cbdata_index[theType].pool->free((void *)p);
487
c->cbdata::~cbdata();
488
cbdata_index[theType].pool->free(c);
493
cbdataReferenceValid(const void *p)
498
return 1; /* A NULL pointer cannot become invalid */
500
debug(45, 9) ("cbdataReferenceValid: %p\n", p);
503
c = (cbdata *) hash_lookup(cbdata_htable, p);
505
c = (cbdata *) (((char *) p) - cbdata::Offset);
510
assert(c->locks > 0);
517
cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line)
519
cbdataInternalReferenceDoneValid(void **pp, void **tp)
522
void *p = (void *) *pp;
523
int valid = cbdataReferenceValid(p);
527
cbdataInternalUnlockDbg(p, file, line);
530
cbdataInternalUnlock(p);
544
cbdata::dump(StoreEntry *sentry) const
547
void *p = (void *)hash.key;
549
void *p = (void *)&data;
551
storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' :
552
'!', p, type, locks, file, line);
555
struct CBDataDumper : public unary_function<cbdata, void>
557
CBDataDumper(StoreEntry *anEntry):where(anEntry){}
559
void operator()(cbdata const &x)
570
cbdataDump(StoreEntry * sentry)
572
storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
575
storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
576
CBDataDumper dumper(sentry);
577
for_each (cbdataEntries, dumper);
578
storeAppendPrintf(sentry, "\n");
579
storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n");
581
for (int i = 1; i < cbdata_types; i++) {
582
MemAllocator *pool = cbdata_index[i].pool;
586
int obj_size = pool->objectSize();
588
int obj_size = pool->objectSize() - cbdata::Offset;
590
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);
595
storeAppendPrintf(sentry, "detailed allocation information only available when compiled with CBDATA_DEBUG\n");
599
storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
602
CBDATA_CLASS_INIT(generic_cbdata);
606
struct CBDataCallDumper : public unary_function<CBDataCall, void>
608
CBDataCallDumper (StoreEntry *anEntry):where(anEntry){}
610
void operator()(CBDataCall const &x)
612
storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line);
618
struct CBDataHistoryDumper : public CBDataDumper
620
CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry){}
622
void operator()(cbdata const &x)
624
CBDataDumper::operator()(x);
625
storeAppendPrintf(where, "\n");
626
storeAppendPrintf(where, "Action\tFile\tLine\n");
627
for_each (x.calls,callDumper);
628
storeAppendPrintf(where, "\n");
632
CBDataCallDumper callDumper;
636
cbdataDumpHistory(StoreEntry *sentry)
638
storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
639
storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
640
CBDataHistoryDumper dumper(sentry);
641
for_each (cbdataEntries, dumper);