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);
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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