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