2. Perf 环境搭建:Android NDK 环境配置、交叉编译 Perf、在 Android 设备上部署 Perf 二进制

在 Android 驱动开发与调优场景中,目标设备通常运行精简的 Linux 内核,且不包含完整的 perf 工具链。因此,我们需要在主机端使用 Android NDK 提供的交叉编译工具链,为特定架构(如 arm64)编译 perf 二进制,并将其部署到 Android 设备上。本章将详细拆解这一流程,确保你能够搭建一个可复用的 Perf 交叉编译环境。

前置条件: 主机需安装 Linux 发行版(推荐 Ubuntu 22.04 LTS),并已配置好 Android SDK 与 NDK(版本建议 r25c 或更高)。确保已安装必要的构建工具:git、flex、bison、libelf-dev、libssl-dev。

2.1 Android NDK 环境配置

Android NDK 提供了针对不同目标架构的独立工具链。我们使用 NDK 自带的 make_standalone_toolchain.py 脚本或直接使用 CMake 工具链文件来配置交叉编译环境。以下步骤基于 NDK r25c 进行说明。

  1. 下载并解压 NDK: 从 Google 官方渠道下载 android-ndk-r25c-linux.zip,解压至 /opt/android-ndk。
  2. 设置环境变量: 将 NDK 路径加入 PATH,并导出 ANDROID_NDK_HOME。
  3. 验证工具链: 使用 aarch64-linux-android21-clang++ --version 确认交叉编译器可用。
关键配置示例(.bashrc):
export ANDROID_NDK_HOME=/opt/android-ndk-r25c
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
# 验证
aarch64-linux-android21-clang --version

下表总结了不同 Android 架构对应的 NDK 工具链前缀,编译 Perf 时需根据目标设备选择正确的架构。

目标架构 NDK 工具链前缀 API 级别示例 常见设备
arm64-v8a aarch64-linux-android 21 Pixel 6/7/8, 高通 SM8550
armeabi-v7a armv7a-linux-androideabi 16 老旧设备或 IoT 模块
x86_64 x86_64-linux-android 21 Android 模拟器

2.2 交叉编译 Perf

Perf 源码位于 Linux 内核树的 tools/perf 目录下。我们不需要完整的内核源码,只需克隆内核仓库并进入该目录即可。交叉编译的核心在于指定正确的 CC、AR、LD 等变量,并禁用本地依赖。

常见陷阱: 直接使用主机 gcc 编译的 Perf 无法在 Android 设备上运行(ABI 不兼容)。必须使用 NDK 的 clang 编译器,并链接 Android 的 libc 与 libm。

以下是针对 arm64 架构的编译步骤:

  1. 获取内核源码: git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
  2. 进入 Perf 目录: cd linux/tools/perf
  3. 配置交叉编译环境: 设置 CC、AR、LD 为 NDK 工具链版本。
  4. 执行 make: 使用 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- 进行编译。
完整编译脚本(build_perf_android.sh):
#!/bin/bash
export NDK=/opt/android-ndk-r25c
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export TARGET=aarch64-linux-android
export API=21

export CC=$TOOLCHAIN/bin/${TARGET}${API}-clang
export AR=$TOOLCHAIN/bin/${TARGET}-ar
export LD=$TOOLCHAIN/bin/${TARGET}-ld

cd linux/tools/perf
make ARCH=arm64 CROSS_COMPILE=${TARGET}- \
     CC=$CC AR=$AR LD=$LD \
     NO_LIBELF=1 NO_LIBUNWIND=1 NO_LIBAUDIT=1 NO_LIBNUMA=1 \
     NO_LIBPERL=1 NO_LIBPYTHON=1 NO_LIBBABELTRACE=1 \
     -j$(nproc)

编译成功后,会在 linux/tools/perf 目录下生成 perf 可执行文件。使用 file perf 命令确认其架构为 ELF 64-bit LSB executable, ARM aarch64。

2.3 在 Android 设备上部署 Perf 二进制

将编译好的 Perf 二进制推送到 Android 设备,并赋予可执行权限。推荐放置在 /data/local/tmp 目录下,该目录具有可执行权限且重启后不会丢失(但可能被系统清理)。

  1. 连接设备: 使用 adb devices 确认设备已连接。
  2. 推送二进制: adb push perf /data/local/tmp/
  3. 赋予权限: adb shell chmod 755 /data/local/tmp/perf
  4. 验证运行: adb shell /data/local/tmp/perf --version
提示: 如果设备内核开启了 CONFIG_PERF_EVENTS 且 perf_event_paranoid 设置为 1 或更低,普通应用即可使用 Perf。若遇到权限错误,可尝试 adb shell setprop security.perf_harden 0(需要 root 权限)。

部署完成后,建议执行一次简单的 perf list 来验证硬件事件和软件事件是否被正确枚举。如果输出为空或报错,说明内核配置或权限存在问题。

部署与验证命令汇总:
# 推送
adb push perf /data/local/tmp/perf
adb shell chmod 755 /data/local/tmp/perf

# 验证
adb shell /data/local/tmp/perf --version
adb shell /data/local/tmp/perf list 2>&1 | head -20

2.4 交叉编译与部署流程总览

下图展示了从主机环境配置到 Android 设备上运行 Perf 的完整流程,包含关键决策点与验证环节。

Perf 交叉编译与部署流程 Perf 交叉编译与部署流程 主机环境准备 NDK 工具链配置 获取内核 Perf 源码 交叉编译 Perf 验证二进制架构 adb 推送至设备 chmod 755 赋权 运行 perf --version perf list 验证 说明:若 perf list 无输出,请检查内核 CONFIG_PERF_EVENTS 及 security.perf_harden 属性

2.5 常见问题与排查

问题现象 可能原因 解决方案
perf: not found 二进制未推送或路径错误 使用 adb shell ls -l /data/local/tmp/perf 确认文件存在
perf: error while loading shared libraries 缺少 Android 运行时库或链接错误 使用 NDK 的 aarch64-linux-android-readelf -d perf 检查动态库依赖
perf: permission denied 未赋予可执行权限 执行 adb shell chmod 755 /data/local/tmp/perf
perf list 输出为空 内核未开启 CONFIG_PERF_EVENTS 检查 /proc/config.gz 或重新编译内核
编译时 fatal error: 'stddef.h' not found NDK 工具链路径未正确设置 确认 CC 变量指向正确的 clang 二进制
安全提示: 在 Android 设备上使用 Perf 可能需要 root 权限或关闭 SELinux 限制。生产环境设备请遵循安全策略,仅在调试阶段临时调整 perf_event_paranoid 或 SELinux 策略。