附录 D.4 Trace 原理与使用

本篇深入 virglrenderer 的性能插桩(tracing)机制,是正文第17章 §17.3的配套延伸。源码以 src/virgl_util.h / src/virgl_util.c 为准。

tracing 用极少的代码,给核心与各后端的关键路径打上带时间戳的区间,输出到 Perfetto / sysprof / stderr。它的精巧之处全在一个 C 语言的 RAII 技巧上。

D.4.1 编译期门控:零成本抽象

整套 trace 由宏 ENABLE_TRACING 门控。不开启时,所有 TRACE_* 宏展开为空:

c 复制代码
#else /* ENABLE_TRACING */
#define TRACE_INIT()
#define TRACE_FUNC()
#define TRACE_SCOPE(SCOPE)
#define TRACE_SCOPE_SLOW(SCOPE)
#define TRACE_SCOPE_BEGIN(SCOPE) NULL
#define TRACE_SCOPE_END(SCOPE_OBJ) (void)SCOPE_OBJ
#endif

因此 release 构建里这些埋点一条指令都不剩 ------真正的零成本抽象。-Dtracing= 的值决定 ENABLE_TRACING 取哪个后端:

c 复制代码
#define TRACE_WITH_PERFETTO 1
#define TRACE_WITH_STDERR   2
#define TRACE_WITH_PERCETTO 3
#define TRACE_WITH_SYSPROF  4

D.4.2 核心:用 cleanup 属性做 C 版 RAII

这是整个机制的灵魂:

c 复制代码
#define TRACE_FUNC()       TRACE_SCOPE(__func__)
#define TRACE_SCOPE(SCOPE) \
   void *trace_dummy __attribute__((cleanup (trace_end), unused)) = \
   trace_begin(SCOPE)

在函数开头写一句 TRACE_FUNC();,它展开成声明一个局部变量 trace_dummy:

  1. 进入时 :初始化表达式 trace_begin(SCOPE) 立即执行 → 记录"scope 开始",返回一个"scope 对象"(void*)存进 trace_dummy;
  2. 离开时 :__attribute__((cleanup(trace_end))) 告诉 GCC/Clang------当 trace_dummy 离开作用域 时(return/break/goto/正常落地......任何一条退出路径 ),编译器自动插入一次 trace_end(&trace_dummy) → 记录"scope 结束"。
c 复制代码
int foo(...) {
    TRACE_FUNC();          // ← begin 在这里
    if (err) return -1;    // ← 这里自动 end
    ...
    return 0;              // ← 这里也自动 end
}                          // ← 正常落地也自动 end

