AArch64架构调用链性能数据采集原理

简介: 本次分享的主题是AArch64架构调用链性能数据采集原理,由阿里云苏轩楠分享。主要分为五个部分:1. 术语解释2. Frame Pointer RegisterStack Unwind3. Dwarf-based Stack Unwind4. /BRBE/CSRE Stack Unwind5. Kernel-space Stack Unwind&eBPF Unwinders

AArch64架构调用链性能数据采集原理

内容分析:

1. 术语解释

2. Frame Pointer RegisterStack Unwind

3. Dwarf-based Stack Unwind

4. /BRBE/CSRE Stack Unwind

5. Kernel-space Stack Unwind&eBPF Unwinders

 

本次分享的主题可以帮助到一些场景,比如在原有的平台上迁移到 Arm64 适配或优化的过程中会涉及到性能数据收集的步骤。性能数据收集例如调度栈的收集可以容易获得到相关信息的渠道,但是我遇到在 x86 上做性能优化时,如何获得程序运行时的调度栈的信息恰恰会卡住整个团队的进度。本次分享使用 Arm 平台对调度链进行收集。

image.png

本次分享有五部分。第一进行术语的解释,第二部分基于栈寄存器的调用链收集,第三部分基于 Dwarf 调用链的收集,这两部分都是基于软件调用链的收集,第四部分是基于硬件。第五部分在 Linux 内核中获得当下比较流行的基于 eBPF 系统数据收集,收集该调用栈会遇到什么问题或选择。

 

01.术语解释

image.png

首先介绍调用栈收集前介绍一些概念。一个调用栈由一个方法、一个帧组成。在编译原理中成为 Active Record 活动记录。一个帧有起始地址,保持方法的局部变量。也有栈顶地址,通常有一个单独的寄存器进行保存,例如X29是帧的起始地址,X31/XP用来保存栈顶的地址。帧与帧之间如何进行交互呢?通过 Calling Convention 调用约定。从A方法调用到B方法存在一个存参的约定。例如在 Arm 上X0是第一个参数,X1是第二个参数,如果超过X8可能需要通过栈来传输。所以从一个帧到另一个帧存在主动调用和被动调用的关系,主动调用是 Caller,被调用者是 Callee。

image.png

当一个事件发生时或者在程序中下一个断点时,需要从事件发生的点回溯到起始的调用方,称为 Stack Unwinder,一层一层解,从 Main方法一层一层调用。该过程为 Stack Unwinder。如图中的例子,该帧有一些参数和局部变量,FP(X29)指向帧的起始,SP(X31)指向栈底。

image.png

一层一层解开后就可以进行符号化。从帧到对应的符 Symbolization 。

image.png

图中中间是一个个帧, Frame 右侧是内部结构。一帧一帧解开后是地址,从地址到右侧的 Foo()、bar()、baz()是一个符号化过程。

介绍完术语后,介绍如何解开一个帧。

 

02.Frame Pointer RegisterStack Unwind

image.png

如下有一个示例 unwind.c。

image.png

在 Main 方法中调用 Foo(),在Foo()中调用Bar(),接着进行编译。

image.png

此处使用了一个参数 -fno-omit-frame-pointer,可以看到编号为54的 STP 的指令会将X29和X30保存到栈上。右侧保存X29、X30,同时将58位置上保存X29,更新 SP,指向新的位置。

image.png

Main 调用Foo,foo中是如何调用呢?28与2C的位置相同,但4C处返回时会调用上一帧的位置。右侧是在调用 Foo 后的状态,由于没有局部变量,所以保存上一帧的状态。接着调用 Bar。

image.png

调用 Bar 时,没有 STP 和X29、X30的寄存器。进入后寄存器的状态会指向该位置,其中会往上指,所以会产生链式的结构,一层一层往上解开栈。但是由于在调用Bar时,并没有保存 Foo 的返回位置,只是 Foo 保存了 Main 的返回位置,所以中间的 Foo 看不到。做 Stack Unwinder 时看不到这个地方。

