XRay 检测工具

版本:

截至 2016-11-08 版本为 1

简介

XRay 是一种函数调用跟踪系统,它结合了编译器插入的检测点和一个可以在运行时动态启用和禁用检测的运行时库。

有关 XRay 的更多高级信息,请参阅 XRay 白皮书

本文档介绍了如何在 LLVM 中实现的 XRay 中使用 XRay。

LLVM 中的 XRay

XRay 由三个主要部分组成

  • 编译器插入的检测点。

  • 用于在运行时启用/禁用跟踪的运行时库。

  • 一套用于分析跟踪的工具。

    注意:截至 2018 年 7 月 25 日,XRay 仅适用于运行 Linux 的以下架构:x86_64、arm7(无拇指指令)、aarch64、powerpc64le、mips、mipsel、mips64、mips64el、NetBSD:x86_64、FreeBSD:x86_64 和 OpenBSD:x86_64。

编译器插入的检测点以最终生成的二进制文件中的 nop-sleds 形式出现,以及一个名为 xray_instr_map 的 ELF 段,其中包含指向这些检测点的条目。运行时库依赖于能够访问 xray_instr_map 的条目,并在运行时覆盖检测点。

使用 XRay

您可以通过几种方式使用 XRay

  • 检测您的 C/C++/Objective-C/Objective-C++ 应用程序。

  • 生成具有正确函数属性的 LLVM IR。

本节的其余部分将介绍这些主要方法,以及稍后如何自定义 XRay 在 XRay 检测的二进制文件中执行的操作。

检测您的 C/C++/Objective-C 应用程序

为您的应用程序获取 XRay 检测的最简单方法是在您的 clang 调用中启用 -fxray-instrument 标志。

例如

clang -fxray-instrument ...

默认情况下,至少包含 200 条指令(或包含循环)的函数将获得 XRay 检测点。您可以通过 -fxray-instruction-threshold= 标志调整该数字。

clang -fxray-instrument -fxray-instruction-threshold=1 ...

可以使用 -fxray-ignore-loops 禁用循环检测,以仅使用指令阈值。您还可以使用源代码级属性专门检测二进制文件中的函数,使其始终或从不进行检测。您可以使用 GCC 样式属性或 C++11 样式属性来实现。

[[clang::xray_always_instrument]] void always_instrumented();

[[clang::xray_never_instrument]] void never_instrumented();

void alt_always_instrumented() __attribute__((xray_always_instrument));

void alt_never_instrumented() __attribute__((xray_never_instrument));

链接二进制文件时,您可以手动链接 XRay 运行时库,也可以使用 clang 使用 -fxray-instrument 标志自动链接它。或者,您可以从编译器运行时库中静态链接 XRay 运行时库——这些存档文件将采用 libclang_rt.xray-{arch} 的名称,其中 {arch} 是 clang 支持的助记符(x86_64、arm7 等)。

LLVM 函数属性

如果您直接使用 LLVM IR,则可以向函数添加 function-instrument 字符串属性,以获得与 C/C++/Objective-C 源代码级属性类似的效果。

define i32 @always_instrument() uwtable "function-instrument"="xray-always" {
  ; ...
}

define i32 @never_instrument() uwtable "function-instrument"="xray-never" {
  ; ...
}

您还可以设置 xray-instruction-threshold 属性并提供一个数字字符串值,表示函数在进行检测之前应包含多少条指令。

define i32 @maybe_instrument() uwtable "xray-instruction-threshold"="2" {
  ; ...
}

特殊情况文件

可以通过使用特殊情况文件而不是将属性添加到原始源文件中来赋予属性。您可以使用它来标记某些函数和类,使其从文件中从不、始终或使用第一个参数日志进行检测。文件的格式如下所述

# Comments are supported
[always]
fun:always_instrument
fun:log_arg1=arg1 # Log the first argument for the function

[never]
fun:never_instrument

这些文件可以通过 -fxray-attr-list= 标志提供给 clang。您可以通过该标志的多个实例加载多个文件。

XRay 运行时库

