asan_stats.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. //===-- asan_stats.cc -----------------------------------------------------===//
  2. //
  3. // This file is distributed under the University of Illinois Open Source
  4. // License. See LICENSE.TXT for details.
  5. //
  6. //===----------------------------------------------------------------------===//
  7. //
  8. // This file is a part of AddressSanitizer, an address sanity checker.
  9. //
  10. // Code related to statistics collected by AddressSanitizer.
  11. //===----------------------------------------------------------------------===//
  12. #include "asan_interceptors.h"
  13. #include "asan_internal.h"
  14. #include "asan_stats.h"
  15. #include "asan_thread.h"
  16. #include "sanitizer_common/sanitizer_allocator_interface.h"
  17. #include "sanitizer_common/sanitizer_mutex.h"
  18. #include "sanitizer_common/sanitizer_stackdepot.h"
  19. namespace __asan {
  20. AsanStats::AsanStats() {
  21. Clear();
  22. }
  23. void AsanStats::Clear() {
  24. CHECK(REAL(memset));
  25. REAL(memset)(this, 0, sizeof(AsanStats));
  26. }
  27. static void PrintMallocStatsArray(const char *prefix,
  28. uptr (&array)[kNumberOfSizeClasses]) {
  29. Printf("%s", prefix);
  30. for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
  31. if (!array[i]) continue;
  32. Printf("%zu:%zu; ", i, array[i]);
  33. }
  34. Printf("\n");
  35. }
  36. void AsanStats::Print() {
  37. Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
  38. malloced>>20, malloced_redzones>>20, mallocs);
  39. Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
  40. Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
  41. Printf("Stats: %zuM really freed by %zu calls\n",
  42. really_freed>>20, real_frees);
  43. Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
  44. (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
  45. mmaps, munmaps);
  46. PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
  47. PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
  48. PrintMallocStatsArray(" frees by size class: ", freed_by_size);
  49. PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
  50. Printf("Stats: malloc large: %zu small slow: %zu\n",
  51. malloc_large, malloc_small_slow);
  52. }
  53. void AsanStats::MergeFrom(const AsanStats *stats) {
  54. uptr *dst_ptr = reinterpret_cast<uptr*>(this);
  55. const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
  56. uptr num_fields = sizeof(*this) / sizeof(uptr);
  57. for (uptr i = 0; i < num_fields; i++)
  58. dst_ptr[i] += src_ptr[i];
  59. }
  60. static BlockingMutex print_lock(LINKER_INITIALIZED);
  61. static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
  62. static AsanStats dead_threads_stats(LINKER_INITIALIZED);
  63. static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
  64. // Required for malloc_zone_statistics() on OS X. This can't be stored in
  65. // per-thread AsanStats.
  66. static uptr max_malloced_memory;
  67. static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
  68. AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
  69. AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
  70. if (AsanThread *t = tctx->thread)
  71. accumulated_stats->MergeFrom(&t->stats());
  72. }
  73. static void GetAccumulatedStats(AsanStats *stats) {
  74. stats->Clear();
  75. {
  76. ThreadRegistryLock l(&asanThreadRegistry());
  77. asanThreadRegistry()
  78. .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
  79. }
  80. stats->MergeFrom(&unknown_thread_stats);
  81. {
  82. BlockingMutexLock lock(&dead_threads_stats_lock);
  83. stats->MergeFrom(&dead_threads_stats);
  84. }
  85. // This is not very accurate: we may miss allocation peaks that happen
  86. // between two updates of accumulated_stats_. For more accurate bookkeeping
  87. // the maximum should be updated on every malloc(), which is unacceptable.
  88. if (max_malloced_memory < stats->malloced) {
  89. max_malloced_memory = stats->malloced;
  90. }
  91. }
  92. void FlushToDeadThreadStats(AsanStats *stats) {
  93. BlockingMutexLock lock(&dead_threads_stats_lock);
  94. dead_threads_stats.MergeFrom(stats);
  95. stats->Clear();
  96. }
  97. void FillMallocStatistics(AsanMallocStats *malloc_stats) {
  98. AsanStats stats;
  99. GetAccumulatedStats(&stats);
  100. malloc_stats->blocks_in_use = stats.mallocs;
  101. malloc_stats->size_in_use = stats.malloced;
  102. malloc_stats->max_size_in_use = max_malloced_memory;
  103. malloc_stats->size_allocated = stats.mmaped;
  104. }
  105. AsanStats &GetCurrentThreadStats() {
  106. AsanThread *t = GetCurrentThread();
  107. return (t) ? t->stats() : unknown_thread_stats;
  108. }
  109. static void PrintAccumulatedStats() {
  110. AsanStats stats;
  111. GetAccumulatedStats(&stats);
  112. // Use lock to keep reports from mixing up.
  113. BlockingMutexLock lock(&print_lock);
  114. stats.Print();
  115. StackDepotStats *stack_depot_stats = StackDepotGetStats();
  116. Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
  117. stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
  118. PrintInternalAllocatorStats();
  119. }
  120. } // namespace __asan
  121. // ---------------------- Interface ---------------- {{{1
  122. using namespace __asan; // NOLINT
  123. uptr __sanitizer_get_current_allocated_bytes() {
  124. AsanStats stats;
  125. GetAccumulatedStats(&stats);
  126. uptr malloced = stats.malloced;
  127. uptr freed = stats.freed;
  128. // Return sane value if malloced < freed due to racy
  129. // way we update accumulated stats.
  130. return (malloced > freed) ? malloced - freed : 1;
  131. }
  132. uptr __sanitizer_get_heap_size() {
  133. AsanStats stats;
  134. GetAccumulatedStats(&stats);
  135. return stats.mmaped - stats.munmaped;
  136. }
  137. uptr __sanitizer_get_free_bytes() {
  138. AsanStats stats;
  139. GetAccumulatedStats(&stats);
  140. uptr total_free = stats.mmaped
  141. - stats.munmaped
  142. + stats.really_freed
  143. + stats.really_freed_redzones;
  144. uptr total_used = stats.malloced
  145. + stats.malloced_redzones;
  146. // Return sane value if total_free < total_used due to racy
  147. // way we update accumulated stats.
  148. return (total_free > total_used) ? total_free - total_used : 1;
  149. }
  150. uptr __sanitizer_get_unmapped_bytes() {
  151. return 0;
  152. }
  153. void __asan_print_accumulated_stats() {
  154. PrintAccumulatedStats();
  155. }