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