[compiler-rt] r189744 - Improve collecting malloc stats in ASan

Alexey Samsonov samsonov at google.com
Mon Sep 2 01:39:07 PDT 2013


Author: samsonov
Date: Mon Sep  2 03:39:07 2013
New Revision: 189744

URL: http://llvm.org/viewvc/llvm-project?rev=189744&view=rev
Log:
Improve collecting malloc stats in ASan

Summary:
This change makes races between updates of thread-local stats and
merging all the thread-local stats together less harmful.

Reviewers: kcc

Reviewed By: kcc

CC: dvyukov, llvm-commits

Differential Revision: http://llvm-reviews.chandlerc.com/D1572

Added:
    compiler-rt/trunk/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc
Modified:
    compiler-rt/trunk/lib/asan/asan_stats.cc
    compiler-rt/trunk/lib/asan/asan_stats.h
    compiler-rt/trunk/lib/asan/asan_thread.cc

Modified: compiler-rt/trunk/lib/asan/asan_stats.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_stats.cc?rev=189744&r1=189743&r2=189744&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_stats.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_stats.cc Mon Sep  2 03:39:07 2013
@@ -21,6 +21,10 @@
 namespace __asan {
 
 AsanStats::AsanStats() {
+  Clear();
+}
+
+void AsanStats::Clear() {
   CHECK(REAL(memset));
   REAL(memset)(this, 0, sizeof(AsanStats));
 }
@@ -54,77 +58,63 @@ void AsanStats::Print() {
              malloc_large, malloc_small_slow);
 }
 
-static BlockingMutex print_lock(LINKER_INITIALIZED);
-
-static void PrintAccumulatedStats() {
-  AsanStats stats;
-  GetAccumulatedStats(&stats);
-  // Use lock to keep reports from mixing up.
-  BlockingMutexLock lock(&print_lock);
-  stats.Print();
-  StackDepotStats *stack_depot_stats = StackDepotGetStats();
-  Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
-         stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
-  PrintInternalAllocatorStats();
+void AsanStats::MergeFrom(const AsanStats *stats) {
+  uptr *dst_ptr = reinterpret_cast<uptr*>(this);
+  const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
+  uptr num_fields = sizeof(*this) / sizeof(uptr);
+  for (uptr i = 0; i < num_fields; i++)
+    dst_ptr[i] += src_ptr[i];
 }
 
+static BlockingMutex print_lock(LINKER_INITIALIZED);
+
 static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
-static AsanStats accumulated_stats(LINKER_INITIALIZED);
+static AsanStats dead_threads_stats(LINKER_INITIALIZED);
+static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
 // Required for malloc_zone_statistics() on OS X. This can't be stored in
 // per-thread AsanStats.
 static uptr max_malloced_memory;
-static BlockingMutex acc_stats_lock(LINKER_INITIALIZED);
 
-static void FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
-  acc_stats_lock.CheckLocked();
-  uptr *dst = (uptr*)&accumulated_stats;
-  uptr *src = (uptr*)stats;
-  uptr num_fields = sizeof(*stats) / sizeof(uptr);
-  for (uptr i = 0; i < num_fields; i++) {
-    dst[i] += src[i];
-    src[i] = 0;
-  }
-}
-
-static void FlushThreadStats(ThreadContextBase *tctx_base, void *arg) {
+static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
+  AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
   AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
   if (AsanThread *t = tctx->thread)
-    FlushToAccumulatedStatsUnlocked(&t->stats());
+    accumulated_stats->MergeFrom(&t->stats());
 }
 
