Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add memory instrumentation support #197

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Initial implementation of a rootbench memory tracing library.
It implements a C library which records information about the memory allocations.
The library is loaded using the LD_PRELOAD mechanism (and DYLD_INSERT_LIBRARIES
for osx).

GoogleBenchmark displays the memory allocation results are only in the json
format. For instance:

DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=./lib/Instrumentation/libRBInstrumentation.dylib ./root/interpreter/InterpreterLookupHelperBenchmarks --benchma
rk_format=json:

...
{
      "name": "BM_LookupHelper_Leak",
      "run_name": "BM_LookupHelper_Leak",
      "run_type": "iteration",
      "repetitions": 0,
      "repetition_index": 0,
      "threads": 1,
      "iterations": 1,
      "real_time": 1.3199219449888916e+09,
      "cpu_time": 8.0450300000000000e+08,
      "time_unit": "ns",
      "allocs_per_iter": 6.0000000000000000e+00,
      "max_bytes_used": 3368
}
vgvassilev committed Dec 4, 2020
commit 485216ab37dcffed325af16a1295b60bd7c3aff5
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -82,6 +82,15 @@ if(rootbench-datafiles)
endif(rootbench-datafiles)
set(RB_DATASETDIR ${PROJECT_BINARY_DIR}/rootbench-datafiles)

#---Define some constants for Constants.h.in ---------------------------------------------------
if(WIN32)
set(RB_ON_WIN32 1)
set(RB_ON_UNIX 0)
else(WIN32)
set(RB_ON_WIN32 0)
set(RB_ON_UNIX 1)
endif()

#---Enable and setup CTest.
include(RootBenchCTest)

39 changes: 34 additions & 5 deletions cmake/modules/AddRootBench.cmake
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ function(RB_ADD_GBENCHMARK benchmark)
# FIXME: For better coherence we could restrict the libraries the test suite could link
# against. For example, tests in Core should link only against libCore. This could be tricky
# to implement because some ROOT components create more than one library.
target_link_libraries(${benchmark} ${ARG_LIBRARIES} gbenchmark RBSupport rt)
target_link_libraries(${benchmark} PUBLIC ${ARG_LIBRARIES} gbenchmark RBSupport rt)
#ROOT_PATH_TO_STRING(mangled_name ${benchmark} PATH_SEPARATOR_REPLACEMENT "-")
#ROOT_ADD_TEST(gbench${mangled_name}
# COMMAND ${benchmark}
@@ -76,6 +76,29 @@ function(RB_ADD_GBENCHMARK benchmark)
FIXTURES_REQUIRED "setup-${benchmark};download-${benchmark}-datafiles")
endfunction(RB_ADD_GBENCHMARK)

#----------------------------------------------------------------------------
# function RB_ADD_GBENCHMARK(<benchmark> source1 source2... LIBRARIES libs)
#----------------------------------------------------------------------------
function(RB_ADD_MEM_GBENCHMARK benchmark)
RB_ADD_GBENCHMARK(${benchmark} ${ARGN})

set (benchmark_env)
if(APPLE)
target_link_libraries(${benchmark} PUBLIC
-Wl,-bind_at_load -Wl,-undefined -Wl,dynamic_lookup
)
set (benchmark_env "DYLD_FORCE_FLAT_NAMESPACE=1" "DYLD_INSERT_LIBRARIES=$<TARGET_FILE:RBInstrumentation>")
elseif(NOT MSVC)
target_link_libraries(${benchmark} PUBLIC
-Wl,--unresolved-symbols=ignore-in-object-files
)
set (benchmark_env "LD_PRELOAD=$<TARGET_FILE:RBInstrumentation>")
endif()
if (benchmark_env)
set_property(TEST rootbench-${benchmark} APPEND PROPERTY ENVIRONMENT ${benchmark_env})
endif(benchmark_env)

endfunction(RB_ADD_MEM_GBENCHMARK)

#----------------------------------------------------------------------------
# function RB_ADD_PYTESTBENCHMARK(<benchmark> filename)
@@ -126,13 +149,19 @@ endfunction()
function(RB_ADD_LIBRARY library)
cmake_parse_arguments(ARG "" "" "LIBRARIES;DEPENDENCIES" ${ARGN})
set(sources ${ARG_UNPARSED_ARGUMENTS})
include_directories(BEFORE ${ROOTBENCH_SOURCE_DIR}/include)
add_library(${library} STATIC ${sources})
if (ARG_LIBRARIES OR ARG_DEPENDENCIES)
target_link_libraries(${library} ${ARG_LIBRARIES} ${ARG_DEPENDENCIES})
endif()
target_include_directories(${library} BEFORE PUBLIC ${ROOTBENCH_SOURCE_DIR}/include)
target_link_libraries(${library} PUBLIC ${ARG_LIBRARIES} ${ARG_DEPENDENCIES} gbenchmark)
endfunction(RB_ADD_LIBRARY)

