xref: /openbsd-src/gnu/llvm/compiler-rt/lib/asan/asan_stats.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- asan_stats.cpp ----------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of AddressSanitizer, an address sanity checker.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Code related to statistics collected by AddressSanitizer.
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick #include "asan_interceptors.h"
143cab2bb3Spatrick #include "asan_internal.h"
153cab2bb3Spatrick #include "asan_stats.h"
163cab2bb3Spatrick #include "asan_thread.h"
173cab2bb3Spatrick #include "sanitizer_common/sanitizer_allocator_interface.h"
183cab2bb3Spatrick #include "sanitizer_common/sanitizer_mutex.h"
193cab2bb3Spatrick #include "sanitizer_common/sanitizer_stackdepot.h"
203cab2bb3Spatrick 
213cab2bb3Spatrick namespace __asan {
223cab2bb3Spatrick 
AsanStats()233cab2bb3Spatrick AsanStats::AsanStats() {
243cab2bb3Spatrick   Clear();
253cab2bb3Spatrick }
263cab2bb3Spatrick 
Clear()273cab2bb3Spatrick void AsanStats::Clear() {
283cab2bb3Spatrick   CHECK(REAL(memset));
293cab2bb3Spatrick   REAL(memset)(this, 0, sizeof(AsanStats));
303cab2bb3Spatrick }
313cab2bb3Spatrick 
PrintMallocStatsArray(const char * prefix,uptr (& array)[kNumberOfSizeClasses])323cab2bb3Spatrick static void PrintMallocStatsArray(const char *prefix,
333cab2bb3Spatrick                                   uptr (&array)[kNumberOfSizeClasses]) {
343cab2bb3Spatrick   Printf("%s", prefix);
353cab2bb3Spatrick   for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
363cab2bb3Spatrick     if (!array[i]) continue;
373cab2bb3Spatrick     Printf("%zu:%zu; ", i, array[i]);
383cab2bb3Spatrick   }
393cab2bb3Spatrick   Printf("\n");
403cab2bb3Spatrick }
413cab2bb3Spatrick 
Print()423cab2bb3Spatrick void AsanStats::Print() {
433cab2bb3Spatrick   Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
443cab2bb3Spatrick              malloced>>20, malloced_redzones>>20, mallocs);
453cab2bb3Spatrick   Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
463cab2bb3Spatrick   Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
473cab2bb3Spatrick   Printf("Stats: %zuM really freed by %zu calls\n",
483cab2bb3Spatrick              really_freed>>20, real_frees);
493cab2bb3Spatrick   Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
503cab2bb3Spatrick              (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
513cab2bb3Spatrick              mmaps, munmaps);
523cab2bb3Spatrick 
533cab2bb3Spatrick   PrintMallocStatsArray("  mallocs by size class: ", malloced_by_size);
543cab2bb3Spatrick   Printf("Stats: malloc large: %zu\n", malloc_large);
553cab2bb3Spatrick }
563cab2bb3Spatrick 
MergeFrom(const AsanStats * stats)573cab2bb3Spatrick void AsanStats::MergeFrom(const AsanStats *stats) {
583cab2bb3Spatrick   uptr *dst_ptr = reinterpret_cast<uptr*>(this);
593cab2bb3Spatrick   const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
603cab2bb3Spatrick   uptr num_fields = sizeof(*this) / sizeof(uptr);
613cab2bb3Spatrick   for (uptr i = 0; i < num_fields; i++)
623cab2bb3Spatrick     dst_ptr[i] += src_ptr[i];
633cab2bb3Spatrick }
643cab2bb3Spatrick 
65*810390e3Srobert static Mutex print_lock;
663cab2bb3Spatrick 
673cab2bb3Spatrick static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
683cab2bb3Spatrick static AsanStats dead_threads_stats(LINKER_INITIALIZED);
69*810390e3Srobert static Mutex dead_threads_stats_lock;
703cab2bb3Spatrick // Required for malloc_zone_statistics() on OS X. This can't be stored in
713cab2bb3Spatrick // per-thread AsanStats.
723cab2bb3Spatrick static uptr max_malloced_memory;
733cab2bb3Spatrick 
MergeThreadStats(ThreadContextBase * tctx_base,void * arg)743cab2bb3Spatrick static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
753cab2bb3Spatrick   AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
763cab2bb3Spatrick   AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
773cab2bb3Spatrick   if (AsanThread *t = tctx->thread)
783cab2bb3Spatrick     accumulated_stats->MergeFrom(&t->stats());
793cab2bb3Spatrick }
803cab2bb3Spatrick 
GetAccumulatedStats(AsanStats * stats)813cab2bb3Spatrick static void GetAccumulatedStats(AsanStats *stats) {
823cab2bb3Spatrick   stats->Clear();
833cab2bb3Spatrick   {
843cab2bb3Spatrick     ThreadRegistryLock l(&asanThreadRegistry());
853cab2bb3Spatrick     asanThreadRegistry()
863cab2bb3Spatrick         .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
873cab2bb3Spatrick   }
883cab2bb3Spatrick   stats->MergeFrom(&unknown_thread_stats);
893cab2bb3Spatrick   {
90*810390e3Srobert     Lock lock(&dead_threads_stats_lock);
913cab2bb3Spatrick     stats->MergeFrom(&dead_threads_stats);
923cab2bb3Spatrick   }
933cab2bb3Spatrick   // This is not very accurate: we may miss allocation peaks that happen
943cab2bb3Spatrick   // between two updates of accumulated_stats_. For more accurate bookkeeping
953cab2bb3Spatrick   // the maximum should be updated on every malloc(), which is unacceptable.
963cab2bb3Spatrick   if (max_malloced_memory < stats->malloced) {
973cab2bb3Spatrick     max_malloced_memory = stats->malloced;
983cab2bb3Spatrick   }
993cab2bb3Spatrick }
1003cab2bb3Spatrick 
FlushToDeadThreadStats(AsanStats * stats)1013cab2bb3Spatrick void FlushToDeadThreadStats(AsanStats *stats) {
102*810390e3Srobert   Lock lock(&dead_threads_stats_lock);
1033cab2bb3Spatrick   dead_threads_stats.MergeFrom(stats);
1043cab2bb3Spatrick   stats->Clear();
1053cab2bb3Spatrick }
1063cab2bb3Spatrick 
FillMallocStatistics(AsanMallocStats * malloc_stats)1073cab2bb3Spatrick void FillMallocStatistics(AsanMallocStats *malloc_stats) {
1083cab2bb3Spatrick   AsanStats stats;
1093cab2bb3Spatrick   GetAccumulatedStats(&stats);
1103cab2bb3Spatrick   malloc_stats->blocks_in_use = stats.mallocs;
1113cab2bb3Spatrick   malloc_stats->size_in_use = stats.malloced;
1123cab2bb3Spatrick   malloc_stats->max_size_in_use = max_malloced_memory;
1133cab2bb3Spatrick   malloc_stats->size_allocated = stats.mmaped;
1143cab2bb3Spatrick }
1153cab2bb3Spatrick 
GetCurrentThreadStats()1163cab2bb3Spatrick AsanStats &GetCurrentThreadStats() {
1173cab2bb3Spatrick   AsanThread *t = GetCurrentThread();
1183cab2bb3Spatrick   return (t) ? t->stats() : unknown_thread_stats;
1193cab2bb3Spatrick }
1203cab2bb3Spatrick 
PrintAccumulatedStats()1213cab2bb3Spatrick static void PrintAccumulatedStats() {
1223cab2bb3Spatrick   AsanStats stats;
1233cab2bb3Spatrick   GetAccumulatedStats(&stats);
1243cab2bb3Spatrick   // Use lock to keep reports from mixing up.
125*810390e3Srobert   Lock lock(&print_lock);
1263cab2bb3Spatrick   stats.Print();
127*810390e3Srobert   StackDepotStats stack_depot_stats = StackDepotGetStats();
1283cab2bb3Spatrick   Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
129*810390e3Srobert          stack_depot_stats.n_uniq_ids, stack_depot_stats.allocated >> 20);
1303cab2bb3Spatrick   PrintInternalAllocatorStats();
1313cab2bb3Spatrick }
1323cab2bb3Spatrick 
1333cab2bb3Spatrick }  // namespace __asan
1343cab2bb3Spatrick 
1353cab2bb3Spatrick // ---------------------- Interface ---------------- {{{1
1363cab2bb3Spatrick using namespace __asan;
1373cab2bb3Spatrick 
__sanitizer_get_current_allocated_bytes()1383cab2bb3Spatrick uptr __sanitizer_get_current_allocated_bytes() {
1393cab2bb3Spatrick   AsanStats stats;
1403cab2bb3Spatrick   GetAccumulatedStats(&stats);
1413cab2bb3Spatrick   uptr malloced = stats.malloced;
1423cab2bb3Spatrick   uptr freed = stats.freed;
1433cab2bb3Spatrick   // Return sane value if malloced < freed due to racy
1443cab2bb3Spatrick   // way we update accumulated stats.
1453cab2bb3Spatrick   return (malloced > freed) ? malloced - freed : 1;
1463cab2bb3Spatrick }
1473cab2bb3Spatrick 
__sanitizer_get_heap_size()1483cab2bb3Spatrick uptr __sanitizer_get_heap_size() {
1493cab2bb3Spatrick   AsanStats stats;
1503cab2bb3Spatrick   GetAccumulatedStats(&stats);
1513cab2bb3Spatrick   return stats.mmaped - stats.munmaped;
1523cab2bb3Spatrick }
1533cab2bb3Spatrick 
__sanitizer_get_free_bytes()1543cab2bb3Spatrick uptr __sanitizer_get_free_bytes() {
1553cab2bb3Spatrick   AsanStats stats;
1563cab2bb3Spatrick   GetAccumulatedStats(&stats);
1573cab2bb3Spatrick   uptr total_free = stats.mmaped
1583cab2bb3Spatrick                   - stats.munmaped
1593cab2bb3Spatrick                   + stats.really_freed;
1603cab2bb3Spatrick   uptr total_used = stats.malloced
1613cab2bb3Spatrick                   + stats.malloced_redzones;
1623cab2bb3Spatrick   // Return sane value if total_free < total_used due to racy
1633cab2bb3Spatrick   // way we update accumulated stats.
1643cab2bb3Spatrick   return (total_free > total_used) ? total_free - total_used : 1;
1653cab2bb3Spatrick }
1663cab2bb3Spatrick 
__sanitizer_get_unmapped_bytes()1673cab2bb3Spatrick uptr __sanitizer_get_unmapped_bytes() {
1683cab2bb3Spatrick   return 0;
1693cab2bb3Spatrick }
1703cab2bb3Spatrick 
__asan_print_accumulated_stats()1713cab2bb3Spatrick void __asan_print_accumulated_stats() {
1723cab2bb3Spatrick   PrintAccumulatedStats();
1733cab2bb3Spatrick }
174