XRay 运行时库是编译器运行时项目的一部分,它实现了执行插入检测点的修补和取消修补的运行时组件。当您使用 clang 链接您的二进制文件和 -fxray-instrument 标志时,它将自动链接 XRay 运行时库。

XRay 运行时的默认实现将在 main 开始之前启用 XRay 检测,这适用于生命周期较短的应用程序。此实现还记录所有函数进入和退出事件,这可能会导致结果跟踪中产生大量记录。

此外,默认情况下,XRay 跟踪的文件名为 xray-log.XXXXXX,其中 XXXXXX 部分是随机生成的。

这些选项可以通过 XRAY_OPTIONS 环境变量进行控制,我们在下面列出了这些选项及其默认值。

选项

类型

默认值

描述

patch_premain

布尔值

false

是否在 main 之前修补检测点。

xray_mode

const char*

""

main 之前安装和初始化的默认模式。

xray_logfile_base

const char*

xray-log.

XRay 日志文件的文件名基础。

verbosity

整数

0

运行时详细程度级别。

如果您选择不使用 XRay 运行时库提供的默认日志记录实现和/或控制 XRay 检测的运行时间/方式,则可以直接使用 XRay API 来执行此操作。为此,您需要包含编译器运行时库 xray 目录中的 xray_log_interface.h。我们在下面列出了重要的 API 函数

  • __xray_log_register_mode(...):针对字符串模式标识符注册日志记录实现。该实现是 xray/xray_log_interface.h 中定义的 XRayLogImpl 的实例。

  • __xray_log_select_mode(...):选择要安装的模式,与字符串模式标识符相关联。只有使用 __xray_log_register_mode(...) 注册的实现才能使用此函数选择。

  • __xray_log_init_mode(...):此函数允许初始化和重新初始化已安装的日志记录实现。有关详细信息,请参阅 xray/xray_log_interface.h,它是 XRay 编译器运行时库安装的一部分。

初始化日志记录实现后,可以通过 __xray_log_finalize() 函数完成实现来“停止”它。完成例程与初始化相反。完成时,可以通过 __xray_log_flushLog() 函数清除实现的数据。对于支持内存中处理的实现,它们应注册一个迭代器函数,以便通过 __xray_log_set_buffer_iterator(...) 提供对数据的访问,这允许调用 __xray_log_process_buffers(...) 函数的代码处理内存中的数据。

所有这些在 xray/xray_log_interface.h 头文件中得到了更好的解释。

基本模式

XRay 支持一种基本日志记录模式,该模式将跟踪应用程序的执行,并定期追加到单个日志中。可以通过在 XRAY_OPTIONS 环境变量中设置 xray_mode=xray-basic 来安装/启用此模式。结合 patch_premain=true,这可以允许从头到尾跟踪应用程序。

与通过 __xray_log_select_mode(...) 安装的所有其他模式一样,可以通过 __xray_log_init_mode(...) 函数配置该实现,提供模式字符串和标志选项。基本模式特定的默认值可以在 XRAY_BASIC_OPTIONS 环境变量中提供。

飞行数据记录器模式

XRay 支持一种日志记录模式,该模式允许应用程序仅捕获固定内存大小的事件。飞行数据记录器 (FDR) 模式的工作原理非常类似于飞机的“黑匣子”,它将数据持续记录到固定大小的循环缓冲区队列中,并在缓冲区完成并刷新之前以编程方式提供数据。要在您的应用程序上使用 FDR 模式,您可以在 XRAY_OPTIONS 环境变量中将 xray_mode 变量设置为 xray-fdr。FDR 模式实现的其他选项可以在 XRAY_FDR_OPTIONS 环境变量中提供。可以通过调用 __xray_log_init_mode("xray-fdr", <configuration string>) 在选择/安装后进行编程配置。

将缓冲区刷新到磁盘时,结果将是 XRay FDR 格式 描述的二进制跟踪格式。

当 FDR 模式开启时,它将继续写入和循环使用内存缓冲区,直到日志记录实现完成——此时它可以被刷新并在以后重新初始化。为此,我们按照以下提供的流程进行操作

