You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现仅打印一次特定函数的唯一调用栈(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;
}

解决方案

要实现仅打印唯一调用栈,核心思路是记录已输出过的调用栈标识,后续调用时先检查是否已存在,存在则跳过输出。具体实现如下:

修改步骤

  1. 添加全局存储与锁:用一个哈希集合存储已输出的调用栈字符串,配合互斥锁保证多线程环境下的访问安全。
  2. 生成调用栈唯一标识:将每一行调用栈信息拼接成完整字符串,作为该调用栈的唯一签名。
  3. 添加检查逻辑:生成签名后先查询集合,仅当签名不存在时才输出调用栈并将签名存入集合。

修改后的完整代码

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:37:42