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