diff --git a/cmake/modules/AddRootBench.cmake b/cmake/modules/AddRootBench.cmake index 2b0f6144d..93629fd8f 100644 --- a/cmake/modules/AddRootBench.cmake +++ b/cmake/modules/AddRootBench.cmake @@ -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( 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=$") + elseif(NOT MSVC) + target_link_libraries(${benchmark} PUBLIC + -Wl,--unresolved-symbols=ignore-in-object-files + ) + set (benchmark_env "LD_PRELOAD=$") + 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( 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( 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( source1 source2... LIBRARIES libs) #---------------------------------------------------------------------------- diff --git a/include/rootbench/Instrumentation/Memory.h b/include/rootbench/Instrumentation/Memory.h new file mode 100644 index 000000000..576ce07e5 --- /dev/null +++ b/include/rootbench/Instrumentation/Memory.h @@ -0,0 +1,24 @@ +/// \file Memory.h +/// +/// The file contains interfaces of the C memory instrumentation library. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + +#ifndef RB_MEMORY_H +#define RB_MEMORY_H + +#include + +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 diff --git a/include/rootbench/RBConfig.h b/include/rootbench/RBConfig.h index 57fe76b10..dbc53a858 100644 --- a/include/rootbench/RBConfig.h +++ b/include/rootbench/RBConfig.h @@ -2,9 +2,10 @@ #ifndef RB_CONFIG_H #define RB_CONFIG_H -#include "rootbench/Support/ErrorHandling.h" #include // RB::kDatasetDirectory +#include "rootbench/Support/ErrorHandling.h" + #include #include @@ -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(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(); + } } } diff --git a/include/rootbench/Support/MemoryManager.h b/include/rootbench/Support/MemoryManager.h new file mode 100644 index 000000000..73eb64dc8 --- /dev/null +++ b/include/rootbench/Support/MemoryManager.h @@ -0,0 +1,40 @@ +/// \file MemoryManager.h +/// +/// The file contains the implementation of benchmark memory tracking. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + +#ifndef RB_MEMORY_MANAGER_H +#define RB_MEMORY_MANAGER_H + +#include "benchmark/benchmark.h" + + +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(new RB::MemoryManager()); + static struct InstrumentationRegistrer { + InstrumentationRegistrer() { + benchmark::RegisterMemoryManager(mm.get()); + } + ~InstrumentationRegistrer() { + benchmark::RegisterMemoryManager(nullptr); + } + } __mem_mgr_register; +} + + +#endif // RB_MEMORY_MANAGER_H diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 8ebe40a1f..dccc9646b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,2 +1,3 @@ #---Add the support libraries. +add_subdirectory(Instrumentation) add_subdirectory(Support) diff --git a/lib/Instrumentation/CMakeLists.txt b/lib/Instrumentation/CMakeLists.txt new file mode 100644 index 000000000..c75b30abb --- /dev/null +++ b/lib/Instrumentation/CMakeLists.txt @@ -0,0 +1,3 @@ +RB_ADD_C_LIBRARY(RBInstrumentation SHARED + Memory.c + ) diff --git a/lib/Instrumentation/Memory.c b/lib/Instrumentation/Memory.c new file mode 100644 index 000000000..9f84c435b --- /dev/null +++ b/lib/Instrumentation/Memory.c @@ -0,0 +1,47 @@ +/// \file Memory.c +/// +/// The file contains code of the C memory instrumentation library. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + + +#include "rootbench/Instrumentation/Memory.h" + +#define _GNU_SOURCE +#include +#include +#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; +} diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index 0403c4920..1a61ae094 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -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}) diff --git a/lib/Support/MemoryManager.cxx b/lib/Support/MemoryManager.cxx new file mode 100644 index 000000000..5976a2cc6 --- /dev/null +++ b/lib/Support/MemoryManager.cxx @@ -0,0 +1,44 @@ +/// \file MemoryManager.h +/// +/// The file contains interfaces for benchmark memory tracking. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + +#include + +#include + +extern "C" { + #include +} + +#include + +// 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 diff --git a/root/interpreter/CMakeLists.txt b/root/interpreter/CMakeLists.txt index f0ffa6bea..095d1269b 100644 --- a/root/interpreter/CMakeLists.txt +++ b/root/interpreter/CMakeLists.txt @@ -4,4 +4,8 @@ RB_ADD_GBENCHMARK(InterpreterBenchmarks RB_ADD_GBENCHMARK(InterpreterBenchmarksNoPCH RunInterpreterNoPCH.cxx - LABEL short) \ No newline at end of file + LABEL short) + +RB_ADD_MEM_GBENCHMARK(InterpreterLookupHelperBenchmarks + LookupHelper.cxx LIBRARIES Core + LABEL short) diff --git a/root/interpreter/LookupHelper.cxx b/root/interpreter/LookupHelper.cxx new file mode 100644 index 000000000..43d50a9ce --- /dev/null +++ b/root/interpreter/LookupHelper.cxx @@ -0,0 +1,24 @@ +#include + +#include + +#include + + +static void BM_LookupHelper_Leak(benchmark::State &state) { + gInterpreter->Declare(R"CODE( +#ifndef BM_LookupHelper_Leak_Guard +#define BM_LookupHelper_Leak_Guard + template 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::value_type"); +} + + +BENCHMARK(BM_LookupHelper_Leak); +BENCHMARK_MAIN();