测试生产者

在客户端,LNT 自带许多内置的测试数据生产者。本节重点介绍 LLVM 测试套件(又名 nightly test)生成器,因为它是在 LNT 基础设施上运行的主要测试,但请注意,LNT 还包括其他有趣的数据测试,例如 Clang 编译时性能。

LNT 还使添加新的测试数据生产者变得容易,并包括自定义数据导入器(例如,将 buildbot 构建信息导入到)和动态测试数据生成器(例如,滥用基础设施来绘制图表,例如)的示例。

运行本地服务器

设置本地 LNT 服务器以查看测试结果非常有用,无论是供个人使用还是在将结果提交到公共服务器之前预览结果。要设置一次性服务器进行测试

# Create a new installation in /tmp/FOO.
$ lnt create /tmp/FOO
created LNT configuration in '/tmp/FOO'
...

# Run a local LNT server.
$ lnt runserver /tmp/FOO &> /tmp/FOO/runserver.log &
[2] 69694

# Watch the server log.
$ tail -f /tmp/FOO/runserver.log
* Running on https://127.0.0.1:8000/
...

运行测试

内置测试旨在通过 lnt 工具运行。以下是用于处理内置测试的可用工具

lnt showtests

列出可用测试。测试使用可扩展架构定义。FIXME:指向关于如何添加新测试的文档。

lnt runtest [<run options>] <test name> ... test arguments ...

运行指定的测试。运行工具本身接受许多所有测试通用的选项。最常见的选项是 --submit=<url>,它指定在测试完成后将结果提交到的服务器。有关可用选项的更多信息,请参阅 lnt runtest --help

其余选项将传递给测试工具本身。这些选项特定于测试,但行为良好的测试应响应 lnt runtest <test name> --help。以下部分提供了关于内置测试的具体文档。

内置测试

LLVM CMake 测试套件

llvm 测试套件可以使用 test-suite 内置测试运行。

通过 CMake 和 lit 运行测试套件使用不同的 LNT 测试

rm -rf /tmp/BAR
lnt runtest test-suite \
     --sandbox /tmp/BAR \
     --cc ~/llvm.obj.64/Release+Asserts/bin/clang \
     --cxx ~/llvm.obj.64/Release+Asserts/bin/clang++ \
     --use-cmake=/usr/local/bin/cmake \
     --use-lit=~/llvm/utils/lit/lit.py \
     --test-suite ~/llvm-test-suite \
     --cmake-cache Release

由于 CMake 测试套件使用 lit 来运行测试并比较其输出,因此 LNT 需要知道您的 LLVM lit 安装路径。测试套件在 CMake 缓存中保存了一些常用配置。--cmake-cache 标志和 --cmake-define 标志允许您更改 LNT 如何为测试套件运行配置 cmake。

LLVM Makefile 测试套件 (又名 LLVM Nightly Test)

注意

Makefile 测试套件已弃用。请考虑使用基于 cmake 的 lnt runtest test-suite 模式。它正在积极维护,收集额外的指标(如代码大小),并具有额外的功能,例如生成和使用 PGO 数据。

nt 内置测试在“nightly test”配置中运行 LLVM 测试套件执行和性能测试。此测试允许运行许多不同的应用程序和基准测试(例如,SPEC),具有各种编译选项,以及在几种不同的配置中(例如,使用像 clangllvm-gcc 这样的 LLVM 编译器,使用 LLVM lli 位代码解释器在 LLVM JIT 编译器下运行,或测试新的代码生成器 pass)。

nt 测试要求 LLVM 测试套件仓库、可用的 LLVM 编译器以及 LLVM 源代码和构建树可用。目前,LLVM 构建树预计已在 Release+Asserts 配置中构建。与之前的 NewNightlyTest.pl 不同,nt 工具不检出或构建任何内容,用户需要管理自己的 LLVM 源代码和构建树。理想情况下,每个组件都应基于相同的 LLVM 修订版本(LLVM 测试套件可能除外),但这并非必需。