image.png

这是第一个问题,第二个问题是如果将栈底的寄存器省略掉后会完全不保存,无法构建一个链向上回溯。由于它可以作为一个通用的寄存器进行使用,所以程序可以使用通用寄存器,降低将变量存到栈上的机会,提高性能。 Java 测试在一些极限的条件下大概有8%的性能影响。

以上就是性能问题和中间不保存丢失的问题。

image.png

还有此处,我们很依赖于保存上下文、栈帧的起始和结束位置。但是如果此时有一个远程的跳转 Black Miss,而代码不在指定的 Catch 中就会发生 Catch Miss。 Catch Miss 指的是代码的起始位置,但是代码的起始位置无法回溯到栈,这一层会被忽略掉,还没有开始构建栈的信息。或者在构建 Return 时就出去就会产生这样的问题。

 

03.Dwarf-based Stack Unwind

第二个方法基于 Dwarf 信息作为 Stack unwind 的工作。

  image.png

image.png

Dwarf 信息是一个格式标准,不仅使用在 Stack Unwind,在 GDB 中也会大量使用 Dwarf 信息进行 Stack Unwind 。将栈的边界信息或者调试信息存在单独的F文件或 Section 中。如果只做 Stack Unwind,不需要使用 gcc-g 生成 Dwarf 信息。因为 Stack Unwind 是栈的回溯信息,存在 .eh_frame 中。该信息用来做异常处理,例如写 C 代码调用 C++ 就会进行异常处理,所以需要存该信息进行回溯。 .eh_frame section 一定存在,所以可以进行一层层解开。

image.png

如果使用 Dwarfdump 命令,Dwarf 出结果后可以看到 Bar 方法下面有一些汇编和条目。其中 CFA=00(r31)位置的 CFA 代表 Canonical Frame Address,是帧的起始位置。R31是栈底位置,R31加上00表示栈底和栈顶在一起。第二条的400698位置,由于我们已经在栈帧上开辟了一个字节空间,CFA 会变成R31加 SP 变成栈的起始位置。最后一条4006b8位置执行Return 指令, Return 之前有 Add 指令,将SP加回16字节,栈顶位置与栈底位置在重复位置,所以偏移为00。虽然没有保存栈的上下文的 Frame Pointer 在栈帧上,但是通过该信息可以准确还原。如果 Exception 出现 Catch Miss 在起始位置也没有关系,可以进行正常回溯。

后面类似,可以通过 Bar方法进行回溯,一层一层往上。

image.png

看似该方案很好,但实际上还存在一个问题:速度慢。它是一个基于查表的方式,该表若是全部存下来会非常大。所以提出使用一个状态机或字节码快速描述该表,在执行时使用状态机或字节码去解开。

image.png

此处定义了一些字节码,但是存在一些缺陷。

image.png

非常慢,而且容易出错。由于是一个状态机,所以也存在安全问题。所以该机制并不完美。

 

04. /BRBE/CSRE Stack Unwind

image.png

之后是一个硬件机制 Arm64 BRBE/CSRE或Intel。

image.png

通过硬件机制去记录跳转过程,好处是开销很低并且非常可靠。下面是一个例子。

image.png

可以看到 Perf 命令中 any_call 会将跳转的位置罗列出:从哪里跳转到哪里。但是存在一个问题:硬件记录时存储成本太高。通常来讲,栈的深度足够长是不能回溯到起始位置的。

image.png

还有 Inline 函数展示不出。

以上介绍了几种能用的方式。

 

05.Kernel-space Stack Unwind&eBPF Unwinders

image.png

最后介绍内核态。

image.png

如果在内核上做 Stack Unwinder 有两种方式。第一基于 Frame Pointer Register,第二是 ORC Unwinder。

image.png

另外 eBPF 分为两部分。如果解的是内核态的栈,与上述一致。如果解用户态的栈,有很多人无法使用 ORC 技术,会提前基于 Dwarf 信息构建表,通过 eBPF 的 Map 机制将表的信息送入内核,在里面构建状态机进行解。好处是不用将用户态的栈从内核拷到用户态中,可以节省一定效率。所以该机制相对较好,未来还在探索更好的方式。

