diff --git a/CMakeLists.txt b/CMakeLists.txt index d610d834..dd988a75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,21 @@ option(ENABLE_API "Enable C API SDK" true) option(ENABLE_CXX_API "Enable C++ API SDK" false) option(ENABLE_TESTS "Enable Tests" true) option(ENABLE_SERVER "Enable Server" true) +option(ENABLE_MEM_DEBUG "Enable Memory Debug" false) +option(ENABLE_ASAN "Enable Address Sanitize" false) + +if (ENABLE_MEM_DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-wrap,free -Wl,-wrap,malloc -Wl,-wrap,realloc -Wl,-wrap,calloc") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-wrap,free -Wl,-wrap,malloc -Wl,-wrap,realloc -Wl,-wrap,calloc") + add_definitions(-DENABLE_MEM_DEBUG) + message(STATUS "已启用内存调试功能") +endif () + +if (ENABLE_ASAN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + message(STATUS "已启用Address Sanitize") +endif () set(LINK_LIB_LIST zlmediakit zltoolkit) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 7d791214..b29a136c 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -170,6 +170,8 @@ static ApiArgsType getAllArgs(const Parser &parser) { return allArgs; } +extern uint64_t getTotalMemUsage(); + static inline void addHttpListener(){ GET_CONFIG(bool, api_debug, API::kApiDebug); //注册监听kBroadcastHttpRequest事件 @@ -1046,6 +1048,11 @@ void installWebApi() { val["data"]["RtpPacket"] = (Json::UInt64)(ObjectStatistic::count()); val["data"]["RtmpPacket"] = (Json::UInt64)(ObjectStatistic::count()); +#ifdef ENABLE_MEM_DEBUG + auto bytes = getTotalMemUsage(); + val["data"]["totalMemUsage"] = (Json::UInt64)bytes; + val["data"]["totalMemUsageMB"] = (int)(bytes / 1024 / 1024); +#endif }); ////////////以下是注册的Hook API//////////// diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 41c59083..5fc2dc3a 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -307,11 +307,117 @@ const string kBenchmarkMode = "benchmark_mode"; } // namespace mediakit - -void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line){ - if(failed) { +extern "C" { +void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line) { + if (failed) { _StrPrinter printer; - printer << "Assertion failed: (" << exp << "), function " << func << ", file " << file << ", line " << line << "."; + printer << "Assertion failed: (" << exp << "), function " << func << ", file " << file << ", line " << line + << "."; throw std::runtime_error(printer); } } +} + +#ifdef ENABLE_MEM_DEBUG + +static atomic mem_usage(0); + +uint64_t getTotalMemUsage() { + return mem_usage.load(); +} + +extern "C" { + +#include +#define MAGIC_BYTES 0xFEFDFCFB +#define MAGIC_BYTES_SIZE 4 +#define MEM_PREFIX_SIZE 8 + +extern void *__real_malloc(size_t); +extern void __real_free(void *); +extern void *__real_realloc(void *ptr, size_t c); + +void *__wrap_malloc(size_t c) { + c += MEM_PREFIX_SIZE; + char *ret = (char *) __real_malloc(c); + if (ret) { + mem_usage += c; + *((uint32_t *) (ret)) = MAGIC_BYTES; + *((uint32_t *) (ret + MAGIC_BYTES_SIZE)) = c; + return ret + MEM_PREFIX_SIZE; + } + return nullptr; +} + +void __wrap_free(void *ptr) { + if (!ptr) { + return; + } + ptr = (char *) ptr - MEM_PREFIX_SIZE; + uint32_t magic = *((uint32_t *) (ptr)); + if (magic != MAGIC_BYTES) { + throw std::invalid_argument("attempt to free invalid memory"); + } + mem_usage -= *((uint32_t *) ((char *) ptr + MAGIC_BYTES_SIZE)); + __real_free(ptr); +} + +void *__wrap_calloc(size_t __nmemb, size_t __size) { + auto size = __nmemb * __size; + auto ret = malloc(size); + if (ret) { + memset(ret, 0, size); + } + return ret; +} + +void *__wrap_realloc(void *ptr, size_t c){ + if (!ptr) { + return malloc(c); + } + c += MEM_PREFIX_SIZE; + ptr = (char *) ptr - MEM_PREFIX_SIZE; + + uint32_t magic = *((uint32_t *) (ptr)); + if (magic != MAGIC_BYTES) { + throw std::invalid_argument("attempt to realloc invalid memory"); + } + auto old_size = *((uint32_t *) ((char *) ptr + MAGIC_BYTES_SIZE)); + char *ret = (char *) __real_realloc(ptr, c); + if (ret) { + mem_usage += c - old_size; + *((uint32_t *) (ret)) = MAGIC_BYTES; + *((uint32_t *) (ret + MAGIC_BYTES_SIZE)) = c; + return ret + MEM_PREFIX_SIZE; + } + free(ptr); + mem_usage -= old_size; + return nullptr; +} + +} + +void *operator new(std::size_t size) { + auto ret = malloc(size); + if (ret) { + return ret; + } + throw std::bad_alloc(); +} + +void operator delete(void *ptr) { + free(ptr); +} + +void *operator new[](std::size_t size) { + auto ret = malloc(size); + if (ret) { + return ret; + } + throw std::bad_alloc(); +} + +void operator delete[](void *ptr) { + free(ptr); +} +#endif