一行埋点,覆盖所有返回路径,零手工配对 ------这就是它比"手动 enter/exit"高明的地方,也杜绝了漏写 end 导致的 scope 不平衡。unused 只是压掉"变量未使用"告警。
trace_end(void **) 收到的是 &trace_dummy(cleanup 回调固定传变量地址),函数内 *dummy 才拿到那个 scope 对象。
trace_begin/end 编译器(cleanup) foo() 函数体 trace_begin/end 编译器(cleanup) foo() 函数体 #mermaid-svg-WAAgu3PSqZp7wP8y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WAAgu3PSqZp7wP8y .error-icon{fill:#552222;}#mermaid-svg-WAAgu3PSqZp7wP8y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WAAgu3PSqZp7wP8y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WAAgu3PSqZp7wP8y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WAAgu3PSqZp7wP8y .marker.cross{stroke:#333333;}#mermaid-svg-WAAgu3PSqZp7wP8y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WAAgu3PSqZp7wP8y p{margin:0;}#mermaid-svg-WAAgu3PSqZp7wP8y .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-WAAgu3PSqZp7wP8y text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-WAAgu3PSqZp7wP8y .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-WAAgu3PSqZp7wP8y .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-WAAgu3PSqZp7wP8y .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-WAAgu3PSqZp7wP8y .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-WAAgu3PSqZp7wP8y #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-WAAgu3PSqZp7wP8y .sequenceNumber{fill:white;}#mermaid-svg-WAAgu3PSqZp7wP8y #sequencenumber{fill:#333;}#mermaid-svg-WAAgu3PSqZp7wP8y #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-WAAgu3PSqZp7wP8y .messageText{fill:#333;stroke:none;}#mermaid-svg-WAAgu3PSqZp7wP8y .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-WAAgu3PSqZp7wP8y .labelText,#mermaid-svg-WAAgu3PSqZp7wP8y .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-WAAgu3PSqZp7wP8y .loopText,#mermaid-svg-WAAgu3PSqZp7wP8y .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-WAAgu3PSqZp7wP8y .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-WAAgu3PSqZp7wP8y .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-WAAgu3PSqZp7wP8y .noteText,#mermaid-svg-WAAgu3PSqZp7wP8y .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-WAAgu3PSqZp7wP8y .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-WAAgu3PSqZp7wP8y .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-WAAgu3PSqZp7wP8y .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-WAAgu3PSqZp7wP8y .actorPopupMenu{position:absolute;}#mermaid-svg-WAAgu3PSqZp7wP8y .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-WAAgu3PSqZp7wP8y .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-WAAgu3PSqZp7wP8y .actor-man circle,#mermaid-svg-WAAgu3PSqZp7wP8y line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-WAAgu3PSqZp7wP8y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 执行函数主体... 进入:trace_begin("foo") → 记录开始,返回 scope 对象 任意 return / 落地 自动 trace_end(&trace_dummy) → 记录结束(算时长)

D.4.3 begin/end 的四种实现

trace_begin 返回的 void* 是该 scope 的私有状态,各后端用法不同:

后端 trace_begin 返回 原理
percetto NULL 内联 TRACE_EVENT_BEGIN/END(virgl, ...),begin/end 由库内按分类栈隐式配对,无需每 scope 对象
perfetto(vperfetto) NULL vperfetto_min_beginTrackEvent/endTrackEvent,连系统 perfetto 服务 (USE_SYSTEM_BACKEND),数据进系统级 trace
sysprof malloc 的结构体 begin 存时间戳+名字 ;end 时算时长 now-begin,sysprof_collector_mark(...) 打一个 mark,再 free
stderr scope 名字串 维护 nesting_depth 缩进,begin 打 ENTER:xxx、end 打 EXIT,纯文本看调用嵌套

以 sysprof 为例(最能看出"为什么需要返回对象"):

c 复制代码
void *trace_begin(const char *scope) {
   struct virgl_sysprof_entry *t = malloc(sizeof(*t));
   t->begin = SYSPROF_CAPTURE_CURRENT_TIME;      /* 记开始时刻 */
   snprintf(t->name, sizeof(t->name), "%s", scope);
   return t;                                      /* ← 带着状态传给 end */
}
void trace_end(void **p) {
   struct virgl_sysprof_entry *t = *p;
   sysprof_collector_mark(t->begin,
                          SYSPROF_CAPTURE_CURRENT_TIME - t->begin, /* 时长 */
                          "virglrenderer", t->name, NULL);
   free(t);
}

perfetto/percetto 返回 NULL,是因为它们在库内部用每线程的事件栈自动把 begin/end 配对,不需要应用侧保存状态。stderr 则把 scope 名原样返回,end 时打印并回退缩进。

D.4.4 几个变体

c 复制代码
#define TRACE_SCOPE_BEGIN(SCOPE) trace_begin(SCOPE)     /* 手动 begin */
#define TRACE_SCOPE_END(OBJ)     trace_end(&OBJ)        /* 手动 end */
  • TRACE_SCOPE_BEGIN/END :当 begin 与 end 不在同一作用域时用------例如 drm/drm_context.c 里一个 ccmd 的 begin/end 跨语句、vrend/vrend_renderer.c 批处理里按条目收尾。这时 cleanup 自动化不适用,手动配对。
  • TRACE_SCOPE_SLOW :只在 DEBUG 构建生效,release 下展空------给高频、低价值、不想污染正式 trace 的点用。
  • 分类(仅 percetto) :virgl(主)、virgls(detailed,标 "slow"),可按详细级别过滤。