相关文章
|
26天前
|
存储 调度 C++
16 倍性能提升,成本降低 98%! 解读 SLS 向量索引架构升级改造
大规模数据如何进行语义检索? 当前 SLS 已经支持一站式的语义检索功能,能够用于 RAG、Memory、语义聚类、多模态数据等各种场景的应用。本文分享了 SLS 在语义检索功能上,对模型推理和部署、构建流水线等流程的优化,最终带给用户更高性能和更低成本的针对大规模数据的语义索引功能。
190 15
|
3月前
|
存储 数据挖掘 BI
2-5 倍性能提升,30% 成本降低,阿里云 SelectDB 存算分离架构助力波司登集团实现降本增效
波司登集团升级大数据架构,采用阿里云数据库 SelectDB 版,实现资源隔离与弹性扩缩容,查询性能提升 2-5 倍,总体成本降低 30% 以上,效率提升 30%,助力销售旺季高效运营。
270 9
|
5月前
|
人工智能 API 数据安全/隐私保护
Apifox 与 Apipost 的 API 文档引擎对比:底层架构、性能与可扩展性分析
深入探索市场上两大主流API工具——Apifox和Apipost的文档能力时,发现了令人惊讶的差距。这不仅仅是功能多寡的问题,更关乎开发效率与团队协作的质变。
|
6月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
3月前
|
机器学习/深度学习 算法 文件存储
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
神经架构搜索(NAS)正被广泛应用于大模型及语言/视觉模型设计,如LangVision-LoRA-NAS、Jet-Nemotron等。本文回顾NAS核心技术,解析其自动化设计原理,探讨强化学习、进化算法与梯度方法的应用与差异,揭示NAS在大模型时代的潜力与挑战。
722 6
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
|
2月前
|
存储 JSON 数据处理
ClkLog埋点与用户行为分析系统:架构升级与性能全面提升
随着越来越多企业在实际业务中使用 ClkLog,数据规模和分析需求也不断提升,部分用户日活已经超过10万,为了顺应这一趋势,ClkLog 秉持 “开放透明、持续演进”的理念,推出了迄今为止最重要的一次性能优化升级。新版本在大规模数据处理与复杂查询场景中,性能表现实现了跨越式提升。经过多轮研发与严格测试,新版本现已正式上线:在原有付费版 1.0 的基础上架构全面升级,并同步发布全新的 2.0 版本。为用户带来更强的性能与更广的适用场景。
|
1月前
|
机器学习/深度学习 自然语言处理 监控
23_Transformer架构详解:从原理到PyTorch实现
Transformer架构自2017年Google发表的论文《Attention Is All You Need》中提出以来,彻底改变了深度学习特别是自然语言处理领域的格局。在短短几年内,Transformer已成为几乎所有现代大型语言模型(LLM)的基础架构,包括BERT、GPT系列、T5等革命性模型。与传统的RNN和LSTM相比,Transformer通过自注意力机制实现了并行化训练,极大提高了模型的训练效率和性能。
|
4月前
|
存储 监控 算法
园区导航系统技术架构实现与原理解构
本文聚焦园区导航场景中室内外定位精度不足、车辆调度路径规划低效、数据孤岛难以支撑决策等技术痛点,从架构设计到技术原理,对该系统从定位到数据中台进行技术拆解。
187 0
园区导航系统技术架构实现与原理解构
|
4月前
|
数据采集 机器学习/深度学习 自然语言处理
智能风险管理的技术架构:2025从数据采集到自主决策的全链路解析
本文系统梳理了项目风险管理的技术演进历程,从文档驱动到智能化阶段,深入解析各时期关键技术与工具架构,并结合实践案例提出前瞻性实施策略,助力项目管理专业人士构建智能风险管理体系。
274 2

热门文章

最新文章