该工具基于 Intel Processor Trace(Intel-PT)的程序 trace 流,在程序运行时,对指定调用函数进行分析, 或者保存 trace 流,进行历史分析。
支持:
=> 函数分析
- 指定函数的时延分布直方图,平均时延。
- 按上层函数划分指定函数的时延分布。
- 给出指定函数的子函数调用时延。
- 支持函数时延按 On-CPU 和 Off-CPU (Schedule) 统计 (需要 root 权限)。
=> timeline 时间线分析
- 按线程给出函数时延随着 trace 时间的 timeline 变换图。
- 查看函数在某一时间段的 trace 数据,进行分析(函数或者火焰图分析)。
- 查看某个时间点的函数栈 pstack。
=> Flamegraph 火焰图分析
-
基于函数时延的火焰图:使用 pt_flame 对解码数据分析。
-
cpu 火焰图:和传统基于 cpu 采样一致。
=> 历史分析
- 先 trace 某段时间全量数据,再分析 (可以在不同机器,需要拷贝二进制到相同目录)。
Intel processor tool - func_latency
usage ./func_latency [-b bin/mysqld] [-f func] [-p pid] [-d trace_time] [-P perf_tool] [-s] [-i]
Linux version 4.2+ is required for Intel PT
Linux version 5.10+ is required for IP filtering when tracing
-b / --binary --- binary file path, empty for kernel func
-f / --func --- target's func name
-d / --duration --- trace time (seconds), 0.01 seconds by default
-p / --pid --- existing process ID
-T / --tid --- existing thread ID (comma separated list), example like tid1,tid2
-C / --cpu --- cpu list to trace, example like 0-47
-w / --worker_num --- parallel worker num, 10 by default
-s / --parallel_script --- if use parallel script
-t / --per_thread --- use per_thread mode to trace data, better in multi-cores
-o / --offcpu --- trace offcpu time at the same time, which requires root privilege
-i / --ip_filter --- use ip_filter when tracing function
-I / --func_idx --- for ip_filter, choose function index if there exists multiple one, '#0' by default
-P / --perf --- perf tool path, 'perf' by default
-a / --ancestor --- only analyze target function with 'ancestor' function in its call chain,
eg, 'test#100,200', we shows the result of target function
where its ancestor latency is between 100ns and 200 ns.
-c / --code_block --- show the code block latency of target function
--srcline --- show the address, source file and line number of functions
--history --- for history trace, 1: generate perf.data, 2: use perf.data
-D / --result_dir --- the result directory to save and use perf.data and temporary files
-U / --unfold_gathered_line
--- unfold the call-line which gathered for simplicity, like interrupts that
may be called from multiple locations
--pt_config --- set config term for intel pt event, currently 'cyc=1' by default
--li/--latency_interval--- show the trace between the latency interval (ns), format: "min,max"
-v / --verbose --- verbose, be more verbose (show debug message, etc)
-h / --help --- show this help
Timeline mode:
-l / --timeline --- show the target's func's latency by timeline for each thread
--ti/--time_interval --- show the trace between the time interval (ns), format:"start,min,max"
--tu/--timeline_unit --- the unit size in the timeline grapth, we caculate the average
latency in the unit, 1 by default
Flamegraph mode:
-F / --flamegraph --- show the flamegraph, "latency, cpu"
--pt_flame --- the installed path of pt_flame, latency-based flamegraph required通过 yum 安装,func_latency 和定制的 perf 二进制会安装在 /usr/share/pt_func_perf 目录下。
yum install pt_func_perf -b test
/usr/share/pt_func_perf/func_latency --help- 增大系统 perf_event_mlock_kb 大小,减少 trace 数据丢失。
- 使用 per-thread 模式,性能衰减更低,减少 switch 事件。
- trace 内核函数,offcpu 统计需要。
sudo bash -c "su -"
echo 131072 > /proc/sys/kernel/perf_event_mlock_kb
echo -1 > /proc/sys/kernel/perf_event_paranoid
echo 0 > /proc/sys/kernel/kptr_restrict- 安装并行 script 的 perf 工具依赖库,加快 script 速度(-s 参数开启)。
# 安装支持 symbol 解析库
sudo yum install binutils-devel elfutils-libelf-devel -y make- 使用 IP_filting(-i)可以只 trace 指定函数,数据基本不丢失,不过需要在 Linux 5.10+ 版本支持。
./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t- 不支持 IP_filting 可使用全量 trace 所有函数(适合历史分析),再过滤。缺点:速度慢,采样数据量大,容易丢数据,可能会出现由于缺栈的异常较大时延,perf 内部实现了 script 期间的 ip_filing,可进行加速,大约比全量 快 5 倍。
./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -t- 当函数有多个定义时,可以使用 -I 参数指定使用第几个 symbol。
./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t -I "#2"- 查看函数执行时 oncpu 和 offcpu 时延比例 (-o, 需要 root 权限)。
sudo ./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t -o- 根据指定祖先函数 (-a) 过滤分析目标函数,例如指定 "-f func_a -a func_b#1,100",可以查看从 func_b 时延在 1ns 到 100ns 时,func_a 的时延情况,这在分析函数时延长尾时的瓶颈非常有用。
sudo ./func_latency -b bin/mysqld -f "buf_page_get_gen" -a "do_command#1,100000" -d 1 -p 60416 -s -i -t- 除了包括子函数的延迟,还可以显示代码块的延迟(-c)。代码块包含一个分支结束和另一个分支开始的位置,我们可以根据程序汇编代码分析最耗时的指令。
sudo ./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t -c- 默认我们显示子函数在目标函数的调用位置,我们可以显示每个函数定义的地址,源文件路径和行号,需要指定 '--srcline'。
sudo ./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t --srcline- 查看函数时延在 min 纳秒到 max 纳秒的函数执行信息 (--li/--latency_interval=min,max)。
./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t --li=0,200000- 查看函数时延随着时间线分布图 (-l),可以通过 --tu/--timeline_unit 指定求平均打点的 step,默认每个时延打一个点。
./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t -l --tu=1- 查看时间从采样时间 start 开始, min 纳秒到 max 纳秒的时延( --ti/--time_interval=start,min,max)。
./func_latency -b bin/mysqld -f "do_command" -d 1 -p 60416 -s -i -t -l --tu=1 --ti=5133313694755869,100000,200000- 基于函数时延的火焰图:使用 pt_flame 对解码数据分析,火焰图输出在 flame.svg 中。
./func_latency --flamegraph="latency" -d 1 -p 60416 -t -s- cpu 火焰图:和传统基于 cpu 采样一致。
./func_latency --flamegraph="cpu" -d 1 -p 60416 -t -s- trace 数据 (--history=1),相当于 perf record 保存数据在 perf.data 中。可以将 perf.data 和带有调试信息的程序二进制拷贝到其他机器进行分析。
./func_latency -d 10 -p 60416 -t --history=1- 使用 perf.data,并分析 trace 数据 (--history=2),可以用前面所有场景分析,通过 -T 查看指定某个线程的 trace。
./func_latency -b bin/mysqld -f "do_command" -d 1 -s -t -l --history=2- 我们可以指定保存和使用 perf.data 的目录 (-D=[name]),这样我们可以很方便的比较多个不同的 trace 结果,发现不同时刻程序的运行差异。
./func_latency -d 10 -p 60416 -t -D "trace1" --history=1在 sysbench oltp_read_only(24 cores, 128 threads)负载下测试。
[ 10s ] thds: 128 tps: 14361.84 qps: 229809.51
[ 11s ] thds: 128 tps: 14527.77 qps: 232428.35
[ 12s ] thds: 128 tps: 14351.81 qps: 229664.99
[ 13s ] thds: 128 tps: 14549.71 qps: 232707.43
[ 14s ] thds: 128 tps: 14346.20 qps: 229687.19
[ 15s ] thds: 128 tps: 14334.92 qps: 229213.65
[ 16s ] thds: 128 tps: 14169.18 qps: 226838.86 # start trace
[ 17s ] thds: 128 tps: 14054.97 qps: 224836.49
[ 18s ] thds: 128 tps: 13833.66 qps: 221274.61
[ 19s ] thds: 128 tps: 13967.35 qps: 223444.64
[ 20s ] thds: 128 tps: 13811.13 qps: 220993.01
[ 21s ] thds: 128 tps: 13991.60 qps: 223962.59
[ 22s ] thds: 128 tps: 13825.28 qps: 221247.52
[ 23s ] thds: 128 tps: 14108.98 qps: 225739.62
[ 24s ] thds: 128 tps: 13837.81 qps: 221382.92
[ 25s ] thds: 128 tps: 13953.96 qps: 223300.31 - 统计 do_command 运行 1s 的时延分布, 查看 schedule 时延。
$ sudo ./func_latency -b "bin/mysqld" -f "do_command" -d 1 -p 60416 -s -i -t -o
Histogram - Latency of [do_command]:
ns : cnt distribution sched distribution
1024 -> 2047 : 0 | | 6 | |
2048 -> 4095 : 0 | | 1 | |
4096 -> 8191 : 0 | | 6 | |
8192 -> 16383 : 0 | | 27 | |
16384 -> 32767 : 132 | | 150 | |
32768 -> 65535 : 8487 |*** | 16063 |****** |
65536 -> 131071 : 22384 |******* | 27139 |********** |
131072 -> 262143 : 39483 |************** | 33818 |************ |
262144 -> 524287 : 56138 |********************| 52723 |********************|
524288 -> 1048575 : 44559 |*************** | 39089 |************** |
1048576 -> 2097151 : 35373 |************ | 28610 |********** |
2097152 -> 4194303 : 4663 |* | 2167 | |
4194304 -> 8388607 : 23 | | 12 | |
8388608 -> 16777215 : 2 | | 2 | |
trace count: 211244, average latency: 610493 ns
sched count: 199813, sched latency: 503275 ns, cpu percent: 2264 %
sched total: 205337, sched each time: 517752 ns- do_command 的子函数平均时延。
Histogram - Child functions's Latency of [do_command]:
name : avg cnt call_line sched_time cpu_pct(%) distribution (total)
Protocol_classic::get_command : 502643 211244 sql_parse.cc:1378 495234 156.51 |********************|
asm_sysvec_call_function_single : 192055 7 - 189609 0.00 | |
dispatch_command : 107245 211347 sql_parse.cc:1438 8025 2096.98 |**** |
asm_sysvec_apic_timer_interrupt : 50563 35 - 43869 0.02 | |
__irqentry_text_start : 14598 195 - 3143 0.22 | |
vio_description : 338 211347 sql_parse.cc:1420 5 7.03 | |
my_net_set_read_timeout : 40 422591 sql_parse.cc:1434 1 1.64 | |
Diagnostics_area::reset_diagnostics_area : 23 211244 sql_parse.cc:1337 1 0.46 | |
Protocol_classic::get_output_packet : 10 422719 sql_parse.cc:1439 0 0.44 | |
Protocol_classic::get_net : 7 211244 sql_parse.cc:1345 0 0.15 | |- do_command 来自父函数 handle_connection 的时延分布。
Histogram - Latency of [do_command] called from [handle_connection]:
ns : cnt distribution sched distribution
1024 -> 2047 : 0 | | 6 | |
2048 -> 4095 : 0 | | 1 | |
4096 -> 8191 : 0 | | 6 | |
8192 -> 16383 : 0 | | 27 | |
16384 -> 32767 : 132 | | 150 | |
32768 -> 65535 : 8487 |*** | 16063 |****** |
65536 -> 131071 : 22384 |******* | 27139 |********** |
131072 -> 262143 : 39483 |************** | 33818 |************ |
262144 -> 524287 : 56138 |********************| 52723 |********************|
524288 -> 1048575 : 44559 |*************** | 39089 |************** |
1048576 -> 2097151 : 35373 |************ | 28610 |********** |
2097152 -> 4194303 : 4663 |* | 2167 | |
4194304 -> 8388607 : 23 | | 12 | |
8388608 -> 16777215 : 2 | | 2 | |
trace count: 211244, average latency: 610493 ns
sched count: 199813, sched latency: 503275 ns, cpu percent: 2264 %- do_command 来自 handle_connection 的子函数调用时延。
Histogram - Child functions's Latency of [do_command] called from [handle_connection]:
name : avg cnt call_line sched_time cpu_pct(%) distribution (total)
Protocol_classic::get_command : 502643 211244 sql_parse.cc:1378 495234 156.51 |********************|
asm_sysvec_call_function_single : 192055 7 - 189609 0.00 | |
dispatch_command : 107194 211244 sql_parse.cc:1438 8012 2095.16 |**** |
asm_sysvec_apic_timer_interrupt : 50563 35 - 43869 0.02 | |
__irqentry_text_start : 14598 195 - 3143 0.22 | |
vio_description : 338 211244 sql_parse.cc:1420 5 7.03 | |
my_net_set_read_timeout : 40 422488 sql_parse.cc:1434 1 1.64 | |
Diagnostics_area::reset_diagnostics_area : 23 211244 sql_parse.cc:1337 1 0.46 | |
Protocol_classic::get_output_packet : 10 422488 sql_parse.cc:1439 0 0.44 | |
Protocol_classic::get_net : 7 211244 sql_parse.cc:1345 0 0.15 | |- 通过 -c,可以看到 do_command 的代码块维度的时延。
Histogram - Child functions's Latency of [do_command]:
name : avg cnt call_line sched_time cpu_pct(%) distribution (total)
Protocol_classic::get_command : 502643 211244 sql_parse.cc:1378 495234 156.51 |********************|
asm_sysvec_call_function_single : 192055 7 - 189609 0.00 | |
dispatch_command : 107245 211347 sql_parse.cc:1438 8025 2096.98 |**** |
asm_sysvec_apic_timer_interrupt : 50563 35 - 43869 0.02 | |
__irqentry_text_start : 14598 195 - 3143 0.22 | |
vio_description : 338 211347 sql_parse.cc:1420 5 7.03 | |
my_net_set_read_timeout : 40 422591 sql_parse.cc:1434 1 1.64 | |
Diagnostics_area::reset_diagnostics_area : 23 211244 sql_parse.cc:1337 1 0.46 | |
Protocol_classic::get_output_packet : 10 422719 sql_parse.cc:1439 0 0.44 | |
*code block: 454-463 : 18 10518 sql_parse.cc:1434-sql_parse.cc:1434 0 0.18 | |
*code block: 162-182 : 17 10518 sql_parse.cc:1381-sql_parse.cc:1381 0 0.17 | |
*code block: 729-739 : 17 10517 sql_parse.cc:1439-sql_parse.cc:1439 0 0.18 | |
*code block: 441-448 : 4 10518 sql_string.h:387-sql_string.h:387 0 0.05 | |
*code block: 88-118 : 3 10518 sql_class.h:4943-sql_parse.cc:1347 0 0.03 | |
*code block: 712-724 : 1 10518 sql_parse.cc:1438-sql_parse.cc:1438 0 0.02 | |
*code block: 429-436 : 1 10518 sql_parse.cc:1431-sql_parse.cc:1431 0 0.01 | |
*code block: 608-621 : 1 10518 sql_string.h:388-sql_string.h:388 0 0.01 | |
*code block: 468-471 : 0 10518 sql_parse.cc:1436-sql_parse.cc:1436 0 0.00 | |
*code block: 76-83 : 0 10518 sql_parse.cc:1345-sql_parse.cc:1345 0 0.01 | |
...- trace 10s 的全量数据。得到 perf.data。
$ ./func_latency -d 10 -p 60416 -t --history=1
[ perf record: Woken up 0 times to write data ]
[ perf record: Captured and wrote 2863.829 MB perf.data ]- 使用历史数据查看 trx_commit 线程 id 为 123173 的时间分布,每 100 次 latency 取平均,也可以通过 --ti=start,min,max 缩小时间范围查看,或者 --li=min,max 缩小查看具体时延范围的 profile。图中在 5 s 的时候做过一次落盘切换,commit 时间发生改变。
$./func_latency -b bin/mysqld -f "trx_commit" -d 10 -t -s -l --tu=100 -T 123173 --history=2- 使用 latency 火焰图查看,每个 function 包括对应的时延平均值。
./func_latency --flamegraph="latency" -d 1 -p 60416 -t -s