#----------------------------------------------------------------------------
# function RB_ADD_C_LIBRARY(<library> source1 source2... LIBRARIES libs)
#----------------------------------------------------------------------------
function(RB_ADD_C_LIBRARY library)
RB_ADD_LIBRARY(${library} ${ARGN})
set_target_properties(${library} PROPERTIES LINKER_LANGUAGE C)
endfunction(RB_ADD_C_LIBRARY)

#----------------------------------------------------------------------------
# function RB_ADD_TOOL(<binary> source1 source2... LIBRARIES libs)
#----------------------------------------------------------------------------
3 changes: 3 additions & 0 deletions include/rootbench/Constants.h.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
namespace RB {
static constexpr const char* kDatasetDirectory = "@RB_DATASETDIR@";
}

#cmakedefine RB_ON_WIN32 ${RB_ON_WIN32}
#cmakedefine RB_ON_UNIX ${RB_ON_UNIX}
24 changes: 24 additions & 0 deletions include/rootbench/Instrumentation/Memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// \file Memory.h
///
/// The file contains interfaces of the C memory instrumentation library.
///
/// \author Vassil Vassilev <vvasilev@cern.ch>
///
/// \date Oct, 2020
///

#ifndef RB_MEMORY_H
#define RB_MEMORY_H

#include <stddef.h>

struct malloc_stats {
size_t num_malloc_calls;
size_t max_bytes_used;
};

void reset_malloc_stats();
const struct malloc_stats get_current_malloc_stats();


#endif // RB_MEMORY_H
32 changes: 27 additions & 5 deletions include/rootbench/RBConfig.h
Original file line number Diff line number Diff line change
@@ -2,9 +2,10 @@
#ifndef RB_CONFIG_H
#define RB_CONFIG_H

#include "rootbench/Support/ErrorHandling.h"
#include <rootbench/Constants.h> // RB::kDatasetDirectory

#include "rootbench/Support/ErrorHandling.h"

#include <string>

#include <stdlib.h>
@@ -29,16 +30,37 @@ namespace RB {
rb_abort("Please set the ROOTSYS env variable!");
}

constexpr const char* GetPreloadEnvVar() {
#ifdef __APPLE__
// FIXME: Remove the need of DYLD_FORCE_FLAT_NAMESPACE by using interposing.
return "DYLD_INSERT_LIBRARIES";
#elif defined(RB_ON_UNIX)
return "LD_PRELOAD";
#else
# error Unsupported Platform;
#endif
}

/// Checks if we have set up the LD_PRELOAD mechanism for binary
/// instrumentation
inline bool IsLdPreloadEnabled() {
if (char* rootsys = std::getenv(GetPreloadEnvVar()))
return true;
return false;
}

/// Return the absolute path to the directory where data will be downloaded
inline std::string GetDataDir() {
return RB::kDatasetDirectory;
return RB::kDatasetDirectory;
}

/// Like assert, but it does not disappear if -DNDEBUG
inline void Ensure(bool b)
inline void Ensure(bool b, const std::string& reason = "")
{
if (!b)
std::abort();
if (!b) {
printf("Aborting with reason: '%s'\n", reason.c_str());
std::abort();
}
}
}

41 changes: 41 additions & 0 deletions include/rootbench/Support/MemoryManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/// \file MemoryManager.h
///
/// The file contains the implementation of benchmark memory tracking.
///
/// \author Vassil Vassilev <vvasilev@cern.ch>
///
/// \date Oct, 2020
///

#ifndef RB_MEMORY_MANAGER_H
#define RB_MEMORY_MANAGER_H

#include "benchmark/benchmark.h"

#include <memory>

namespace RB {
/// Records number of allocations per iteration and the maximum bites used
/// for a benchmark.
struct MemoryManager : public benchmark::MemoryManager {
size_t cur_num_allocs = 0;
size_t cur_max_bytes_used = 0;
void Start() override;
void Stop(Result* result) override;
};
} // end RB

namespace {
static auto mm = std::unique_ptr<RB::MemoryManager>(new RB::MemoryManager());
static struct InstrumentationRegistrer {
InstrumentationRegistrer() {
benchmark::RegisterMemoryManager(mm.get());
}
~InstrumentationRegistrer() {
benchmark::RegisterMemoryManager(nullptr);
}
} __mem_mgr_register;
}


