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
Show file tree
Hide file tree
Changes from all commits
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
18 changes: 17 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ include(PytestBenchmark)

#---Add ROOT include direcories and used compilation flags
include_directories(${ROOT_INCLUDE_DIRS})
add_definitions(${ROOT_CXX_FLAGS})
string(REPLACE " " ";" ROOT_C_FLAGS_LIST ${ROOT_C_FLAGS})
string(REPLACE " " ";" ROOT_CXX_FLAGS_LIST ${ROOT_CXX_FLAGS})
foreach(C_COMPILE_FLAG ${ROOT_C_FLAGS_LIST})
add_compile_options($<$<COMPILE_LANGUAGE:C>:${C_COMPILE_FLAG}>)
endforeach()
foreach(CXX_COMPILE_FLAG ${ROOT_CXX_FLAGS_LIST})
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${CXX_COMPILE_FLAG}>)
endforeach()

#---Include rootbench options
include(RootBenchOptions)
Expand All @@ -75,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)

Expand Down
42 changes: 37 additions & 5 deletions cmake/modules/AddRootBench.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ 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)
if (NOT APPLE)
target_link_libraries(${benchmark} PUBLIC rt)
endif()
#ROOT_PATH_TO_STRING(mangled_name ${benchmark} PATH_SEPARATOR_REPLACEMENT "-")
#ROOT_ADD_TEST(gbench${mangled_name}
# COMMAND ${benchmark}
Expand Down Expand Up @@ -76,6 +79,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)
Expand Down Expand Up @@ -126,13 +152,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)
#----------------------------------------------------------------------------
Expand Down
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 <[email protected]>
///
/// \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
37 changes: 30 additions & 7 deletions include/rootbench/RBConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
#ifndef RB_CONFIG_H
#define RB_CONFIG_H

#include <stdlib.h>

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

#include "rootbench/Support/ErrorHandling.h"

#include <string>

#include <stdlib.h>

namespace RB {
/// Returns a path to temporary file system (preferably in-memory). The path
/// is set in the RB_TEMP_FS environment variable.
Expand All @@ -28,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();
}
}
}

Expand Down
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 <[email protected]>
///
/// \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
7 changes: 3 additions & 4 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
RB_ADD_LIBRARY(RBSupport
ErrorHandling.cxx
)
target_include_directories(RBSupport PUBLIC ${PROJECT_BINARY_DIR}/include ${PROJECT_SOURCE_DIR}/include)
#---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 <[email protected]>
///
/// \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;
}
8 changes: 8 additions & 0 deletions lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +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})
2 changes: 1 addition & 1 deletion lib/ErrorHandling.cxx → lib/Support/ErrorHandling.cxx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
///\file Contains utilities to handle error situations.

#include "rootbench/ErrorHandling.h"
#include "rootbench/Support/ErrorHandling.h"

#include <iostream>

Expand Down
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 <[email protected]>
///
/// \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
Expand Up @@ -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();