K3 平台 Perfetto 性能分析指南

K3 平台 Perfetto 性能分析

全系统 Trace 使能指南
Spacemit RISC-V 64-bit  |  Bianbu 4.0.1  |  Kernel 6.18.3+  |  Perfetto v49.0

1  平台背景

K3 是 Spacemit(进迭时空)的 RISC-V 64 位 SoC,搭载 Bianbu Linux(基于 Ubuntu 22.04 + 内核 6.x)。本文档记录如何在该平台上从零使能 Perfetto 全系统性能分析框架,并以 V2D/DMA 驱动插桩 为完整示例,演示如何为任意新驱动模块添加 trace 事件。

组件版本 / 说明
SoC / ISASpacemit K3  |  RISC-V rv64gc
操作系统Bianbu 4.0.1(Ubuntu 22.04 base)
内核6.18.3(自行编译,含 ftrace + PowerVR 模块化)
Perfettov49.0-40b529923(源码自编译,RISC-V 原生)

2  底层准备:编译内核

2.1  必要内核配置项

配置项说明
CONFIG_FTRACE=y基础 ftrace 框架(必须
CONFIG_DYNAMIC_FTRACE=y动态 ftrace,支持运行时开关(必须
CONFIG_TRACE_PRINTK=y允许 trace_printk() 写入 ring buffer
CONFIG_FUNCTION_TRACER=y函数级追踪
CONFIG_SCHED_TRACER=y调度延迟追踪(可选)
CONFIG_SPACEMIT_V2D=yV2D 驱动(built-in,tracepoint 随内核加载)
CONFIG_IMG_POWERVR_ROGUE=mPowerVR GPU 驱动(可加载模块示例)

⚠  若驱动以 =m 编译,其 tracepoint 只在 insmod 后才出现于 tracefs。必须在 modprobe 完成之后再启动 traced_probes,否则事件不会被感知。

2.2  编译与安装

$ bash
cd /home/perise/source
git clone https://gitee.com/bianbu-linux/linux-6.18.git && cd linux-6.18

基于平台 defconfig 打开 ftrace

make ARCH=riscv k3_defconfig
scripts/config --enable FTRACE --enable DYNAMIC_FTRACE --enable TRACE_PRINTK

编译(原生 RISC-V,无需交叉工具链)

make -j$(nproc)
sudo make install && sudo make modules_install
sudo update-initramfs -u && sudo reboot

3  编译并部署 Perfetto

3.1  编译步骤

$ bash
cd /home/perise/source
git clone https://android.googlesource.com/platform/external/perfetto
cd perfetto

安装构建依赖

tools/install-build-deps

生成构建文件

tools/gn gen out/linux_riscv64 --args=‘is_debug=false’

只编译必要组件(加快速度)

tools/ninja -C out/linux_riscv64 perfetto traced traced_probes

验证

./out/linux_riscv64/perfetto --version

输出: Perfetto v49.0-40b529923

3.2  安装到系统路径

$ bash
sudo cp out/linux_riscv64/perfetto       /usr/local/bin/
sudo cp out/linux_riscv64/traced         /usr/local/bin/
sudo cp out/linux_riscv64/traced_probes  /usr/local/bin/

4  Perfetto 架构

4.1  进程角色

进程职责
traced中央协调守护进程;管理 session、ring buffer、IPC
traced_probes内核数据源生产者;操作 tracefs 并读取 ftrace ring buffer
perfetto (CLI)消费者客户端;发送配置、写出 .pftrace 文件

4.2  关键规则:何时重启守护进程

每次内核重启加载新模块(insmod)、或修改了带 tracepoint 的驱动后,必须重新运行 start_traced.sh。旧进程不会自动感知新注册的 tracepoint,这是"采集不到事件"最常见的根因。

4.3  ftrace 数据流

  traced_probes                      tracefs
      │
      ├─ 写 "1" → events/<subsys>/<event>/enable
      ├─ 写 "1" → tracing_on
      │                       ↑
      │            内核执行 trace_xxx() ← tracepoint 触发
      │
      └─ 读 per_cpu/cpuN/trace_pipe_raw → traced → .pftrace

5  启动守护进程

start_traced.sh
#!/bin/bash
export PERFETTO_CONSUMER_SOCK_NAME=/tmp/perfetto-consumer
export PERFETTO_PRODUCER_SOCK_NAME=/tmp/perfetto-producer

关键:每次都重启,确保识别当前内核的 tracepoint

pkill -f “traced$” 2>/dev/null; pkill -f traced_probes 2>/dev/null
sleep 1; rm -f /tmp/perfetto-consumer /tmp/perfetto-producer

nohup setsid /home/perise/source/perfetto/out/linux_riscv64/traced
–producer-socket=/tmp/perfetto-producer
–consumer-socket=/tmp/perfetto-consumer
> /tmp/traced.log 2>&1 &
sleep 0.5
nohup setsid /home/perise/source/perfetto/out/linux_riscv64/traced_probes
> /tmp/traced_probes.log 2>&1 &
sleep 2
echo “[OK] traced=$(pgrep -f ‘traced$’) probes=$(pgrep -f traced_probes)”

6  采集配置格式

config.cfg
buffers: { size_kb: 65536  fill_policy: RING_BUFFER }

data_sources: { config {
name: “linux.ftrace”
ftrace_config {
# 格式: “<subsystem>/<event_name>”
ftrace_events: “sched/sched_switch”
ftrace_events: “sched/sched_wakeup”
# 自定义事件(示例:V2D)
ftrace_events: “v2d/v2d_job_submit”
ftrace_events: “v2d/v2d_dma_map_start”
buffer_size_kb: 8192
}
}}

data_sources: { config {
name: “linux.process_stats”
process_stats_config { scan_all_processes_on_start: true }
}}

duration_ms: 15000

运行采集
export PERFETTO_CONSUMER_SOCK_NAME=/tmp/perfetto-consumer
export PERFETTO_PRODUCER_SOCK_NAME=/tmp/perfetto-producer
sudo perfetto --txt --config config.cfg --out /tmp/trace.pftrace

7  为新驱动添加 Trace 事件(V2D/DMA 完整示例)

V2D 是 K3 平台内置的 2D 图形加速器,通过 DMA-buf 进行内存传输。本章以完整可编译的代码演示如何为任意驱动添加 TRACE_EVENT 插桩。

7.1  接入步骤总览

  1. 创建 <drv>_trace.h——声明 TRACE_EVENT 宏
  2. 修改 Makefile——添加 ccflags-y += -I$(src)
  3. 修改 <drv>.c——CREATE_TRACE_POINTS + 调用 trace_xxx()
  4. 编译 & 安装内核,重启
  5. 验证 tracefs 中事件可见,手动 ftrace 测试
  6. 重启 traced_probes,Perfetto 采集

7.2  第一步:创建 v2d_trace.h

drivers/soc/spacemit/v2d/ 目录下创建 v2d_trace.h

v2d_trace.h
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM v2d    /* 决定 tracefs 子目录名: events/v2d/ */

#if !defined(_TRACE_V2D_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_V2D_H
#include <linux/tracepoint.h>

/* ── Job 生命周期 ───────────────────────────────────────── */
TRACE_EVENT(v2d_job_submit,
TP_PROTO(u32 seqno, u32 w, u32 h, int acq_fence_fd),
TP_ARGS(seqno, w, h, acq_fence_fd),
TP_STRUCT__entry(
__field(u32, seqno) __field(u32, w) __field(u32, h)
__field(int, acq_fence_fd)
),
TP_fast_assign(
__entry->seqno=seqno; __entry->w=w; __entry->h=h;
__entry->acq_fence_fd=acq_fence_fd;
),
TP_printk(“seqno=%u dst=%ux%u acq_fence=%d”,
__entry->seqno, __entry->w, __entry->h, __entry->acq_fence_fd));

TRACE_EVENT(v2d_job_start,
TP_PROTO(u32 seqno, u32 w, u32 h), TP_ARGS(seqno, w, h),
TP_STRUCT__entry(__field(u32,seqno) __field(u32,w) __field(u32,h)),
TP_fast_assign(__entry->seqno=seqno; __entry->w=w; __entry->h=h;),
TP_printk(“seqno=%u dst=%ux%u”, __entry->seqno, __entry->w, __entry->h));

TRACE_EVENT(v2d_irq_eof,
TP_PROTO(u32 seqno, u32 irqstatus), TP_ARGS(seqno, irqstatus),
TP_STRUCT__entry(__field(u32,seqno) __field(u32,irqstatus)),
TP_fast_assign(__entry->seqno=seqno; __entry->irqstatus=irqstatus;),
TP_printk(“seqno=%u irqstatus=0x%x”, __entry->seqno, __entry->irqstatus));

TRACE_EVENT(v2d_job_done,
TP_PROTO(u32 seqno, int reset), TP_ARGS(seqno, reset),
TP_STRUCT__entry(__field(u32,seqno) __field(int,reset)),
TP_fast_assign(__entry->seqno=seqno; __entry->reset=reset;),
TP_printk(“seqno=%u reset=%d”, __entry->seqno, __entry->reset));

/* ── DMA-buf map/unmap ─────────────────────────────────── */
TRACE_EVENT(v2d_dma_map_start,
TP_PROTO(int tbu_id, unsigned long size), TP_ARGS(tbu_id, size),
TP_STRUCT__entry(__field(int,tbu_id) __field(unsigned long,size)),
TP_fast_assign(__entry->tbu_id=tbu_id; __entry->size=size;),
TP_printk(“tbu_id=%d size=%lu”, __entry->tbu_id, __entry->size));

TRACE_EVENT(v2d_dma_map_end,
TP_PROTO(int tbu_id, u64 dma_addr), TP_ARGS(tbu_id, dma_addr),
TP_STRUCT__entry(__field(int,tbu_id) __field(u64,dma_addr)),
TP_fast_assign(__entry->tbu_id=tbu_id; __entry->dma_addr=dma_addr;),
TP_printk(“tbu_id=%d dma_addr=0x%llx”, __entry->tbu_id, __entry->dma_addr));

TRACE_EVENT(v2d_dma_unmap_start,
TP_PROTO(u32 seqno), TP_ARGS(seqno),
TP_STRUCT__entry(__field(u32,seqno)),
TP_fast_assign(__entry->seqno=seqno;),
TP_printk(“seqno=%u”, __entry->seqno));

TRACE_EVENT(v2d_dma_unmap_end,
TP_PROTO(u32 seqno), TP_ARGS(seqno),
TP_STRUCT__entry(__field(u32,seqno)),
TP_fast_assign(__entry->seqno=seqno;),
TP_printk(“seqno=%u”, __entry->seqno));

#endif /* _TRACE_V2D_H */

/* 必须放在所有 #endif 之后 /
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH . /
相对于驱动源码目录 */
#define TRACE_INCLUDE_FILE v2d_trace
#include <trace/define_trace.h>

7.3  第二步:修改 Makefile

drivers/soc/spacemit/v2d/Makefile
ccflags-y += -I$(src)    # 关键!$(src) 指向驱动源码目录
obj-$(CONFIG_SPACEMIT_V2D) += v2d.o
v2d-y := v2d_drv.o v2d_hw.o v2d_iommu.o

⚠  不加 -I$(src) 时,编译报 v2d_trace.h: No such file or directory。原因:include <trace/define_trace.h> 触发二次包含,编译器当前目录是内核根目录,找不到驱动目录下的 v2d_trace.h

7.4  第三步:在 v2d_drv.c 中插桩

v2d_drv.c(插桩片段)
/* 在所有 #include 之后,仅此一个 .c 文件添加 */
#define CREATE_TRACE_POINTS
#include "v2d_trace.h"

/* ── 插桩点 1:v2d_job_submit() ── 任务提交入口 /
trace_v2d_job_submit(
atomic_read(&info->seqno) + 1,
pTask->param.dst.w, /
V2D_SURFACE_S 字段是 w/h,不是 width/height */
pTask->param.dst.h,
pTask->acquireFencefd);

/* ── 插桩点 2:v2d_post_work_func() ── HW 开始前 */
trace_v2d_job_start(atomic_read(&info->seqno),
post->pTask->param.dst.w, post->pTask->param.dst.h);

/* ── 插桩点 3:v2d_irq_handler() ── EOF 中断 */
trace_v2d_irq_eof(atomic_read(&info->seqno), irqstatus);

/* ── 插桩点 4:v2d_work_done() ── fence signal 前 /
/
注意:seqno 是 dma_fence 结构体字段,不是函数 */
trace_v2d_job_done((u32)pCompleteFence->seqno, info->do_reset);

/* ── 插桩点 5/6:get_addr_from_dmabuf() ── DMA map /
trace_v2d_dma_map_start(pInfo->tbu_id, pInfo->dmabuf->size);
/
… dma_buf_attach / dma_buf_map_attachment … */
trace_v2d_dma_map_end(pInfo->tbu_id, (u64)addr);

/* ── 插桩点 7/8:v2d_put_dmabuf() ── DMA unmap /
u32 _seqno = cfg->pCompleteFence ?
(u32)cfg->pCompleteFence->seqno : 0;
trace_v2d_dma_unmap_start(_seqno);
/
… cleanup loop … */
trace_v2d_dma_unmap_end(_seqno);

7.5  已知编译陷阱

错误信息原因与修复
has no member 'width'/'height'V2D_SURFACE_S 字段是 w / h(uint16_t),不是 width / height
implicit declaration of 'dma_fence_seqno'seqno 是 struct dma_fence 直接字段,改写 fence->seqno
Module version mismatch (6.18.3+)未提交的 git 改动使版本追加 + 后缀;
ln -sfn /lib/modules/6.18.3+ /lib/modules/6.18.3-pvr
v2d_trace.h: No such file or directoryMakefile 缺少 ccflags-y += -I$(src)

7.6  手动 ftrace 验证

$ bash
# 内核重启后,确认 event 目录存在
ls /sys/kernel/tracing/events/v2d/
# 预期输出:
# enable  filter  v2d_dma_map_end  v2d_dma_map_start
# v2d_dma_unmap_end  v2d_dma_unmap_start  v2d_irq_eof
# v2d_job_done  v2d_job_start  v2d_job_submit

手动打开事件,运行负载

echo 1 > /sys/kernel/tracing/events/v2d/enable
echo 1 > /sys/kernel/tracing/tracing_on
/usr/local/bin/v2d_test --fill
cat /sys/kernel/tracing/trace | grep v2d

实测输出:

v2d_test-1768 [004] 2920.816178: v2d_job_submit: seqno=15 dst=320x240 acq_fence=-1

v2d-176 [007] 2920.816227: v2d_dma_map_start: tbu_id=2 size=307200

v2d-176 [007] 2920.816283: v2d_dma_map_end: tbu_id=2 dma_addr=0xa0000000

v2d-176 [007] 2920.816284: v2d_job_start: seqno=15 dst=320x240

<idle>-0 [004] 2920.816397: v2d_irq_eof: seqno=15 irqstatus=0x1

清理

echo 0 > /sys/kernel/tracing/events/v2d/enable
echo 0 > /sys/kernel/tracing/tracing_on
echo > /sys/kernel/tracing/trace

8  Perfetto 采集 V2D 事件

8.1  采集脚本 capture_v2d.sh

capture_v2d.sh
#!/bin/bash
# 用法: sudo bash /home/perise/capture_v2d.sh [时长秒]
DURATION=${1:-15}
OUT=/tmp/v2d_trace_$(date +%Y%m%d_%H%M%S).pftrace
export PERFETTO_CONSUMER_SOCK_NAME=/tmp/perfetto-consumer
export PERFETTO_PRODUCER_SOCK_NAME=/tmp/perfetto-producer

每次重启以识别最新 tracepoint

pkill -f “traced$” 2>/dev/null; pkill -f traced_probes 2>/dev/null; sleep 1
nohup setsid /home/perise/source/perfetto/out/linux_riscv64/traced
–producer-socket=/tmp/perfetto-producer
–consumer-socket=/tmp/perfetto-consumer
> /tmp/traced.log 2>&1 &
sleep 0.5
nohup setsid /home/perise/source/perfetto/out/linux_riscv64/traced_probes
> /tmp/traced_probes.log 2>&1 &
sleep 2
echo 0 > /proc/sys/kernel/kptr_restrict

cat > /tmp/v2d.cfg << CFG
buffers: { size_kb: 65536 fill_policy: RING_BUFFER }
data_sources: { config {
name: “linux.ftrace”
ftrace_config {
ftrace_events: “sched/sched_switch”
ftrace_events: “sched/sched_wakeup”
ftrace_events: “v2d/v2d_job_submit”
ftrace_events: “v2d/v2d_job_start”
ftrace_events: “v2d/v2d_irq_eof”
ftrace_events: “v2d/v2d_job_done”
ftrace_events: “v2d/v2d_dma_map_start”
ftrace_events: “v2d/v2d_dma_map_end”
ftrace_events: “v2d/v2d_dma_unmap_start”
ftrace_events: “v2d/v2d_dma_unmap_end”
buffer_size_kb: 8192
}
}}
data_sources: { config {
name: “linux.process_stats”
process_stats_config { scan_all_processes_on_start: true }
}}
duration_ms: $((DURATION * 1000))
CFG

echo “[] 采集 ${DURATION}s,请在另一终端运行 v2d_test…"
/home/perise/source/perfetto/out/linux_riscv64/perfetto
–txt --config /tmp/v2d.cfg --out “$OUT”
chmod 644 “$OUT” && echo "[
] 完成: $(ls -lh $OUT)”

