news 2026/9/24 13:56:52

RenderDoc 集成解读:使用 Lizard 库在 Android 设备上采集 ARM 硬件计数器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RenderDoc 集成解读:使用 Lizard 库在 Android 设备上采集 ARM 硬件计数器
  • 开发工具
  • 调试器
  • 图形学
  • GPU

【免费下载链接】renderdoc

RenderDoc is a stand-alone graphics debugging tool.

项目地址:https://gitcode.com/gh_mirrors/re/renderdoc
点击查看免费下载

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.cpplizard_api.cpplizard_communication.cpphwcpipe_api.cppgator_api.cppsocket.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 计数器

ClassTypeCLASS_ABSOLUTE/CLASS_DELTA)描述计数器值是绝对值还是采样区间内的增量,UnitTypeUNITS_BYTEUNITS_CELSIUSUNITS_HZUNITS_MHZUNITS_PAGESUNITS_RPMUNITS_SUNITS_VUNITS_MVUNITS_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_SUMMARYFRAME_BACKTRACEFRAME_COUNTERFRAME_BLOCK_COUNTERFRAME_PERF_ATTRSFRAME_PERF等;
  • 命令类型:COMMAND_REQUEST_XML(请求计数器 XML 定义)、COMMAND_DELIVER_XML(下发 XML)、COMMAND_APC_START(开始 APC 采集)、COMMAND_APC_STOP(停止采集)、COMMAND_DISCONNECTCOMMAND_PING
  • 响应类型:RESPONSE_XMLRESPONSE_APC_DATARESPONSE_ACKRESPONSE_NAKRESPONSE_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);

完整函数签名与语义如下:

函数签名作用
InitLizardInstance (*)(const char *host, int port)初始化 Lizard 实例,host/port为目标 gatord 的 IP 与端口
Destroyvoid (*)(LizardInstance ctx)销毁实例并将指针置 NULL
GetAvailableCountersCountuint32_t (*)(LizardInstance ctx)返回可用计数器数量(ID 范围为[1, MAX_UINT],0 无效)
GetCounterDescriptionLZD_Result (*)(ctx, id, LizardCounterDescription*)填充计数器描述(见下表)
EnableCountervoid (*)(ctx, id)启用某个计数器参与采集
DisableCountervoid (*)(ctx, id)禁用某个计数器
DisableAllCountersvoid (*)(ctx)禁用全部计数器(默认状态下所有计数器均为禁用)
StartCaptureLZD_Result (*)(ctx)开始采集,实际采集在独立线程执行
StopCaptureLZD_Result (*)(ctx)停止采集
ReadCounterIntint64_t (*)(ctx, id)以整型读取计数器测量值
ReadCounterDoubledouble (*)(ctx, id)以双精度浮点读取计数器测量值

LizardCounterDescription结构体携带计数器的完整元数据:idshort_namenametitledescriptioncategorymultiplier(换算倍率)、units(单位)、class_type(绝对值/增量)、result_typeLZD_TYPE_INT/LZD_TYPE_DOUBLE)。

典型采集流程为:InitGetAvailableCountersCount+GetCounterDescription枚举 →EnableCounter选择目标计数器 →StartCapture→ (采样)→StopCaptureReadCounterInt/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 定义了三个关键类型:

  • Valueint64_tdouble的联合体,统一承载两种类型的测量值;
  • LizardCounterData:某个计数器在某次采集区间内的值序列(getLength()个采样点),可通过getIntValues()/getDoubleValues()按类型取用,isInt()判断其类型;
  • LizardCounterDataStore:以内部 key(即计数器 ID)为索引、值为向量序列的存储映射,由后台线程持续写入、采集端批量读取。

RenderDoc 侧集成:ARMCounters 桥接层

RenderDoc 通过 arm_counters.cpp 将 Lizard 封装为标准的ARMCounters计数器后端,与 RenderDoc 的GPUCounter枚举体系打通。关键调用链:

  1. ARMCounters::Init()LoadApi(&m_Api)加载 Lizard API → 校验version == LIZARD_VERSION_0_1m_Api->Init("127.0.0.1", 8080)连接本机 gatord(固定端口 8080,因此 gatord 需监听该端口)→GetAvailableCountersCount枚举,若为 0 则失败返回;
  2. 枚举阶段:对每个LizardCounterId调用GetCounterDescription,再经ARMCreateCounterDescription映射为 RenderDoc 的CounterDescription——result_type映射为CompType::UInt/Floatunits映射为CounterUnit::Bytes/Celsius/Hertz/Seconds/Volt等,计数器索引映射到GPUCounter::FirstARM + idx
  3. 采集生命周期:BeginPass中逐个EnableCounterBeginSample(eventId)调用StartCaptureEndSample调用StopCapture后按计数器的resultType分别用ReadCounterInt/ReadCounterDouble取数并写入m_CounterData[eventId][counterId]
  4. 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.

项目地址:https://gitcode.com/gh_mirrors/re/renderdoc
点击查看免费下载
上一篇:Hyperledger Fabric 中的 multierr:Go 多错误聚合库的能力演进与源码解析(v0.1.0 → v1.11.0)
下一篇:如何快速入门Grbl:从Arduino烧录到基本G代码测试的10个步骤

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 13:55:18

树莓派DIY智能灌溉控制器:MQTT+继电器HAT+土壤湿度传感器实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 13:53:23

arrylist怎么让他变得不可修改

在Java中,要将一个 ArrayList变得不可修改,你可以使用以下几种方法:###1. 使用 Collections.unmodifiableListJava 提供了 Collections.unmodifiableList 方法,可以生成一个不可修改的视图。这种方式返回的列表将不允许添加、删除…

作者头像 李华
网站建设 2026/9/24 13:53:14

【Dv2Admin】Django配置线上ws反向代理

在 Web 应用程序的部署过程中,安全性、稳定性和实时通信是开发者们普遍关注的重点。Django 是一个非常流行的 Web 框架,常与 Nginx 配合使用,以便实现反向代理、负载均衡以及 SSL 加密等功能。除此之外,实时功能(如 WebSocket)也是现代应用中经常使用的技术。 在项目中实…

作者头像 李华
网站建设 2026/9/24 13:52:32

gsd-core 中 bracket 阶段 ID 约定的统一显示与配置校验解析

【免费下载链接】gsd-core Git. Ship. Done - Core 项目地址: https://gitcode.com/gh_mirrors/ge/gsd-core 点击查看 免费下载 本文围绕 gsd-core 的 ADR-612「bracket 阶段 ID 约定」显示面落地(issue #3638 / PR 4111)展开:当…

作者头像 李华