diff --git a/addr2sym.hpp b/addr2sym.hpp index cccf3bd..8e31895 100644 --- a/addr2sym.hpp +++ b/addr2sym.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #if __unix__ # include @@ -114,3 +115,11 @@ inline std::string addr2sym(void *addr) { return oss.str(); } #endif + +inline std::vector addrList2symList(std::vector addrList) { + std::vector symList; + for(auto addr : addrList) { + symList.push_back(addr2sym(addr)); + } + return symList; +} diff --git a/alloc_action.hpp b/alloc_action.hpp index 9723120..d1a1490 100644 --- a/alloc_action.hpp +++ b/alloc_action.hpp @@ -2,6 +2,7 @@ #include #include +#include enum class AllocOp { New, @@ -23,7 +24,7 @@ struct AllocAction { void *ptr; size_t size; size_t align; - void *caller; + std::vector call_stack; int64_t time; }; diff --git a/malloc_hook.cpp b/malloc_hook.cpp index 14fae64..6e65a82 100644 --- a/malloc_hook.cpp +++ b/malloc_hook.cpp @@ -192,14 +192,14 @@ struct EnableGuard { } void on(AllocOp op, void *ptr, size_t size, size_t align, - void *caller) const { + std::vector call_stack) const { if (ptr) { auto now = std::chrono::high_resolution_clock::now(); int64_t time = std::chrono::duration_cast( now.time_since_epoch()) .count(); per_thread->actions.push_back( - AllocAction{op, tid, ptr, size, align, caller, time}); + AllocAction{op, tid, ptr, size, align, call_stack, time}); } } @@ -222,6 +222,13 @@ extern "C" void *__libc_reallocarray(void *ptr, size_t nmemb, size_t size) noexcept; extern "C" void *__libc_valloc(size_t size) noexcept; extern "C" void *__libc_memalign(size_t align, size_t size) noexcept; + +static std::vector return_call_stack(){ + std::vector callStack(1024); + callStack.resize(backtrace(callStack.data(), callStack.size())); + return {callStack.begin() + 1, callStack.end()}; +} + # define REAL_LIBC(name) __libc_##name # ifndef MAY_OVERRIDE_MALLOC # define MAY_OVERRIDE_MALLOC 1 @@ -230,20 +237,25 @@ extern "C" void *__libc_memalign(size_t align, size_t size) noexcept; # define MAY_SUPPORT_MEMALIGN 1 # endif # undef RETURN_ADDRESS +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) +# define RETURN_ADDRESS return_call_stack() +#endif +# ifndef RETURN_ADDRESS # ifdef __has_builtin # if __has_builtin(__builtin_return_address) # if __has_builtin(__builtin_extract_return_addr) # define RETURN_ADDRESS \ - __builtin_extract_return_addr(__builtin_return_address(0)) + std::vector{__builtin_extract_return_addr(__builtin_return_address(0))} # else -# define RETURN_ADDRESS __builtin_return_address(0) +# define RETURN_ADDRESS std::vector{__builtin_return_address(0)} # endif # endif # elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) -# define RETURN_ADDRESS __builtin_return_address(0) +# define RETURN_ADDRESS std::vector{__builtin_return_address(0)} +# endif # endif # ifndef RETURN_ADDRESS -# define RETURN_ADDRESS ((void *)0) +# define RETURN_ADDRESS (std::vector{0}) # pragma message("Cannot find __builtin_return_address") # endif # define CSTDLIB_NOEXCEPT noexcept @@ -278,8 +290,58 @@ static void *msvc_reallocarray(void *ptr, size_t nmemb, size_t size) noexcept { # include -# pragma intrinsic(_ReturnAddress) -# define RETURN_ADDRESS _ReturnAddress() +static std::vector return_call_stack(){ + std::vector callStack; + + // 获取当前上下文 + CONTEXT context; + RtlCaptureContext(&context); + + // 初始化 STACKFRAME64 + STACKFRAME64 stackFrame; + ZeroMemory(&stackFrame, sizeof(STACKFRAME64)); + +#ifdef _M_IX86 + stackFrame.AddrPC.Offset = context.Eip; + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context.Ebp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrStack.Offset = context.Esp; + stackFrame.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + stackFrame.AddrPC.Offset = context.Rip; + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context.Rbp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrStack.Offset = context.Rsp; + stackFrame.AddrStack.Mode = AddrModeFlat; +#endif + + // 遍历堆栈 + while (StackWalk64( +#ifdef _M_IX86 + IMAGE_FILE_MACHINE_I386, +#elif _M_X64 + IMAGE_FILE_MACHINE_AMD64, +#endif + GetCurrentProcess(), + GetCurrentThread(), + &stackFrame, + &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) { + if (stackFrame.AddrPC.Offset == 0) { + break; // 结束遍历 + } + callStack.push_back((void*)stackFrame.AddrPC.Offset); + } + + return {callStack.begin() + 1, callStack.end()}; +} + +# define RETURN_ADDRESS return_call_stack() # define CSTDLIB_NOEXCEPT #else @@ -290,7 +352,7 @@ static void *msvc_reallocarray(void *ptr, size_t nmemb, size_t size) noexcept { # ifndef MAY_OVERRIDE_MEMALIGN # define MAY_SUPPORT_MEMALIGN 0 # endif -# define RETURN_ADDRESS ((void *)1) +# define RETURN_ADDRESS (std::vector{1}) # define CSTDLIB_NOEXCEPT #endif diff --git a/plot_actions.cpp b/plot_actions.cpp index 0a28776..7ca13d7 100644 --- a/plot_actions.cpp +++ b/plot_actions.cpp @@ -34,8 +34,8 @@ struct LifeBlock { uint32_t end_tid; void *ptr; size_t size; - void *start_caller; - void *end_caller; + std::vector start_call_stack; + std::vector end_call_stack; int64_t start_time; int64_t end_time; }; @@ -131,7 +131,7 @@ struct SvgWriter { } void text(double x, double y, std::string const &color, - std::string const &alignment, std::string const &text) { + std::string const &alignment, std::string const &text, std::string const &title) { x += margin; // HTML escape text: std::string html; @@ -146,8 +146,21 @@ struct SvgWriter { default: html += c; break; } } + std::string html_title; + html_title.reserve(title.size()); + for (auto c: title) { + switch (c) { + case '&': html_title += "&"; break; + case '<': html_title += "<"; break; + case '>': html_title += ">"; break; + case '"': html_title += """; break; + case '\'': html_title += "'"; break; + default: html_title += c; break; + } + } out << "" << html << "\n"; + << "\"" << alignment << ">" << "" << html_title << "" + << html << "\n"; } SvgWriter(SvgWriter &&) = delete; @@ -470,15 +483,15 @@ void mallocvis_plot_alloc_actions(std::vector actions) { if (kAllocOpIsAllocation[(size_t)action.op]) { living.insert({action.ptr, {action.op, action.op, action.tid, action.tid, - action.ptr, action.size, action.caller, - action.caller, action.time, action.time}}); + action.ptr, action.size, action.call_stack, + action.call_stack, action.time, action.time}}); } else { auto it = living.find(action.ptr); if (it != living.end()) { it->second.end_op = action.op; it->second.end_tid = action.tid; it->second.end_time = action.time; - it->second.end_caller = action.caller; + it->second.end_call_stack = action.call_stack; dead.insert(it->second); living.erase(it); } @@ -513,10 +526,10 @@ void mallocvis_plot_alloc_actions(std::vector actions) { if (block.end_time > block.start_time) { end_time = std::max(end_time, block.end_time); } - start_caller = std::min(start_caller, (uintptr_t)block.start_caller); - end_caller = std::max(end_caller, (uintptr_t)block.start_caller); - start_caller = std::min(start_caller, (uintptr_t)block.end_caller); - end_caller = std::max(end_caller, (uintptr_t)block.end_caller); + start_caller = std::min(start_caller, (uintptr_t)block.start_call_stack[0]); + end_caller = std::max(end_caller, (uintptr_t)block.start_call_stack[0]); + start_caller = std::min(start_caller, (uintptr_t)block.end_call_stack[0]); + end_caller = std::max(end_caller, (uintptr_t)block.end_call_stack[0]); start_ptr = std::min(start_ptr, (uintptr_t)block.ptr); end_ptr = std::max(end_ptr, (uintptr_t)block.ptr + block.size); tids.insert({block.start_tid, tids.size()}); @@ -527,11 +540,11 @@ void mallocvis_plot_alloc_actions(std::vector actions) { for (auto &[_, block]: living) { start_time = std::min(start_time, block.start_time); block.end_time = end_time; - block.end_caller = nullptr; - start_caller = std::min(start_caller, (uintptr_t)block.start_caller); - end_caller = std::max(end_caller, (uintptr_t)block.start_caller); - start_caller = std::min(start_caller, (uintptr_t)block.end_caller); - end_caller = std::max(end_caller, (uintptr_t)block.end_caller); + block.end_call_stack[0] = nullptr; + start_caller = std::min(start_caller, (uintptr_t)block.start_call_stack[0]); + end_caller = std::max(end_caller, (uintptr_t)block.start_call_stack[0]); + start_caller = std::min(start_caller, (uintptr_t)block.end_call_stack[0]); + end_caller = std::max(end_caller, (uintptr_t)block.end_call_stack[0]); start_ptr = std::min(start_ptr, (uintptr_t)block.ptr); end_ptr = std::max(end_ptr, (uintptr_t)block.ptr + block.size); tids.insert({block.start_tid, tids.size()}); @@ -562,10 +575,10 @@ void mallocvis_plot_alloc_actions(std::vector actions) { double z0 = 0; double z1 = 0; if (options.z_indicates == PlotOptions::Caller) { - z0 = ((uintptr_t)block.start_caller - start_caller) * + z0 = ((uintptr_t)block.start_call_stack[0] - start_caller) * caller_scale; z1 = - ((uintptr_t)block.end_caller - start_caller) * caller_scale; + ((uintptr_t)block.end_call_stack[0] - start_caller) * caller_scale; } else if (options.z_indicates == PlotOptions::Thread) { z0 = tids.at(block.start_tid); z1 = tids.at(block.end_tid); @@ -600,8 +613,8 @@ void mallocvis_plot_alloc_actions(std::vector actions) { for (auto const &block: dead) { double height = eval_height(block); total_height += height; - callers.insert(block.start_caller); - callers.insert(block.end_caller); + callers.insert(block.start_call_stack[0]); + callers.insert(block.end_call_stack[0]); } if (options.layout == PlotOptions::Address) { total_height = end_ptr - start_ptr; @@ -634,13 +647,13 @@ void mallocvis_plot_alloc_actions(std::vector actions) { auto eval_color = [&](LifeBlock const &block) -> std::pair { - return {caller_color(block.start_caller), - caller_color(block.end_caller)}; + return {caller_color(block.start_call_stack[0]), + caller_color(block.end_call_stack[0])}; }; auto eval_text = - [](LifeBlock const &block) -> std::pair { - return {addr2sym(block.start_caller), addr2sym(block.end_caller)}; + [](LifeBlock const &block) -> std::pair, std::vector> { + return {addrList2symList(block.start_call_stack), addrList2symList(block.end_call_stack)}; }; SvgWriter svg(options.path.empty() ? "malloc.html" : options.path, @@ -669,12 +682,18 @@ void mallocvis_plot_alloc_actions(std::vector actions) { fontHeight1 *= max_width / (fontHeight * 0.5 * text1.size()); } + std::string title; + for(auto &ti : text1) + { + title += ti; + title += '\n'; + } svg.text( x, y + height * 0.5, color1, " style=\"dominant-baseline:central;text-anchor:" "end;font-size:" + std::to_string(fontHeight1) + "px;\"", - text1); + text1[0], title); } if (!text2.empty()) { auto max_width = @@ -684,12 +703,18 @@ void mallocvis_plot_alloc_actions(std::vector actions) { fontHeight1 *= max_width / (fontHeight * 0.5 * text2.size()); } + std::string title; + for(auto &ti : text2) + { + title += ti; + title += '\n'; + } svg.text( x + width, y + height * 0.5, color2, " style=\"dominant-baseline:central;text-anchor:" "start;font-size:" + std::to_string(fontHeight1) + "px;\"", - text2); + text2[0], title); } } } @@ -714,12 +739,18 @@ void mallocvis_plot_alloc_actions(std::vector actions) { fontHeight1 *= max_width / (fontHeight * 0.5 * text1.size()); } + std::string title; + for(auto &ti : text1) + { + title += ti; + title += '\n'; + } svg.text( x, y + height * 0.5, color1, " style=\"dominant-baseline:central;text-anchor:" "end;font-size:" + std::to_string(fontHeight1) + "px;\"", - text1); + text1[0], title); } if (!text2.empty()) { auto max_width = @@ -729,12 +760,18 @@ void mallocvis_plot_alloc_actions(std::vector actions) { fontHeight1 *= max_width / (fontHeight * 0.5 * text2.size()); } + std::string title; + for(auto &ti : text2) + { + title += ti; + title += '\n'; + } svg.text( x + width, y + height * 0.5, color2, " style=\"dominant-baseline:central;text-anchor:" "start;font-size:" + std::to_string(fontHeight1) + "px;\"", - text2); + text2[0], title); } } y += height;