Skip to content

Commit

Permalink
[memprof] Add a raw binary format to serialize memprof profiles.
Browse files Browse the repository at this point in the history
This change implements the raw binary format discussed in
https://lists.llvm.org/pipermail/llvm-dev/2021-September/153007.html

Summary of changes
* Add a new memprof option to choose binary or text (default) format.
* Add a rawprofile library which serializes the MIB map to profile.
* Add a unit test for rawprofile.
* Mark sanitizer procmaps methods as virtual to be able to mock them.
* Extend memprof_profile_dump regression test.

Differential Revision: https://reviews.llvm.org/D113317
  • Loading branch information
snehasish committed Nov 11, 2021
1 parent 1243cef commit 545866c
Show file tree
Hide file tree
Showing 10 changed files with 607 additions and 27 deletions.
7 changes: 7 additions & 0 deletions compiler-rt/lib/memprof/CMakeLists.txt
Expand Up @@ -10,6 +10,7 @@ set(MEMPROF_SOURCES
memprof_malloc_linux.cpp
memprof_mibmap.cpp
memprof_posix.cpp
memprof_rawprofile.cpp
memprof_rtl.cpp
memprof_shadow_setup.cpp
memprof_stack.cpp
Expand Down Expand Up @@ -38,6 +39,7 @@ SET(MEMPROF_HEADERS
memprof_mapping.h
memprof_meminfoblock.h
memprof_mibmap.h
memprof_rawprofile.h
memprof_stack.h
memprof_stats.h
memprof_thread.h
Expand Down Expand Up @@ -195,3 +197,8 @@ foreach(arch ${MEMPROF_SUPPORTED_ARCH})
add_dependencies(memprof clang_rt.memprof-${arch}-symbols)
endif()
endforeach()


if(COMPILER_RT_INCLUDE_TESTS)
add_subdirectory(tests)
endif()
64 changes: 47 additions & 17 deletions compiler-rt/lib/memprof/memprof_allocator.cpp
Expand Up @@ -17,6 +17,7 @@
#include "memprof_mapping.h"
#include "memprof_meminfoblock.h"
#include "memprof_mibmap.h"
#include "memprof_rawprofile.h"
#include "memprof_stack.h"
#include "memprof_thread.h"
#include "sanitizer_common/sanitizer_allocator_checks.h"
Expand All @@ -27,7 +28,9 @@
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_list.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_vector.h"

#include <sched.h>
#include <time.h>
Expand Down Expand Up @@ -220,26 +223,57 @@ struct Allocator {
// Holds the mapping of stack ids to MemInfoBlocks.
MIBMapTy MIBMap;

bool destructing;
bool constructed = false;
atomic_uint8_t destructing;
atomic_uint8_t constructed;
bool print_text;

// ------------------- Initialization ------------------------
explicit Allocator(LinkerInitialized)
: destructing(false), constructed(true) {}
~Allocator() { FinishAndPrint(); }
explicit Allocator(LinkerInitialized) : print_text(flags()->print_text) {
atomic_store_relaxed(&destructing, 0);
atomic_store_relaxed(&constructed, 1);
}

~Allocator() {
atomic_store_relaxed(&destructing, 1);
FinishAndWrite();
}

static void PrintCallback(const uptr Key, LockedMemInfoBlock *const &Value,
void *Arg) {
SpinMutexLock(&Value->mutex);
Value->mib.Print(Key, bool(Arg));
}

void FinishAndPrint() {
if (common_flags()->print_module_map)
void FinishAndWrite() {
if (print_text && common_flags()->print_module_map)
DumpProcessMap();
if (!flags()->print_terse)
Printf("Live on exit:\n");

allocator.ForceLock();

InsertLiveBlocks();
if (print_text) {
MIBMap.ForEach(PrintCallback,
reinterpret_cast<void *>(flags()->print_terse));
StackDepotPrintAll();
} else {
// Serialize the contents to a raw profile. Format documented in
// memprof_rawprofile.h.
char *Buffer = nullptr;

MemoryMappingLayout Layout(/*cache_enabled=*/true);
u64 BytesSerialized = SerializeToRawProfile(MIBMap, Layout, Buffer);
CHECK(Buffer && BytesSerialized && "could not serialize to buffer");
report_file.Write(Buffer, BytesSerialized);
}

allocator.ForceUnlock();
}

// Inserts any blocks which have been allocated but not yet deallocated.
void InsertLiveBlocks() {
if (print_text && !flags()->print_terse)
Printf("Live on exit:\n");

allocator.ForEachChunk(
[](uptr chunk, void *alloc) {
u64 user_requested_size;
Expand All @@ -256,12 +290,6 @@ struct Allocator {
InsertOrMerge(m->alloc_context_id, newMIB, A->MIBMap);
},
this);

destructing = true;
MIBMap.ForEach(PrintCallback,
reinterpret_cast<void *>(flags()->print_terse));
StackDepotPrintAll();
allocator.ForceUnlock();
}

void InitLinkerInitialized() {
Expand Down Expand Up @@ -393,7 +421,9 @@ struct Allocator {

u64 user_requested_size =
atomic_exchange(&m->user_requested_size, 0, memory_order_acquire);
if (memprof_inited && memprof_init_done && constructed && !destructing) {
if (memprof_inited && memprof_init_done &&
atomic_load_relaxed(&constructed) &&
!atomic_load_relaxed(&destructing)) {
u64 c = GetShadowCount(p, user_requested_size);
long curtime = GetTimestamp();

Expand Down Expand Up @@ -666,7 +696,7 @@ uptr __sanitizer_get_allocated_size(const void *p) {
}

int __memprof_profile_dump() {
instance.FinishAndPrint();
instance.FinishAndWrite();
// In the future we may want to return non-zero if there are any errors
// detected during the dumping process.
return 0;
Expand Down
4 changes: 3 additions & 1 deletion compiler-rt/lib/memprof/memprof_flags.inc
Expand Up @@ -35,5 +35,7 @@ MEMPROF_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true,
"realloc(p, 0) is equivalent to free(p) by default (Same as the "
"POSIX standard). If set to false, realloc(p, 0) will return a "
"pointer to an allocated space which can not be used.")
MEMPROF_FLAG(bool, print_text, true,
"If set, prints the heap profile in text format. Else use the raw binary serialization format.")
MEMPROF_FLAG(bool, print_terse, false,
"If set, prints memory profile in a terse format.")
"If set, prints memory profile in a terse format. Only applicable if print_text = true.")

0 comments on commit 545866c

Please sign in to comment.