]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cbdata.cc
Merged from trunk
[thirdparty/squid.git] / src / cbdata.cc
1
2 /*
3 * $Id: cbdata.cc,v 1.77 2008/02/26 21:49:34 amosjeffries 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 \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 "cbdata.h"
52 #include "CacheManager.h"
53 #include "Store.h"
54 #if CBDATA_DEBUG
55 #include "Stack.h"
56 #endif
57 #include "Generic.h"
58
59 #if WITH_VALGRIND
60 #define HASHED_CBDATA 1
61 #endif
62
63 static int cbdataCount = 0;
64 #if CBDATA_DEBUG
65 dlink_list cbdataEntries;
66 #endif
67
68 #if CBDATA_DEBUG
69
70 class CBDataCall
71 {
72
73 public:
74 CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine){}
75
76 char const *label;
77 char const *file;
78 int line;
79 };
80
81 #endif
82
83 /// \ingroup CBDATAInternal
84 #define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
85
86 /// \ingroup CBDATAInternal
87 class cbdata
88 {
89 /** \todo examine making cbdata templated on this - so we get type
90 * safe access to data - RBC 20030902 */
91 public:
92 #if HASHED_CBDATA
93 hash_link hash; // Must be first
94 #endif
95
96 #if CBDATA_DEBUG
97
98 void dump(StoreEntry *)const;
99 #endif
100
101 #if !HASHED_CBDATA
102 void *operator new(size_t size, void *where);
103 void operator delete(void *where, void *where2);
104 #else
105 MEMPROXY_CLASS(cndata);
106 #endif
107
108 ~cbdata();
109 int valid;
110 int locks;
111 cbdata_type type;
112 #if CBDATA_DEBUG
113
114 void addHistory(char const *label, char const *file, int line)
115 {
116 if (calls.size() > 1000)
117 return;
118
119 calls.push_back(new CBDataCall(label, file, line));
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 line) 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 #ifdef CBDATA_DEBUG
178 static OBJH cbdataDumpHistory;
179 #endif
180
181 /// \ingroup CBDATAInternal
182 struct CBDataIndex
183 {
184 MemAllocator *pool;
185 FREE *free_func;
186 }
187 *cbdata_index = NULL;
188
189 /// \ingroup CBDATAInternal
190 int cbdata_types = 0;
191
192 #if HASHED_CBDATA
193 static hash_table *cbdata_htable = NULL;
194
195 static int
196 cbdata_cmp(const void *p1, const void *p2)
197 {
198 return (char *) p1 - (char *) p2;
199 }
200
201 static unsigned int
202 cbdata_hash(const void *p, unsigned int mod)
203 {
204 return ((unsigned long) p >> 8) % mod;
205 }
206 #endif
207
208
209 cbdata::~cbdata()
210 {
211 #if CBDATA_DEBUG
212 CBDataCall *aCall;
213
214 while ((aCall = calls.pop()))
215 delete aCall;
216
217 #endif
218
219 FREE *free_func = cbdata_index[type].free_func;
220
221 #if HASHED_CBDATA
222 void *p = hash.key;
223 #else
224 void *p = &data;
225 #endif
226
227 if (free_func)
228 free_func(p);
229 }
230
231 static void
232 cbdataInternalInitType(cbdata_type type, const char *name, int size, FREE * free_func)
233 {
234 char *label;
235 assert (type == cbdata_types + 1);
236
237 cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
238 memset(&cbdata_index[type], 0, sizeof(*cbdata_index));
239 cbdata_types = type;
240
241 label = (char *)xmalloc(strlen(name) + 20);
242
243 snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type);
244
245 #if !HASHED_CBDATA
246 assert((size_t)cbdata::Offset == (sizeof(cbdata) - ((cbdata *)NULL)->dataSize()));
247 size += cbdata::Offset;
248 #endif
249
250 cbdata_index[type].pool = memPoolCreate(label, size);
251
252 cbdata_index[type].free_func = free_func;
253
254 #if HASHED_CBDATA
255 if (!cbdata_htable)
256 cbdata_htable = hash_create(cbdata_cmp, 1 << 12, cbdata_hash);
257 #endif
258 }
259
260 cbdata_type
261 cbdataInternalAddType(cbdata_type type, const char *name, int size, FREE * free_func)
262 {
263 if (type)
264 return type;
265
266 type = (cbdata_type)(cbdata_types + 1);
267
268 cbdataInternalInitType(type, name, size, free_func);
269
270 return type;
271 }
272
273 void
274 cbdataRegisterWithCacheManager(void)
275 {
276 CacheManager *manager=CacheManager::GetInstance();
277 manager->registerAction("cbdata",
278 "Callback Data Registry Contents",
279 cbdataDump, 0, 1);
280 #if CBDATA_DEBUG
281
282 manager->registerAction("cbdatahistory",
283 "Detailed call history for all current cbdata contents",
284 cbdataDumpHistory, 0, 1);
285 #endif
286 }
287
288 void *
289 #if CBDATA_DEBUG
290 cbdataInternalAllocDbg(cbdata_type type, const char *file, int line)
291 #else
292 cbdataInternalAlloc(cbdata_type type)
293 #endif
294 {
295 cbdata *c;
296 void *p;
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
300 */
301 #if HASHED_CBDATA
302 c = new cbdata;
303 p = cbdata_index[type].pool->alloc();
304 c->hash.key = p;
305 hash_join(cbdata_htable, &c->hash);
306 #else
307 c = new (cbdata_index[type].pool->alloc()) cbdata;
308 p = (void *)&c->data;
309 #endif
310
311 c->type = type;
312 c->valid = 1;
313 c->locks = 0;
314 c->cookie = (long) c ^ cbdata::Cookie;
315 cbdataCount++;
316 #if CBDATA_DEBUG
317
318 c->file = file;
319 c->line = line;
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);
324 #endif
325
326 return p;
327 }
328
329 void *
330 #if CBDATA_DEBUG
331 cbdataInternalFreeDbg(void *p, const char *file, int line)
332 #else
333 cbdataInternalFree(void *p)
334 #endif
335 {
336 cbdata *c;
337 #if HASHED_CBDATA
338 c = (cbdata *) hash_lookup(cbdata_htable, p);
339 #else
340 c = (cbdata *) (((char *) p) - cbdata::Offset);
341 #endif
342 #if CBDATA_DEBUG
343
344 debugs(45, 3, "cbdataFree: " << p << " " << file << ":" << line);
345 #else
346
347 debugs(45, 9, "cbdataFree: " << p);
348 #endif
349
350 c->check(__LINE__);
351 assert(c->valid);
352 c->valid = 0;
353 #if CBDATA_DEBUG
354
355 c->addHistory("Free", file, line);
356 #endif
357
358 if (c->locks) {
359 debugs(45, 9, "cbdataFree: " << p << " has " << c->locks << " locks, not freeing");
360 return NULL;
361 }
362
363 cbdataCount--;
364 debugs(45, 9, "cbdataFree: Freeing " << p);
365 #if CBDATA_DEBUG
366
367 dlinkDelete(&c->link, &cbdataEntries);
368 #endif
369
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
379 */
380 cbdata_type theType = c->type;
381 #if HASHED_CBDATA
382 hash_remove_link(cbdata_htable, &c->hash);
383 delete c;
384 cbdata_index[theType].pool->free((void *)p);
385 #else
386 c->cbdata::~cbdata();
387 cbdata_index[theType].pool->free(c);
388 #endif
389 return NULL;
390 }
391
392 void
393 #if CBDATA_DEBUG
394 cbdataInternalLockDbg(const void *p, const char *file, int line)
395 #else
396 cbdataInternalLock(const void *p)
397 #endif
398 {
399 cbdata *c;
400
401 if (p == NULL)
402 return;
403
404 #if HASHED_CBDATA
405 c = (cbdata *) hash_lookup(cbdata_htable, p);
406 #else
407 c = (cbdata *) (((char *) p) - cbdata::Offset);
408 #endif
409
410 #if CBDATA_DEBUG
411
412 debugs(45, 3, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1) << " " << file << ":" << line);
413
414 c->addHistory("Reference", file, line);
415
416 #else
417
418 debugs(45, 9, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1));
419
420 #endif
421
422 c->check(__LINE__);
423
424 assert(c->locks < 65535);
425
426 c->locks++;
427 }
428
429 void
430 #if CBDATA_DEBUG
431 cbdataInternalUnlockDbg(const void *p, const char *file, int line)
432 #else
433 cbdataInternalUnlock(const void *p)
434 #endif
435 {
436 cbdata *c;
437
438 if (p == NULL)
439 return;
440
441 #if HASHED_CBDATA
442 c = (cbdata *) hash_lookup(cbdata_htable, p);
443 #else
444 c = (cbdata *) (((char *) p) - cbdata::Offset);
445 #endif
446
447 #if CBDATA_DEBUG
448
449 debugs(45, 3, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1) << " " << file << ":" << line);
450
451 c->addHistory("Dereference", file, line);
452
453 #else
454
455 debugs(45, 9, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1));
456
457 #endif
458
459 c->check(__LINE__);
460
461 assert(c != NULL);
462
463 assert(c->locks > 0);
464
465 c->locks--;
466
467 if (c->valid || c->locks)
468 return;
469
470 cbdataCount--;
471
472 debugs(45, 9, "cbdataUnlock: Freeing " << p);
473
474 #if CBDATA_DEBUG
475
476 dlinkDelete(&c->link, &cbdataEntries);
477
478 #endif
479
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
489 */
490 cbdata_type theType = c->type;
491 #if HASHED_CBDATA
492 hash_remove_link(cbdata_htable, &c->hash);
493 delete c;
494 cbdata_index[theType].pool->free((void *)p);
495 #else
496 c->cbdata::~cbdata();
497 cbdata_index[theType].pool->free(c);
498 #endif
499 }
500
501 int
502 cbdataReferenceValid(const void *p)
503 {
504 cbdata *c;
505
506 if (p == NULL)
507 return 1; /* A NULL pointer cannot become invalid */
508
509 debugs(45, 9, "cbdataReferenceValid: " << p);
510
511 #if HASHED_CBDATA
512 c = (cbdata *) hash_lookup(cbdata_htable, p);
513 #else
514 c = (cbdata *) (((char *) p) - cbdata::Offset);
515 #endif
516
517 c->check(__LINE__);
518
519 assert(c->locks > 0);
520
521 return c->valid;
522 }
523
524 int
525 #if CBDATA_DEBUG
526 cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line)
527 #else
528 cbdataInternalReferenceDoneValid(void **pp, void **tp)
529 #endif
530 {
531 void *p = (void *) *pp;
532 int valid = cbdataReferenceValid(p);
533 *pp = NULL;
534 #if CBDATA_DEBUG
535
536 cbdataInternalUnlockDbg(p, file, line);
537 #else
538
539 cbdataInternalUnlock(p);
540 #endif
541
542 if (valid) {
543 *tp = p;
544 return 1;
545 } else {
546 *tp = NULL;
547 return 0;
548 }
549 }
550
551 #if CBDATA_DEBUG
552 void
553 cbdata::dump(StoreEntry *sentry) const
554 {
555 #if HASHED_CBDATA
556 void *p = (void *)hash.key;
557 #else
558 void *p = (void *)&data;
559 #endif
560 storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' :
561 '!', p, type, locks, file, line);
562 }
563
564 struct CBDataDumper : public unary_function<cbdata, void>
565 {
566 CBDataDumper(StoreEntry *anEntry):where(anEntry){}
567
568 void operator()(cbdata const &x)
569 {
570 x.dump(where);
571 }
572
573 StoreEntry *where;
574 };
575
576 #endif
577
578 static void
579 cbdataDump(StoreEntry * sentry)
580 {
581 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
582 #if CBDATA_DEBUG
583
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");
589
590 for (int i = 1; i < cbdata_types; i++) {
591 MemAllocator *pool = cbdata_index[i].pool;
592
593 if (pool) {
594 #if HASHED_CBDATA
595 int obj_size = pool->objectSize();
596 #else
597 int obj_size = pool->objectSize() - cbdata::Offset;
598 #endif
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);
600 }
601 }
602
603 #else
604 storeAppendPrintf(sentry, "detailed allocation information only available when compiled with CBDATA_DEBUG\n");
605
606 #endif
607
608 storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
609 }
610
611 CBDATA_CLASS_INIT(generic_cbdata);
612
613 #if CBDATA_DEBUG
614
615 struct CBDataCallDumper : public unary_function<CBDataCall, void>
616 {
617 CBDataCallDumper (StoreEntry *anEntry):where(anEntry){}
618
619 void operator()(CBDataCall const &x)
620 {
621 storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line);
622 }
623
624 StoreEntry *where;
625 };
626
627 struct CBDataHistoryDumper : public CBDataDumper
628 {
629 CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry){}
630
631 void operator()(cbdata const &x)
632 {
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");
638 }
639
640 StoreEntry *where;
641 CBDataCallDumper callDumper;
642 };
643
644 void
645 cbdataDumpHistory(StoreEntry *sentry)
646 {
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);
651 }
652
653 #endif