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