8.2  两终端操作流程

终端 1(采集)终端 2(负载)
sudo bash /home/perise/capture_v2d.sh 15等提示出现后运行
采集中……sudo /usr/local/bin/v2d_test --fill
sudo /usr/local/bin/v2d_test --blit
15s 后自动完成
scp perise@192.168.31.9:/tmp/v2d_trace_*.pftrace ~/

诊断提示:采集期间执行以下命令确认事件已 enable(值必须为 1):

cat /sys/kernel/tracing/events/v2d/v2d_job_submit/enable  # → 1
cat /sys/kernel/tracing/tracing_on                          # → 1

若值为 0,说明 traced_probes 未正确启动,重新运行 start_traced.sh。

8.3  Perfetto UI 查看

  1. 访问 https://ui.perfetto.dev,上传 .pftrace 文件
  2. 左侧搜索框输入 v2d,可筛出全部 8 个 V2D 事件
  3. v2d_job_submitv2d_job_startv2d_irq_eofv2d_job_done 构成完整 Job 时序
  4. v2d_dma_map_start/end 显示 DMA-buf 映射耗时
  5. 配合 sched_switch 可分析 V2D 线程在各 CPU 核的调度情况

9  通用新模块 Trace 接入 SOP

步骤操作
1. 头文件创建 <drv>_trace.h,声明 TRACE_SYSTEM 和所有 TRACE_EVENT 宏,末尾写 TRACE_INCLUDE_PATH / TRACE_INCLUDE_FILE
2. Makefile添加 ccflags-y += -I$(src)
3. 驱动 .c一处 #define CREATE_TRACE_POINTS + #include "<drv>_trace.h",关键路径调用 trace_xxx()
4. 编译安装make modules_install + reboot(或 rmmod/insmod)
5. 验证 tracefsls /sys/kernel/tracing/events/<TRACE_SYSTEM>/ 确认 event 目录
6. ftrace 测试echo 1 > events/<sys>/enable;运行负载;cat trace
7. 重启 probessudo bash /home/perise/start_traced.sh(每次内核/模块变化后必须)
8. 配置采集ftrace_events: "<TRACE_SYSTEM>/<event_name>"
9. 可视化下载 .pftrace,上传 ui.perfetto.dev