// Patch the sleds, if we haven't yet.
auto patch_status = __xray_patch();

// Maybe handle the patch_status errors.

// When we want to flush the log, we need to finalize it first, to give
// threads a chance to return buffers to the queue.
auto finalize_status = __xray_log_finalize();
if (finalize_status != XRAY_LOG_FINALIZED) {
  // maybe retry, or bail out.
}

// At this point, we are sure that the log is finalized, so we may try
// flushing the log.
auto flush_status = __xray_log_flushLog();
if (flush_status != XRAY_LOG_FLUSHED) {
  // maybe retry, or bail out.
}

FDR 模式实现的默认设置将创建类似于基本日志实现的日志,但具有不同的日志格式。所有跟踪分析工具(以及跟踪读取库)都将支持 FDR 模式格式的所有版本,因为我们将来会添加更多功能和记录类型。

注意:我们不承诺永久支持我们将来支持的日志版本更新。格式的弃用将在开发人员邮件列表中宣布和讨论。

跟踪分析工具

我们目前在LLVM中拥有一个跟踪分析工具的雏形,可以在tools/llvm-xray目录中找到。 llvm-xray工具目前支持以下子命令

  • extract: 从二进制文件中提取检测映射,并将其以YAML格式返回。

  • account: 使用各种排序和输出格式选项(支持CSV、YAML和控制台友好的TEXT)执行基本的函数调用统计。

  • convert: 将XRay日志文件从一种格式转换为另一种格式。我们可以将二进制XRay跟踪(基本模式和FDR模式)转换为YAML、火焰图友好的文本格式,以及Chrome跟踪查看器(catapult) <https://github.com/catapult-project/catapult>格式。

  • graph: 生成XRay跟踪中发现的函数之间函数调用关系的DOT图。

  • stack: 从XRay跟踪中函数调用的时间线上重建函数调用栈。

这些子命令使用LLVM发行版中分发的XRay库的一部分中发现的各种库组件。这些是

  • llvm/XRay/Trace.h : 一个跟踪读取库,用于方便地将受支持格式的XRay跟踪加载到方便的内存中表示形式。所有处理跟踪的分析工具都使用此实现。

  • llvm/XRay/Graph.h : graph子命令用来方便地表示具有与边和顶点相关的统计信息的函数调用图的半通用图类型。

  • llvm/XRay/InstrumentationMap.h: 一个方便的工具,用于分析XRay检测对象文件和二进制文件中的检测映射。 extractstack子命令使用此特定库。

最小化二进制文件大小

XRay支持几个不同的检测点,包括function-entryfunction-exitcustomtyped点。可以使用-fxray-instrumentation-bundle=标志单独启用这些选项。例如,如果您只想检测函数入口和自定义点,您可以指定

clang -fxray-instrument -fxray-instrumentation-bundle=function-entry,custom ...

这将完全省略其他雪橇类型,从而减小二进制文件的大小。您还可以使用检测组仅检测函数的采样子集。例如,要仅检测四分之一的可用函数,请调用

clang -fxray-instrument -fxray-function-groups=4

将根据函数名称的哈希值任意选择一个子集。要采样不同的子集,您可以使用-fxray-selected-function-group=指定一个介于0到xray-function-groups - 1之间的组号。这些选项可以一起用于生成具有不同检测子集的多个二进制文件。如果您只需要在运行时控制何时跟踪哪些函数,最好使用XRay运行时库的__xray_patch_function()方法有选择地修补和取消修补您需要的各个函数。

未来工作

围绕XRay检测系统构建工具集的扩展工作正在进行中。

跟踪分析工具

  • 正在努力集成或开发工具以可视化XRay跟踪中的发现。特别是,stack工具正在扩展到允许绘制和探索每个调用栈中持续时间的输出格式。

  • 对于大型检测二进制文件,生成的XRay跟踪的大小可能会迅速变得难以处理。我们正在努力将修剪技术和启发式方法集成到分析工具中,以便筛选跟踪并仅显示相关信息。

更多平台

我们期待为将XRay移植到更多架构和操作系统做出贡献。