cbdata.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
3  *
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.
7  */
8 
9 /* DEBUG: section 45 Callback Data Registry */
10 
11 #include "squid.h"
12 #include "cbdata.h"
13 #include "Generic.h"
14 #include "mem/Pool.h"
15 #include "mgr/Registration.h"
16 #include "Store.h"
17 
18 #include <climits>
19 
20 #if USE_CBDATA_DEBUG
21 #include <algorithm>
22 #include <vector>
23 #endif
24 
25 #if WITH_VALGRIND
26 #include <map>
27 #endif
28 
29 static int cbdataCount = 0;
30 #if USE_CBDATA_DEBUG
31 dlink_list cbdataEntries;
32 #endif
33 
34 #if USE_CBDATA_DEBUG
35 
36 class CBDataCall
37 {
38 
39 public:
40  CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine) {}
41 
42  char const *label;
43  char const *file;
44  int line;
45 };
46 
47 #endif
48 
49 #define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
50 
60 class cbdata
61 {
62 #if !WITH_VALGRIND
63 public:
64  void *operator new(size_t, void *where) {return where;}
69  void operator delete(void *, void *) {}
70 #else
72 #endif
73 
76 public:
77 #if USE_CBDATA_DEBUG
78 
79  void dump(StoreEntry *)const;
80 #endif
81  cbdata() :
82  valid(0),
83  locks(0),
85 #if USE_CBDATA_DEBUG
86  file(NULL),
87  line(0),
88 #endif
89  cookie(0),
90  data(NULL)
91  {}
92  ~cbdata();
93 
94  int valid;
95  int32_t locks;
97 #if USE_CBDATA_DEBUG
98 
99  void addHistory(char const *label, char const *aFile, int aLine) {
100  if (calls.size() > 1000)
101  return;
102 
103  calls.push_back(new CBDataCall(label, aFile, aLine));
104  }
105 
106  dlink_node link;
107  const char *file;
108  int line;
109  std::vector<CBDataCall*> calls; // used as a stack with random access operator
110 #endif
111 
112  /* cookie used while debugging */
113  long cookie;
114  void check(int) const {assert(cookie == ((long)this ^ Cookie));}
115  static const long Cookie;
116 
117 #if !WITH_VALGRIND
118  size_t dataSize() const { return sizeof(data);}
119  static long MakeOffset();
120  static const long Offset;
121 #endif
122  /* MUST be the last per-instance member */
123  void *data;
124 };
125 
126 const long cbdata::Cookie((long)0xDEADBEEF);
127 #if !WITH_VALGRIND
128 const long cbdata::Offset(MakeOffset());
129 
130 long
131 cbdata::MakeOffset()
132 {
133  cbdata *zero = (cbdata *)0L;
134  void **dataOffset = &zero->data;
135  return (long)dataOffset;
136 }
137 #endif
138 
140 #if USE_CBDATA_DEBUG
141 static OBJH cbdataDumpHistory;
142 #endif
143 
144 struct CBDataIndex {
146 }
147 *cbdata_index = NULL;
148 
149 int cbdata_types = 0;
150 
151 #if WITH_VALGRIND
152 static std::map<const void *, cbdata *> cbdata_htable;
153 #endif
154 
156 {
157 #if USE_CBDATA_DEBUG
158 
159  while (!calls.empty()) {
160  delete calls.back();
161  calls.pop_back();
162  }
163 
164 #endif
165 
166 #if WITH_VALGRIND
167  void *p = data;
168 #else
169  void *p = this;
170 #endif
172 }
173 
174 static void
176 {
177  char *label;
178  assert (type == cbdata_types + 1);
179 
180  cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
181  memset(&cbdata_index[type], 0, sizeof(*cbdata_index));
182  cbdata_types = type;
183 
184  label = (char *)xmalloc(strlen(name) + 20);
185 
186  snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type);
187 
188 #if !WITH_VALGRIND
189  assert((size_t)cbdata::Offset == (sizeof(cbdata) - ((cbdata *)NULL)->dataSize()));
190  size += cbdata::Offset;
191 #endif
192 
193  cbdata_index[type].pool = memPoolCreate(label, size);
194 }
195 
198 {
199  if (type)
200  return type;
201 
202  type = (cbdata_type)(cbdata_types + 1);
203 
204  cbdataInternalInitType(type, name, size);
205 
206  return type;
207 }
208 
209 void
211 {
212  Mgr::RegisterAction("cbdata",
213  "Callback Data Registry Contents",
214  cbdataDump, 0, 1);
215 #if USE_CBDATA_DEBUG
216 
217  Mgr::RegisterAction("cbdatahistory",
218  "Detailed call history for all current cbdata contents",
219  cbdataDumpHistory, 0, 1);
220 #endif
221 }
222 
223 void *
224 cbdataInternalAlloc(cbdata_type type, const char *file, int line)
225 {
226  cbdata *c;
227  void *p;
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
231  */
232 #if WITH_VALGRIND
233  c = new cbdata;
234  p = cbdata_index[type].pool->alloc();
235  c->data = p;
236  cbdata_htable.emplace(p,c);
237 #else
238  c = new (cbdata_index[type].pool->alloc()) cbdata;
239  p = (void *)&c->data;
240 #endif
241 
242  c->type = type;
243  c->valid = 1;
244  c->locks = 0;
245  c->cookie = (long) c ^ cbdata::Cookie;
246  ++cbdataCount;
247 #if USE_CBDATA_DEBUG
248 
249  c->file = file;
250  c->line = line;
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);
255 #else
256  debugs(45, 9, "Allocating " << p);
257 #endif
258 
259  return p;
260 }
261 
262 void
263 cbdataRealFree(cbdata *c, const char *file, const int line)
264 {
265 #if WITH_VALGRIND
266  void *p = c->data;
267 #else
268  void *p = (void *)&c->data;
269 #endif
270 
271  --cbdataCount;
272 #if USE_CBDATA_DEBUG
273  debugs(45, 3, "Freeing " << p << ' ' << file << ':' << line);
274  dlinkDelete(&c->link, &cbdataEntries);
275 #else
276  debugs(45, 9, "Freeing " << p);
277 #endif
278 
279 #if WITH_VALGRIND
280  cbdata_htable.erase(p);
281  delete c;
282 #else
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
292  */
293  c->cbdata::~cbdata();
294 #endif
295 }
296 
297 void *
298 cbdataInternalFree(void *p, const char *file, int line)
299 {
300  cbdata *c;
301 #if WITH_VALGRIND
302  c = cbdata_htable.at(p);
303 #else
304  c = (cbdata *) (((char *) p) - cbdata::Offset);
305 #endif
306 #if USE_CBDATA_DEBUG
307  debugs(45, 3, p << " " << file << ":" << line);
308 #else
309  debugs(45, 9, p);
310 #endif
311 
312  c->check(__LINE__);
313  assert(c->valid);
314  c->valid = 0;
315 #if USE_CBDATA_DEBUG
316 
317  c->addHistory("Free", file, line);
318 #endif
319 
320  if (c->locks) {
321  debugs(45, 9, p << " has " << c->locks << " locks, not freeing");
322  return NULL;
323  }
324 
325  cbdataRealFree(c, file, line);
326  return NULL;
327 }
328 
329 void
330 #if USE_CBDATA_DEBUG
331 cbdataInternalLockDbg(const void *p, const char *file, int line)
332 #else
333 cbdataInternalLock(const void *p)
334 #endif
335 {
336  cbdata *c;
337 
338  if (p == NULL)
339  return;
340 
341 #if WITH_VALGRIND
342  c = cbdata_htable.at(p);
343 #else
344  c = (cbdata *) (((char *) p) - cbdata::Offset);
345 #endif
346 
347 #if USE_CBDATA_DEBUG
348  debugs(45, 3, p << "=" << (c ? c->locks + 1 : -1) << " " << file << ":" << line);
349  c->addHistory("Reference", file, line);
350 #else
351  debugs(45, 9, p << "=" << (c ? c->locks + 1 : -1));
352 #endif
353 
354  c->check(__LINE__);
355 
356  assert(c->locks < INT_MAX);
357 
358  ++ c->locks;
359 }
360 
361 void
362 #if USE_CBDATA_DEBUG
363 cbdataInternalUnlockDbg(const void *p, const char *file, int line)
364 #else
365 cbdataInternalUnlock(const void *p)
366 #endif
367 {
368  cbdata *c;
369 
370  if (p == NULL)
371  return;
372 
373 #if WITH_VALGRIND
374  c = cbdata_htable.at(p);
375 #else
376  c = (cbdata *) (((char *) p) - cbdata::Offset);
377 #endif
378 
379 #if USE_CBDATA_DEBUG
380  debugs(45, 3, p << "=" << (c ? c->locks - 1 : -1) << " " << file << ":" << line);
381  c->addHistory("Dereference", file, line);
382 #else
383  debugs(45, 9, p << "=" << (c ? c->locks - 1 : -1));
384 #endif
385 
386  c->check(__LINE__);
387 
388  assert(c != NULL);
389 
390  assert(c->locks > 0);
391 
392  -- c->locks;
393 
394  if (c->locks)
395  return;
396 
397  if (c->valid) {
398 #if USE_CBDATA_DEBUG
399  debugs(45, 3, "CBDATA valid with no references ... cbdata=" << p << " " << file << ":" << line);
400 #endif
401  return;
402  }
403 
404 #if USE_CBDATA_DEBUG
405  cbdataRealFree(c, file, line);
406 #else
407  cbdataRealFree(c, NULL, 0);
408 #endif
409 }
410 
411 int
412 cbdataReferenceValid(const void *p)
413 {
414  cbdata *c;
415 
416  if (p == NULL)
417  return 1; /* A NULL pointer cannot become invalid */
418 
419  debugs(45, 9, p);
420 
421 #if WITH_VALGRIND
422  c = cbdata_htable.at(p);
423 #else
424  c = (cbdata *) (((char *) p) - cbdata::Offset);
425 #endif
426 
427  c->check(__LINE__);
428 
429  assert(c->locks > 0);
430 
431  return c->valid;
432 }
433 
434 int
435 #if USE_CBDATA_DEBUG
436 cbdataInternalReferenceDoneValidDbg(void **pp, void **tp, const char *file, int line)
437 #else
438 cbdataInternalReferenceDoneValid(void **pp, void **tp)
439 #endif
440 {
441  void *p = (void *) *pp;
442  int valid = cbdataReferenceValid(p);
443  *pp = NULL;
444 #if USE_CBDATA_DEBUG
445 
446  cbdataInternalUnlockDbg(p, file, line);
447 #else
448 
450 #endif
451 
452  if (valid) {
453  *tp = p;
454  return 1;
455  } else {
456  *tp = NULL;
457  return 0;
458  }
459 }
460 
461 #if USE_CBDATA_DEBUG
462 void
463 cbdata::dump(StoreEntry *sentry) const
464 {
465 #if WITH_VALGRIND
466  void *p = data;
467 #else
468  void *p = (void *)&data;
469 #endif
470  storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' :
471  '!', p, type, locks, file, line);
472 }
473 
474 struct CBDataDumper : public unary_function<cbdata, void> {
475  CBDataDumper(StoreEntry *anEntry):where(anEntry) {}
476 
477  void operator()(cbdata const &x) {
478  x.dump(where);
479  }
480 
481  StoreEntry *where;
482 };
483 
484 #endif
485 
486 static void
488 {
489  storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
490 #if USE_CBDATA_DEBUG
491 
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");
497 
498  for (int i = 1; i < cbdata_types; ++i) {
499  MemAllocator *pool = cbdata_index[i].pool;
500 
501  if (pool) {
502 #if WITH_VALGRIND
503  int obj_size = pool->objectSize();
504 #else
505  int obj_size = pool->objectSize() - cbdata::Offset;
506 #endif
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());
508  }
509  }
510 
511 #else
512  storeAppendPrintf(sentry, "detailed allocation information only available when compiled with --enable-debug-cbdata\n");
513 
514 #endif
515 
516  storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
517 }
518 
519 CallbackData &
521 {
522  if (data_ != other.data_) { // assignment to self and no-op assignments
523  auto old = data_;
524  data_ = cbdataReference(other.data_);
525  cbdataReferenceDone(old);
526  }
527  return *this;
528 }
529 
531 
532 #if USE_CBDATA_DEBUG
533 
534 struct CBDataCallDumper : public unary_function<CBDataCall, void> {
535  CBDataCallDumper (StoreEntry *anEntry):where(anEntry) {}
536 
537  void operator()(CBDataCall * const &x) {
538  storeAppendPrintf(where, "%s\t%s\t%d\n", x->label, x->file, x->line);
539  }
540 
541  StoreEntry *where;
542 };
543 
544 struct CBDataHistoryDumper : public CBDataDumper {
545  CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry) {}
546 
547  void operator()(cbdata const &x) {
548  CBDataDumper::operator()(x);
549  storeAppendPrintf(where, "\n");
550  storeAppendPrintf(where, "Action\tFile\tLine\n");
551  std::for_each (x.calls.begin(), x.calls.end(), callDumper);
552  storeAppendPrintf(where, "\n");
553  }
554 
555  StoreEntry *where;
556  CBDataCallDumper callDumper;
557 };
558 
559 void
560 cbdataDumpHistory(StoreEntry *sentry)
561 {
562  storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
563  storeAppendPrintf(sentry, "Pointer\tType\tLocks\tAllocated by\n");
564  CBDataHistoryDumper dumper(sentry);
565  for_each (cbdataEntries, dumper);
566 }
567 
568 #endif
569 
#define assert(EX)
Definition: assert.h:17
#define cbdataReferenceDone(var)
Definition: cbdata.h:350
void * cbdataInternalFree(void *p, const char *file, int line)
Definition: cbdata.cc:298
#define MEMPROXY_CLASS(CLASS)
Definition: cbdata.cc:60
int type
Definition: errorpage.cc:78
virtual void freeOne(void *)=0
static int cbdataCount
Definition: cbdata.cc:29
int i
Definition: membanger.c:49
int cbdata_type
Definition: cbdata.h:195
CallbackData & operator=(const CallbackData &other)
Definition: cbdata.cc:520
long cookie
Definition: cbdata.cc:113
char * p
Definition: membanger.c:43
void OBJH(StoreEntry *)
Definition: forward.h:44
T & for_each(L const &head, T &visitor)
Definition: Generic.h:23
cbdata_type cbdataInternalAddType(cbdata_type type, const char *name, int size)
Definition: cbdata.cc:197
void cbdataInternalUnlock(const void *p)
Definition: cbdata.cc:365
#define memPoolCreate
Definition: Pool.h:325
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
#define cbdataReference(var)
Definition: cbdata.h:341
void RegisterAction(char const *action, char const *desc, OBJH *handler, int pw_req_flag, int atomic)
Definition: Registration.cc:16
int cbdataInternalReferenceDoneValid(void **pp, void **tp)
Definition: cbdata.cc:438
void * xrealloc(void *s, size_t sz)
Definition: xalloc.cc:137
static size_t dataSize(DB_ENTRY *data)
virtual char const * objectType() const
Definition: Pool.cc:107
struct CBDataIndex * cbdata_index
static std::map< const void *, cbdata * > cbdata_htable
Definition: cbdata.cc:152
Mem::Meter inuse
Definition: Pool.h:99
MemAllocator * pool
Definition: cbdata.cc:145
virtual MemPoolMeter const & getMeter() const =0
bool SIGHDLR int STUB void int
Definition: stub_tools.cc:68
cbdata()
Definition: cbdata.cc:81
static const long Cookie
Definition: cbdata.cc:115
an old-style void* callback parameter
Definition: cbdata.h:376
#define xmalloc
int32_t locks
Definition: cbdata.cc:95
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
void * cbdataInternalAlloc(cbdata_type type, const char *file, int line)
Definition: cbdata.cc:224
static void cbdataInternalInitType(cbdata_type type, const char *name, int size)
Definition: cbdata.cc:175
cbdata_type type
Definition: cbdata.cc:96
static OBJH cbdataDump
Definition: cbdata.cc:139
int valid
Definition: cbdata.cc:94
int cbdata_types
Definition: cbdata.cc:149
int const char size_t
Definition: stub_liblog.cc:86
void * data
Definition: cbdata.cc:123
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:412
void cbdataInternalLock(const void *p)
Definition: cbdata.cc:333
virtual size_t objectSize() const =0
#define INT_MAX
Definition: types.h:76
void * data_
raw callback data, maybe invalid
Definition: cbdata.h:392
void cbdataRegisterWithCacheManager(void)
Definition: cbdata.cc:210
void check(int) const
Definition: cbdata.cc:114
ssize_t currentLevel() const
Definition: Meter.h:28
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:875
static const cbdata_type CBDATA_UNKNOWN
Definition: cbdata.h:196
#define NULL
Definition: types.h:166
void cbdataRealFree(cbdata *c, const char *file, const int line)
Definition: cbdata.cc:263
~cbdata()
Definition: cbdata.cc:155
int size
Definition: ModDevPoll.cc:77
virtual void * alloc()=0

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors