CMS 3D CMS Logo

memory_proxies.cc
Go to the documentation of this file.
1 #include <memory>
2 #include <cassert>
3 #include <atomic>
4 #include <array>
5 #include <mutex>
6 #include <limits>
7 #include <cstddef>
8 #include <malloc.h>
9 #define ALLOC_USE_PTHREADS
10 #if defined(ALLOC_USE_PTHREADS)
11 #include <pthread.h>
12 #else
13 #include <unistd.h>
14 #include <sys/syscall.h>
15 #endif
16 
19 
20 #include <dlfcn.h> // dlsym
21 
22 #if !defined(__x86_64__) && !defined(__i386__)
23 #define USE_LOCAL_MALLOC
24 #endif
25 #if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 28)
26 //needed for sl7
27 #define USE_LOCAL_MALLOC
28 #endif
29 
30 namespace {
31  std::atomic<bool>& alloc_monitor_running_state() {
32  static std::atomic<bool> s_state = false;
33  return s_state;
34  }
35 
36  template <typename T>
37  T get(const char* iName) {
38  void* original = dlsym(RTLD_NEXT, iName);
40  return reinterpret_cast<T>(original);
41  }
42 
43  inline auto thread_id() {
44 #if defined(ALLOC_USE_PTHREADS)
45  /*NOTE: if use pthread_self, the values returned by linux had lots of hash collisions when using a simple % hash. Worked better if first divided value by 0x700 and then did %. [test done on el8] */
46  return pthread_self();
47 #else
48  return syscall(SYS_gettid);
49 #endif
50  }
51 #ifdef USE_LOCAL_MALLOC
52  // this is a very simple-minded allocator used for any allocations
53  // before we've finished our setup. In particular, this avoids a
54  // chicken/egg problem if dlsym() allocates any memory.
55  // Size was chosen to be 2x what ARM64 uses as an emergency buffer
56  // for libstdc++ exception handling.
57  constexpr auto max_align = alignof(std::max_align_t);
58  alignas(max_align) char tmpbuff[131072];
59  unsigned long tmppos = 0;
60  unsigned long tmpallocs = 0;
61 
62  void* local_malloc(size_t size) noexcept {
63  // round up so next alloc is aligned
64  size = ((size + max_align - 1) / max_align) * max_align;
65  if (tmppos + size < sizeof(tmpbuff)) {
66  void* retptr = tmpbuff + tmppos;
67  tmppos += size;
68  ++tmpallocs;
69  return retptr;
70  } else {
71  return nullptr;
72  }
73  }
74 
75  //can use local_malloc since static memory buffers are guaranteed to be zero initialized
76  void* local_calloc(size_t nitems, size_t item_size) noexcept { return local_malloc(nitems * item_size); }
77 
78  inline bool is_local_alloc(void* ptr) noexcept { return ptr >= (void*)tmpbuff && ptr <= (void*)(tmpbuff + tmppos); }
79 
80  // the pointers in this struct should only be modified during
81  // global construction at program startup, so thread safety
82  // should not be an issue.
83  struct Originals {
84  inline static void init() noexcept {
85  if (not set) {
86  set = true; // must be first to avoid recursion
87  malloc = get<decltype(&::malloc)>("malloc");
88  calloc = get<decltype(&::calloc)>("calloc");
89  }
90  }
91  CMS_SA_ALLOW static decltype(&::malloc) malloc;
92  CMS_SA_ALLOW static decltype(&::calloc) calloc;
93  CMS_SA_ALLOW static bool set;
94  };
95 
96  decltype(&::malloc) Originals::malloc = local_malloc;
97  decltype(&::calloc) Originals::calloc = local_calloc;
98  bool Originals::set = false;
99 #else
100  constexpr inline bool is_local_alloc(void* ptr) noexcept { return false; }
101 #endif
102 
103  struct ThreadTracker {
104  static constexpr unsigned int kEntries = 128;
105  using entry_type = decltype(thread_id());
106  std::array<std::atomic<entry_type>, kEntries> used_threads_;
107  std::array<std::mutex, kEntries> used_threads_mutex_;
108 
109  ThreadTracker() {
110  //put a value which will not match the % used when looking up the entry
111  entry_type entry = 0;
112  for (auto& v : used_threads_) {
113  v = ++entry;
114  }
115  }
116 
117  std::size_t thread_index(entry_type id) const {
118 #if defined(ALLOC_USE_PTHREADS)
119  return (id / 0x700) % kEntries;
120 #else
121  return id % kEntries;
122 #endif
123  }
124 
125  //returns true if the thread had not already stopped reporting
126  bool stop_reporting() {
127  auto id = thread_id();
128  auto index = thread_index(id);
129  //are we already in this thread?
130  if (id == used_threads_[index]) {
131  return false;
132  }
133  used_threads_mutex_[index].lock();
134  used_threads_[index] = id;
135  return true;
136  }
137 
138  void start_reporting() {
139  auto id = thread_id();
140  auto index = thread_index(id);
141  auto& v = used_threads_[index];
142  if (v == static_cast<entry_type>(index + 1)) {
143  return;
144  }
145  assert(v == id);
146  v = index + 1;
147  used_threads_mutex_[index].unlock();
148  }
149  };
150 
151  static ThreadTracker& getTracker() {
152  static ThreadTracker s_tracker;
153  return s_tracker;
154  }
155 
156 } // namespace
157 
158 using namespace cms::perftools;
159 
160 extern "C" {
161 void alloc_monitor_start() { alloc_monitor_running_state() = true; }
162 void alloc_monitor_stop() { alloc_monitor_running_state() = false; }
163 
164 bool alloc_monitor_stop_thread_reporting() { return getTracker().stop_reporting(); }
165 
166 void alloc_monitor_start_thread_reporting() { getTracker().start_reporting(); }
167 
168 //----------------------------------------------------------------
169 //C memory functions
170 
171 #ifdef USE_LOCAL_MALLOC
172 void* malloc(size_t size) noexcept {
173  const auto original = Originals::malloc;
174  Originals::init();
175  if (not alloc_monitor_running_state()) {
176  return original(size);
177  }
178  auto& reg = AllocMonitorRegistry::instance();
179  return reg.allocCalled(
180  size, [size, original]() { return original(size); }, [](auto ret) { return malloc_usable_size(ret); });
181 }
182 
183 void* calloc(size_t nitems, size_t item_size) noexcept {
184  const auto original = Originals::calloc;
185  Originals::init();
186  if (not alloc_monitor_running_state()) {
187  return original(nitems, item_size);
188  }
189  auto& reg = AllocMonitorRegistry::instance();
190  return reg.allocCalled(
191  nitems * item_size,
192  [nitems, item_size, original]() { return original(nitems, item_size); },
193  [](auto ret) { return malloc_usable_size(ret); });
194 }
195 #else
196 void* malloc(size_t size) noexcept {
197  CMS_SA_ALLOW static const auto original = get<decltype(&::malloc)>("malloc");
198  if (not alloc_monitor_running_state()) {
199  return original(size);
200  }
201  auto& reg = AllocMonitorRegistry::instance();
202  return reg.allocCalled(
203  size, [size]() { return original(size); }, [](auto ret) { return malloc_usable_size(ret); });
204 }
205 
206 void* calloc(size_t nitems, size_t item_size) noexcept {
207  CMS_SA_ALLOW static const auto original = get<decltype(&::calloc)>("calloc");
208  if (not alloc_monitor_running_state()) {
209  return original(nitems, item_size);
210  }
211  auto& reg = AllocMonitorRegistry::instance();
212  return reg.allocCalled(
213  nitems * item_size,
214  [nitems, item_size]() { return original(nitems, item_size); },
215  [](auto ret) { return malloc_usable_size(ret); });
216 }
217 #endif
218 
219 void* realloc(void* ptr, size_t size) noexcept {
220  CMS_SA_ALLOW static const auto original = get<decltype(&::realloc)>("realloc");
221  if (not alloc_monitor_running_state()) {
222  return original(ptr, size);
223  }
224  size_t oldsize = malloc_usable_size(ptr);
225  void* ret;
226  auto& reg = AllocMonitorRegistry::instance();
227  {
228  //incase this calls malloc/free
229  [[maybe_unused]] auto g = reg.makeGuard();
230  ret = original(ptr, size);
231  }
232  size_t used = malloc_usable_size(ret);
233  if (used != oldsize) {
234  reg.deallocCalled(
235  ptr, [](auto) {}, [oldsize](auto) { return oldsize; });
236  reg.allocCalled(
237  size, []() { return nullptr; }, [used](auto) { return used; });
238  }
239  return ret;
240 }
241 
242 void* aligned_alloc(size_t alignment, size_t size) noexcept {
243  CMS_SA_ALLOW static const auto original = get<decltype(&::aligned_alloc)>("aligned_alloc");
244  if (not alloc_monitor_running_state()) {
245  return original(alignment, size);
246  }
247 
248  auto& reg = AllocMonitorRegistry::instance();
249  return reg.allocCalled(
250  size,
251  [alignment, size]() { return original(alignment, size); },
252  [](auto ret) { return malloc_usable_size(ret); });
253 }
254 
255 //used by tensorflow
256 int posix_memalign(void** memptr, size_t alignment, size_t size) noexcept {
257  CMS_SA_ALLOW static const auto original = get<decltype(&::posix_memalign)>("posix_memalign");
258  if (not alloc_monitor_running_state()) {
259  return original(memptr, alignment, size);
260  }
261 
262  auto& reg = AllocMonitorRegistry::instance();
263  int ret;
264  reg.allocCalled(
265  size,
266  [&ret, memptr, alignment, size]() {
267  ret = original(memptr, alignment, size);
268  return *memptr;
269  },
270  [](auto ret) { return malloc_usable_size(ret); });
271  return ret;
272 }
273 
274 //used by libc
275 void* memalign(size_t alignment, size_t size) noexcept {
276  CMS_SA_ALLOW static const auto original = get<decltype(&::memalign)>("memalign");
277  if (not alloc_monitor_running_state()) {
278  return original(alignment, size);
279  }
280 
281  auto& reg = AllocMonitorRegistry::instance();
282  return reg.allocCalled(
283  size,
284  [alignment, size]() { return original(alignment, size); },
285  [](auto ret) { return malloc_usable_size(ret); });
286 }
287 
288 void free(void* ptr) noexcept {
289  CMS_SA_ALLOW static const auto original = get<decltype(&::free)>("free");
290  // ignore memory allocated from our static array at startup
291  if (not is_local_alloc(ptr)) {
292  if (not alloc_monitor_running_state()) {
293  original(ptr);
294  return;
295  }
296 
297  auto& reg = AllocMonitorRegistry::instance();
298  reg.deallocCalled(
299  ptr, [](auto ptr) { original(ptr); }, [](auto ptr) { return malloc_usable_size(ptr); });
300  }
301 }
302 } // extern "C"
303 
304 //----------------------------------------------------------------
305 //C++ memory functions
306 
307 #define CPP_MEM_OVERRIDE
308 
309 #if defined(CPP_MEM_OVERRIDE)
310 #include <new>
311 
312 void* operator new(std::size_t size) {
313  CMS_SA_ALLOW static const auto original = get<void* (*)(std::size_t)>("_Znwm");
314  if (not alloc_monitor_running_state()) {
315  return original(size);
316  }
317 
318  auto& reg = AllocMonitorRegistry::instance();
319  return reg.allocCalled(
320  size, [size]() { return original(size); }, [](auto ret) { return malloc_usable_size(ret); });
321 } //_Znwm
322 
323 void operator delete(void* ptr) noexcept {
324  CMS_SA_ALLOW static const auto original = get<void (*)(void*)>("_ZdlPv");
325  if (not alloc_monitor_running_state()) {
326  original(ptr);
327  return;
328  }
329 
330  auto& reg = AllocMonitorRegistry::instance();
331  reg.deallocCalled(
332  ptr, [](auto ptr) { original(ptr); }, [](auto ptr) { return malloc_usable_size(ptr); });
333 } //_ZdlPv
334 
335 void* operator new[](std::size_t size) {
336  CMS_SA_ALLOW static const auto original = get<void* (*)(std::size_t)>("_Znam");
337  if (not alloc_monitor_running_state()) {
338  return original(size);
339  }
340 
341  auto& reg = AllocMonitorRegistry::instance();
342  return reg.allocCalled(
343  size, [size]() { return original(size); }, [](auto ret) { return malloc_usable_size(ret); });
344 } //_Znam
345 
346 void operator delete[](void* ptr) noexcept {
347  CMS_SA_ALLOW static const auto original = get<void (*)(void*)>("_ZdaPv");
348 
349  if (not alloc_monitor_running_state()) {
350  original(ptr);
351  return;
352  }
353  auto& reg = AllocMonitorRegistry::instance();
354  reg.deallocCalled(
355  ptr, [](auto ptr) { original(ptr); }, [](auto ptr) { return malloc_usable_size(ptr); });
356 } //_ZdaPv
357 
358 void* operator new(std::size_t size, std::align_val_t al) {
359  CMS_SA_ALLOW static const auto original = get<void* (*)(std::size_t, std::align_val_t)>("_ZnwmSt11align_val_t");
360  if (not alloc_monitor_running_state()) {
361  return original(size, al);
362  }
363 
364  auto& reg = AllocMonitorRegistry::instance();
365  return reg.allocCalled(
366  size, [size, al]() { return original(size, al); }, [](auto ret) { return malloc_usable_size(ret); });
367 } //_ZnwmSt11align_val_t
368 
369 void* operator new[](std::size_t size, std::align_val_t al) {
370  CMS_SA_ALLOW static const auto original = get<void* (*)(std::size_t, std::align_val_t)>("_ZnamSt11align_val_t");
371 
372  if (not alloc_monitor_running_state()) {
373  return original(size, al);
374  }
375 
376  auto& reg = AllocMonitorRegistry::instance();
377  return reg.allocCalled(
378  size, [size, al]() { return original(size, al); }, [](auto ret) { return malloc_usable_size(ret); });
379 } //_ZnamSt11align_val_t
380 
381 void* operator new(std::size_t size, const std::nothrow_t& tag) noexcept {
382  CMS_SA_ALLOW static const auto original =
383  get<void* (*)(std::size_t, const std::nothrow_t&) noexcept>("_ZnwmRKSt9nothrow_t");
384 
385  if (not alloc_monitor_running_state()) {
386  return original(size, tag);
387  }
388 
389  auto& reg = AllocMonitorRegistry::instance();
390  return reg.allocCalled(
391  size, [size, &tag]() { return original(size, tag); }, [](auto ret) { return malloc_usable_size(ret); });
392 } //_ZnwmRKSt9nothrow_t
393 
394 void* operator new[](std::size_t size, const std::nothrow_t& tag) noexcept {
395  CMS_SA_ALLOW static const auto original =
396  get<void* (*)(std::size_t, const std::nothrow_t&) noexcept>("_ZnamRKSt9nothrow_t");
397 
398  if (not alloc_monitor_running_state()) {
399  return original(size, tag);
400  }
401 
402  auto& reg = AllocMonitorRegistry::instance();
403  return reg.allocCalled(
404  size, [size, &tag]() { return original(size, tag); }, [](auto ret) { return malloc_usable_size(ret); });
405 } //_ZnamRKSt9nothrow_t
406 
407 void* operator new(std::size_t size, std::align_val_t al, const std::nothrow_t& tag) noexcept {
408  CMS_SA_ALLOW static const auto original =
409  get<void* (*)(std::size_t, std::align_val_t, const std::nothrow_t&) noexcept>(
410  "_ZnwmSt11align_val_tRKSt9nothrow_t");
411 
412  if (not alloc_monitor_running_state()) {
413  return original(size, al, tag);
414  }
415 
416  auto& reg = AllocMonitorRegistry::instance();
417  return reg.allocCalled(
418  size, [size, al, &tag]() { return original(size, al, tag); }, [](auto ret) { return malloc_usable_size(ret); });
419 } //_ZnwmSt11align_val_tRKSt9nothrow_t
420 
421 void* operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t& tag) noexcept {
422  CMS_SA_ALLOW static const auto original =
423  get<void* (*)(std::size_t, std::align_val_t, const std::nothrow_t&) noexcept>(
424  "_ZnamSt11align_val_tRKSt9nothrow_t");
425 
426  if (not alloc_monitor_running_state()) {
427  return original(size, al, tag);
428  }
429 
430  auto& reg = AllocMonitorRegistry::instance();
431  return reg.allocCalled(
432  size, [size, al, &tag]() { return original(size, al, tag); }, [](auto ret) { return malloc_usable_size(ret); });
433 } //_ZnamSt11align_val_tRKSt9nothrow_t
434 
435 void operator delete(void* ptr, std::align_val_t al) noexcept {
436  CMS_SA_ALLOW static const auto original = get<void (*)(void*, std::align_val_t) noexcept>("_ZdlPvSt11align_val_t");
437 
438  if (not alloc_monitor_running_state()) {
439  original(ptr, al);
440  return;
441  }
442  auto& reg = AllocMonitorRegistry::instance();
443  reg.deallocCalled(
444  ptr, [al](auto ptr) { original(ptr, al); }, [](auto ptr) { return malloc_usable_size(ptr); });
445 } //_ZdlPvSt11align_val_t
446 
447 void operator delete[](void* ptr, std::align_val_t al) noexcept {
448  CMS_SA_ALLOW static const auto original = get<void (*)(void*, std::align_val_t) noexcept>("_ZdaPvSt11align_val_t");
449 
450  if (not alloc_monitor_running_state()) {
451  original(ptr, al);
452  return;
453  }
454  auto& reg = AllocMonitorRegistry::instance();
455  reg.deallocCalled(
456  ptr, [al](auto ptr) { original(ptr, al); }, [](auto ptr) { return malloc_usable_size(ptr); });
457 } //_ZdaPvSt11align_val_t
458 
459 void operator delete(void* ptr, std::size_t sz) noexcept {
460  CMS_SA_ALLOW static const auto original = get<void (*)(void*, std::size_t) noexcept>("_ZdlPvm");
461 
462  if (not alloc_monitor_running_state()) {
463  original(ptr, sz);
464  return;
465  }
466  auto& reg = AllocMonitorRegistry::instance();
467  reg.deallocCalled(
468  ptr, [sz](auto ptr) { original(ptr, sz); }, [](auto ptr) { return malloc_usable_size(ptr); });
469 } //_ZdlPvm
470 
471 void operator delete[](void* ptr, std::size_t sz) noexcept {
472  CMS_SA_ALLOW static const auto original = get<void (*)(void*, std::size_t) noexcept>("_ZdaPvm");
473 
474  if (not alloc_monitor_running_state()) {
475  original(ptr, sz);
476  return;
477  }
478  auto& reg = AllocMonitorRegistry::instance();
479  reg.deallocCalled(
480  ptr, [sz](auto ptr) { original(ptr, sz); }, [](auto ptr) { return malloc_usable_size(ptr); });
481 } //_ZdaPvm
482 
483 void operator delete(void* ptr, std::size_t sz, std::align_val_t al) noexcept {
484  CMS_SA_ALLOW static const auto original =
485  get<void (*)(void*, std::size_t, std::align_val_t) noexcept>("_ZdlPvmSt11align_val_t");
486 
487  if (not alloc_monitor_running_state()) {
488  original(ptr, sz, al);
489  return;
490  }
491  auto& reg = AllocMonitorRegistry::instance();
492  reg.deallocCalled(
493  ptr, [sz, al](auto ptr) { original(ptr, sz, al); }, [](auto ptr) { return malloc_usable_size(ptr); });
494 } //_ZdlPvmSt11align_val_t
495 
496 void operator delete[](void* ptr, std::size_t sz, std::align_val_t al) noexcept {
497  CMS_SA_ALLOW static const auto original =
498  get<void (*)(void*, std::size_t, std::align_val_t) noexcept>("_ZdaPvmSt11align_val_t");
499 
500  if (not alloc_monitor_running_state()) {
501  original(ptr, sz, al);
502  return;
503  }
504  auto& reg = AllocMonitorRegistry::instance();
505  reg.deallocCalled(
506  ptr, [sz, al](auto ptr) { original(ptr, sz, al); }, [](auto ptr) { return malloc_usable_size(ptr); });
507 } //_ZdaPvmSt11align_val_t
508 
509 void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
510  CMS_SA_ALLOW static const auto original =
511  get<void (*)(void*, const std::nothrow_t&) noexcept>("_ZdlPvRKSt9nothrow_t");
512 
513  if (not alloc_monitor_running_state()) {
514  original(ptr, tag);
515  return;
516  }
517  auto& reg = AllocMonitorRegistry::instance();
518  reg.deallocCalled(
519  ptr, [&tag](auto ptr) { original(ptr, tag); }, [](auto ptr) { return malloc_usable_size(ptr); });
520 } //_ZdlPvRKSt9nothrow_t
521 
522 void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
523  CMS_SA_ALLOW static const auto original =
524  get<void (*)(void*, const std::nothrow_t&) noexcept>("_ZdaPvRKSt9nothrow_t");
525 
526  if (not alloc_monitor_running_state()) {
527  original(ptr, tag);
528  return;
529  }
530  auto& reg = AllocMonitorRegistry::instance();
531  reg.deallocCalled(
532  ptr, [&tag](auto ptr) { original(ptr, tag); }, [](auto ptr) { return malloc_usable_size(ptr); });
533 } //_ZdaPvRKSt9nothrow_t
534 
535 void operator delete(void* ptr, std::align_val_t al, const std::nothrow_t& tag) noexcept {
536  CMS_SA_ALLOW static const auto original =
537  get<void (*)(void*, std::align_val_t, const std::nothrow_t&) noexcept>("_ZdlPvSt11align_val_tRKSt9nothrow_t");
538 
539  if (not alloc_monitor_running_state()) {
540  original(ptr, al, tag);
541  return;
542  }
543  auto& reg = AllocMonitorRegistry::instance();
544  reg.deallocCalled(
545  ptr, [al, &tag](auto ptr) { original(ptr, al, tag); }, [](auto ptr) { return malloc_usable_size(ptr); });
546 } //_ZdlPvSt11align_val_tRKSt9nothrow_t
547 
548 void operator delete[](void* ptr, std::align_val_t al, const std::nothrow_t& tag) noexcept {
549  CMS_SA_ALLOW static const auto original =
550  get<void (*)(void*, std::align_val_t, const std::nothrow_t&) noexcept>("_ZdaPvSt11align_val_tRKSt9nothrow_t");
551 
552  if (not alloc_monitor_running_state()) {
553  original(ptr, al, tag);
554  return;
555  }
556  auto& reg = AllocMonitorRegistry::instance();
557  reg.deallocCalled(
558  ptr, [al, &tag](auto ptr) { original(ptr, al, tag); }, [](auto ptr) { return malloc_usable_size(ptr); });
559 } //_ZdaPvSt11align_val_tRKSt9nothrow_t
560 
561 #endif
size
Write out results.
#define CMS_SA_ALLOW
ret
prodAgent to be discontinued
int init
Definition: HydjetWrapper.h:66
int posix_memalign(void **memptr, size_t alignment, size_t size) noexcept
assert(be >=bs)
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e g
Definition: Activities.doc:4
void free(void *ptr) noexcept
static AllocMonitorRegistry & instance()
void * aligned_alloc(size_t alignment, size_t size) noexcept
void alloc_monitor_start_thread_reporting()
void * malloc(size_t size) noexcept
void * realloc(void *ptr, size_t size) noexcept
void alloc_monitor_start()
void * memalign(size_t alignment, size_t size) noexcept
void alloc_monitor_stop()
bool alloc_monitor_stop_thread_reporting()
long double T
void * calloc(size_t nitems, size_t item_size) noexcept