-static void UpdateAccumulatedStatsUnlocked() {
-  acc_stats_lock.CheckLocked();
+static void GetAccumulatedStats(AsanStats *stats) {
+  stats->Clear();
   {
     ThreadRegistryLock l(&asanThreadRegistry());
-    asanThreadRegistry().RunCallbackForEachThreadLocked(FlushThreadStats, 0);
+    asanThreadRegistry()
+        .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
+  }
+  stats->MergeFrom(&unknown_thread_stats);
+  {
+    BlockingMutexLock lock(&dead_threads_stats_lock);
+    stats->MergeFrom(&dead_threads_stats);
   }
-  FlushToAccumulatedStatsUnlocked(&unknown_thread_stats);
   // This is not very accurate: we may miss allocation peaks that happen
   // between two updates of accumulated_stats_. For more accurate bookkeeping
   // the maximum should be updated on every malloc(), which is unacceptable.
-  if (max_malloced_memory < accumulated_stats.malloced) {
-    max_malloced_memory = accumulated_stats.malloced;
+  if (max_malloced_memory < stats->malloced) {
+    max_malloced_memory = stats->malloced;
   }
 }
 
-void FlushToAccumulatedStats(AsanStats *stats) {
-  BlockingMutexLock lock(&acc_stats_lock);
-  FlushToAccumulatedStatsUnlocked(stats);
-}
-
-void GetAccumulatedStats(AsanStats *stats) {
-  BlockingMutexLock lock(&acc_stats_lock);
-  UpdateAccumulatedStatsUnlocked();
-  internal_memcpy(stats, &accumulated_stats, sizeof(accumulated_stats));
+void FlushToDeadThreadStats(AsanStats *stats) {
+  BlockingMutexLock lock(&dead_threads_stats_lock);
+  dead_threads_stats.MergeFrom(stats);
+  stats->Clear();
 }
 
 void FillMallocStatistics(AsanMallocStats *malloc_stats) {
-  BlockingMutexLock lock(&acc_stats_lock);
-  UpdateAccumulatedStatsUnlocked();
-  malloc_stats->blocks_in_use = accumulated_stats.mallocs;
-  malloc_stats->size_in_use = accumulated_stats.malloced;
+  AsanStats stats;
+  GetAccumulatedStats(&stats);
+  malloc_stats->blocks_in_use = stats.mallocs;
+  malloc_stats->size_in_use = stats.malloced;
   malloc_stats->max_size_in_use = max_malloced_memory;
-  malloc_stats->size_allocated = accumulated_stats.mmaped;
+  malloc_stats->size_allocated = stats.mmaped;
 }
 
 AsanStats &GetCurrentThreadStats() {
@@ -132,36 +122,48 @@ AsanStats &GetCurrentThreadStats() {
   return (t) ? t->stats() : unknown_thread_stats;
 }
 
+static void PrintAccumulatedStats() {
+  AsanStats stats;
+  GetAccumulatedStats(&stats);
+  // Use lock to keep reports from mixing up.
+  BlockingMutexLock lock(&print_lock);
+  stats.Print();
+  StackDepotStats *stack_depot_stats = StackDepotGetStats();
+  Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
+         stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
+  PrintInternalAllocatorStats();
+}
+
 }  // namespace __asan
 
 // ---------------------- Interface ---------------- {{{1
 using namespace __asan;  // NOLINT
 
 uptr __asan_get_current_allocated_bytes() {
-  BlockingMutexLock lock(&acc_stats_lock);
-  UpdateAccumulatedStatsUnlocked();
-  uptr malloced = accumulated_stats.malloced;
-  uptr freed = accumulated_stats.freed;
+  AsanStats stats;
+  GetAccumulatedStats(&stats);
+  uptr malloced = stats.malloced;
+  uptr freed = stats.freed;
   // Return sane value if malloced < freed due to racy
   // way we update accumulated stats.
   return (malloced > freed) ? malloced - freed : 1;
 }
 
 uptr __asan_get_heap_size() {
-  BlockingMutexLock lock(&acc_stats_lock);
-  UpdateAccumulatedStatsUnlocked();
-  return accumulated_stats.mmaped - accumulated_stats.munmaped;
+  AsanStats stats;
+  GetAccumulatedStats(&stats);
+  return stats.mmaped - stats.munmaped;
 }
 
 uptr __asan_get_free_bytes() {
-  BlockingMutexLock lock(&acc_stats_lock);
-  UpdateAccumulatedStatsUnlocked();
-  uptr total_free = accumulated_stats.mmaped
-                  - accumulated_stats.munmaped
-                  + accumulated_stats.really_freed
-                  + accumulated_stats.really_freed_redzones;
-  uptr total_used = accumulated_stats.malloced
-                  + accumulated_stats.malloced_redzones;
+  AsanStats stats;
+  GetAccumulatedStats(&stats);
+  uptr total_free = stats.mmaped
+                  - stats.munmaped
+                  + stats.really_freed
+                  + stats.really_freed_redzones;
+  uptr total_used = stats.malloced
+                  + stats.malloced_redzones;
   // Return sane value if total_free < total_used due to racy
   // way we update accumulated stats.
   return (total_free > total_used) ? total_free - total_used : 1;

Modified: compiler-rt/trunk/lib/asan/asan_stats.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_stats.h?rev=189744&r1=189743&r2=189744&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_stats.h (original)
+++ compiler-rt/trunk/lib/asan/asan_stats.h Mon Sep  2 03:39:07 2013
@@ -52,18 +52,16 @@ struct AsanStats {
   // Default ctor for thread-local stats.
   AsanStats();
 
-  // Prints formatted stats to stderr.
-  void Print();
+  void Print();  // Prints formatted stats to stderr.
+  void Clear();
+  void MergeFrom(const AsanStats *stats);
 };
 
 // Returns stats for GetCurrentThread(), or stats for fake "unknown thread"
 // if GetCurrentThread() returns 0.
 AsanStats &GetCurrentThreadStats();
-// Flushes all thread-local stats to accumulated stats, and makes
-// a copy of accumulated stats.
-void GetAccumulatedStats(AsanStats *stats);
-// Flushes a given stats into accumulated stats.
-void FlushToAccumulatedStats(AsanStats *stats);
+// Flushes a given stats into accumulated stats of dead threads.
+void FlushToDeadThreadStats(AsanStats *stats);
 
 // A cross-platform equivalent of malloc_statistics_t on Mac OS.
 struct AsanMallocStats {

Modified: compiler-rt/trunk/lib/asan/asan_thread.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_thread.cc?rev=189744&r1=189743&r2=189744&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_thread.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_thread.cc Mon Sep  2 03:39:07 2013
@@ -97,7 +97,7 @@ void AsanThread::Destroy() {
   }
 
   asanThreadRegistry().FinishThread(tid());
-  FlushToAccumulatedStats(&stats_);
+  FlushToDeadThreadStats(&stats_);
   // We also clear the shadow on thread destruction because
   // some code may still be executing in later TSD destructors
   // and we don't want it to have any poisoned stack.

Added: compiler-rt/trunk/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc?rev=189744&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc Mon Sep  2 03:39:07 2013
@@ -0,0 +1,43 @@
+// RUN: %clangxx_asan -O0 %s -o %t && %t
+// RUN: %clangxx_asan -O2 %s -o %t && %t
+
+#include <assert.h>
+#include <pthread.h>
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+const size_t kLargeAlloc = 1UL << 20;
+
+void* allocate(void *arg) {
+  volatile void *ptr = malloc(kLargeAlloc);
+  free((void*)ptr);
+  return 0;
+}
+
+void* check_stats(void *arg) {
+  assert(__asan_get_current_allocated_bytes() > 0);
+  return 0;
+}
+
+int main() {
+  size_t used_mem = __asan_get_current_allocated_bytes();
+  printf("Before: %zu\n", used_mem);
+  const int kNumIterations = 1000;
+  for (int iter = 0; iter < kNumIterations; iter++) {
+    pthread_t thr[4];
+    for (int j = 0; j < 4; j++) {
+      assert(0 ==
+             pthread_create(&thr[j], 0, (j < 2) ? allocate : check_stats, 0));
+    }
+    for (int j = 0; j < 4; j++)
+      assert(0 == pthread_join(thr[j], 0));
+    used_mem = __asan_get_current_allocated_bytes();
+    if (used_mem > kLargeAlloc) {
+      printf("After iteration %d: %zu\n", iter, used_mem);
+      return 1;
+    }
+  }
+  printf("Success after %d iterations\n", kNumIterations);
+  return 0;
+}





More information about the llvm-commits mailing list