3 * $Id: cbdata.cc,v 1.60 2003/09/01 23:41:17 robertc 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
55 static int cbdataCount
= 0;
57 dlink_list cbdataEntries
;
66 CBDataCall (char const *callLabel
, char const *aFile
, int aLine
) : label(callLabel
), file(aFile
), line(aLine
){}
75 #define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
83 void dump(StoreEntry
*)const;
86 void *operator new(size_t size
, void *where
);
87 void operator delete(void *where
, void *where
);
95 void addHistory(char const *label
, char const *file
, int line
)
97 if (calls
.size() > 1000)
100 calls
.push_back(new CBDataCall(label
, file
, line
));
106 Stack
<CBDataCall
*> calls
;
109 /* cookie used while debugging */
111 /* MUST be the last per-instance member */
112 /* TODO: examine making cbdata templated on this - so we get type
113 * safe access to data - RBC 20030902 */
115 void check() const { assert(cookie
== ((long)this ^ Cookie
));}
117 size_t dataSize() const { return sizeof(data
);}
119 static const long Cookie
;
120 static long MakeOffset();
121 static const long Offset
;
124 const long cbdata::Cookie((long)0xDEADBEEF);
125 const long cbdata::Offset(MakeOffset());
128 cbdata::operator new(size_t size
, void *where
)
130 // assert (size == sizeof(cbdata));
135 cbdata::operator delete(void *where
, void *where2
)
137 /* Only ever invoked when placement new throws
138 * an exception. Used to prevent an incorrect
146 cbdata
*zero
= (cbdata
*)0L;
147 void **dataOffset
= &zero
->data
;
148 return (long)dataOffset
;
151 static OBJH cbdataDump
;
153 static OBJH cbdataDumpHistory
;
162 *cbdata_index
= NULL
;
163 int cbdata_types
= 0;
170 while ((aCall
= calls
.pop()))
175 FREE
*free_func
= cbdata_index
[type
].free_func
;
182 cbdataInternalInitType(cbdata_type type
, const char *name
, int size
, FREE
* free_func
)
186 if (type
>= cbdata_types
) {
187 cbdata_index
= (CBDataIndex
*)xrealloc(cbdata_index
, (type
+ 1) * sizeof(*cbdata_index
));
188 memset(&cbdata_index
[cbdata_types
], 0,
189 (type
+ 1 - cbdata_types
) * sizeof(*cbdata_index
));
190 cbdata_types
= type
+ 1;
193 if (cbdata_index
[type
].pool
)
196 label
= (char *)xmalloc(strlen(name
) + 20);
198 snprintf(label
, strlen(name
) + 20, "cbdata %s (%d)", name
, (int) type
);
200 assert((size_t)cbdata::Offset
== (sizeof(cbdata
) - ((cbdata
*)NULL
)->dataSize()));
202 cbdata_index
[type
].pool
= memPoolCreate(label
, size
+ cbdata::Offset
);
204 cbdata_index
[type
].free_func
= free_func
;
208 cbdataInternalAddType(cbdata_type type
, const char *name
, int size
, FREE
* free_func
)
213 type
= (cbdata_type
)cbdata_types
;
215 cbdataInternalInitType(type
, name
, size
, free_func
);
223 debug(45, 3) ("cbdataInit\n");
224 cachemgrRegister("cbdata",
225 "Callback Data Registry Contents",
229 cachemgrRegister("cbdatahistory",
230 "Detailed call history for all current cbdata contents",
231 cbdataDumpHistory
, 0, 1);
233 #define CREATE_CBDATA(type) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), NULL)
234 #define CREATE_CBDATA_FREE(type, free_func) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), free_func)
236 * most of these should be moved out to their respective module.
238 CREATE_CBDATA(ErrorState
);
239 CREATE_CBDATA(FwdState
);
240 CREATE_CBDATA(generic_cbdata
);
241 CREATE_CBDATA(helper
);
242 CREATE_CBDATA(helper_server
);
243 CREATE_CBDATA(statefulhelper
);
244 CREATE_CBDATA(helper_stateful_server
);
245 CREATE_CBDATA_FREE(peer
, peerDestroy
);
246 CREATE_CBDATA(ps_state
);
247 CREATE_CBDATA(RemovalPolicy
);
248 CREATE_CBDATA(RemovalPolicyWalker
);
249 CREATE_CBDATA(RemovalPurgeWalker
);
254 cbdataInternalAllocDbg(cbdata_type type
, const char *file
, int line
)
256 cbdataInternalAlloc(cbdata_type type
)
260 assert(type
> 0 && type
< cbdata_types
);
261 p
= new (memPoolAlloc(cbdata_index
[type
].pool
)) cbdata
;
262 // p = (cbdata *)memPoolAlloc(cbdata_index[type].pool);
267 p
->cookie
= (long) p
^ cbdata::Cookie
;
273 p
->calls
= Stack
<CBDataCall
*> ();
274 p
->addHistory("Alloc", file
, line
);
275 dlinkAdd(p
, &p
->link
, &cbdataEntries
);
276 debug(45, 3) ("cbdataAlloc: %p %s:%d\n", &p
->data
, file
, line
);
279 return (void *) &p
->data
;
284 cbdataInternalFreeDbg(void *p
, const char *file
, int line
)
286 cbdataInternalFree(void *p
)
290 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
293 debug(45, 3) ("cbdataFree: %p %s:%d\n", p
, file
, line
);
296 debug(45, 3) ("cbdataFree: %p\n", p
);
304 c
->addHistory("Free", file
, line
);
308 debug(45, 3) ("cbdataFree: %p has %d locks, not freeing\n",
314 debug(45, 3) ("cbdataFree: Freeing %p\n", p
);
317 dlinkDelete(&c
->link
, &cbdataEntries
);
320 cbdata_type theType
= c
->type
;
321 c
->cbdata::~cbdata();
323 /* This is ugly. But: operator delete doesn't get
324 * the type parameter, so we can't use that
325 * to free the memory.
326 * So, we free it ourselves.
327 * Note that this means a non-placement
328 * new would be a seriously bad idea.
329 * Lastly, if we where a templated class,
330 * we could use the normal delete operator
331 * and it would Just Work. RBC 20030902
333 memPoolFree(cbdata_index
[theType
].pool
, c
);
339 cbdataInternalLockDbg(const void *p
, const char *file
, int line
)
341 cbdataInternalLock(const void *p
)
349 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
353 debug(45, 3) ("cbdataLock: %p=%d %s:%d\n", p
, c
? c
->locks
+ 1 : -1, file
, line
);
355 c
->addHistory("Reference", file
, line
);
359 debug(45, 3) ("cbdataLock: %p=%d\n", p
, c
? c
->locks
+ 1 : -1);
365 assert(c
->locks
< 65535);
372 cbdataInternalUnlockDbg(const void *p
, const char *file
, int line
)
374 cbdataInternalUnlock(const void *p
)
382 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
386 debug(45, 3) ("cbdataUnlock: %p=%d %s:%d\n", p
, c
? c
->locks
- 1 : -1, file
, line
);
388 c
->addHistory("Dereference", file
, line
);
392 debug(45, 3) ("cbdataUnlock: %p=%d\n", p
, c
? c
->locks
- 1 : -1);
400 assert(c
->locks
> 0);
404 if (c
->valid
|| c
->locks
)
409 debug(45, 3) ("cbdataUnlock: Freeing %p\n", p
);
413 dlinkDelete(&c
->link
, &cbdataEntries
);
417 cbdata_type theType
= c
->type
;
419 c
->cbdata::~cbdata();
421 /* This is ugly. But: operator delete doesn't get
422 * the type parameter, so we can't use that
423 * to free the memory.
424 * So, we free it ourselves.
425 * Note that this means a non-placement
426 * new would be a seriously bad idea.
427 * Lastly, if we where a templated class,
428 * we could use the normal delete operator
429 * and it would Just Work. RBC 20030902
431 memPoolFree(cbdata_index
[theType
].pool
, c
);
435 cbdataReferenceValid(const void *p
)
440 return 1; /* A NULL pointer cannot become invalid */
442 debug(45, 3) ("cbdataReferenceValid: %p\n", p
);
444 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
448 assert(c
->locks
> 0);
455 cbdataInternalReferenceDoneValidDbg(void **pp
, void **tp
, const char *file
, int line
)
457 cbdataInternalReferenceDoneValid(void **pp
, void **tp
)
460 void *p
= (void *) *pp
;
461 int valid
= cbdataReferenceValid(p
);
465 cbdataInternalUnlockDbg(p
, file
, line
);
468 cbdataInternalUnlock(p
);
482 cbdata::dump(StoreEntry
*sentry
) const
484 storeAppendPrintf(sentry
, "%c%p\t%d\t%d\t%20s:%-5d\n", valid
? ' ' :
485 '!', &data
, type
, locks
, file
, line
);
488 struct CBDataDumper
: public unary_function
<cbdata
, void>
490 CBDataDumper(StoreEntry
*anEntry
):where(anEntry
){}
492 void operator()(cbdata
const &x
)
503 cbdataDump(StoreEntry
* sentry
)
505 storeAppendPrintf(sentry
, "%d cbdata entries\n", cbdataCount
);
508 storeAppendPrintf(sentry
, "Pointer\tType\tLocks\tAllocated by\n");
509 CBDataDumper
dumper(sentry
);
510 for_each (cbdataEntries
, dumper
);
511 storeAppendPrintf(sentry
, "\n");
512 storeAppendPrintf(sentry
, "types\tsize\tallocated\ttotal\n");
514 for (int i
= 1; i
< cbdata_types
; i
++) {
515 MemPool
*pool
= cbdata_index
[i
].pool
;
518 int obj_size
= pool
->obj_size
- cbdata::Offset
;
519 storeAppendPrintf(sentry
, "%s\t%d\t%d\t%d\n", pool
->label
+ 7, obj_size
, pool
->meter
.inuse
.level
, obj_size
* pool
->meter
.inuse
.level
);
524 storeAppendPrintf(sentry
, "detailed allocation information only available when compiled with CBDATA_DEBUG\n");
528 storeAppendPrintf(sentry
, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
533 struct CBDataCallDumper
: public unary_function
<CBDataCall
, void>
535 CBDataCallDumper (StoreEntry
*anEntry
):where(anEntry
){}
537 void operator()(CBDataCall
const &x
)
539 storeAppendPrintf(where
, "%s\t%s\t%d\n", x
.label
, x
.file
, x
.line
);
545 struct CBDataHistoryDumper
: public CBDataDumper
547 CBDataHistoryDumper(StoreEntry
*anEntry
):CBDataDumper(anEntry
),where(anEntry
), callDumper(anEntry
){}
549 void operator()(cbdata
const &x
)
551 CBDataDumper::operator()(x
);
552 storeAppendPrintf(where
, "\n");
553 storeAppendPrintf(where
, "Action\tFile\tLine\n");
554 for_each (x
.calls
,callDumper
);
555 storeAppendPrintf(where
, "\n");
559 CBDataCallDumper callDumper
;
563 cbdataDumpHistory(StoreEntry
*sentry
)
565 storeAppendPrintf(sentry
, "%d cbdata entries\n", cbdataCount
);
566 storeAppendPrintf(sentry
, "Pointer\tType\tLocks\tAllocated by\n");
567 CBDataHistoryDumper
dumper(sentry
);
568 for_each (cbdataEntries
, dumper
);