10  rvtrace:RISC-V 硬件指令 Trace

rvtrace 是 K3 平台的 RISC-V Nexus 协议硬件 trace,通过 CoreSight TMC ETF 将 CPU 取指记录写入 AUX buffer。与 ftrace 软件插桩不同,rvtrace 零侵入、极低开销,可捕获精确到指令级的执行轨迹。

$ bash
# 前置条件
echo -1 > /proc/sys/kernel/perf_event_paranoid
echo  0 > /proc/sys/kernel/kptr_restrict

采集(sinkid=1 对应 tmc_etf0)

sudo perf record -e rvtrace/sinkid=1/ -a
–clockid CLOCK_MONOTONIC
–vmlinux=/home/perise/source/linux-6.18/vmlinux
-o rvtrace.data – sleep 5

解码为 branch 记录

sudo perf script -i rvtrace.data
–vmlinux=/home/perise/source/linux-6.18/vmlinux
–itrace=b > branches.txt

转换为 Chrome Trace JSON(可导入 Perfetto UI)

python3 /home/perise/rvtrace_to_json.py branches.txt > rvtrace.json

上传 rvtrace.json 到 ui.perfetto.dev

11  常见问题排查

现象原因与解决
events/v2d/ 目录不存在CONFIG_SPACEMIT_V2D 未打开,或模块未 insmod
Perfetto 采集不到 v2d 事件traced_probes 在事件注册前启动;重启 traced_probes(执行 start_traced.sh)
采集期间 enable=0traced_probes 启动失败或 socket 权限问题;以 root 重启
"EnableTracing IPC request rejected"traced 以非 root 启动,perfetto 以 root 运行;重启 traced 为 root
traced 在 SSH 断开后被杀缺少 nohup setsid;start_traced.sh 已包含此修复
rvtrace 采集失败(permission denied)perf_event_paranoid 未设为 -1;需以 root 运行
rvtrace "failed to get insn info"kptr_restrict 未设为 0;或 --vmlinux 路径错误
内核模块版本不匹配(6.18.3+)未提交 git 改动追加 + 后缀;
ln -sfn /lib/modules/6.18.3+ /lib/modules/6.18.3-pvr