测试在用户指定的沙箱目录内运行 LLVM 测试套件构建和执行。默认情况下,每个测试运行都将在沙箱内的带时间戳的目录中完成,并将结果保留下来以供事后分析。目前,用户负责清理这些目录以管理磁盘空间。

测试始终应使用树外构建运行——这是一种更健壮的模型,并允许在许多测试运行中共享相同的源代码树。当前的一个限制是,如果先进行树内构建,然后再进行树外构建,则 LLVM 测试套件仓库将无法正常工作。非常重要的是,LLVM 测试套件仓库应保持原始状态。

以下命令显示了在本地构建上运行 nt 测试套件的示例

$ rm -rf /tmp/BAR
$ lnt runtest nt \
     --sandbox /tmp/BAR \
     --cc ~/llvm.obj.64/Release+Asserts/bin/clang \
     --cxx ~/llvm.obj.64/Release+Asserts/bin/clang++ \
     --llvm-src ~/llvm \
     --llvm-obj ~/llvm.obj.64 \
     --test-suite ~/llvm-test-suite \
     TESTER_NAME \
      -j 16
2010-04-17 23:46:40: using nickname: 'TESTER_NAME__clang_DEV__i386'
2010-04-17 23:46:40: creating sandbox: '/tmp/BAR'
2010-04-17 23:46:40: starting test in '/private/tmp/BAR/test-2010-04-17_23-46-40'
2010-04-17 23:46:40: configuring...
2010-04-17 23:46:50: testing...
2010-04-17 23:51:04: loading test data...
2010-04-17 23:51:05: generating report: '/private/tmp/BAR/test-2010-04-17_23-46-40/report.json'

前七个参数都是必需的——它们指定沙箱路径、要测试的编译器以及所需源代码和构建的路径。TESTER_NAME 参数用于导出此测试者的名称(与关于被测编译器的一些推断信息结合使用)。此名称用作测试机器的简短标识符;通常它应该是机器的主机名或负责测试人员的姓名。-j 16 参数是可选的,在本例中,它指定应使用最多 16 个进程并行运行测试。

在本例中,我们可以从输出中看到测试创建了一个新的沙箱目录,然后在该沙箱的子目录中运行了测试。测试在测试进行中输出有限的摘要信息。完整信息可以在测试构建目录中的 .log 文件中找到(例如,configure.logtest.log)。

最后的测试步骤是在测试目录内生成测试报告。此报告现在可以直接提交到 LNT 服务器。例如,如果我们有一个如前所述运行的本地服务器,我们可以运行

$ lnt submit https://127.0.0.1:8000/submitRun \
    /tmp/BAR/test-2010-04-17_23-46-40/report.json
STATUS: 0

OUTPUT:
IMPORT: /tmp/FOO/lnt_tmp/data-2010-04-17_16-54-35ytpQm_.plist
  LOAD TIME: 0.34s
  IMPORT TIME: 5.23s
ADDED: 1 machines
ADDED: 1 runs
ADDED: 1990 tests
COMMITTING RESULT: DONE
TOTAL IMPORT TIME: 5.57s

并在我们的本地服务器上查看结果。

基于 LNT 的 NT 测试模块

为了支持更复杂的测试,或者不容易集成到 LLVM 测试套件模块的更严格的 SingleSource 或 MultiSource 布局中的测试,nt 内置测试为 LLVM 测试套件测试提供了一种机制,这些测试仅定义扩展测试模块。这些测试传递测试运行的用户配置参数,并期望以 LNT 本地格式返回测试结果。

测试模块通过在 LLVM 测试套件仓库中 LNTBased 根目录的子目录中提供 TestModule 文件来定义。TestModule 文件应是格式良好的 Python 模块,该模块提供一个 test_class 全局变量,该变量应是 lnt.tests.nt.TestModule 抽象基类的子类。

测试类应覆盖 execute_test 方法,该方法传递一个选项字典,其中包含适用于测试执行的 NT 用户参数,并且测试应将测试结果作为 lnt.testing.TestSamples 对象列表返回。