D.4.5 插桩点一览

TRACE_FUNC() / TRACE_SCOPE() 遍布核心与各后端:

区域 典型文件 代表埋点
核心 virglrenderer.c、virgl_context.c init、context 创建、submit_cmd、fence、资源/blob
Venus venus/vkr_*(renderer/queue/ring/command_buffer/device_memory...) ring 空转/通知、队列提交、命令缓冲
DRM drm/drm_context.c、drm/msm/msm_renderer.c 每个 ccmd 一个 scope(TRACE_SCOPE(ccmd->name))
vrend vrend/vrend_decode.c、vrend/vrend_renderer.c 命令解码、对象操作

D.4.6 使用方法

① 编译时选后端:

bash 复制代码
meson setup build -Dtracing=perfetto   # 或 sysprof / stderr / percetto / none(默认)
ninja -C build

② 运行时产出数据:

  • perfetto :trace_init() 以 USE_SYSTEM_BACKEND 连接系统 traced 服务。先起 perfetto 服务并配置数据源,跑负载后把 trace 拉进 ui.perfetto.dev 看时间线(区间耗时、嵌套、跨线程并发)。
  • sysprof:数据经 sysprof-capture collector 进入 sysprof 捕获,用 Sysprof GUI 查看 mark。
  • stderr :无需任何服务,直接把带缩进的 ENTER:/EXIT: 打到标准错误,适合快速看调用嵌套与粗粒度耗时。

③ 入口初始化 :程序会在入口调一次 TRACE_INIT()(如 virgl_renderer_init、vkr_renderer_init)启动对应后端。

D.4.7 端到端

#mermaid-svg-5rhbA4AEncXQCPZu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5rhbA4AEncXQCPZu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5rhbA4AEncXQCPZu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5rhbA4AEncXQCPZu .error-icon{fill:#552222;}#mermaid-svg-5rhbA4AEncXQCPZu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5rhbA4AEncXQCPZu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5rhbA4AEncXQCPZu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5rhbA4AEncXQCPZu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5rhbA4AEncXQCPZu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5rhbA4AEncXQCPZu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5rhbA4AEncXQCPZu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5rhbA4AEncXQCPZu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5rhbA4AEncXQCPZu .marker.cross{stroke:#333333;}#mermaid-svg-5rhbA4AEncXQCPZu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5rhbA4AEncXQCPZu p{margin:0;}#mermaid-svg-5rhbA4AEncXQCPZu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5rhbA4AEncXQCPZu .cluster-label text{fill:#333;}#mermaid-svg-5rhbA4AEncXQCPZu .cluster-label span{color:#333;}#mermaid-svg-5rhbA4AEncXQCPZu .cluster-label span p{background-color:transparent;}#mermaid-svg-5rhbA4AEncXQCPZu .label text,#mermaid-svg-5rhbA4AEncXQCPZu span{fill:#333;color:#333;}#mermaid-svg-5rhbA4AEncXQCPZu .node rect,#mermaid-svg-5rhbA4AEncXQCPZu .node circle,#mermaid-svg-5rhbA4AEncXQCPZu .node ellipse,#mermaid-svg-5rhbA4AEncXQCPZu .node polygon,#mermaid-svg-5rhbA4AEncXQCPZu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5rhbA4AEncXQCPZu .rough-node .label text,#mermaid-svg-5rhbA4AEncXQCPZu .node .label text,#mermaid-svg-5rhbA4AEncXQCPZu .image-shape .label,#mermaid-svg-5rhbA4AEncXQCPZu .icon-shape .label{text-anchor:middle;}#mermaid-svg-5rhbA4AEncXQCPZu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5rhbA4AEncXQCPZu .rough-node .label,#mermaid-svg-5rhbA4AEncXQCPZu .node .label,#mermaid-svg-5rhbA4AEncXQCPZu .image-shape .label,#mermaid-svg-5rhbA4AEncXQCPZu .icon-shape .label{text-align:center;}#mermaid-svg-5rhbA4AEncXQCPZu .node.clickable{cursor:pointer;}#mermaid-svg-5rhbA4AEncXQCPZu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5rhbA4AEncXQCPZu .arrowheadPath{fill:#333333;}#mermaid-svg-5rhbA4AEncXQCPZu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5rhbA4AEncXQCPZu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5rhbA4AEncXQCPZu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5rhbA4AEncXQCPZu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5rhbA4AEncXQCPZu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5rhbA4AEncXQCPZu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5rhbA4AEncXQCPZu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5rhbA4AEncXQCPZu .cluster text{fill:#333;}#mermaid-svg-5rhbA4AEncXQCPZu .cluster span{color:#333;}#mermaid-svg-5rhbA4AEncXQCPZu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5rhbA4AEncXQCPZu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5rhbA4AEncXQCPZu rect.text{fill:none;stroke-width:0;}#mermaid-svg-5rhbA4AEncXQCPZu .icon-shape,#mermaid-svg-5rhbA4AEncXQCPZu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5rhbA4AEncXQCPZu .icon-shape p,#mermaid-svg-5rhbA4AEncXQCPZu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5rhbA4AEncXQCPZu .icon-shape .label rect,#mermaid-svg-5rhbA4AEncXQCPZu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5rhbA4AEncXQCPZu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5rhbA4AEncXQCPZu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5rhbA4AEncXQCPZu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} perfetto
sysprof
stderr
入口 TRACE_INIT()

