- 开发工具
- 调试器
- 图形学
- GPU
【免费下载链接】renderdoc
RenderDoc is a stand-alone graphics debugging tool.
Lizard 是 RenderDoc 仓库中内置的一个开源库(MIT 许可),专门用于在 Android 设备上采集 ARM 硬件计数器,其源码位于 renderdoc/driver/ihv/arm/official/lizard/ 目录。它既可以直接调用少量硬件计数器,也可以通过 ARM Streamline 生态的gatord守护进程获取更完整的 Mali GPU/CPU 计数器集合。读完本文,你将掌握 Lizard 的部署流程(构建并推送gatord、以-M参数指定 Mali 设备型号启动)、其 C/C++ 两套 API 的完整调用方式,以及 RenderDoc 如何通过 arm_counters.cpp 把 Lizard 桥接为自身的性能计数器后端。
Lizard 在 RenderDoc 中的定位
Lizard 由 Samsung Electronics (UK) Limited 于 2020 年开发并开源,其定位是"在 Android 设备上捕获 ARM 硬件计数器的库"(原文:Lizard is a library to capture ARM hardware counters on Android devices)。在 RenderDoc 的项目结构中,它位于 IHV(Independent Hardware Vendor,独立硬件厂商)集成层之下:
- AMD 使用 GPUPerfAPI / RGP / AGS(renderdoc/driver/ihv/amd/)
- Intel 使用 metrics discovery(renderdoc/driver/ihv/intel/)
- NVIDIA 使用 NVAPI / PerfSDK / Aftermath(renderdoc/driver/ihv/nv/)
- ARM 则使用 Lizard(renderdoc/driver/ihv/arm/official/lizard/)
从构建配置 renderdoc/driver/ihv/arm/CMakeLists.txt 可以看到:只有目标平台为ANDROID时,Lizard 的源文件(lizard.cpp、lizard_api.cpp、lizard_communication.cpp、hwcpipe_api.cpp、gator_api.cpp、socket.cpp以及 HWCPipe 及其 Mali/PMU vendor 实现)才会被编译进rdoc_arm对象库;非 Android 平台则回退到arm_counters_stub.cpp空实现。这印证了 Lizard 的 Android 专属属性。
Lizard 的能力模型是一个双源计数器系统(定义于 include/lizard/lizard_counter.hpp 的SourceType枚举):
| 计数器来源 | 说明 |
|---|---|
SOURCE_GATORD | 通过 gatord 守护进程(ARM Streamline 的 gator 后端)采集 |
SOURCE_HWCPIPE_CPU | 通过 HWCPipe 的 PMU profiler 采集 CPU 计数器 |
SOURCE_HWCPIPE_GPU | 通过 HWCPipe 的 Mali profiler 采集 GPU 计数器 |
ClassType(CLASS_ABSOLUTE/CLASS_DELTA)描述计数器值是绝对值还是采样区间内的增量,UnitType(UNITS_BYTE、UNITS_CELSIUS、UNITS_HZ、UNITS_MHZ、UNITS_PAGES、UNITS_RPM、UNITS_S、UNITS_V、UNITS_MV、UNITS_UNKNOWN)则描述数值单位。
部署 gatord:ARM 硬件计数器的完整采集通道
README 明确指出,使用 gatord 这一步是可选的——Lizard 本身已经可以直接查询少量硬件计数器(走 HWCPipe 通道)。但如果想要获取更完整的计数器集合,就需要部署 gatord。完整步骤如下。
第 1 步:为 Android 构建 gatord
gatord是 ARM Streamline gator 采集体系中的设备端守护进程。需要先在 Android 工具链环境下完成其交叉编译,得到 Android 可执行文件。
第 2 步:推送到设备可读/可写/可执行目录
将gatord复制到 Android 设备上一个公开可读、可写且可执行的目录,官方推荐使用/data/local/tmp(该目录天然具备应用沙箱外的临时执行权限):
$ adb push gatord /data/local/tmp/gatord第 3 步:以-M参数指定 Mali 设备型号并启动
$ adb shell ./data/local/tmp/gatord -M G71其中-M参数用于指定 Mali 设备类型,例如 G71、G76 等(对应 Mali Midgard/Bifrost 系列 GPU 的型号代号)。实际型号需要与设备内置的 Mali GPU 型号一致,否则计数器枚举可能不完整或无法匹配。
启动后 gatord 会监听网络端口,等待 Lizard 客户端(位于 Android 设备本机)连接并协商计数器配置。从 gator_constants.hpp 可以看出 Lizard 与 gatord 之间是一套自定义的二进制协议,包含:
- 帧类型:
FRAME_SUMMARY、FRAME_BACKTRACE、FRAME_COUNTER、FRAME_BLOCK_COUNTER、FRAME_PERF_ATTRS、FRAME_PERF等; - 命令类型:
COMMAND_REQUEST_XML(请求计数器 XML 定义)、COMMAND_DELIVER_XML(下发 XML)、COMMAND_APC_START(开始 APC 采集)、COMMAND_APC_STOP(停止采集)、COMMAND_DISCONNECT、COMMAND_PING; - 响应类型:
RESPONSE_XML、RESPONSE_APC_DATA、RESPONSE_ACK、RESPONSE_NAK、RESPONSE_ERROR。
gatord_xml_reader.cpp(gatord_xml_reader.cpp)负责解析 gatord 下发的计数器 XML 描述,将其转换为 Lizard 内部统一的计数器描述结构。
不经 gatord 的轻量通道:HWCPipe 直采
Lizard 内嵌了 HWCPipe(其自身 README 位于 thirdparty/hwcpipe/README.md),HWCPipe 是"一个读取 CPU 和 GPU 硬件计数器的简单可扩展接口",支持:
- 自动平台检测(构造
hwcpipe::HWCPipe后自动识别 CPU/GPU 计数器集); run()/sample()/stop()三阶段采集循环,sample()按需返回Measurements结构体;- 通过
CpuCounter/GpuCounter枚举或 JSON 字符串(如{"cpu": ["Cycles", "Instructions"], "gpu": ["GpuCycles"]})选择计数器。
在 Android 上使用 HWCPipe 通道时,需要在设备上开启 profiling 权限,某些设备默认是关闭的:
adb shell setprop security.perf_harden 0从 C 入口加载 Lizard:lizard_api.h 详解
Lizard 对外暴露的是一个纯 C 接口,头文件为 include/lizard/lizard_api.h,通过版本化结构体LizardApi组织全部函数指针,当前版本为LIZARD_VERSION_0_1。加载方式如下(头文件内附的官方示例):
void* lib = dlopen("liblizard.so", RTLD_LAZY); LZD_LoadApi_PFN loadApi = (LZD_LoadApi_PFN)dlsym(lib, "LoadApi"); struct LizardApi* api; if (loadApi(&api) != LZD_OK) { // report failure and return } if (api->version != LIZARD_VERSION_0_1) { // report version mismatch and return } LizardInstance ctx = api->Init("127.0.0.1", 8080);完整函数签名与语义如下:
| 函数 | 签名 | 作用 |
|---|---|---|
Init | LizardInstance (*)(const char *host, int port) | 初始化 Lizard 实例,host/port为目标 gatord 的 IP 与端口 |
Destroy | void (*)(LizardInstance ctx) | 销毁实例并将指针置 NULL |
GetAvailableCountersCount | uint32_t (*)(LizardInstance ctx) | 返回可用计数器数量(ID 范围为[1, MAX_UINT],0 无效) |
GetCounterDescription | LZD_Result (*)(ctx, id, LizardCounterDescription*) | 填充计数器描述(见下表) |
EnableCounter | void (*)(ctx, id) | 启用某个计数器参与采集 |
DisableCounter | void (*)(ctx, id) | 禁用某个计数器 |
DisableAllCounters | void (*)(ctx) | 禁用全部计数器(默认状态下所有计数器均为禁用) |
StartCapture | LZD_Result (*)(ctx) | 开始采集,实际采集在独立线程执行 |
StopCapture | LZD_Result (*)(ctx) | 停止采集 |
ReadCounterInt | int64_t (*)(ctx, id) | 以整型读取计数器测量值 |
ReadCounterDouble | double (*)(ctx, id) | 以双精度浮点读取计数器测量值 |
LizardCounterDescription结构体携带计数器的完整元数据:id、short_name、name、title、description、category、multiplier(换算倍率)、units(单位)、class_type(绝对值/增量)、result_type(LZD_TYPE_INT/LZD_TYPE_DOUBLE)。
典型采集流程为:Init→GetAvailableCountersCount+GetCounterDescription枚举 →EnableCounter选择目标计数器 →StartCapture→ (采样)→StopCapture→ReadCounterInt/ReadCounterDouble读取结果 →Destroy。
C++ 封装:lizard.hpp 的使用模式
C++ 层封装在 include/lizard/lizard.hpp 的lizard::Lizard类中,内部组合了GatorApi(gatord 通信)、HwcPipeApi(HWCPipe 通信)以及两个后台线程CommunicationThread/HwcPipeThread。核心成员:
bool configure(const char *hostname, uint32_t port):同时尝试配置 gatord 通道(configureGatord)与 HWCPipe 通道(configureHwcPipe),只要任一成功即返回 true;uint32_t availableCountersCount()/availableCounters(...):查询可用计数器列表;void enableCounters(const LizardCounterId*, uint32_t)/disableCounters(...):按 ID 批量启用/禁用;bool startCapture():非阻塞,底层通信由独立线程完成;内部先startGatord()(重发计数器配置、启动会话、拉起通信线程),再startHwcPipe();void endCapture():停止两个通道并回收线程;readCounterInt/readCounterDouble/readCounter/getCounterInfo:读取测量值与计数器元数据。
实现细节(lizard.cpp)中值得注意的点:
- 采集前
m_dataStore.clear()清空上一轮数据; - gatord 通道启动时会按已启用且
sourceType() == SOURCE_GATORD的计数器调用resendConfiguration,随后startSession并创建CommunicationThread,线程内持续接收 gatord 下发的 APC 计数数据; - HWCPipe 通道会按启用的 CPU/GPU 计数器调用
m_HwcPipeApi->enableCounters(...)后启动HwcPipeThread; configureGatord期间会完成建连、版本协商(sendVersion)、计数器枚举(init)与断开(sendDisconnect)的完整握手,即配置阶段是一次性的短连接。
计数器数据结构与数据存储
include/lizard/lizard_counter.hpp 定义了三个关键类型:
Value:int64_t与double的联合体,统一承载两种类型的测量值;LizardCounterData:某个计数器在某次采集区间内的值序列(getLength()个采样点),可通过getIntValues()/getDoubleValues()按类型取用,isInt()判断其类型;LizardCounterDataStore:以内部 key(即计数器 ID)为索引、值为向量序列的存储映射,由后台线程持续写入、采集端批量读取。
RenderDoc 侧集成:ARMCounters 桥接层
RenderDoc 通过 arm_counters.cpp 将 Lizard 封装为标准的ARMCounters计数器后端,与 RenderDoc 的GPUCounter枚举体系打通。关键调用链:
ARMCounters::Init():LoadApi(&m_Api)加载 Lizard API → 校验version == LIZARD_VERSION_0_1→m_Api->Init("127.0.0.1", 8080)连接本机 gatord(固定端口 8080,因此 gatord 需监听该端口)→GetAvailableCountersCount枚举,若为 0 则失败返回;- 枚举阶段:对每个
LizardCounterId调用GetCounterDescription,再经ARMCreateCounterDescription映射为 RenderDoc 的CounterDescription——result_type映射为CompType::UInt/Float,units映射为CounterUnit::Bytes/Celsius/Hertz/Seconds/Volt等,计数器索引映射到GPUCounter::FirstARM + idx; - 采集生命周期:
BeginPass中逐个EnableCounter,BeginSample(eventId)调用StartCapture,EndSample调用StopCapture后按计数器的resultType分别用ReadCounterInt/ReadCounterDouble取数并写入m_CounterData[eventId][counterId]; GetCounterData将内部存储转成CounterResult(eventId, counter, value)列表返回给上层(如性能计数器查看器)。
这意味着在 Android 上使用 RenderDoc 的性能计数器功能时,用户只需先按本文部署并启动 gatord,RenderDoc 的 ARM 后端会自动通过 127.0.0.1:8080 与之协作,无需关心底层协议细节。
部署与使用注意事项
- gatord 目录权限:
/data/local/tmp是官方推荐目录,需保证"公开可读、可写、可执行",否则 gatord 无法启动或被 Android SELinux 策略拦截; -M参数:必须与设备的 Mali GPU 型号匹配(如 G71、G76),型号错误会导致计数器定义不匹配;- 网络约束:RenderDoc 侧默认以
127.0.0.1:8080连接,gatord 与 RenderDoc 必须运行在同一 Android 设备的本机回环上(或自行修改端口约定); - HWCPipe 权限:若不使用 gatord、走 HWCPipe 直采通道,需先执行
adb shell setprop security.perf_harden 0开启 profiling; - 平台限定:Lizard 仅在 Android 构建中生效,桌面平台编译的是 arm_counters_stub.cpp 空实现,这是构建配置 CMakeLists.txt 明确约束的。
对想深入研究的读者,建议依次阅读 lizard_api.h(C 接口契约)、lizard.cpp(双通道调度逻辑)、gator_constants.hpp(gatord 协议定义)以及 arm_counters.cpp(RenderDoc 集成样例),即可完整还原"设备端采集 → 本机协议传输 → 上层展示"的全链路。
- 开发工具
- 调试器
- 图形学
- GPU
【免费下载链接】renderdoc
RenderDoc is a stand-alone graphics debugging tool.
相关推荐
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考