LinuxBPF学习笔记 - 堆栈跟踪[3]

Posted 宣之于口

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LinuxBPF学习笔记 - 堆栈跟踪[3]相关的知识,希望对你有一定的参考价值。

一、简介

堆栈跟踪,也称为堆栈回溯跟踪或调用跟踪,是显示代码流的一系列函数. 堆栈跟踪可以用于了解导致事件的代码路径, 以及对内核和用户代码进行性能分析(profiling)以观察执行时间.

举例说明:

func_c()	# 当前函数
func_b()
func_a()	# 堆栈的底部

二、堆栈遍历

BPF提供了用于记录堆栈跟踪的特殊映射类型,并且可以使用基于帧指针或基于ORC的堆栈遍历来获取它们

1. 基于帧指针

对于 x86-64 架构,共有16个64位通用寄存器。其中%RPB 是栈帧指针,用于标识当前栈帧的起始位置(栈底)。

帧指针技术遵循的惯例是: 始终可以在寄存器 x86_64 上的%RBP中找到栈帧的起始位置,并且将返回地址存储在与所存储RBP的已知偏移量(+8)处。 这意味着,任何中断程序的调试器或跟踪器都可以读取%RBP,然后通过遍历%RBP链表并以已知的偏移量获取地址来轻松获取堆栈跟踪. [TODO: 这部分内容暂未理解, 待补充, 详见原文 P101]

三、火焰图

堆栈跟踪的定时采样可以收集成千上万个堆栈,每个堆栈可以长达数十或数百行。

  • Linux perf profiler将其总结为调用树,显示每个路径的百分比
  • BCC profile 计算每个堆栈(唯一)的出现次数

首先我们考虑以下示例: 由输出可见, 代码路径func_a() -> func_b() -> func_c()出现了7次

func_e
func_d
func_b
func_a
1

func_b
func_a
2

func_c
func_b
func_a
7

1. 基本概念

火焰图是基于上述结果产生的SVG 图片,可以用来展示 CPU 的调用栈. 其具有以下特性:

  • 每个框代表堆栈中的一个方法(栈桢), 最上面的框为正在运行的方法
  • y轴显示堆栈深度(堆栈中的帧数)
  • x轴没有像大多数图形一样显示从左到右的时间流逝, 从左到右的顺序是按字母顺序排序的帧,以最大程度地合并帧; 宽度代表了调用栈在全局出现的次数,次数代表着出现频率

由于这是显示CPU样本的火焰图,进一步分析可以得到:

  • 我们最主要的关注点要放在方块的宽度上: func_c:70%,func_b : 20%, func_c: 10%. func_afunc_d未直接在CPU上采样
  • 观察火焰图底部或中部方块的宽度占比意义不大, 例如func_a函数宽度为100%, 但真正消耗时间的它的子调用是func_c
  • 我们更应该关注的是火焰图顶部的一些 平顶山,顶部说明它没有子调用,方块宽说明它耗时长。

2. 生成火焰图

我们通过点击和鼠标指向可以展示出更多的信息。下图就是一个典型的火焰图,从结构上,它是由多个大小和颜色各异的方块构成,每个方块上都有字符,它们底部连接在一块,组成火焰的基底,顶部分出许多“小火苗”

# 生成脚本文件
[root@rumia ~] perf script -i perf.data &> perf.unfold
# 执行完成后生成perf.svg图片,可以下载到本地,用浏览器打开 perf.svg
[root@rumia ~] ./FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
[root@rumia ~] ./FlameGraph/flamegraph.pl perf.folded > perf.svg

3. IDEA 结合火焰图

使用前注意Idea版本, 具体不太清楚哪个版本, 最新的肯定是可以~

a. 开启火焰图

  • 按下快捷键 shift alt command + /
  • 选择 4 Experimental features
  • 勾选 idea.profiler.enbaled

b. 运行

示例代码:

public static void main(String[] args) 
    Map map = new HashMap();
    for (int i = 0; i < 10; i++) 
        byte[] b = new byte[1024*1024];
        map.put(i,b);
    
    System.out.println(map);

  • 选择 CPU Profiler执行程序, 然后就可以查看堆栈火焰图

  • 选择Allocation Profiler 执行程序, 然后就可以查看堆分配火焰图: 此处调用堆栈的宽度与堆内存分配量成正比. 最顶部的调用堆栈元素代表分配的数据类型,即数组(byte [])或对象(StandardFilter)

以上是关于LinuxBPF学习笔记 - 堆栈跟踪[3]的主要内容,如果未能解决你的问题,请参考以下文章

LinuxBPF学习笔记 - 性能分析方法论[5]

LinuxBPF学习笔记 - 性能分析方法论[5]

LinuxBPF学习笔记 - 调试技术[4]

LinuxBPF学习笔记 - 调试技术[4]

LinuxBPF学习笔记 - bpftrace开发[7]

LinuxBPF学习笔记 - bpftrace开发[7]