execute_test 方法传递以下描述模块本身信息的选项

  • MODULENAME - 模块的名称(主要用于生成结构良好的测试名称)。

  • SRCROOT - 模块源目录的路径。

  • OBJROOT - 模块应用于临时输出(构建产品)的目录的路径。该目录保证存在,但不保证是干净的。

该方法传递以下适用于测试应如何执行的选项

  • THREADS - 测试期间要运行的并行进程数。

  • BUILD_THREADS - 构建测试时要使用的并行进程数(如果适用)。

该方法传递以下指定如何以及是否应远程执行测试的选项。如果存在这些参数中的任何一个,则保证所有参数都存在。

  • REMOTE_HOST - 要在其上执行测试的远程机器的主机名。

  • REMOTE_USER - 登录到远程机器的用户名。

  • REMOTE_PORT - 连接到远程机器的端口。

  • REMOTE_CLIENT - 要用于连接到远程机器的 rsh 兼容客户端。

该方法传递以下指定如何构建测试的选项

  • CC - 要使用的 C 编译器命令。

  • CXX - 要使用的 C++ 编译器命令。

  • CFLAGS - 用于构建 C 代码的编译器标志。

  • CXXFLAGS - 用于构建 C++ 代码的编译器标志。

该方法传递以下可选参数,这些参数指定用于各种命令的环境

  • COMPILE_ENVIRONMENT_OVERRIDES [可选] - 如果给定,则为在编译时使用的 env 样式环境变量覆盖列表。

  • LINK_ENVIRONMENT_OVERRIDES [可选] - 如果给定,则为在链接时使用的 env 样式环境变量覆盖列表。

  • EXECUTION_ENVIRONMENT_OVERRIDES [可选] - 如果给定,则为在执行测试时使用的 env 样式环境变量覆盖列表。

有关更多信息,请参阅 LLVM 测试套件仓库中 LNT/Examples 目录下的示例测试。

捕获 Linux perf профили (profile) 信息

当在测试套件中使用 CMake 驱动程序时,LNT 还可以使用 linux perf 捕获 профили (profile) 信息。然后可以通过 LNT WebUI 浏览此信息,如 https://blog.llvm.net.cn/2016/06/using-lnt-to-track-performance.html 所示。

要捕获这些 профили (profiles),请使用命令行选项 --use-perf=all。使用此选项评估生成代码性能的典型命令行如下所示

lnt runtest test-suite \
     --sandbox SANDBOX \
     --cc ~/bin/clang \
     --use-cmake=/usr/local/bin/cmake \
     --use-lit=~/llvm/utils/lit/lit.py \
     --test-suite ~/llvm-test-suite \
     --benchmarking-only \
     --build-threads 8 \
     --threads 1 \
     --use-perf=all \
     --exec-multisample=5 \
     --run-under 'taskset -c 1'

二分法:--single-result--single-result-predicate

CMake-based 测试套件的 LNT 驱动程序带有助手,用于通过 llvmlab bisect 二分查找一致性和性能变化。

llvmlab bisectzorg 仓库的一部分,允许通过构建缓存轻松二分查找某些谓词。有效使用 llvmlab 的关键是设计良好的谓词命令 - 在“通过”时以零退出,在“失败”时以非零退出。

LNT 通常运行一个或多个测试,然后生成测试报告。除非发生内部错误,否则它始终以状态零退出。--single-result 参数更改了 LNT 的行为 - 它将仅运行一个特定测试,并将谓词应用于该测试的结果以确定 LNT 的退出状态。

--single-result-predicate 参数定义要使用的谓词。这是一个 Python 表达式,它在包含几个预设变量的上下文中执行

  • status - 布尔值,通过或失败(通过为 True,失败为 False)。

  • exec_time - 执行时间(请注意,exec 是 Python 中的保留关键字!)

  • compile (或 compile_time) - 编译时间

从测试返回的任何指标(例如“score”或“hash”)也添加到上下文中。

