diff --git a/CMakeLists.txt b/CMakeLists.txt index d062266..f3e1746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Library -add_library(heapusage SHARED src/humain.cpp src/hulog.cpp src/humalloc.cpp) +add_library(heapusage SHARED src/humain.cpp src/hulog.cpp src/humalloc.cpp src/huapi.cpp) +set_target_properties(heapusage PROPERTIES PUBLIC_HEADER "src/huapi.h") target_compile_features(heapusage PRIVATE cxx_variadic_templates) -install(TARGETS heapusage LIBRARY DESTINATION lib) +install(TARGETS heapusage + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) target_link_libraries(heapusage pthread dl) # Dependency backward-cpp providing more detailed stacktraces on Linux, when diff --git a/README.md b/README.md index 817083d..fe5cce8 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,20 @@ Examples: heapusage -t all -m 0 ./ex002 analyze heap allocations of any size with all tools. +Programs being ran with Heapusage can themselves also request reports from +Heapusage, while the program is running, by using the `hu_report()` public API +function. For doing so, they must include the `huapi.h` public header file, link +with the Heapusage shared library itself and call `hu_report()` when wanted. +Still, this only works if the program is running through the `heapusage` tool. + +Alternatively, on Linux, sending a `SIGUSR1` signal to the program being run +through Heapusage will also produce a Heapusage report on demand. + +For both `hu_report()` and `SIGUSR1`, it should be noted that the report will +reflect the state when they are used, which can e.g. report non-freed memory +that might be still released before the program exits and, therefore, not +necessarily constitute a memory leak. + Output Format ============= Example output: diff --git a/src/huapi.cpp b/src/huapi.cpp new file mode 100644 index 0000000..5eeaaa1 --- /dev/null +++ b/src/huapi.cpp @@ -0,0 +1,22 @@ +/* + * huapi.cpp + * + * Copyright (C) 2017-2021 Kristofer Berggren + * All rights reserved. + * + * heapusage is distributed under the BSD 3-Clause license, see LICENSE for details. + * + */ + + +/* ----------- Includes ------------------------------------------ */ +#include "hulog.h" + + +/* ----------- Global Functions ---------------------------------- */ + +extern "C" void hu_report() +{ + log_message("hu_report: Request Log Summary\n"); + log_summary_safe(); +} diff --git a/src/huapi.h b/src/huapi.h new file mode 100644 index 0000000..77cb918 --- /dev/null +++ b/src/huapi.h @@ -0,0 +1,27 @@ +/* + * huapi.h + * + * Copyright (C) 2021 Kristofer Berggren + * All rights reserved. + * + * heapusage is distributed under the BSD 3-Clause license, see LICENSE for details. + * + */ + +#ifndef _HUAPI_H_ +#define _HUAPI_H_ + + +/* ----------- Global Function Prototypes ------------------------ */ + +#ifdef __cplusplus +extern "C" { +#endif + +void hu_report(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _HUAPI_H_ */ diff --git a/src/hulog.cpp b/src/hulog.cpp index e6e3302..39e2d1d 100644 --- a/src/hulog.cpp +++ b/src/hulog.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -385,6 +386,30 @@ void hu_sig_handler(int sig, siginfo_t* si, void* /*ucontext*/) exit(EXIT_FAILURE); } +void log_message(const char *format, ...) +{ + va_list args; + + FILE *f = NULL; + if (hu_log_file) + { + f = fopen(hu_log_file, "a"); + } + + if (!f) + { + return; + } + + fprintf(f, "==%d== MESSAGE: ", pid); + + va_start(args, format); + vfprintf(f, format, args); + va_end(args); + + fclose(f); +} + void log_summary() { FILE *f = NULL; @@ -402,7 +427,7 @@ void log_summary() unsigned long long leak_total_blocks = 0; /* Group results by callstack */ - static std::map, hu_allocinfo_t> allocations_by_callstack; + std::map, hu_allocinfo_t> allocations_by_callstack; for (auto it = allocations->begin(); it != allocations->end(); ++it) { std::vector callstack; @@ -465,6 +490,17 @@ void log_summary() fclose(f); } +void log_summary_safe() +{ + hu_set_bypass(true); + log_enable(0); + + log_summary(); + + log_enable(1); + hu_set_bypass(false); +} + void hu_log_remove_freed_allocation(void* ptr) { if (!hu_log_free) diff --git a/src/hulog.h b/src/hulog.h index 778ab31..2aa8bc1 100644 --- a/src/hulog.h +++ b/src/hulog.h @@ -24,5 +24,7 @@ void log_enable(int flag); void log_event(int event, void* ptr, size_t size); void log_invalid_access(void* ptr); void hu_sig_handler(int sig, siginfo_t* si, void* /*ucontext*/); +void log_message(const char *format, ...); void log_summary(); +void log_summary_safe(); void hu_log_remove_freed_allocation(void* ptr); diff --git a/src/humain.cpp b/src/humain.cpp index 428a5eb..d0589c2 100644 --- a/src/humain.cpp +++ b/src/humain.cpp @@ -91,6 +91,34 @@ static inline bool hu_get_env_bool(const char* name) return (strcmp(value, "1") == 0); } +#if defined(__linux__) +static void hu_sigusr_handler(int sig) +{ + switch (sig) { + case SIGUSR1: + log_message("Caught User Signal: SIGUSR1 (%d): Request Log Summary\n", sig); + log_summary_safe(); + break; + default: + log_message("Caught User Signal: Unexpected Signal (%d): Ignore\n", sig); + break; + } +} +#endif + +static void hu_sigusr_init(void) +{ +#if defined(__linux__) + struct sigaction sa; + + sa.sa_handler = hu_sigusr_handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); +#endif +} + /* ----------- Global Functions ---------------------------------- */ extern "C" @@ -116,6 +144,8 @@ void __attribute__ ((constructor)) hu_init(void) hu_malloc_init(hu_overflow, hu_useafterfree, hu_minsize); } + hu_sigusr_init(); + /* Do not enable preload for child processes */ unsetenv("DYLD_INSERT_LIBRARIES"); unsetenv("LD_PRELOAD");