]>
Commit | Line | Data |
---|---|---|
365e5b34 | 1 | |
e1ac4723 | 2 | /* |
8000a965 | 3 | * $Id: cbdata.cc,v 1.53 2003/02/12 06:11:00 robertc Exp $ |
e1ac4723 | 4 | * |
5 | * DEBUG: section 45 Callback Data Registry | |
28c60158 | 6 | * ORIGINAL AUTHOR: Duane Wessels |
7 | * Modified by Moez Mahfoudh (08/12/2000) | |
e48ed433 | 8 | * History added by Robert Collins (2002-10-25) |
e1ac4723 | 9 | * |
2b6662ba | 10 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 11 | * ---------------------------------------------------------- |
e1ac4723 | 12 | * |
2b6662ba | 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. | |
e1ac4723 | 21 | * |
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. | |
26 | * | |
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. | |
31 | * | |
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 | |
cbdec147 | 34 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 35 | * |
e1ac4723 | 36 | */ |
37 | ||
38 | /* | |
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 | |
45 | * when finished. | |
e1ac4723 | 46 | */ |
a2172245 | 47 | |
48 | #include "squid.h" | |
e6ccf245 | 49 | #include "Store.h" |
e48ed433 | 50 | #if CBDATA_DEBUG |
51 | #include "Stack.h" | |
52 | #endif | |
53 | #include "Generic.h" | |
a2172245 | 54 | |
8b9a2d90 | 55 | static int cbdataCount = 0; |
fa80a8ef | 56 | #if CBDATA_DEBUG |
57 | dlink_list cbdataEntries; | |
58 | #endif | |
8b9a2d90 | 59 | |
e48ed433 | 60 | #if CBDATA_DEBUG |
61 | class CBDataCall { | |
62 | public: | |
63 | CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine){} | |
64 | char const *label; | |
65 | char const *file; | |
66 | int line; | |
67 | }; | |
68 | #endif | |
69 | ||
a2172245 | 70 | typedef struct _cbdata { |
e48ed433 | 71 | #if CBDATA_DEBUG |
72 | void dump(StoreEntry *)const; | |
73 | #endif | |
74 | void deleteSelf(); | |
3015e83a | 75 | int valid; |
76 | int locks; | |
72711e31 | 77 | int type; |
4e3f712d | 78 | #if CBDATA_DEBUG |
e48ed433 | 79 | void addHistory(char const *label, char const *file, int line) const |
80 | { | |
81 | if (calls->count > 100) | |
82 | return; | |
83 | stackPush (calls, new CBDataCall(label, file, line)); | |
84 | } | |
fa80a8ef | 85 | dlink_node link; |
4e3f712d | 86 | const char *file; |
87 | int line; | |
e48ed433 | 88 | Stack *calls; |
4e3f712d | 89 | #endif |
fa80a8ef | 90 | long y; /* cookie used while debugging */ |
28c60158 | 91 | union { |
92 | void *pointer; | |
93 | double double_float; | |
94 | int integer; | |
95 | } data; | |
a2172245 | 96 | } cbdata; |
97 | ||
db1cd23c | 98 | static OBJH cbdataDump; |
e48ed433 | 99 | #ifdef CBDATA_DEBUG |
100 | static OBJH cbdataDumpHistory; | |
101 | #endif | |
a2172245 | 102 | |
e6ccf245 | 103 | struct CBDataIndex { |
72711e31 | 104 | MemPool *pool; |
105 | FREE *free_func; | |
106 | } *cbdata_index = NULL; | |
28c60158 | 107 | int cbdata_types = 0; |
108 | ||
109 | #define OFFSET_OF(type, member) ((int)(char *)&((type *)0L)->member) | |
e6ccf245 | 110 | #define CBDATA_COOKIE (long)0xDEADBEEF |
fa80a8ef | 111 | #define CBDATA_CHECK(c) assert(c->y == ((long)c ^ CBDATA_COOKIE)) |
28c60158 | 112 | |
e48ed433 | 113 | void |
114 | _cbdata::deleteSelf() | |
115 | { | |
116 | #if CBDATA_DEBUG | |
117 | CBDataCall *aCall; | |
118 | while ((aCall = (CBDataCall *)stackPop(calls))) | |
119 | delete aCall; | |
120 | stackDestroy(calls); | |
121 | #endif | |
122 | FREE *free_func = cbdata_index[type].free_func; | |
123 | if (free_func) | |
124 | free_func(&data); | |
125 | memPoolFree(cbdata_index[type].pool, this); | |
126 | } | |
127 | ||
fa80a8ef | 128 | static void |
129 | cbdataInternalInitType(cbdata_type type, const char *name, int size, FREE * free_func) | |
a2172245 | 130 | { |
28c60158 | 131 | char *label; |
132 | if (type >= cbdata_types) { | |
e6ccf245 | 133 | cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index)); |
72711e31 | 134 | memset(&cbdata_index[cbdata_types], 0, |
135 | (type + 1 - cbdata_types) * sizeof(*cbdata_index)); | |
28c60158 | 136 | cbdata_types = type + 1; |
137 | } | |
72711e31 | 138 | if (cbdata_index[type].pool) |
28c60158 | 139 | return; |
e6ccf245 | 140 | label = (char *)xmalloc(strlen(name) + 20); |
28c60158 | 141 | snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type); |
142 | assert(OFFSET_OF(cbdata, data) == (sizeof(cbdata) - sizeof(((cbdata *) NULL)->data))); | |
72711e31 | 143 | cbdata_index[type].pool = memPoolCreate(label, size + OFFSET_OF(cbdata, data)); |
144 | cbdata_index[type].free_func = free_func; | |
a2172245 | 145 | } |
146 | ||
28c60158 | 147 | cbdata_type |
fa80a8ef | 148 | cbdataInternalAddType(cbdata_type type, const char *name, int size, FREE * free_func) |
a2172245 | 149 | { |
28c60158 | 150 | if (type) |
151 | return type; | |
e6ccf245 | 152 | type = (cbdata_type)cbdata_types; |
fa80a8ef | 153 | cbdataInternalInitType(type, name, size, free_func); |
28c60158 | 154 | return type; |
a2172245 | 155 | } |
156 | ||
a2172245 | 157 | void |
158 | cbdataInit(void) | |
159 | { | |
3015e83a | 160 | debug(45, 3) ("cbdataInit\n"); |
22f3fd98 | 161 | cachemgrRegister("cbdata", |
162 | "Callback Data Registry Contents", | |
1da3b90b | 163 | cbdataDump, 0, 1); |
e48ed433 | 164 | #if CBDATA_DEBUG |
165 | cachemgrRegister("cbdatahistory", | |
166 | "Detailed call history for all current cbdata contents", | |
167 | cbdataDumpHistory, 0, 1); | |
168 | #endif | |
fa80a8ef | 169 | #define CREATE_CBDATA(type) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), NULL) |
170 | #define CREATE_CBDATA_FREE(type, free_func) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), free_func) | |
171 | /* XXX | |
172 | * most of these should be moved out to their respective module. | |
173 | */ | |
28c60158 | 174 | CREATE_CBDATA(ConnStateData); |
175 | CREATE_CBDATA(ErrorState); | |
176 | CREATE_CBDATA(FwdState); | |
177 | CREATE_CBDATA(generic_cbdata); | |
178 | CREATE_CBDATA(helper); | |
179 | CREATE_CBDATA(helper_server); | |
94439e4e | 180 | CREATE_CBDATA(statefulhelper); |
181 | CREATE_CBDATA(helper_stateful_server); | |
72711e31 | 182 | CREATE_CBDATA_FREE(peer, peerDestroy); |
28c60158 | 183 | CREATE_CBDATA(ps_state); |
184 | CREATE_CBDATA(RemovalPolicy); | |
185 | CREATE_CBDATA(RemovalPolicyWalker); | |
186 | CREATE_CBDATA(RemovalPurgeWalker); | |
a2172245 | 187 | } |
188 | ||
28c60158 | 189 | void * |
4e3f712d | 190 | #if CBDATA_DEBUG |
72711e31 | 191 | cbdataInternalAllocDbg(cbdata_type type, const char *file, int line) |
4e3f712d | 192 | #else |
72711e31 | 193 | cbdataInternalAlloc(cbdata_type type) |
4e3f712d | 194 | #endif |
a2172245 | 195 | { |
28c60158 | 196 | cbdata *p; |
197 | assert(type > 0 && type < cbdata_types); | |
e6ccf245 | 198 | p = (cbdata *)memPoolAlloc(cbdata_index[type].pool); |
28c60158 | 199 | p->type = type; |
28c60158 | 200 | p->valid = 1; |
201 | p->locks = 0; | |
fa80a8ef | 202 | p->y = (long) p ^ CBDATA_COOKIE; |
8b9a2d90 | 203 | cbdataCount++; |
fa80a8ef | 204 | #if CBDATA_DEBUG |
e48ed433 | 205 | p->file = file; |
206 | p->line = line; | |
207 | p->calls = stackCreate(); | |
208 | p->addHistory("Alloc", file, line); | |
fa80a8ef | 209 | dlinkAdd(p, &p->link, &cbdataEntries); |
159be25d | 210 | debug(45, 3) ("cbdataAlloc: %p %s:%d\n", &p->data, file, line); |
fa80a8ef | 211 | #endif |
28c60158 | 212 | return (void *) &p->data; |
b8a2718d | 213 | } |
214 | ||
c29316a4 | 215 | void * |
fa80a8ef | 216 | #if CBDATA_DEBUG |
217 | cbdataInternalFreeDbg(void *p, const char *file, int line) | |
218 | #else | |
72711e31 | 219 | cbdataInternalFree(void *p) |
fa80a8ef | 220 | #endif |
a2172245 | 221 | { |
28c60158 | 222 | cbdata *c; |
159be25d | 223 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
fa80a8ef | 224 | #if CBDATA_DEBUG |
225 | debug(45, 3) ("cbdataFree: %p %s:%d\n", p, file, line); | |
226 | #else | |
3015e83a | 227 | debug(45, 3) ("cbdataFree: %p\n", p); |
fa80a8ef | 228 | #endif |
fa80a8ef | 229 | CBDATA_CHECK(c); |
3015e83a | 230 | c->valid = 0; |
e48ed433 | 231 | #if CBDATA_DEBUG |
232 | c->addHistory("Free", file, line); | |
233 | #endif | |
3015e83a | 234 | if (c->locks) { |
aab9676e | 235 | debug(45, 3) ("cbdataFree: %p has %d locks, not freeing\n", |
b716a8ad | 236 | p, c->locks); |
c29316a4 | 237 | return NULL; |
3015e83a | 238 | } |
28c60158 | 239 | cbdataCount--; |
240 | debug(45, 3) ("cbdataFree: Freeing %p\n", p); | |
fa80a8ef | 241 | #if CBDATA_DEBUG |
242 | dlinkDelete(&c->link, &cbdataEntries); | |
243 | #endif | |
e48ed433 | 244 | c->deleteSelf(); |
c29316a4 | 245 | return NULL; |
72711e31 | 246 | } |
247 | ||
a2172245 | 248 | void |
b7e6c377 | 249 | #if CBDATA_DEBUG |
fa80a8ef | 250 | cbdataInternalLockDbg(const void *p, const char *file, int line) |
b7e6c377 | 251 | #else |
fa80a8ef | 252 | cbdataInternalLock(const void *p) |
b7e6c377 | 253 | #endif |
a2172245 | 254 | { |
3015e83a | 255 | cbdata *c; |
256 | if (p == NULL) | |
257 | return; | |
28c60158 | 258 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
b7e6c377 | 259 | #if CBDATA_DEBUG |
fa80a8ef | 260 | debug(45, 3) ("cbdataLock: %p=%d %s:%d\n", p, c ? c->locks + 1 : -1, file, line); |
e48ed433 | 261 | c->addHistory("Reference", file, line); |
fa80a8ef | 262 | #else |
263 | debug(45, 3) ("cbdataLock: %p=%d\n", p, c ? c->locks + 1 : -1); | |
b7e6c377 | 264 | #endif |
fa80a8ef | 265 | CBDATA_CHECK(c); |
59e12f93 | 266 | assert(c->locks < 65535); |
fa80a8ef | 267 | c->locks++; |
a2172245 | 268 | } |
269 | ||
270 | void | |
b7e6c377 | 271 | #if CBDATA_DEBUG |
fa80a8ef | 272 | cbdataInternalUnlockDbg(const void *p, const char *file, int line) |
b7e6c377 | 273 | #else |
fa80a8ef | 274 | cbdataInternalUnlock(const void *p) |
b7e6c377 | 275 | #endif |
a2172245 | 276 | { |
3015e83a | 277 | cbdata *c; |
278 | if (p == NULL) | |
279 | return; | |
28c60158 | 280 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
fa80a8ef | 281 | #if CBDATA_DEBUG |
282 | debug(45, 3) ("cbdataUnlock: %p=%d %s:%d\n", p, c ? c->locks - 1 : -1, file, line); | |
e48ed433 | 283 | c->addHistory("Dereference", file, line); |
fa80a8ef | 284 | #else |
285 | debug(45, 3) ("cbdataUnlock: %p=%d\n", p, c ? c->locks - 1 : -1); | |
286 | #endif | |
287 | CBDATA_CHECK(c); | |
3015e83a | 288 | assert(c != NULL); |
289 | assert(c->locks > 0); | |
290 | c->locks--; | |
aab9676e | 291 | if (c->valid || c->locks) |
3015e83a | 292 | return; |
28c60158 | 293 | cbdataCount--; |
294 | debug(45, 3) ("cbdataUnlock: Freeing %p\n", p); | |
fa80a8ef | 295 | #if CBDATA_DEBUG |
296 | dlinkDelete(&c->link, &cbdataEntries); | |
297 | #endif | |
e48ed433 | 298 | c->deleteSelf(); |
a2172245 | 299 | } |
300 | ||
301 | int | |
fa80a8ef | 302 | cbdataReferenceValid(const void *p) |
a2172245 | 303 | { |
3015e83a | 304 | cbdata *c; |
305 | if (p == NULL) | |
28c60158 | 306 | return 1; /* A NULL pointer cannot become invalid */ |
fa80a8ef | 307 | debug(45, 3) ("cbdataReferenceValid: %p\n", p); |
28c60158 | 308 | c = (cbdata *) (((char *) p) - OFFSET_OF(cbdata, data)); |
fa80a8ef | 309 | CBDATA_CHECK(c); |
3015e83a | 310 | assert(c->locks > 0); |
311 | return c->valid; | |
a2172245 | 312 | } |
313 | ||
fa80a8ef | 314 | int |
315 | #if CBDATA_DEBUG | |
316 | cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line) | |
317 | #else | |
318 | cbdataInternalReferenceDoneValid(void **pp, void **tp) | |
319 | #endif | |
320 | { | |
321 | void *p = (void *) *pp; | |
322 | int valid = cbdataReferenceValid(p); | |
323 | *pp = NULL; | |
324 | #if CBDATA_DEBUG | |
325 | cbdataInternalUnlockDbg(p, file, line); | |
326 | #else | |
327 | cbdataInternalUnlock(p); | |
328 | #endif | |
329 | if (valid) { | |
330 | *tp = p; | |
331 | return 1; | |
332 | } else { | |
333 | *tp = NULL; | |
334 | return 0; | |
335 | } | |
336 | } | |
337 | ||
e48ed433 | 338 | #if CBDATA_DEBUG |
339 | void | |
340 | _cbdata::dump(StoreEntry *sentry) const | |
341 | { | |
342 | storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' : | |
343 | '!', &data, type, locks, file, line); | |
344 | } | |
345 | ||
346 | struct CBDataDumper : public unary_function<_cbdata, void> | |
347 | { | |
348 | CBDataDumper(StoreEntry *anEntry):where(anEntry){} | |
349 | void operator()(_cbdata const &x) { | |
350 | x.dump(where); | |
351 | } | |
352 | StoreEntry *where; | |
353 | }; | |
354 | #endif | |
fa80a8ef | 355 | |
db1cd23c | 356 | static void |
3015e83a | 357 | cbdataDump(StoreEntry * sentry) |
a2172245 | 358 | { |
8b9a2d90 | 359 | storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount); |
fa80a8ef | 360 | #if CBDATA_DEBUG |
361 | storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n"); | |
e48ed433 | 362 | CBDataDumper dumper(sentry); |
363 | for_each (cbdataEntries, dumper); | |
fa80a8ef | 364 | storeAppendPrintf(sentry, "\n"); |
365 | storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n"); | |
e48ed433 | 366 | for (int i = 1; i < cbdata_types; i++) { |
fa80a8ef | 367 | MemPool *pool = cbdata_index[i].pool; |
368 | if (pool) { | |
369 | int obj_size = pool->obj_size - OFFSET_OF(cbdata, data); | |
370 | 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); | |
371 | } | |
372 | } | |
373 | #else | |
374 | storeAppendPrintf(sentry, "detailed allocation information only available when compiled with CBDATA_DEBUG\n"); | |
375 | #endif | |
376 | storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n"); | |
a2172245 | 377 | } |
e48ed433 | 378 | |
379 | #if CBDATA_DEBUG | |
380 | ||
381 | template <class T> | |
382 | T& for_each(Stack const &collection, T& visitor) | |
383 | { | |
732735ed | 384 | for (size_t index = 0; index < collection.count; ++index) |
e48ed433 | 385 | visitor(*(typename T::argument_type const *)collection.items[index]); |
386 | return visitor; | |
387 | }; | |
388 | ||
389 | struct CBDataCallDumper : public unary_function<CBDataCall, void> | |
390 | { | |
391 | CBDataCallDumper (StoreEntry *anEntry):where(anEntry){} | |
392 | void operator()(CBDataCall const &x) { | |
393 | storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line); | |
394 | } | |
395 | StoreEntry *where; | |
396 | }; | |
397 | ||
398 | struct CBDataHistoryDumper : public CBDataDumper | |
399 | { | |
400 | CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry){} | |
401 | void operator()(_cbdata const &x) { | |
402 | CBDataDumper::operator()(x); | |
403 | storeAppendPrintf(where, "\n"); | |
404 | storeAppendPrintf(where, "Action\tFile\tLine\n"); | |
405 | for_each (*x.calls,callDumper); | |
406 | storeAppendPrintf(where, "\n"); | |
407 | } | |
408 | StoreEntry *where; | |
409 | CBDataCallDumper callDumper; | |
410 | }; | |
411 | ||
412 | void | |
413 | cbdataDumpHistory(StoreEntry *sentry) | |
414 | { | |
415 | storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount); | |
416 | storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n"); | |
417 | CBDataHistoryDumper dumper(sentry); | |
418 | for_each (cbdataEntries, dumper); | |
419 | } | |
420 | #endif |