]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cbdata.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[thirdparty/squid.git] / src / cbdata.cc
1
2 /*
3 * $Id$
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 \defgroup CBDATAInternal Callback Data Allocator Internals
40 \ingroup CBDATAAPI
41 *
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
48 * when finished.
49 */
50
51 #include "squid.h"
52 #include "cbdata.h"
53 #include "mgr/Registration.h"
54 #include "Store.h"
55 #if USE_CBDATA_DEBUG
56 #include "Stack.h"
57 #endif
58 #include "Generic.h"
59
60 #if WITH_VALGRIND
61 #define HASHED_CBDATA 1
62 #endif
63
64 static int cbdataCount = 0;
65 #if USE_CBDATA_DEBUG
66 dlink_list cbdataEntries;
67 #endif
68
69 #if USE_CBDATA_DEBUG
70
71 class CBDataCall
72 {
73
74 public:
75 CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine) {}
76
77 char const *label;
78 char const *file;
79 int line;
80 };
81
82 #endif
83
84 /// \ingroup CBDATAInternal
85 #define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
86
87 /// \ingroup CBDATAInternal
88 class cbdata
89 {
90 /** \todo examine making cbdata templated on this - so we get type
91 * safe access to data - RBC 20030902 */
92 public:
93 #if HASHED_CBDATA
94 hash_link hash; // Must be first
95 #endif
96
97 #if USE_CBDATA_DEBUG
98
99 void dump(StoreEntry *)const;
100 #endif
101
102 #if !HASHED_CBDATA
103 void *operator new(size_t size, void *where);
104 void operator delete(void *where, void *where2);
105 #else
106 MEMPROXY_CLASS(cndata);
107 #endif
108
109 ~cbdata();
110 int valid;
111 int32_t locks;
112 cbdata_type type;
113 #if USE_CBDATA_DEBUG
114
115 void addHistory(char const *label, char const *aFile, int aLine) {
116 if (calls.size() > 1000)
117 return;
118
119 calls.push_back(new CBDataCall(label, aFile, aLine));
120 }
121
122 dlink_node link;
123 const char *file;
124 int line;
125 Stack<CBDataCall*> calls;
126 #endif
127
128 /* cookie used while debugging */
129 long cookie;
130 void check(int aLine) const {assert(cookie == ((long)this ^ Cookie));}
131 static const long Cookie;
132
133 #if !HASHED_CBDATA
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 */
138 void *data;
139 #endif
140
141 };
142
143 const long cbdata::Cookie((long)0xDEADBEEF);
144 #if !HASHED_CBDATA
145 const long cbdata::Offset(MakeOffset());
146
147 void *
148 cbdata::operator new(size_t size, void *where)
149 {
150 // assert (size == sizeof(cbdata));
151 return where;
152 }
153
154 /**
155 * Only ever invoked when placement new throws
156 * an exception. Used to prevent an incorrect
157 * free.
158 */
159 void
160 cbdata::operator delete(void *where, void *where2)
161 {
162 ; // empty.
163 }
164
165 long
166 cbdata::MakeOffset()
167 {
168 cbdata *zero = (cbdata *)0L;
169 void **dataOffset = &zero->data;
170 return (long)dataOffset;
171 }
172 #else
173 MEMPROXY_CLASS_INLINE(cbdata);
174 #endif
175
176 static OBJH cbdataDump;
177 #if USE_CBDATA_DEBUG
178 static OBJH cbdataDumpHistory;
179 #endif
180
181 /// \ingroup CBDATAInternal
182 struct CBDataIndex {
183 MemAllocator *pool;
184 FREE *free_func;
185 }
186 *cbdata_index = NULL;
187
188 /// \ingroup CBDATAInternal
189 int cbdata_types = 0;
190
191 #if HASHED_CBDATA
192 static hash_table *cbdata_htable = NULL;
193
194 static int
195 cbdata_cmp(const void *p1, const void *p2)
196 {
197 return (char *) p1 - (char *) p2;
198 }
199
200 static unsigned int
201 cbdata_hash(const void *p, unsigned int mod)
202 {
203 return ((unsigned long) p >> 8) % mod;
204 }
205 #endif
206
207
208 cbdata::~cbdata()
209 {
210 #if USE_CBDATA_DEBUG
211 CBDataCall *aCall;
212
213 while ((aCall = calls.pop()))
214 delete aCall;
215
216 #endif
217
218 FREE *free_func = cbdata_index[type].free_func;
219
220 #if HASHED_CBDATA
221 void *p = hash.key;
222 #else
223 void *p = &data;
224 #endif
225
226 if (free_func)
227 free_func(p);
228 }
229
230 static void
231 cbdataInternalInitType(cbdata_type type, const char *name, int size, FREE * free_func)
232 {
233 char *label;
234 assert (type == cbdata_types + 1);
235
236 cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
237 memset(&cbdata_index[type], 0, sizeof(*cbdata_index));
238 cbdata_types = type;
239
240 label = (char *)xmalloc(strlen(name) + 20);
241
242 snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type);
243
244 #if !HASHED_CBDATA
245 assert((size_t)cbdata::Offset == (sizeof(cbdata) - ((cbdata *)NULL)->dataSize()));
246 size += cbdata::Offset;
247 #endif
248
249 cbdata_index[type].pool = memPoolCreate(label, size);
250
251 cbdata_index[type].free_func = free_func;
252
253 #if HASHED_CBDATA
254 if (!cbdata_htable)
255 cbdata_htable = hash_create(cbdata_cmp, 1 << 12, cbdata_hash);
256 #endif
257 }
258
259 cbdata_type
260 cbdataInternalAddType(cbdata_type type, const char *name, int size, FREE * free_func)
261 {
262 if (type)
263 return type;
264
265 type = (cbdata_type)(cbdata_types + 1);
266
267 cbdataInternalInitType(type, name, size, free_func);
268
269 return type;
270 }
271
272 void
273 cbdataRegisterWithCacheManager(void)
274 {
275 Mgr::RegisterAction("cbdata",
276 "Callback Data Registry Contents",
277 cbdataDump, 0, 1);
278 #if USE_CBDATA_DEBUG
279
280 Mgr::RegisterAction("cbdatahistory",
281 "Detailed call history for all current cbdata contents",
282 cbdataDumpHistory, 0, 1);
283 #endif
284 }
285
286 void *
287 #if USE_CBDATA_DEBUG
288 cbdataInternalAllocDbg(cbdata_type type, const char *file, int line)
289 #else
290 cbdataInternalAlloc(cbdata_type type)
291 #endif
292 {
293 cbdata *c;
294 void *p;
295 assert(type > 0 && type <= cbdata_types);
296 /* placement new: the pool alloc gives us cbdata + user type memory space
297 * and we init it with cbdata at the start of it
298 */
299 #if HASHED_CBDATA
300 c = new cbdata;
301 p = cbdata_index[type].pool->alloc();
302 c->hash.key = p;
303 hash_join(cbdata_htable, &c->hash);
304 #else
305 c = new (cbdata_index[type].pool->alloc()) cbdata;
306 p = (void *)&c->data;
307 #endif
308
309 c->type = type;
310 c->valid = 1;
311 c->locks = 0;
312 c->cookie = (long) c ^ cbdata::Cookie;
313 cbdataCount++;
314 #if USE_CBDATA_DEBUG
315
316 c->file = file;
317 c->line = line;
318 c->calls = Stack<CBDataCall *> ();
319 c->addHistory("Alloc", file, line);
320 dlinkAdd(c, &c->link, &cbdataEntries);
321 debugs(45, 3, "cbdataAlloc: " << p << " " << file << ":" << line);
322 #endif
323
324 return p;
325 }
326
327 void *
328 #if USE_CBDATA_DEBUG
329 cbdataInternalFreeDbg(void *p, const char *file, int line)
330 #else
331 cbdataInternalFree(void *p)
332 #endif
333 {
334 cbdata *c;
335 #if HASHED_CBDATA
336 c = (cbdata *) hash_lookup(cbdata_htable, p);
337 #else
338 c = (cbdata *) (((char *) p) - cbdata::Offset);
339 #endif
340 #if USE_CBDATA_DEBUG
341
342 debugs(45, 3, "cbdataFree: " << p << " " << file << ":" << line);
343 #else
344
345 debugs(45, 9, "cbdataFree: " << p);
346 #endif
347
348 c->check(__LINE__);
349 assert(c->valid);
350 c->valid = 0;
351 #if USE_CBDATA_DEBUG
352
353 c->addHistory("Free", file, line);
354 #endif
355
356 if (c->locks) {
357 debugs(45, 9, "cbdataFree: " << p << " has " << c->locks << " locks, not freeing");
358 return NULL;
359 }
360
361 cbdataCount--;
362 debugs(45, 9, "cbdataFree: Freeing " << p);
363 #if USE_CBDATA_DEBUG
364
365 dlinkDelete(&c->link, &cbdataEntries);
366 #endif
367
368 /* This is ugly. But: operator delete doesn't get
369 * the type parameter, so we can't use that
370 * to free the memory.
371 * So, we free it ourselves.
372 * Note that this means a non-placement
373 * new would be a seriously bad idea.
374 * Lastly, if we where a templated class,
375 * we could use the normal delete operator
376 * and it would Just Work. RBC 20030902
377 */
378 cbdata_type theType = c->type;
379 #if HASHED_CBDATA
380 hash_remove_link(cbdata_htable, &c->hash);
381 delete c;
382 cbdata_index[theType].pool->freeOne((void *)p);
383 #else
384 c->cbdata::~cbdata();
385 cbdata_index[theType].pool->freeOne(c);
386 #endif
387 return NULL;
388 }
389
390 void
391 #if USE_CBDATA_DEBUG
392 cbdataInternalLockDbg(const void *p, const char *file, int line)
393 #else
394 cbdataInternalLock(const void *p)
395 #endif
396 {
397 cbdata *c;
398
399 if (p == NULL)
400 return;
401
402 #if HASHED_CBDATA
403 c = (cbdata *) hash_lookup(cbdata_htable, p);
404 #else
405 c = (cbdata *) (((char *) p) - cbdata::Offset);
406 #endif
407
408 #if USE_CBDATA_DEBUG
409
410 debugs(45, 3, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1) << " " << file << ":" << line);
411
412 c->addHistory("Reference", file, line);
413
414 #else
415
416 debugs(45, 9, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1));
417
418 #endif
419
420 c->check(__LINE__);
421
422 assert(c->locks < INT_MAX);
423
424 c->locks++;
425 }
426
427 void
428 #if USE_CBDATA_DEBUG
429 cbdataInternalUnlockDbg(const void *p, const char *file, int line)
430 #else
431 cbdataInternalUnlock(const void *p)
432 #endif
433 {
434 cbdata *c;
435
436 if (p == NULL)
437 return;
438
439 #if HASHED_CBDATA
440 c = (cbdata *) hash_lookup(cbdata_htable, p);
441 #else
442 c = (cbdata *) (((char *) p) - cbdata::Offset);
443 #endif
444
445 #if USE_CBDATA_DEBUG
446
447 debugs(45, 3, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1) << " " << file << ":" << line);
448
449 c->addHistory("Dereference", file, line);
450
451 #else
452
453 debugs(45, 9, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1));
454
455 #endif
456
457 c->check(__LINE__);
458
459 assert(c != NULL);
460
461 assert(c->locks > 0);
462
463 c->locks--;
464
465 if (c->valid || c->locks)
466 return;
467
468 cbdataCount--;
469
470 debugs(45, 9, "cbdataUnlock: Freeing " << p);
471
472 #if USE_CBDATA_DEBUG
473
474 dlinkDelete(&c->link, &cbdataEntries);
475
476 #endif
477
478 /* This is ugly. But: operator delete doesn't get
479 * the type parameter, so we can't use that
480 * to free the memory.
481 * So, we free it ourselves.
482 * Note that this means a non-placement
483 * new would be a seriously bad idea.
484 * Lastly, if we where a templated class,
485 * we could use the normal delete operator
486 * and it would Just Work. RBC 20030902
487 */
488 cbdata_type theType = c->type;
489 #if HASHED_CBDATA
490 hash_remove_link(cbdata_htable, &c->hash);
491 delete c;
492 cbdata_index[theType].pool->freeOne((void *)p);
493 #else
494 c->cbdata::~cbdata();
495 cbdata_index[theType].pool->freeOne(c);
496 #endif
497 }
498
499 int
500 cbdataReferenceValid(const void *p)
501 {
502 cbdata *c;
503
504 if (p == NULL)
505 return 1; /* A NULL pointer cannot become invalid */
506
507 debugs(45, 9, "cbdataReferenceValid: " << p);
508
509 #if HASHED_CBDATA
510 c = (cbdata *) hash_lookup(cbdata_htable, p);
511 #else
512 c = (cbdata *) (((char *) p) - cbdata::Offset);
513 #endif
514
515 c->check(__LINE__);
516
517 assert(c->locks > 0);
518
519 return c->valid;
520 }
521
522 int
523 #if USE_CBDATA_DEBUG
524 cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line)
525 #else
526 cbdataInternalReferenceDoneValid(void **pp, void **tp)
527 #endif
528 {
529 void *p = (void *) *pp;
530 int valid = cbdataReferenceValid(p);
531 *pp = NULL;
532 #if USE_CBDATA_DEBUG
533
534 cbdataInternalUnlockDbg(p, file, line);
535 #else
536
537 cbdataInternalUnlock(p);
538 #endif
539
540 if (valid) {
541 *tp = p;
542 return 1;
543 } else {
544 *tp = NULL;
545 return 0;
546 }
547 }
548
549 #if USE_CBDATA_DEBUG
550 void
551 cbdata::dump(StoreEntry *sentry) const
552 {
553 #if HASHED_CBDATA
554 void *p = (void *)hash.key;
555 #else
556 void *p = (void *)&data;
557 #endif
558 storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' :
559 '!', p, type, locks, file, line);
560 }
561
562 struct CBDataDumper : public unary_function<cbdata, void> {
563 CBDataDumper(StoreEntry *anEntry):where(anEntry) {}
564
565 void operator()(cbdata const &x) {
566 x.dump(where);
567 }
568
569 StoreEntry *where;
570 };
571
572 #endif
573
574 static void
575 cbdataDump(StoreEntry * sentry)
576 {
577 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
578 #if USE_CBDATA_DEBUG
579
580 storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
581 CBDataDumper dumper(sentry);
582 for_each (cbdataEntries, dumper);
583 storeAppendPrintf(sentry, "\n");
584 storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n");
585
586 for (int i = 1; i < cbdata_types; i++) {
587 MemAllocator *pool = cbdata_index[i].pool;
588
589 if (pool) {
590 #if HASHED_CBDATA
591 int obj_size = pool->objectSize();
592 #else
593 int obj_size = pool->objectSize() - cbdata::Offset;
594 #endif
595 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);
596 }
597 }
598
599 #else
600 storeAppendPrintf(sentry, "detailed allocation information only available when compiled with --enable-debug-cbdata\n");
601
602 #endif
603
604 storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
605 }
606
607 CBDATA_CLASS_INIT(generic_cbdata);
608
609 #if USE_CBDATA_DEBUG
610
611 struct CBDataCallDumper : public unary_function<CBDataCall, void> {
612 CBDataCallDumper (StoreEntry *anEntry):where(anEntry) {}
613
614 void operator()(CBDataCall const &x) {
615 storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line);
616 }
617
618 StoreEntry *where;
619 };
620
621 struct CBDataHistoryDumper : public CBDataDumper {
622 CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry) {}
623
624 void operator()(cbdata const &x) {
625 CBDataDumper::operator()(x);
626 storeAppendPrintf(where, "\n");
627 storeAppendPrintf(where, "Action\tFile\tLine\n");
628 for_each (x.calls,callDumper);
629 storeAppendPrintf(where, "\n");
630 }
631
632 StoreEntry *where;
633 CBDataCallDumper callDumper;
634 };
635
636 void
637 cbdataDumpHistory(StoreEntry *sentry)
638 {
639 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
640 storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
641 CBDataHistoryDumper dumper(sentry);
642 for_each (cbdataEntries, dumper);
643 }
644
645 #endif