启动所选后端
各处 TRACE_FUNC()/TRACE_SCOPE()

cleanup 自动配对 begin/end
输出后端
系统 perfetto 服务 → ui.perfetto.dev
sysprof collector → Sysprof GUI
ENTER/EXIT 文本(带缩进)

D.4.8 小结

  • 门控 :ENABLE_TRACING 未定义时全部蒸发,零开销;
  • 核心 :TRACE_SCOPE 用一个局部哑变量 + cleanup 属性,把"函数退出"变成自动触发点,精确框定一段代码的起止;
  • 后端 :trace_begin 返回的 void* 承载 scope 状态(sysprof 的时间戳/stderr 的名字;perfetto/percetto 用内部栈返回 NULL);
  • 范围:核心 + Venus/DRM/vrend 全覆盖,不是图形专用;
  • 用法 :-Dtracing= 选后端,perfetto/sysprof 配套 GUI,stderr 零依赖看嵌套。
相关推荐
DeeplyMind9 天前
第9章:VIRGL 后端(OpenGL / vrend)
virtio·virtio-gpu·virglrenderer
DeeplyMind11 天前
第8章: 命令流与编解码
virtio-gpu·virglrenderer
凉、介3 个月前
Virtio(三):Virtqueue 建立与 Buffer 提交
笔记·学习·嵌入式·arm·虚拟化·virtio
凉、介3 个月前
Virtio 系列(一):框架概览
笔记·学习·嵌入式·虚拟化·virtio
Wanliang Li7 个月前
AArch64虚拟化——virtio-mmio实现
linux·虚拟化·virtio·hypervisor·mmio
DeeplyMind1 年前
Linux Virtio 子系统核心数据结构解析
linux·驱动开发·virtio-gpu
DeeplyMind1 年前
Guest → QEMU → Virglrenderer 调用逻辑分析
linux·驱动开发·虚拟化·virtio-gpu·virglrenderer
漫谈网络1 年前
Virtio 半虚拟化技术解析
virtio·半虚拟化技术
深度Linux2 年前
探秘Linux IO虚拟化:virtio的奇幻之旅
linux·c/c++·virtio·虚拟化技术