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