通过 clang++ 的 -finstrument-functions 进行 C++ 函数检测:如何忽略内部标准库调用?

C++ function instrumentation via clang++'s -finstrument-functions : how to ignore internal std library calls?

假设我有这样一个函数:

template<typename It, typename Cmp>
void mysort( It begin, It end, Cmp cmp )
{
    std::sort( begin, end, cmp );
}

当我使用 -finstrument-functions-after-inliningclang++ --version 编译它时:

clang version 11.0.0 (...)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: ...

工具代码使执行时间爆炸,因为每次调用

都会调用我的进入和退出函数
void std::__introsort_loop<...>(...)
void std::__move_median_to_first<...>(...)

我正在对一个非常大的数组进行排序,所以我的程序没有完成:没有检测需要大约 10 秒,使用检测我在 10 分钟后取消了它。

我已经尝试将 __attribute__((no_instrument_function)) 添加到 mysort(以及调用 mysort 的函数),但这似乎对这些标准库没有影响通话有关。

有谁知道是否可以忽略像 std::sort 这样的标准库函数内部的函数检测?理想情况下,我只会 mysort 进行检测,因此一次进入和一次退出!

我很遗憾地看到 clang++ 还不支持 finstrument-functions-exclude-function-listfinstrument-functions-exclude-file-list 之类的东西,但是 g++ 还不支持 -finstrument-functions-after-inlining 我会的理想情况下有,所以我卡住了!

编辑:玩多了之后,似乎对执行时间的影响实际上比描述的要小,所以这不是世界末日。然而,问题仍然存在,因为大多数在 clang 中进行函数检测的人只关心应用程序代码,而不关心那些从(例如)标准库链接的函数。

EDIT2:现在我已经在合理的时间范围内 运行 解决了这个问题,为了进一步强调这个问题:我使用这两个标准库函数从检测代码生成的跟踪结果是 15GB。当我对跟踪进行硬编码以忽略这两个函数地址时,生成的跟踪为 3.7MB!

我 运行 遇到了同样的问题。似乎曾经提出过支持这些标志,但从未合并到主分支中。

https://reviews.llvm.org/D37622

这不是一个直接的答案,因为该工具不支持你想做的事情,但我认为我有一个不错的 work-around。我最后做的是创建一个“跳过列表”之类的。在检测函数(__cyg_profile_func_enter__cyg_profile_func_exit)中,我猜对执行时间贡献最大的部分是打印。如果您能想出一种 short-circuiting 配置文件功能的方法,那应该会有所帮助,即使它不是最理想的。至少它会限制输出文件的大小。

类似

#include <stdint.h>

uintptr_t skipAddrs[] = {
    // assuming 64-bit addresses
    0x123456789abcdef, 0x2468ace2468ace24
};
size_t arrSize = 0;

int main(void)
{   
    ...

    arrSize = sizeof(skipAddrs)/sizeof(skipAddrs[0]);
    // 

    ...
}

void __cyg_profile_func_enter (void *this_fn, void *call_site) {
    for (size_t idx = 0; idx < arrSize; idx++) {
        if ((uintptr_t) this_fn == skipAddrs[idx]) {
            return;
        }
    }
}

我使用 objdump -t binaryFile 之类的东西来检查符号 table 并找到每个函数的地址。

如果您特别想忽略库调用,可能有用的方法是在链接到库之前检查目标文件的符号 table,然后忽略所有在最终文件中出现的新文件二进制。

所有这些都应该可以通过 grepawkpython.

之类的东西实现

您必须向不应检测的函数添加属性 __attribute__((no_instrument_function))。不幸的是,要使其与 C/C++ 标准库函数一起使用并不容易,因为此功能需要编辑所有 C++ 库函数。

您可以采取一些技巧,例如 #define 来自 include/__config 的现有宏来添加此属性。例如,

-D_LIBCPP_INLINE_VISIBILITY=__attribute__((no_instrument_function,internal_linkage))

确保使用 no_instrument_function 附加现有宏定义以避免意外错误。