12  附录:关键路径速查

结果图片
![3a4c11e48cc6164d9d5197b2e3419c97|690x360](upload://w691MPpUp2mqoYAh7wO21KMpiFJ.png) ![89f56f49d48941630b2829f0cd06d9f1|690x436](upload://AcH85COdv9eCJsD2JsMFiDi7O74.png)
一键检查采集环境
echo "=== traced ===" && pgrep -af "traced$"
echo "=== traced_probes ===" && pgrep -af traced_probes
echo "=== v2d events ===" && ls /sys/kernel/tracing/events/v2d/ 2>/dev/null || echo "NOT FOUND"
echo "=== tracing_on ===" && cat /sys/kernel/tracing/tracing_on
echo "=== Perfetto ===" && perfetto --version
文件 / 路径说明
/home/perise/source/perfetto/out/linux_riscv64/编译产物目录
/home/perise/start_traced.sh启动守护进程(每次重启后运行)
/home/perise/capture_v2d.shV2D 专项采集脚本
/home/perise/capture_combined.shPerfetto + rvtrace 联合采集脚本
/home/perise/rvtrace_to_json.pyrvtrace → Chrome Trace JSON 转换器
/tmp/traced_probes.logtraced_probes 日志(含 Ftrace setup 记录)
/sys/kernel/tracing/events/v2d/V2D tracepoint 在 tracefs 中的目录
文档版本:2026-06-09  |  平台:K3 Spacemit RISC-V 64  |  内核:6.18.3  |  Perfetto:v49.0