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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors