asan_stats.cpp 5.5 KB

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