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