如何实现仅打印一次特定函数的唯一调用栈(Backtrace)
仅打印特定函数的唯一调用栈
我正尝试修改DebugInfo/app_backtrace.cpp文件,使其仅打印特定函数的唯一调用栈(backtrace,即调用堆栈)。目前该程序会在函数每次被调用时输出调用栈,而我的场景中该函数嵌套在双层循环内被调用,导致调用栈被无限重复打印。请问如何实现仅打印一次唯一调用栈?
相关代码:
void qux_Before(const CONTEXT* ctxt) { void* buf[128]; PIN_LockClient(); int nptrs = PIN_Backtrace(ctxt, buf, sizeof(buf) / sizeof(buf[0])); ASSERTX(nptrs > 0); char** bt = backtrace_symbols(buf, nptrs); PIN_UnlockClient(); ASSERTX(NULL != bt); for (int i = 0; i < nptrs; i++) { #ifdef TARGET_MAC if (*bt[i] == '_') { // Demangle the C function name bt[i]++; } #endif cout << bt[i] << endl; } free(bt); } void InstImage(IMG img, void* v) { if (IMG_IsMainExecutable(img)) { RTN rtn = RTN_FindByName(img, C_MANGLE("qux")); ASSERTX(RTN_Valid(rtn)); RTN_Open(rtn); RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)qux_Before, IARG_CONST_CONTEXT, IARG_END); RTN_Close(rtn); } } int main(int argc, char** argv) { PIN_InitSymbols(); if (PIN_Init(argc, argv)) { cerr << "usage..." << endl; return EXIT_FAILURE; } IMG_AddInstrumentFunction(InstImage, 0); PIN_StartProgram(); return EXIT_FAILURE; }
解决方案
要实现仅打印唯一调用栈,核心思路是记录已输出过的调用栈标识,后续调用时先检查是否已存在,存在则跳过输出。具体实现如下:
修改步骤
- 添加全局存储与锁:用一个哈希集合存储已输出的调用栈字符串,配合互斥锁保证多线程环境下的访问安全。
- 生成调用栈唯一标识:将每一行调用栈信息拼接成完整字符串,作为该调用栈的唯一签名。
- 添加检查逻辑:生成签名后先查询集合,仅当签名不存在时才输出调用栈并将签名存入集合。
修改后的完整代码
#include <unordered_set> #include <mutex> #include <sstream> // 全局存储已输出的调用栈签名,互斥锁保证线程安全 std::unordered_set<std::string> seen_backtraces; std::mutex bt_mutex; void qux_Before(const CONTEXT* ctxt) { void* buf[128]; PIN_LockClient(); int nptrs = PIN_Backtrace(ctxt, buf, sizeof(buf) / sizeof(buf[0])); ASSERTX(nptrs > 0); char** bt = backtrace_symbols(buf, nptrs); PIN_UnlockClient(); ASSERTX(NULL != bt); // 拼接调用栈为唯一字符串签名 std::ostringstream oss; for (int i = 0; i < nptrs; i++) { #ifdef TARGET_MAC if (*bt[i] == '_') { oss << bt[i] + 1 << "\n"; } else { oss << bt[i] << "\n"; } #else oss << bt[i] << "\n"; #endif } std::string bt_str = oss.str(); // 检查并输出唯一调用栈 { std::lock_guard<std::mutex> lock(bt_mutex); if (seen_backtraces.find(bt_str) == seen_backtraces.end()) { cout << bt_str; seen_backtraces.insert(bt_str); } } free(bt); } // 以下函数保持不变 void InstImage(IMG img, void* v) { if (IMG_IsMainExecutable(img)) { RTN rtn = RTN_FindByName(img, C_MANGLE("qux")); ASSERTX(RTN_Valid(rtn)); RTN_Open(rtn); RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)qux_Before, IARG_CONST_CONTEXT, IARG_END); RTN_Close(rtn); } } int main(int argc, char** argv) { PIN_InitSymbols(); if (PIN_Init(argc, argv)) { cerr << "usage..." << endl; return EXIT_FAILURE; } IMG_AddInstrumentFunction(InstImage, 0); PIN_StartProgram(); return EXIT_FAILURE; }
关键说明
- 线程安全:使用
std::lock_guard自动管理互斥锁,避免多线程访问哈希集合时出现竞态条件。 - 内存占用:哈希集合会持续存储所有唯一调用栈,若程序运行时间长、调用栈数量多,可根据需求添加清理逻辑(比如定时清空或限制集合大小)。
- 签名准确性:通过完整拼接调用栈每一行内容,确保相同调用栈生成完全一致的签名,不会出现误判。
内容的提问来源于stack exchange,提问作者agustinvaca
相关产品推荐
相关产品推荐