#endif // RB_MEMORY_MANAGER_H
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
#---Add the support libraries.
add_subdirectory(Instrumentation)
add_subdirectory(Support)
4 changes: 4 additions & 0 deletions lib/Instrumentation/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
RB_ADD_C_LIBRARY(RBInstrumentation SHARED
Memory.c
LIBRARIES dl
)
47 changes: 47 additions & 0 deletions lib/Instrumentation/Memory.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/// \file Memory.c
///
/// The file contains code of the C memory instrumentation library.
///
/// \author Vassil Vassilev <vvasilev@cern.ch>
///
/// \date Oct, 2020
///


#include "rootbench/Instrumentation/Memory.h"

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#undef _GNU_SOURCE

static void* (*real_malloc)(size_t) = NULL;

static void mtrace_init(void) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
if (!real_malloc) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
}
}

static struct malloc_stats cur_malloc_stats;
void reset_malloc_stats() {
cur_malloc_stats.num_malloc_calls = 0;
cur_malloc_stats.max_bytes_used = 0;
}

const struct malloc_stats get_current_malloc_stats() {
return cur_malloc_stats;
}

void *malloc(size_t size) {
if(!real_malloc)
mtrace_init();

void *p = NULL;
p = real_malloc(size);
cur_malloc_stats.num_malloc_calls++;
cur_malloc_stats.max_bytes_used += size;

return p;
}
7 changes: 6 additions & 1 deletion lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
RB_ADD_LIBRARY(RBSupport
ErrorHandling.cxx
)
MemoryManager.cxx
)
target_include_directories(RBSupport PUBLIC
${PROJECT_BINARY_DIR}/include
${PROJECT_SOURCE_DIR}/include
${GBENCHMARK_INCLUDE_DIR})
44 changes: 44 additions & 0 deletions lib/Support/MemoryManager.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/// \file MemoryManager.h
///
/// The file contains interfaces for benchmark memory tracking.
///
/// \author Vassil Vassilev <vvasilev@cern.ch>
///
/// \date Oct, 2020
///

#include <rootbench/Support/MemoryManager.h>

#include <rootbench/RBConfig.h>

extern "C" {
#include <rootbench/Instrumentation/Memory.h>
}

#include <benchmark/benchmark.h>

// FIXME: Remove the need of DYLD_FORCE_FLAT_NAMESPACE by using interposing.
static void EnsureFlatNamespaceOSX() {
#ifdef __APPLE__
RB::Ensure(std::getenv("DYLD_FORCE_FLAT_NAMESPACE"),
"DYLD_FORCE_FLAT_NAMESPACE not set.");
#endif // __APPLE__
}

namespace RB {
void MemoryManager::Start() {
Ensure(IsLdPreloadEnabled(), std::string(GetPreloadEnvVar()) + " not set.");
EnsureFlatNamespaceOSX();
cur_num_allocs = 0;
cur_max_bytes_used = 0;
reset_malloc_stats();
}

void MemoryManager::Stop(Result* result) {
Ensure(IsLdPreloadEnabled(), std::string(GetPreloadEnvVar()) + " not set.");
EnsureFlatNamespaceOSX();
const malloc_stats& stats = get_current_malloc_stats();
result->num_allocs = stats.num_malloc_calls;
result->max_bytes_used = stats.max_bytes_used;
}
} // end RB
6 changes: 5 additions & 1 deletion root/interpreter/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -4,4 +4,8 @@ RB_ADD_GBENCHMARK(InterpreterBenchmarks

RB_ADD_GBENCHMARK(InterpreterBenchmarksNoPCH
RunInterpreterNoPCH.cxx
LABEL short)
LABEL short)

RB_ADD_MEM_GBENCHMARK(InterpreterLookupHelperBenchmarks
LookupHelper.cxx LIBRARIES Core
LABEL short)
24 changes: 24 additions & 0 deletions root/interpreter/LookupHelper.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <TInterpreter.h>

#include <rootbench/Support/MemoryManager.h>

#include <benchmark/benchmark.h>


static void BM_LookupHelper_Leak(benchmark::State &state) {
gInterpreter->Declare(R"CODE(
#ifndef BM_LookupHelper_Leak_Guard
#define BM_LookupHelper_Leak_Guard
template <class T, class U> class __gmp_expr;
typedef struct{ } __mpz_struct;
typedef __gmp_expr<__mpz_struct[1],__mpz_struct[1]> mpz_class;
#endif // BM_LookupHelper_Leak_Guard
)CODE");

for (auto _ : state)
gInterpreter->ClassInfo_IsEnum("std::vector<mpz_class>::value_type");
}


BENCHMARK(BM_LookupHelper_Leak);
BENCHMARK_MAIN();