forked from OSchip/llvm-project
[asan] add an interface function __sanitizer_print_memory_profile (a basic memory profiler; asan/Linux-only for now)
llvm-svn: 271463
This commit is contained in:
parent
b7a373971d
commit
5a7159c416
|
@ -133,6 +133,12 @@ extern "C" {
|
||||||
const char *s2, size_t n, int result);
|
const char *s2, size_t n, int result);
|
||||||
void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1,
|
void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1,
|
||||||
const char *s2, int result);
|
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
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,6 +13,7 @@ set(ASAN_SOURCES
|
||||||
asan_malloc_linux.cc
|
asan_malloc_linux.cc
|
||||||
asan_malloc_mac.cc
|
asan_malloc_mac.cc
|
||||||
asan_malloc_win.cc
|
asan_malloc_win.cc
|
||||||
|
asan_memory_profile.cc
|
||||||
asan_poisoning.cc
|
asan_poisoning.cc
|
||||||
asan_posix.cc
|
asan_posix.cc
|
||||||
asan_report.cc
|
asan_report.cc
|
||||||
|
|
|
@ -681,12 +681,15 @@ static StackTrace GetStackTraceFromId(u32 id) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 AsanChunkView::GetAllocStackId() { return chunk_->alloc_context_id; }
|
||||||
|
u32 AsanChunkView::GetFreeStackId() { return chunk_->free_context_id; }
|
||||||
|
|
||||||
StackTrace AsanChunkView::GetAllocStack() {
|
StackTrace AsanChunkView::GetAllocStack() {
|
||||||
return GetStackTraceFromId(chunk_->alloc_context_id);
|
return GetStackTraceFromId(GetAllocStackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
StackTrace AsanChunkView::GetFreeStack() {
|
StackTrace AsanChunkView::GetFreeStack() {
|
||||||
return GetStackTraceFromId(chunk_->free_context_id);
|
return GetStackTraceFromId(GetFreeStackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeAllocator(const AllocatorOptions &options) {
|
void InitializeAllocator(const AllocatorOptions &options) {
|
||||||
|
|
|
@ -58,6 +58,8 @@ class AsanChunkView {
|
||||||
uptr AllocTid();
|
uptr AllocTid();
|
||||||
uptr FreeTid();
|
uptr FreeTid();
|
||||||
bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; }
|
bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; }
|
||||||
|
u32 GetAllocStackId();
|
||||||
|
u32 GetFreeStackId();
|
||||||
StackTrace GetAllocStack();
|
StackTrace GetAllocStack();
|
||||||
StackTrace GetFreeStack();
|
StackTrace GetFreeStack();
|
||||||
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
|
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
|
||||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue