3 * $Id: cbdata.cc,v 1.77 2008/02/26 21:49:34 amosjeffries 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 \defgroup CBDATAInternal Callback Data Allocator Internals
42 * These routines manage a set of registered callback data pointers.
43 * One of the easiest ways to make Squid coredump is to issue a
44 * callback to for some data structure which has previously been
45 * freed. With these routines, we register (add) callback data
46 * pointers, lock them just before registering the callback function,
47 * validate them before issuing the callback, and then free them
52 #include "CacheManager.h"
60 #define HASHED_CBDATA 1
63 static int cbdataCount
= 0;
65 dlink_list cbdataEntries
;
74 CBDataCall (char const *callLabel
, char const *aFile
, int aLine
) : label(callLabel
), file(aFile
), line(aLine
){}
83 /// \ingroup CBDATAInternal
84 #define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
86 /// \ingroup CBDATAInternal
89 /** \todo examine making cbdata templated on this - so we get type
90 * safe access to data - RBC 20030902 */
93 hash_link hash
; // Must be first
98 void dump(StoreEntry
*)const;
102 void *operator new(size_t size
, void *where
);
103 void operator delete(void *where
, void *where2
);
105 MEMPROXY_CLASS(cndata
);
114 void addHistory(char const *label
, char const *file
, int line
)
116 if (calls
.size() > 1000)
119 calls
.push_back(new CBDataCall(label
, file
, line
));
125 Stack
<CBDataCall
*> calls
;
128 /* cookie used while debugging */
130 void check(int line
) const {assert(cookie
== ((long)this ^ Cookie
));}
131 static const long Cookie
;
134 size_t dataSize() const { return sizeof(data
);}
135 static long MakeOffset();
136 static const long Offset
;
137 /* MUST be the last per-instance member */
143 const long cbdata::Cookie((long)0xDEADBEEF);
145 const long cbdata::Offset(MakeOffset());
148 cbdata::operator new(size_t size
, void *where
)
150 // assert (size == sizeof(cbdata));
155 * Only ever invoked when placement new throws
156 * an exception. Used to prevent an incorrect
160 cbdata::operator delete(void *where
, void *where2
)
168 cbdata
*zero
= (cbdata
*)0L;
169 void **dataOffset
= &zero
->data
;
170 return (long)dataOffset
;
173 MEMPROXY_CLASS_INLINE(cbdata
);
176 static OBJH cbdataDump
;
178 static OBJH cbdataDumpHistory
;
181 /// \ingroup CBDATAInternal
187 *cbdata_index
= NULL
;
189 /// \ingroup CBDATAInternal
190 int cbdata_types
= 0;
193 static hash_table
*cbdata_htable
= NULL
;
196 cbdata_cmp(const void *p1
, const void *p2
)
198 return (char *) p1
- (char *) p2
;
202 cbdata_hash(const void *p
, unsigned int mod
)
204 return ((unsigned long) p
>> 8) % mod
;
214 while ((aCall
= calls
.pop()))
219 FREE
*free_func
= cbdata_index
[type
].free_func
;
232 cbdataInternalInitType(cbdata_type type
, const char *name
, int size
, FREE
* free_func
)
235 assert (type
== cbdata_types
+ 1);
237 cbdata_index
= (CBDataIndex
*)xrealloc(cbdata_index
, (type
+ 1) * sizeof(*cbdata_index
));
238 memset(&cbdata_index
[type
], 0, sizeof(*cbdata_index
));
241 label
= (char *)xmalloc(strlen(name
) + 20);
243 snprintf(label
, strlen(name
) + 20, "cbdata %s (%d)", name
, (int) type
);
246 assert((size_t)cbdata::Offset
== (sizeof(cbdata
) - ((cbdata
*)NULL
)->dataSize()));
247 size
+= cbdata::Offset
;
250 cbdata_index
[type
].pool
= memPoolCreate(label
, size
);
252 cbdata_index
[type
].free_func
= free_func
;
256 cbdata_htable
= hash_create(cbdata_cmp
, 1 << 12, cbdata_hash
);
261 cbdataInternalAddType(cbdata_type type
, const char *name
, int size
, FREE
* free_func
)
266 type
= (cbdata_type
)(cbdata_types
+ 1);
268 cbdataInternalInitType(type
, name
, size
, free_func
);
274 cbdataRegisterWithCacheManager(void)
276 CacheManager
*manager
=CacheManager::GetInstance();
277 manager
->registerAction("cbdata",
278 "Callback Data Registry Contents",
282 manager
->registerAction("cbdatahistory",
283 "Detailed call history for all current cbdata contents",
284 cbdataDumpHistory
, 0, 1);
290 cbdataInternalAllocDbg(cbdata_type type
, const char *file
, int line
)
292 cbdataInternalAlloc(cbdata_type type
)
297 assert(type
> 0 && type
<= cbdata_types
);
298 /* placement new: the pool alloc gives us cbdata + user type memory space
299 * and we init it with cbdata at the start of it
303 p
= cbdata_index
[type
].pool
->alloc();
305 hash_join(cbdata_htable
, &c
->hash
);
307 c
= new (cbdata_index
[type
].pool
->alloc()) cbdata
;
308 p
= (void *)&c
->data
;
314 c
->cookie
= (long) c
^ cbdata::Cookie
;
320 c
->calls
= Stack
<CBDataCall
*> ();
321 c
->addHistory("Alloc", file
, line
);
322 dlinkAdd(c
, &c
->link
, &cbdataEntries
);
323 debugs(45, 3, "cbdataAlloc: " << p
<< " " << file
<< ":" << line
);
331 cbdataInternalFreeDbg(void *p
, const char *file
, int line
)
333 cbdataInternalFree(void *p
)
338 c
= (cbdata
*) hash_lookup(cbdata_htable
, p
);
340 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
344 debugs(45, 3, "cbdataFree: " << p
<< " " << file
<< ":" << line
);
347 debugs(45, 9, "cbdataFree: " << p
);
355 c
->addHistory("Free", file
, line
);
359 debugs(45, 9, "cbdataFree: " << p
<< " has " << c
->locks
<< " locks, not freeing");
364 debugs(45, 9, "cbdataFree: Freeing " << p
);
367 dlinkDelete(&c
->link
, &cbdataEntries
);
370 /* This is ugly. But: operator delete doesn't get
371 * the type parameter, so we can't use that
372 * to free the memory.
373 * So, we free it ourselves.
374 * Note that this means a non-placement
375 * new would be a seriously bad idea.
376 * Lastly, if we where a templated class,
377 * we could use the normal delete operator
378 * and it would Just Work. RBC 20030902
380 cbdata_type theType
= c
->type
;
382 hash_remove_link(cbdata_htable
, &c
->hash
);
384 cbdata_index
[theType
].pool
->free((void *)p
);
386 c
->cbdata::~cbdata();
387 cbdata_index
[theType
].pool
->free(c
);
394 cbdataInternalLockDbg(const void *p
, const char *file
, int line
)
396 cbdataInternalLock(const void *p
)
405 c
= (cbdata
*) hash_lookup(cbdata_htable
, p
);
407 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
412 debugs(45, 3, "cbdataLock: " << p
<< "=" << (c
? c
->locks
+ 1 : -1) << " " << file
<< ":" << line
);
414 c
->addHistory("Reference", file
, line
);
418 debugs(45, 9, "cbdataLock: " << p
<< "=" << (c
? c
->locks
+ 1 : -1));
424 assert(c
->locks
< 65535);
431 cbdataInternalUnlockDbg(const void *p
, const char *file
, int line
)
433 cbdataInternalUnlock(const void *p
)
442 c
= (cbdata
*) hash_lookup(cbdata_htable
, p
);
444 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
449 debugs(45, 3, "cbdataUnlock: " << p
<< "=" << (c
? c
->locks
- 1 : -1) << " " << file
<< ":" << line
);
451 c
->addHistory("Dereference", file
, line
);
455 debugs(45, 9, "cbdataUnlock: " << p
<< "=" << (c
? c
->locks
- 1 : -1));
463 assert(c
->locks
> 0);
467 if (c
->valid
|| c
->locks
)
472 debugs(45, 9, "cbdataUnlock: Freeing " << p
);
476 dlinkDelete(&c
->link
, &cbdataEntries
);
480 /* This is ugly. But: operator delete doesn't get
481 * the type parameter, so we can't use that
482 * to free the memory.
483 * So, we free it ourselves.
484 * Note that this means a non-placement
485 * new would be a seriously bad idea.
486 * Lastly, if we where a templated class,
487 * we could use the normal delete operator
488 * and it would Just Work. RBC 20030902
490 cbdata_type theType
= c
->type
;
492 hash_remove_link(cbdata_htable
, &c
->hash
);
494 cbdata_index
[theType
].pool
->free((void *)p
);
496 c
->cbdata::~cbdata();
497 cbdata_index
[theType
].pool
->free(c
);
502 cbdataReferenceValid(const void *p
)
507 return 1; /* A NULL pointer cannot become invalid */
509 debugs(45, 9, "cbdataReferenceValid: " << p
);
512 c
= (cbdata
*) hash_lookup(cbdata_htable
, p
);
514 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
519 assert(c
->locks
> 0);
526 cbdataInternalReferenceDoneValidDbg(void **pp
, void **tp
, const char *file
, int line
)
528 cbdataInternalReferenceDoneValid(void **pp
, void **tp
)
531 void *p
= (void *) *pp
;
532 int valid
= cbdataReferenceValid(p
);
536 cbdataInternalUnlockDbg(p
, file
, line
);
539 cbdataInternalUnlock(p
);
553 cbdata::dump(StoreEntry
*sentry
) const
556 void *p
= (void *)hash
.key
;
558 void *p
= (void *)&data
;
560 storeAppendPrintf(sentry
, "%c%p\t%d\t%d\t%20s:%-5d\n", valid
? ' ' :
561 '!', p
, type
, locks
, file
, line
);
564 struct CBDataDumper
: public unary_function
<cbdata
, void>
566 CBDataDumper(StoreEntry
*anEntry
):where(anEntry
){}
568 void operator()(cbdata
const &x
)
579 cbdataDump(StoreEntry
* sentry
)
581 storeAppendPrintf(sentry
, "%d cbdata entries\n", cbdataCount
);
584 storeAppendPrintf(sentry
, "Pointer\tType\tLocks\tAllocated by\n");
585 CBDataDumper
dumper(sentry
);
586 for_each (cbdataEntries
, dumper
);
587 storeAppendPrintf(sentry
, "\n");
588 storeAppendPrintf(sentry
, "types\tsize\tallocated\ttotal\n");
590 for (int i
= 1; i
< cbdata_types
; i
++) {
591 MemAllocator
*pool
= cbdata_index
[i
].pool
;
595 int obj_size
= pool
->objectSize();
597 int obj_size
= pool
->objectSize() - cbdata::Offset
;
599 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
);
604 storeAppendPrintf(sentry
, "detailed allocation information only available when compiled with CBDATA_DEBUG\n");
608 storeAppendPrintf(sentry
, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
611 CBDATA_CLASS_INIT(generic_cbdata
);
615 struct CBDataCallDumper
: public unary_function
<CBDataCall
, void>
617 CBDataCallDumper (StoreEntry
*anEntry
):where(anEntry
){}
619 void operator()(CBDataCall
const &x
)
621 storeAppendPrintf(where
, "%s\t%s\t%d\n", x
.label
, x
.file
, x
.line
);
627 struct CBDataHistoryDumper
: public CBDataDumper
629 CBDataHistoryDumper(StoreEntry
*anEntry
):CBDataDumper(anEntry
),where(anEntry
), callDumper(anEntry
){}
631 void operator()(cbdata
const &x
)
633 CBDataDumper::operator()(x
);
634 storeAppendPrintf(where
, "\n");
635 storeAppendPrintf(where
, "Action\tFile\tLine\n");
636 for_each (x
.calls
,callDumper
);
637 storeAppendPrintf(where
, "\n");
641 CBDataCallDumper callDumper
;
645 cbdataDumpHistory(StoreEntry
*sentry
)
647 storeAppendPrintf(sentry
, "%d cbdata entries\n", cbdataCount
);
648 storeAppendPrintf(sentry
, "Pointer\tType\tLocks\tAllocated by\n");
649 CBDataHistoryDumper
dumper(sentry
);
650 for_each (cbdataEntries
, dumper
);