【问题标题】:C++ function instrumentation via clang++'s -finstrument-functions : how to ignore internal std library calls?通过 clang++ 的 -finstrument-functions 进行 C++ 函数检测:如何忽略内部标准库调用?
【发布时间】:2020-11-09 23:55:21
【问题描述】:

假设我有一个类似的函数:

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!

【问题讨论】:

    标签: c++ clang profiling clang++ instrumentation


    【解决方案1】:

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

    您可以做一些技巧,例如 #define include/__config 中的现有宏也可以添加此属性。例如,

    -D_LIBCPP_INLINE_VISIBILITY=__attribute__((no_instrument_function,internal_linkage))
    

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

    【讨论】:

      【解决方案2】:

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

      https://reviews.llvm.org/D37622

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

      类似

      #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]);
          // https://stackoverflow.com/a/37539/12940429
      
          ...
      }
      
      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 之类的东西来检查符号表并找出每个函数的地址。

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

      grepawkpython 之类的东西应该可以实现所有这些。

      【讨论】:

        猜你喜欢
        • 2013-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-01
        相关资源
        最近更新 更多