默认谓词只是 status - 因此可以开箱即用地用于调试正确性回归。更复杂的谓词是可能的;例如 exec_time < 3.0 将二分查找,假设“良好”结果花费的时间少于 3 秒。

使用 llvmlab 调试性能改进的完整示例

llvmlab bisect --min-rev=261265 --max-rev=261369 \
  lnt runtest test-suite \
    --cc '%(path)s/bin/clang' \
    --sandbox SANDBOX \
    --test-suite /work/llvm-test-suite \
    --use-lit lit \
    --run-under 'taskset -c 5' \
    --cflags '-O3 -mthumb -mcpu=cortex-a57' \
    --single-result MultiSource/Benchmarks/TSVC/Expansion-flt/Expansion-flt \
    --single-result-predicate 'exec_time > 8.0'

生成诊断报告

测试套件模块可以生成诊断报告,这可能有助于弄清楚基准测试的进展情况

lnt runtest test-suite \
       --sandbox /tmp/BAR \
       --cc ~/llvm.obj.64/Release+Asserts/bin/clang \
       --cxx ~/llvm.obj.64/Release+Asserts/bin/clang++ \
       --use-cmake=/usr/local/bin/cmake \
       --use-lit=~/llvm/utils/lit/lit.py \
       --test-suite ~/llvm-test-suite \
       --cmake-cache Release \
       --diagnose --only-test SingleSource/Benchmarks/Stanford/Bubblesort

这将多次运行测试套件,在报告目录中收集有用的信息。该报告收集许多内容,例如执行 профили (profiles)、编译器时间报告、中间文件、二进制文件和构建信息。

交叉编译

在具有 cmake 驱动程序的交叉编译设置中运行测试套件的最佳方法是尽可能使用 cmake 的内置交叉编译支持。实际上,推荐的交叉编译方法是使用 cmake 工具链文件(请参阅 https://cmake.com.cn/cmake/help/v3.0/manual/cmake-toolchains.7.html#cross-compiling

在 X86 机器上以 AArch64 linux 为目标的交叉编译的示例命令行是

lnt runtest test-suite \
       --sandbox SANDBOX \
       --test-suite /work/llvm-test-suite \
       --use-lit lit \
       --cppflags="-O3" \
       --run-under=$HOME/dev/aarch64-emu/aarch64-qemu.sh \
       --cmake-define=CMAKE_TOOLCHAIN_FILE:FILEPATH=$HOME/clang_aarch64_linux.cmake

这里的关键部分是 CMAKE_TOOLCHAIN_FILE 定义。由于您正在进行交叉编译,因此您可能需要 –run-under 命令,因为生成的二进制文件可能无法在您的开发机器上本地运行,但还需要做一些额外的事情(例如在 qemu 模拟器下运行,或将二进制文件传输到开发板)。此处不再进一步解释。

在您的工具链文件中,重要的是指定定义工具链的 cmake 变量必须缓存在 CMakeCache.txt 中,因为那是 lnt 从中读取它们以确定在需要为 json 报告构造元数据时使用了哪个编译器的位置。下面是一个示例。使变量出现在 CMakeCache.txt 中的重要关键字是“CACHE STRING “” FORCE”

$ cat clang_aarch64_linux.cmake
set(CMAKE_SYSTEM_NAME Linux )
set(triple aarch64-linux-gnu )
set(CMAKE_C_COMPILER /home/user/build/bin/clang CACHE STRING "" FORCE)
set(CMAKE_C_COMPILER_TARGET ${triple} CACHE STRING "" FORCE)
set(CMAKE_CXX_COMPILER /home/user/build/bin/clang++ CACHE STRING "" FORCE)
set(CMAKE_CXX_COMPILER_TARGET ${triple} CACHE STRING "" FORCE)
set(CMAKE_SYSROOT /home/user/aarch64-emu/sysroot-glibc-linaro-2.23-2016.11-aarch64-linux-gnu )
set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN /home/user/aarch64-emu/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gnu )
set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN /home/user/aarch64-emu/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gnu )