summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sanitizer/common_interface_defs.h6
-rw-r--r--lib/asan/CMakeLists.txt1
-rw-r--r--lib/asan/asan_allocator.cc7
-rw-r--r--lib/asan/asan_allocator.h2
-rw-r--r--lib/asan/asan_memory_profile.cc100
-rw-r--r--test/asan/TestCases/Linux/print_memory_profile_test.cc25
6 files changed, 139 insertions, 2 deletions
diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h
index 0cd6e9791..1bf3c1c96 100644
--- a/include/sanitizer/common_interface_defs.h
+++ b/include/sanitizer/common_interface_defs.h
@@ -133,6 +133,12 @@ extern "C" {
const char *s2, size_t n, int result);
void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1,
const char *s2, int result);
+
+ // Prints stack traces for all live heap allocations ordered by total
+ // allocation size until `top_percent` of total live heap is shown.
+ // `top_percent` should be between 1 and 100.
+ // Experimental feature currently available only with asan on Linux.
+ void __sanitizer_print_memory_profile(size_t top_percent);
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index 57ac56f8c..07d623242 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -13,6 +13,7 @@ set(ASAN_SOURCES
asan_malloc_linux.cc
asan_malloc_mac.cc
asan_malloc_win.cc
+ asan_memory_profile.cc
asan_poisoning.cc
asan_posix.cc
asan_report.cc
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index 41b1689d9..fb433bb4c 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -681,12 +681,15 @@ static StackTrace GetStackTraceFromId(u32 id) {
return res;
}
+u32 AsanChunkView::GetAllocStackId() { return chunk_->alloc_context_id; }
+u32 AsanChunkView::GetFreeStackId() { return chunk_->free_context_id; }
+
StackTrace AsanChunkView::GetAllocStack() {
- return GetStackTraceFromId(chunk_->alloc_context_id);
+ return GetStackTraceFromId(GetAllocStackId());
}
StackTrace AsanChunkView::GetFreeStack() {
- return GetStackTraceFromId(chunk_->free_context_id);
+ return GetStackTraceFromId(GetFreeStackId());
}
void InitializeAllocator(const AllocatorOptions &options) {
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
index 9807d1bd5..2f9f7aaf8 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -58,6 +58,8 @@ class AsanChunkView {
uptr AllocTid();
uptr FreeTid();
bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; }
+ u32 GetAllocStackId();
+ u32 GetFreeStackId();
StackTrace GetAllocStack();
StackTrace GetFreeStack();
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
diff --git a/lib/asan/asan_memory_profile.cc b/lib/asan/asan_memory_profile.cc
new file mode 100644
index 000000000..e87e6c8e7
--- /dev/null
+++ b/lib/asan/asan_memory_profile.cc
@@ -0,0 +1,100 @@
+//===-- asan_memory_profile.cc.cc -----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file implements __sanitizer_print_memory_profile.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "lsan/lsan_common.h"
+#include "asan/asan_allocator.h"
+
+#if SANITIZER_LINUX // StopTheWorld is currently linux-only.
+
+namespace __asan {
+
+struct AllocationSite {
+ u32 id;
+ uptr total_size;
+ uptr count;
+};
+
+class HeapProfile {
+ public:
+ HeapProfile() : allocations_(1024) {}
+ void Insert(u32 id, uptr size) {
+ total_allocated_ += size;
+ total_count_++;
+ // Linear lookup will be good enough for most cases (although not all).
+ for (uptr i = 0; i < allocations_.size(); i++) {
+ if (allocations_[i].id == id) {
+ allocations_[i].total_size += size;
+ allocations_[i].count++;
+ return;
+ }
+ }
+ allocations_.push_back({id, size, 1});
+ }
+
+ void Print(uptr top_percent) {
+ InternalSort(&allocations_, allocations_.size(),
+ [](const AllocationSite &a, const AllocationSite &b) {
+ return a.total_size > b.total_size;
+ });
+ CHECK(total_allocated_);
+ uptr total_shown = 0;
+ Printf("Live Heap Allocations: %zd bytes from %zd allocations; "
+ "showing top %zd%%\n", total_allocated_, total_count_, top_percent);
+ for (uptr i = 0; i < allocations_.size(); i++) {
+ auto &a = allocations_[i];
+ Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,
+ a.total_size * 100 / total_allocated_, a.count);
+ StackDepotGet(a.id).Print();
+ total_shown += a.total_size;
+ if (total_shown * 100 / total_allocated_ > top_percent)
+ break;
+ }
+ }
+
+ private:
+ uptr total_allocated_ = 0;
+ uptr total_count_ = 0;
+ InternalMmapVector<AllocationSite> allocations_;
+};
+
+static void ChunkCallback(uptr chunk, void *arg) {
+ HeapProfile *hp = reinterpret_cast<HeapProfile*>(arg);
+ AsanChunkView cv = FindHeapChunkByAddress(chunk);
+ if (!cv.IsAllocated()) return;
+ u32 id = cv.GetAllocStackId();
+ if (!id) return;
+ hp->Insert(id, cv.UsedSize());
+}
+
+static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ HeapProfile hp;
+ __lsan::ForEachChunk(ChunkCallback, &hp);
+ hp.Print(reinterpret_cast<uptr>(argument));
+}
+
+} // namespace __asan
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_memory_profile(uptr top_percent) {
+ __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent);
+}
+} // extern "C"
+
+#endif // SANITIZER_LINUX
diff --git a/test/asan/TestCases/Linux/print_memory_profile_test.cc b/test/asan/TestCases/Linux/print_memory_profile_test.cc
new file mode 100644
index 000000000..6bf78efc1
--- /dev/null
+++ b/test/asan/TestCases/Linux/print_memory_profile_test.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx_asan %s -o %t
+// RUN: %t 2>&1 | FileCheck %s
+#include <sanitizer/common_interface_defs.h>
+
+#include <stdio.h>
+
+char *sink[1000];
+
+int main() {
+ int idx = 0;
+ for (int i = 0; i < 17; i++)
+ sink[idx++] = new char[131];
+ for (int i = 0; i < 42; i++)
+ sink[idx++] = new char[24];
+
+ __sanitizer_print_memory_profile(100);
+ __sanitizer_print_memory_profile(50);
+}
+
+// CHECK: Live Heap Allocations: {{.*}}; showing top 100%
+// CHECK: 2227 byte(s) ({{.*}}%) in 17 allocation(s)
+// CHECK: 1008 byte(s) ({{.*}}%) in 42 allocation(s)
+// CHECK: Live Heap Allocations: {{.*}}; showing top 50%
+// CHECK: 2227 byte(s) ({{.*}}%) in 17 allocation(s)
+// CHECK-NOT: 1008 byte