logmem.cc 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. #include "logmem.h"
  2. #include "simulator.h"
  3. #include "lock.h"
  4. #include "timer.h"
  5. #include "hooks_manager.h"
  6. #include <cstdio>
  7. /*
  8. Memory Logger
  9. Logs allocated memory size per allocation site
  10. Usage:
  11. - define LOGMEM_ENABLED in logmem.h
  12. - recompile all sources with DEBUG=1
  13. - run simulation
  14. + to completion (ROI-end)
  15. + send USR1 signal for intermediate statistics
  16. - allocations will be in allocations.out
  17. - ./tools/memtop.py
  18. */
  19. #ifndef LOGMEM_ENABLED
  20. void logmem_enable(bool enabled) {}
  21. void logmem_write_allocations() {}
  22. #else
  23. const int NUM_ITEMS = 1024*1024;
  24. #include "callstack.h"
  25. #include <execinfo.h>
  26. class AllocItem
  27. {
  28. static const unsigned int BACKTRACE_SIZE = 15;
  29. void * backtrace_buffer[BACKTRACE_SIZE];
  30. unsigned int backtrace_n;
  31. static Lock lock;
  32. public:
  33. size_t size;
  34. size_t count;
  35. AllocItem() : backtrace_n(0), size(0), count(0) {}
  36. static unsigned int key() {
  37. void * buffer[BACKTRACE_SIZE];
  38. // use the fast version here
  39. int n = get_call_stack(buffer, 6);
  40. unsigned int k = 0;
  41. for(int i = 2; i < n; ++i)
  42. k += (unsigned long)buffer[i] * (i + (1 << i));
  43. return k % NUM_ITEMS;
  44. }
  45. void init() {
  46. ScopedLock sl(lock);
  47. backtrace_n = backtrace(backtrace_buffer, BACKTRACE_SIZE);
  48. }
  49. void record(size_t _size) {
  50. if (backtrace_n == 0) init();
  51. __sync_fetch_and_add(&size, _size);
  52. __sync_fetch_and_add(&count, 1);
  53. }
  54. void free(size_t _size) {
  55. __sync_fetch_and_sub(&size, _size);
  56. }
  57. void report(FILE * fp) {
  58. for(unsigned int i = 3; i < BACKTRACE_SIZE; ++i)
  59. fprintf(fp, "%lu ", i < backtrace_n ? (unsigned long)backtrace_buffer[i] : 0);
  60. fprintf(fp, "%lu %lu\n", (unsigned long)size, (unsigned long)count);
  61. }
  62. };
  63. struct DataItem
  64. {
  65. unsigned int key;
  66. size_t size;
  67. char data[];
  68. };
  69. AllocItem allocated[NUM_ITEMS];
  70. bool logmem_enabled = false; // global enable (during ROI for instance)
  71. SInt64 mem_total = 0;
  72. Lock AllocItem::lock;
  73. void * __new(size_t size)
  74. {
  75. DataItem *p = (DataItem*)malloc(sizeof(DataItem) + size);
  76. p->key = -1;
  77. p->size = size;
  78. if (logmem_enabled) {
  79. unsigned int k = AllocItem::key();
  80. p->key = k;
  81. allocated[k].record(size);
  82. __sync_fetch_and_add(&mem_total, size);
  83. }
  84. return p->data;
  85. }
  86. void * operator new (size_t size) { return __new(size); }
  87. void * operator new [] (size_t size) { return __new(size); }
  88. void __delete(void* p)
  89. {
  90. DataItem *_p = (DataItem*)(((char*)p) - sizeof(DataItem));
  91. if (logmem_enabled) {
  92. if (_p->key != (unsigned int)-1) {
  93. allocated[_p->key].free(_p->size);
  94. __sync_fetch_and_sub(&mem_total, _p->size);
  95. }
  96. }
  97. free(_p);
  98. }
  99. void operator delete(void* p) { __delete(p); }
  100. void operator delete [] (void* p) { __delete(p); }
  101. /* Output format:
  102. First line: address of rdtsc (first argument of tools/addr2line.py)
  103. Subsequent lines: backtrace addresses, last element is size
  104. Postprocess step: sort allocations.out | uniq -c | sort -n
  105. */
  106. void logmem_write_allocations()
  107. {
  108. printf("[LOGMEM] Total memory consumption increase: %.1f MiB\n", mem_total / (1024.*1024));
  109. FILE* fp = fopen(Sim()->getConfig()->formatOutputFileName("allocations.out").c_str(), "w");
  110. fprintf(fp, "%lu\n", (unsigned long)rdtsc);
  111. for(int i = 0; i < NUM_ITEMS; ++i)
  112. if (allocated[i].size > 0)
  113. allocated[i].report(fp);
  114. fclose(fp);
  115. }
  116. SInt64 logmem_trigger(UInt64, UInt64)
  117. {
  118. printf("[SNIPER] Writing logmem allocations\n");
  119. logmem_write_allocations();
  120. return 0;
  121. }
  122. void logmem_enable(bool enabled)
  123. {
  124. if (!logmem_enabled)
  125. {
  126. // let's see whether get_call_stack() is working
  127. // if -fomit-frame-pointer was used, we should notice this here rather than working with garbage keys
  128. const unsigned int BACKTRACE_SIZE = 5;
  129. void *backtrace_buffer[BACKTRACE_SIZE], *callstack_buffer[BACKTRACE_SIZE];
  130. unsigned int backtrace_n = backtrace(backtrace_buffer, BACKTRACE_SIZE),
  131. callstack_n = get_call_stack(callstack_buffer, BACKTRACE_SIZE);
  132. for(unsigned int i = 1; i < backtrace_n; ++i)
  133. {
  134. LOG_ASSERT_ERROR(i < callstack_n && backtrace_buffer[i] == callstack_buffer[i], "Fast backtrace() not working");
  135. }
  136. Sim()->getHooksManager()->registerHook(HookType::HOOK_SIGUSR1, logmem_trigger, 0);
  137. }
  138. logmem_enabled = enabled;
  139. }
  140. #endif // LOGMEM_ENABLED