2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 45 Callback Data Registry */
15 #include "mgr/Registration.h"
29 static int cbdataCount
= 0;
31 dlink_list cbdataEntries
;
40 CBDataCall (char const *callLabel
, char const *aFile
, int aLine
) : label(callLabel
), file(aFile
), line(aLine
) {}
49 #define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
52 * Manage a set of registered callback data pointers.
53 * One of the easiest ways to make Squid coredump is to issue a
54 * callback to for some data structure which has previously been
55 * freed. With this class, we register (add) callback data
56 * pointers, lock them just before registering the callback function,
57 * validate them before issuing the callback, and then free them
64 void *operator new(size_t, void *where
) {return where
;}
66 * Only ever invoked when placement new throws
67 * an exception. Used to prevent an incorrect free.
69 void operator delete(void *, void *) {}
71 MEMPROXY_CLASS(cbdata
);
74 /** \todo examine making cbdata templated on this - so we get type
75 * safe access to data - RBC 20030902 */
79 void dump(StoreEntry
*)const;
99 void addHistory(char const *label
, char const *aFile
, int aLine
) {
100 if (calls
.size() > 1000)
103 calls
.push_back(new CBDataCall(label
, aFile
, aLine
));
109 std::vector
<CBDataCall
*> calls
; // used as a stack with random access operator
112 /* cookie used while debugging */
114 void check(int) const {assert(cookie
== ((long)this ^ Cookie
));}
115 static const long Cookie
;
118 size_t dataSize() const { return sizeof(data
);}
119 static long MakeOffset();
120 static const long Offset
;
122 /* MUST be the last per-instance member */
126 const long cbdata::Cookie((long)0xDEADBEEF);
128 const long cbdata::Offset(MakeOffset());
133 cbdata
*zero
= (cbdata
*)0L;
134 void **dataOffset
= &zero
->data
;
135 return (long)dataOffset
;
139 static OBJH cbdataDump
;
141 static OBJH cbdataDumpHistory
;
147 *cbdata_index
= NULL
;
149 int cbdata_types
= 0;
152 static std::map
<const void *, cbdata
*> cbdata_htable
;
159 while (!calls
.empty()) {
171 cbdata_index
[type
].pool
->freeOne(p
);
175 cbdataInternalInitType(cbdata_type type
, const char *name
, int size
)
178 assert (type
== cbdata_types
+ 1);
180 cbdata_index
= (CBDataIndex
*)xrealloc(cbdata_index
, (type
+ 1) * sizeof(*cbdata_index
));
181 memset(&cbdata_index
[type
], 0, sizeof(*cbdata_index
));
184 label
= (char *)xmalloc(strlen(name
) + 20);
186 snprintf(label
, strlen(name
) + 20, "cbdata %s (%d)", name
, (int) type
);
189 assert((size_t)cbdata::Offset
== (sizeof(cbdata
) - ((cbdata
*)NULL
)->dataSize()));
190 size
+= cbdata::Offset
;
193 cbdata_index
[type
].pool
= memPoolCreate(label
, size
);
197 cbdataInternalAddType(cbdata_type type
, const char *name
, int size
)
202 type
= (cbdata_type
)(cbdata_types
+ 1);
204 cbdataInternalInitType(type
, name
, size
);
210 cbdataRegisterWithCacheManager(void)
212 Mgr::RegisterAction("cbdata",
213 "Callback Data Registry Contents",
217 Mgr::RegisterAction("cbdatahistory",
218 "Detailed call history for all current cbdata contents",
219 cbdataDumpHistory
, 0, 1);
224 cbdataInternalAlloc(cbdata_type type
, const char *file
, int line
)
228 assert(type
> 0 && type
<= cbdata_types
);
229 /* placement new: the pool alloc gives us cbdata + user type memory space
230 * and we init it with cbdata at the start of it
234 p
= cbdata_index
[type
].pool
->alloc();
236 cbdata_htable
.emplace(p
,c
);
238 c
= new (cbdata_index
[type
].pool
->alloc()) cbdata
;
239 p
= (void *)&c
->data
;
245 c
->cookie
= (long) c
^ cbdata::Cookie
;
251 c
->calls
= std::vector
<CBDataCall
*> ();
252 c
->addHistory("Alloc", file
, line
);
253 dlinkAdd(c
, &c
->link
, &cbdataEntries
);
254 debugs(45, 3, "Allocating " << p
<< " " << file
<< ":" << line
);
256 debugs(45, 9, "Allocating " << p
);
263 cbdataRealFree(cbdata
*c
, const char *file
, const int line
)
268 void *p
= (void *)&c
->data
;
273 debugs(45, 3, "Freeing " << p
<< ' ' << file
<< ':' << line
);
274 dlinkDelete(&c
->link
, &cbdataEntries
);
276 debugs(45, 9, "Freeing " << p
);
280 cbdata_htable
.erase(p
);
283 /* This is ugly. But: operator delete doesn't get
284 * the type parameter, so we can't use that
285 * to free the memory.
286 * So, we free it ourselves.
287 * Note that this means a non-placement
288 * new would be a seriously bad idea.
289 * Lastly, if we where a templated class,
290 * we could use the normal delete operator
291 * and it would Just Work. RBC 20030902
293 c
->cbdata::~cbdata();
298 cbdataInternalFree(void *p
, const char *file
, int line
)
302 c
= cbdata_htable
.at(p
);
304 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
307 debugs(45, 3, p
<< " " << file
<< ":" << line
);
317 c
->addHistory("Free", file
, line
);
321 debugs(45, 9, p
<< " has " << c
->locks
<< " locks, not freeing");
325 cbdataRealFree(c
, file
, line
);
331 cbdataInternalLockDbg(const void *p
, const char *file
, int line
)
333 cbdataInternalLock(const void *p
)
342 c
= cbdata_htable
.at(p
);
344 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
348 debugs(45, 3, p
<< "=" << (c
? c
->locks
+ 1 : -1) << " " << file
<< ":" << line
);
349 c
->addHistory("Reference", file
, line
);
351 debugs(45, 9, p
<< "=" << (c
? c
->locks
+ 1 : -1));
356 assert(c
->locks
< INT_MAX
);
363 cbdataInternalUnlockDbg(const void *p
, const char *file
, int line
)
365 cbdataInternalUnlock(const void *p
)
374 c
= cbdata_htable
.at(p
);
376 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
380 debugs(45, 3, p
<< "=" << (c
? c
->locks
- 1 : -1) << " " << file
<< ":" << line
);
381 c
->addHistory("Dereference", file
, line
);
383 debugs(45, 9, p
<< "=" << (c
? c
->locks
- 1 : -1));
390 assert(c
->locks
> 0);
399 debugs(45, 3, "CBDATA valid with no references ... cbdata=" << p
<< " " << file
<< ":" << line
);
405 cbdataRealFree(c
, file
, line
);
407 cbdataRealFree(c
, NULL
, 0);
412 cbdataReferenceValid(const void *p
)
417 return 1; /* A NULL pointer cannot become invalid */
422 c
= cbdata_htable
.at(p
);
424 c
= (cbdata
*) (((char *) p
) - cbdata::Offset
);
429 assert(c
->locks
> 0);
436 cbdataInternalReferenceDoneValidDbg(void **pp
, void **tp
, const char *file
, int line
)
438 cbdataInternalReferenceDoneValid(void **pp
, void **tp
)
441 void *p
= (void *) *pp
;
442 int valid
= cbdataReferenceValid(p
);
446 cbdataInternalUnlockDbg(p
, file
, line
);
449 cbdataInternalUnlock(p
);
463 cbdata::dump(StoreEntry
*sentry
) const
468 void *p
= (void *)&data
;
470 storeAppendPrintf(sentry
, "%c%p\t%d\t%d\t%20s:%-5d\n", valid
? ' ' :
471 '!', p
, type
, locks
, file
, line
);
474 struct CBDataDumper
: public unary_function
<cbdata
, void> {
475 CBDataDumper(StoreEntry
*anEntry
):where(anEntry
) {}
477 void operator()(cbdata
const &x
) {
487 cbdataDump(StoreEntry
* sentry
)
489 storeAppendPrintf(sentry
, "%d cbdata entries\n", cbdataCount
);
492 storeAppendPrintf(sentry
, "Pointer\tType\tLocks\tAllocated by\n");
493 CBDataDumper
dumper(sentry
);
494 for_each (cbdataEntries
, dumper
);
495 storeAppendPrintf(sentry
, "\n");
496 storeAppendPrintf(sentry
, "types\tsize\tallocated\ttotal\n");
498 for (int i
= 1; i
< cbdata_types
; ++i
) {
499 MemAllocator
*pool
= cbdata_index
[i
].pool
;
503 int obj_size
= pool
->objectSize();
505 int obj_size
= pool
->objectSize() - cbdata::Offset
;
507 storeAppendPrintf(sentry
, "%s\t%d\t%ld\t%ld\n", pool
->objectType() + 7, obj_size
, (long int)pool
->getMeter().inuse
.currentLevel(), (long int)obj_size
* pool
->getMeter().inuse
.currentLevel());
512 storeAppendPrintf(sentry
, "detailed allocation information only available when compiled with --enable-debug-cbdata\n");
516 storeAppendPrintf(sentry
, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
519 CBDATA_CLASS_INIT(generic_cbdata
);
523 struct CBDataCallDumper
: public unary_function
<CBDataCall
, void> {
524 CBDataCallDumper (StoreEntry
*anEntry
):where(anEntry
) {}
526 void operator()(CBDataCall
* const &x
) {
527 storeAppendPrintf(where
, "%s\t%s\t%d\n", x
->label
, x
->file
, x
->line
);
533 struct CBDataHistoryDumper
: public CBDataDumper
{
534 CBDataHistoryDumper(StoreEntry
*anEntry
):CBDataDumper(anEntry
),where(anEntry
), callDumper(anEntry
) {}
536 void operator()(cbdata
const &x
) {
537 CBDataDumper::operator()(x
);
538 storeAppendPrintf(where
, "\n");
539 storeAppendPrintf(where
, "Action\tFile\tLine\n");
540 std::for_each (x
.calls
.begin(), x
.calls
.end(), callDumper
);
541 storeAppendPrintf(where
, "\n");
545 CBDataCallDumper callDumper
;
549 cbdataDumpHistory(StoreEntry
*sentry
)
551 storeAppendPrintf(sentry
, "%d cbdata entries\n", cbdataCount
);
552 storeAppendPrintf(sentry
, "Pointer\tType\tLocks\tAllocated by\n");
553 CBDataHistoryDumper
dumper(sentry
);
554 for_each (cbdataEntries
, dumper
);