测试生成器

在客户端,LNT 带有一系列内置的测试数据生成器。本节重点介绍 LLVM 测试套件(又名夜间测试)生成器,因为它是在 LNT 基础设施中运行的主要测试,但请注意,LNT 还包括针对其他有趣数据片段的测试,例如 Clang 编译时间性能。

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

运行本地服务器

设置本地 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

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

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

运行指定的测试。run 工具本身接受许多对所有测试通用的选项。最常见的选项是 --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 夜间测试)

注意

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

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

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 性能分析信息

在测试套件中使用 CMake 驱动程序时,LNT 还可以使用 linux perf 捕获性能分析信息。然后可以通过 LNT webUI 探索这些信息,如 https://blog.llvm.net.cn/2016/06/using-lnt-to-track-performance.html 所示。

要捕获这些性能分析信息,请使用命令行选项 --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 的测试套件的 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

这将多次运行测试套件,并在报告目录中收集有用的信息。该报告收集了许多内容,例如执行配置文件、编译时间报告、中间文件、二进制文件和构建信息。

交叉编译

在使用 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 )