内核trace框架

Linux内核trace

​ 从广义上讲,linux中的跟踪系统由三层组成:前端、跟踪框架和事件源

image-20220106171636753

​ 事件源是跟踪数据的来源,跟踪框架运行在负责数据收集、计数的内核中,如果它支持内核编程跟踪程序(例如:eBPF),它还以有效的方式执行聚合、汇总和统计。跟踪前端工具提供了与跟踪框架通信的用户界面,使用后处理跟踪器(如果有)进行统计、汇总和聚合采样,并向最终用户进行结果可视化。

术语

  • 性能分析:性能分析的目的是为了获取跟踪事件的样本
  • 跟踪:跟踪记录所有的trace事件
  • 探针:动态或者静态代码中的一种插装点,用于生成可执行聚合/摘要/统计程序的事件。
  • 静态追踪:代码中硬编码的检测点。因为这些是固定的,所以它们可以作为稳定API的一部分提供,并记录在案。
  • 动态追踪:也称为动态检测,这是一种通过实时修改指令文本来检测任何软件事件(如函数调用和返回)的技术。目标软件通常不需要特殊功能来支持动态跟踪,前端可以读取的符号表除外。由于这些工具都是软件文本,因此不认为它是一个稳定的API,并且可能不会在其源代码之外进行记录。

Linux追踪发展史

image-20220106174731151

Linux追踪技术栈

image-20220106174847859

trace事件源

LTT

​ LTT是一个用于跟踪系统详细运行状态和流程的工具,它可以跟踪记录系统中的特定事件。这些事件包括:

  • 系统调用的进入和退出
  • 陷阱/中断(Trap / Irq)的进入和退出
  • 进程调度事件
  • 内核定时器
  • 进程管理相关事件:创建 ,唤醒,信号处理等等
  • 文件系统相关事件:Open / Read / Write / Seek / Ioctl 等等
  • 内存管理相关事件:内存分配/释放等
  • 其他事件:IPC / Socket/ 网络 等等

Kprobes

​ KProbes是一种针对Linux内核的调试机制,它还可以用于监视生产系统中的事件。你可以使用它来排除性能瓶颈、记录特定事件、跟踪问题等。KProbes是由IBM开发的,作为另一种称为DProbes的高级跟踪工具的底层机制。DProbes添加了许多特性,包括它自己用于编写探针处理程序的脚本语言。然而,只有KProbes被合并到标准内核中。Kprobes的介绍文档可以看An introduction to KProbes

​ 内核探测是放置在某个指令地址上的一组处理程序。到目前为止,内核中有三种类型的探针,称为“KProbes”和“JProbes”和”kretprobes (也叫返回探测点)”。KProbe由预处理程序和后处理程序定义。当KProbe安装在特定的指令上并执行该指令时,预处理程序就在被探测的指令执行之前执行。

​ 下边的图描述了KProbes的体系结构。在x86上,KProbes利用异常处理机制,修改标准断点、调试和其他一些异常处理程序,以满足自己的需要。大多数探测的处理是在断点和调试异常处理程序的上下文中完成的,它们组成了KProbes体系结构依赖层。KProbes体系结构独立层是KProbes管理器,用于注册和注销探针。用户在内核模块中提供探测处理程序,这些处理程序通过KProbes管理器注册探测。

​ 使用kprobes技术需要编写kprobes处理程序,并将内核模块安装到内核中运行,注册探针,在探针注册完成之后活动地址会包含一个断点指令(int3),一旦执行到达探测地址,就会执行int3指令,导致控件到达断点处理程序do_int3()inarch/i386/kernel/traps。c、 do_int3()通过中断门调用,因此当控制到达该门时,中断被禁用。此处理程序通知SKProbe发生断点;KProbes检查断点是否通过KProbes的注册函数设置。如果在命中探测器的地址不存在探测器,它只返回0。否则将调用已注册的probefunction。

[KProbes architecture]

uprobes

​ 用户级动态跟踪,允许动态的追踪用户程序,在任何指令中都可以注入断点,性能优化大师Brendan Gregg比较期待的一个功能Linux uprobe: User-Level Dynamic Tracing,它支持追踪用户级功能,既可以追踪用户程序的系统调用又可以跟踪库函数。

USDT/SDT

用户级的静态跟踪工具。GDB在代码中支持SDT探测。SDT代表静态定义的跟踪,探测器被设计成具有微小的运行时代码和数据足迹,以及节点动态重新定位。目前,ELF兼容系统支持两种探测器:

  • SystemTap
  • DTrac

​ DTrace提供了一个C语言的 API,用于通过DTrace_PROBE宏定义与USDT探针功能相同的的DTrace。Linux跟踪生态系统开发人员决定与该API保持源代码兼容,因此任何DTRACE_探测宏都会自动转换为USDT探测,通过SystemTap的API和DTRACE_PROBE()宏集合,可以将探测添加到自己的代码中。USDT探测器可以减小探测程序的开支。

trace框架

ftrace

image-20220107094019802

​ ftrace是最常见的trace工具,内置与内核中,依托于内核的debugfs文件系统,debugfs是Linux内核中一个特殊的文件系统,从2.6.10-rc3.[1]版本开始它是由Greg Kroah-Hartman写的。debugfs是一个简单易用的基于ram的文件系统,专为调试目的而设计。它是内核开发人员向用户空间提供信息的一种简单方法与/proc和sysfs不同,/proc只表示关于进程的信息,sysfs有严格的“一个文件一个值”的规则,而debugfs根本没有规则。开发者可以在那里放置任何他们想要的信息。

​ ftrace可以使用kprobe源和uprobes源,、并提供事件跟踪的功能并提供一些过滤器和可选参数;提供内核事件计数和计时;和功能流步行。它是通过/sys控制的,面向一个root用户,需要写ftrace.txt脚本来安装事件或函数,这有时候会很便捷,而且有前端 — trace-cmd和perf-tools等工具,ftrace不可编程,因此如果需要保存和获取时间戳、计算延迟,保存为立方图,需要付出一定代价将数据转储到用户态进行后续所处理,可以用eBPF进行编程。lwn上有一篇文章介绍了ftace的用法Debugging the kernel using Ftrace

​ ftrace最强大的功能是函数追踪,它使用gcc的-pg选项让内核每个没有标记为no trace的函数调用一个特殊函数“mcount()”,该函数必须在程序集中实现,因为调用不遵循正常的C ABI。当编译选项CONFIG_DYNAMIC_FTRACE被开启,该调用在引导时转换为NOP,以保证系统以100%性能运行,调用mcount()的成本会被记录。由于NOP对于追踪几乎没有作用,保存该列表是为了在保存函数调用过程中的成本

perf_event

image-20220107103526819

​ perf_events是Linux用户的主要跟踪工具,其源代码位于Linux内核中,通常通过Linux工具公共包添加。又名“perf”,位于前端之后,通常用于跟踪并转储到文件(perf.data),该文件相对高效(动态缓冲),然后在以后进行后期处理。大部分ftrace能够做到的它也能做到。它不能执行函数功能流程图,并且有点不稳定(为了更好的安全性/错误检查)。但它可以进行分析(采样)、CPU性能计数器、用户级堆栈转换,并且可以使用DebugInfo进行局部变量的行跟踪。它还支持多个并发用户。与ftrace一样,它还不是内核可编程的,直到可能提出了eBPF支持(patchesh)。如果我建议人们学习一种跟踪工具,那就是perf,因为它可以解决很多问题,而且相对安全。perf Examples

image-20220107133847540

eBPF

​ extended Berkeley Packet Filter是一个内核内虚拟机,可以高效地(JIT)运行事件上的程序。对BPF(Berkeley数据包过滤器)的增强,该过滤器已添加到Linux 4中。x系列内核,允许BPF做的不仅仅是过滤数据包。这些增强功能允许在Linux动态跟踪、静态跟踪和分析事件上执行自定义分析程序。它可能最终为ftrace和perf_事件提供内核内编程,并增强其他跟踪程序。eBPF工作流程如下

  • 写eBPF程序(通常使用C来写)
  • 按照程序内核将探针加到探测源中
  • 程序将数据写到eBPF图或ftrace/perf的缓冲区
  • 读取数据

image-20220107135419135

为了便于追踪BPF为现有的追踪框架提供了可编程的能力(kprobes、uprobes、tracepoints)

Brendan Gregg’s Blog - Linux Extended BPF (eBPF)
image-20220107140603309

​ 比较原始的perf_事件评测和基于eBPF的评测,eBPF提供的沙盒虚拟机允许我们实现内核内可编程跟踪器,我们不需要将事件数据转储到磁盘进行离线分析,因此减少了内核和用户空间之间的数据转换。

image-20220107140901881

SystemTap

​ SystemTap是最强大的追踪工具。它可以做任何事情:评测、跟踪点、kprobe、uprobes(来自SystemTap)、USDT、内核编程等。它将程序编译成内核模块并加载它们——这是一种安全的方法。

systemtap支持:tracepoints、kprobes、uprobes、USDT

Julia Evans的博客LInux tracing system &how they fit together讲了一个例子

  • 决定一个要追踪的kprobe
  • 写systemtap 程序并编译成一个内核模块
  • 安装内核模触发kprobes内核模块的代码(register_kprobe)
  • 内核模块将输出打印到用户空间(使用relayfs或其他方法)

DTrace

image-20220107142308355

LTTng

LTTng优化了事件收集,优于其他跟踪程序,还支持多种事件类型,包括USDT。它是从树上长出来的。它的核心非常简单:通过一组固定的小指令将事件写入跟踪缓冲区。这有助于使其安全快速。缺点是在内核编程中没有简单的方法。

Ktap

ktap是一个非常有前途的跟踪工具,它使用内核中的lua虚拟机进行处理,在没有debuginfo和嵌入式设备的情况下运行良好。它使它进入了登台阶段,在一瞬间,它似乎将赢得Linux上的跟踪竞赛。EBPF开始了内核集成,ktap集成被推迟,直到它可以使用EBPF而不是自己的VM

dtrace4linux

dtrace4linux主要是一个人的兼职工作(Paul Fox)来移植Sun DTrace toLinux。这是令人印象深刻的,一些提供者可以工作,但它在某些方面还不完整,而且更像是一个实验性的工具(不安全)

trace前端

ftrace

ftrace使用虚拟文件sysatem-/sys/kernel/debug/tracing作为用户界面

perf(for perf_event only)

perf是Linux中存在的一个性能监视函数。也称为perf事件。还开发了名为perf的用户空间工具,如果您简单地说perf,您可能会指向这个工具。为了让人有点困惑,我们将在这里编写perf_事件作为内核的函数。

trace-cmd (for ftrace only)

​ 它是ftrace的前端,您可以使用它来收集和显示ftrace数据。有关更多详细信息,请参阅优秀文章ftrace: trace your kernel functions!

perf-tools(ftrace和perf_event的包装器)

image-20220107142754769

框架工具介绍
ftraceiosnoop跟踪磁盘I/O,详细信息包括延迟
iolatency将磁盘I/O延迟汇总为柱状图
execsnoop使用命令行参数详细信息跟踪进程exec()
opensnooptrace open()显示文件名的系统调用
killsnooptrace kill()调用显示进程信号的细节
fs/cachestatTLB缓存命中率细节
net/tcpretrans显示TCP重新传输,包括地址和其他详细信息
system/tpoint跟踪给定的跟踪点
kernel/funccount计数内核函数调用,将字符串与通配符匹配
kernel/functrace计数内核函数调用,将字符串与通配符匹配
kernel/funcslower跟踪速度低于阈值的内核函数
kernel/funcgraph跟踪内核函数调用的图形,显示子函数和时间
kernel/kprobe使用变量动态跟踪内核函数调用或其返回
user/uprobe动态追踪用户态函数调用或者返回
*user/usdt这只是一个正在使用的PoC示例脚本在第条中,未包含在工具包中,usdt(ftrace)
tools/reset-ftrace重置ftrace状态
perf_eventsmisc/perf-stat-hist跟踪点变量聚合的威力
syscount统计系统调用
disk/bitesize磁盘I/O大小的直方图摘要

bcc(基于eBPF)

bcc是一个用于创建高效内核跟踪和操作程序的工具包,它包含了一些有用的工具和示例。它使用扩展的BPF(BerkeleyPacket过滤器),正式名称为eBPF,eBPF首次添加在LInux3.15的一个新特性。bcc使用的很多东西都需要Linux 4.1及以上版本。bcc使BPF编程更易于编写,内核插装使用C(并包括围绕LLVM的C包装器),前端使用Python和lua。它适用于许多任务,包括性能分析和网络流量控制。

image-20220107145141529

image-20220107145347711

USDT

USDT实现的直接方法是基于Uupbe方法。有关解决方案讨论,请参见USDT probes

​ 在今天(太平洋标准时间每两周上午11点)的iovisor电话会议上,我们简要讨论了至少有3种进行USDT探测的方法。这种方式,使用uprobes,是最明显和最直接的解决方案,我们应该继续这样做。但稍后(很久以后),我们可能会研究其他方法,包括ld_PRELOAD,以便跟踪可以是用户模式到用户模式,从而减少开销。这些其他方法也应该大大减少memleak的开销。

events

bcc Reference Guide

bcc支持大部分事件源

  • kprobes
  • kretprobes
  • Tracepoints
  • uprobes
  • uretprobes
  • USDT probes
  • Raw Tracepoints
  • system call tracepoints
USDT probes

[USDT probes]https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#6-usdt-probes

​ 这些是用户静态定义跟踪(USDT)探测,可以放置在一些应用程序或库中,以提供与跟踪点相当的用户级别。为USDT支持方法提供的主要BPF方法是enable_probe()。通过在C中声明一个普通函数,然后通过USDT将其关联为Python中的aUSDT探测来检测USDTProbe。启用探测()。

参数可以通过以下方式读取:bpf_usdt_readarg(index, ctx, &addr)

bpftrace

bpftrace

BPFtrace是一种高级跟踪语言,用于最近的Linux内核(4.x)中提供的Linux增强Berkeley数据包过滤器(eBPF)。BPFtrace使用LLVM作为后端,将脚本编译成BPF字节码,并利用BCC与LinuxBPF系统交互,以及现有的Linux跟踪功能:内核动态跟踪(kprobes)、用户级动态跟踪(UPUBES)和跟踪点。BPFTrace语言的灵感来自awk和C,以及DTrace和SystemTap等前身跟踪程序。

image-20220107150159393

内核态探针

bpftrace支持的参数可以参照github
kprobe - 内核函数开始
kretprobe - 内核函数回调
uprobe - 用户态程序开始
uretprobe - 用户态程序返回
tracepoint -内核静态追踪点
usdt - 用户态静态跟踪点
profile - 定时采样
interval - 定时采样输出
software - 内核软件事件
hardware - 处理器级事件

用户态探针

Syntax:
usdt:binary_path:probe_name
usdt:binary_path:[probe_namespace]:probe_name
usdt:library_path:probe_name
usdt:library_path:[probe_namespace]:probe_name > usdt: Static Tracing, User-Level
Arguments
例如:
bpftrace -e ‘usdt:/root/tick:loop { printf("%s: %d\n", str(arg0), arg1); }’

image-20220107151103250

USDT

image-20220107151200600

  • 在编写时,使用宏DTRACE_PROBE()在适当的源代码位置删除USDT跟踪点
  • 在编译过程中,带有USDT跟踪点的源代码将被翻译成nop指令,同时,USDT元数据将存储在ELF的.note.stapstd部分,
  • 当注册探针时,USDT工具(通常基于uprobe的引擎实现)会读取ELF程序.note.stapstd部分和将指针从空指令跳转到断点(x86上的int3)。这样,无论何时控制器到达标记时,调用int3的中断处理程序,然后依次调用在内核中调用uprobe和附加的eBPF程序来处理事件。如果USDT探测器与信号量相关,前端需要在/proc/$PID/mem中增加信号量的位置来确保探针生效。
  • 撤销探针注册后,USDT会将指令从断点插回nop,不再生成任何事件,同时将型号量的位置与当前探针分离。
先决条件

Kylin—DesktopOS

kylin@local:~$ uname -a
Linux local 5.4.18-35-generic #21-KYLINOS SMP Tue Jul 20 13:33:58 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
kylin@local:~$ lsb_release -a
No LSB modules are available.
Distributor ID:	Kylin
Description:	Kylin V10 SP1
Release:	v10
Codename:	kylin
kylin@local:~$ sudo apt-get install systemtap-sdt-dev

从systemtap-sdt-dev获取

  • sys/sdt.h
  • dtrace命令包装器(仅在信号量时会用到)

这两个都是为了帮助我们生成ELF文件,也就是说,注册探针时,在uprobe模块可以在nop指令的位置插入断点,当注册探针的时候,elf的note部分位置会列出探针的位置

练习代码

# build.sh
#!/bin/bash

dtrace -G -s tick-dtrace.d -o tick-dtrace.o
dtrace -h -s tick-dtrace.d -o tick-dtrace.h
gcc -c tick-main.c
gcc -o tick tick-main.o tick-dtrace.o

readelf -n ./tick
#tick-dtrace.d 
provider tick {
        probe loop1(int);
        probe loop2(int);
}

#pragma D attributes Evolving/Evolving/ISA provider node provider
#pragma D attributes Private/Private/Unknown provider node module
#pragma D attributes Private/Private/Unknown provider node function
#pragma D attributes Private/Private/ISA provider node name
#pragma D attributes Evolving/Evolving/ISA provider node args

#tick-main.c

#include <stdio.h>
#include <unistd.h>
#include "tick-dtrace.h"

int
main(int argc, char *argv[])
{
        int i;

        while(1) {
                i++;
                DTRACE_PROBE1(tick, loop1, i);
                if (TICK_LOOP2_ENABLED()) {
                        DTRACE_PROBE1(tick, loop2, i);
                }
                printf("tick: %d\n", i);
                sleep(5);
        }

        return (0);
}

W/O信号量追踪

hello-usdt.c

#include "sys/sdt.h"
int main() {
DTRACE_PROBE("hello_usdt", "enter");
int reval = 0;
DTRACE_PROBE1("hello_usdt", "exit", reval);
}
kylin@local:~/桌面/trace/bpftrace$ readelf -n hello-usdt

Displaying notes found in: .note.gnu.property
  所有者            Data size 	Description
  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
      Properties: x86 feature: IBT, SHSTK

Displaying notes found in: .note.gnu.build-id
  所有者            Data size 	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 549f341e2a401c1d244c565d4a88ea2405776b2b

Displaying notes found in: .note.ABI-tag
  所有者            Data size 	Description
  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 3.2.0

Displaying notes found in: .note.stapsdt
  所有者            Data size 	Description
  stapsdt              0x0000002e	NT_STAPSDT (SystemTap probe descriptors)
    Provider: "hello_usdt"
    Name: "enter"
    Location: 0x0000000000001131, Base: 0x0000000000002004, Semaphore: 0x0000000000000000
    Arguments: 
  stapsdt              0x00000038	NT_STAPSDT (SystemTap probe descriptors)
    Provider: "hello_usdt"
    Name: "exit"
    Location: 0x0000000000001139, Base: 0x0000000000002004, Semaphore: 0x0000000000000000
    Arguments: -4@-4(%rbp)

USDT可以使用一个信号量来实现可编程的,因为Dtrace的一个特性就是能够通知目标程序正在跟踪特定事件,目标程序能够选择一些花费更更多的方法,通常是使用USDT探针获取或者将参数格式化。

tp_provider.d

provider hello_semaphore_usdt {
probe enter();
probe exit(int exit_code);
}
kylin@local:~/桌面/trace/bpftrace$ dtrace -h -s tp_provider.d -o tp_provider.h
kylin@local:~/桌面/trace/bpftrace$ dtrace -G -s tp_provider.d -o tp_provider.o

tp_provider.h

/* Generated by the Systemtap dtrace wrapper */
#define _SDT_HAS_SEMAPHORES 1
#define STAP_HAS_SEMAPHORES 1 /* deprecated */
#include <sys/sdt.h>
/* HELLO_SEMAPHORE_USDT_ENTER ( ) */
#if defined STAP_SDT_V1
#define HELLO_SEMAPHORE_USDT_ENTER_ENABLED() __builtin_expect (enter_semaphore, 0)
#define hello_semaphore_usdt_enter_semaphore enter_semaphore
#else
#define HELLO_SEMAPHORE_USDT_ENTER_ENABLED() __builtin_expect (hello_semaphore_usdt_enter_semaphore, 0)
#endif
__extension__ extern unsigned short hello_semaphore_usdt_enter_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes")));
#define HELLO_SEMAPHORE_USDT_ENTER() \
DTRACE_PROBE (hello_semaphore_usdt, enter)

/* HELLO_SEMAPHORE_USDT_EXIT ( int exit_code ) */
#if defined STAP_SDT_V1
#define HELLO_SEMAPHORE_USDT_EXIT_ENABLED() __builtin_expect (exit_semaphore, 0)
#define hello_semaphore_usdt_exit_semaphore exit_semaphore
#else
#define HELLO_SEMAPHORE_USDT_EXIT_ENABLED() __builtin_expect (hello_semaphore_usdt_exit_semaphore, 0)
#endif
__extension__ extern unsigned short hello_semaphore_usdt_exit_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes")));
#define HELLO_SEMAPHORE_USDT_EXIT(arg1) \
DTRACE_PROBE1 (hello_semaphore_usdt, exit, arg1)

hello-semaphore-usdt.c

#include "tp_provider.h"
int main() {
if (HELLO_SEMAPHORE_USDT_ENTER_ENABLED()) {
HELLO_SEMAPHORE_USDT_ENTER();
}
int reval = 0;
if (HELLO_SEMAPHORE_USDT_EXIT_ENABLED()) {
HELLO_SEMAPHORE_USDT_EXIT(reval);
}
}
kylin@local:~/桌面/trace/bpftrace$ gcc -c ./hello-semaphore-usdt.ckylin@local:~/桌面/trace/bpftrace$ gcc -o ./hello-semaphore-usdt ./hello-semaphore-usdt.o ./tp_provider.okylin@local:~/桌面/trace/bpftrace$ readelf -n hello-semaphore-usdtDisplaying notes found in: .note.gnu.property  所有者            Data size 	Description  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0      Properties: x86 feature: IBT, SHSTKDisplaying notes found in: .note.gnu.build-id  所有者            Data size 	Description  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)    Build ID: 471956ace242ec8ff1493afb3db33361dbd6d9eeDisplaying notes found in: .note.ABI-tag  所有者            Data size 	Description  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)    OS: Linux, ABI: 3.2.0Displaying notes found in: .note.stapsdt  所有者            Data size 	Description  stapsdt              0x00000034	NT_STAPSDT (SystemTap probe descriptors)    Provider: hello_semaphore_usdt    Name: enter    Location: 0x0000000000001140, Base: 0x0000000000002004, Semaphore: 0x0000000000004010    Arguments:   stapsdt              0x0000003e	NT_STAPSDT (SystemTap probe descriptors)    Provider: hello_semaphore_usdt    Name: exit    Location: 0x0000000000001157, Base: 0x0000000000002004, Semaphore: 0x0000000000004012    Arguments: -4@-4(%rbp)

基于ftrace的USDT探针实例(ftrace+uprobe)

​ USDT是一种放置在二进制文件或者库里面的静态追踪技术,这个探针通过替换编译器在ELF文件的notes部分生成的的nop指令来实现,追踪应用可以检查探针的位置

  1. 在目标程序的宏定义上放置nop指令,并在elf文件中记录
  2. 在追踪程序的elf中读取uprobes的位置

在运行uprobe之前需要先设计程序

kylin@local:~/桌面/trace/ticks$ gdb ./tick GNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.Type "show copying" and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:    <http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from ./tick...(gdb) didirectory    disable      disassemble  disconnect   display      (gdb) disdisable      disassemble  disconnect   display      (gdb) disas mainDump of assembler code for function main:   0x0000000000001169 <+0>:	endbr64    0x000000000000116d <+4>:	push   %rbp   0x000000000000116e <+5>:	mov    %rsp,%rbp   0x0000000000001171 <+8>:	sub    $0x20,%rsp   0x0000000000001175 <+12>:	mov    %edi,-0x14(%rbp)   0x0000000000001178 <+15>:	mov    %rsi,-0x20(%rbp)   0x000000000000117c <+19>:	addl   $0x1,-0x4(%rbp)   0x0000000000001180 <+23>:	nop   0x0000000000001181 <+24>:	movzwl 0x2e8a(%rip),%eax        # 0x4012 <tick_loop2_semaphore>   0x0000000000001188 <+31>:	movzwl %ax,%eax   0x000000000000118b <+34>:	test   %rax,%rax   0x000000000000118e <+37>:	je     0x1191 <main+40>   0x0000000000001190 <+39>:	nop   0x0000000000001191 <+40>:	mov    -0x4(%rbp),%eax   0x0000000000001194 <+43>:	mov    %eax,%esi   0x0000000000001196 <+45>:	lea    0xe67(%rip),%rdi        # 0x2004   0x000000000000119d <+52>:	mov    $0x0,%eax   0x00000000000011a2 <+57>:	callq  0x1060 <printf@plt>   0x00000000000011a7 <+62>:	mov    $0x5,%edi   0x00000000000011ac <+67>:	callq  0x1070 <sleep@plt>   0x00000000000011b1 <+72>:	jmp    0x117c <main+19>End of assembler dump.
使用objdump或者从/proc/pid/maps获取load地址
kylin@local:~/桌面/trace/ticks$ objdump -x ./tick |more./tick:     文件格式 elf64-x86-64./tick体系结构:i386:x86-64,标志 0x00000150:HAS_SYMS, DYNAMIC, D_PAGED起始地址 0x0000000000001080程序头:    PHDR off    0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3         filesz 0x00000000000002d8 memsz 0x00000000000002d8 flags r--  INTERP off    0x0000000000000318 vaddr 0x0000000000000318 paddr 0x0000000000000318 align 2**0         filesz 0x000000000000001c memsz 0x000000000000001c flags r--    LOAD off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12         filesz 0x0000000000000638 memsz 0x0000000000000638 flags r--    LOAD off    0x0000000000001000 vaddr 0x0000000000001000 paddr 0x0000000000001000 align 2**12         filesz 0x0000000000000245 memsz 0x0000000000000245 flags r-x    LOAD off    0x0000000000002000 vaddr 0x0000000000002000 paddr 0x0000000000002000 align 2**12         filesz 0x0000000000000188 memsz 0x0000000000000188 flags r--    LOAD off    0x0000000000002db0 vaddr 0x0000000000003db0 paddr 0x0000000000003db0 align 2**12         filesz 0x0000000000000264 memsz 0x0000000000000268 flags rw- DYNAMIC off    0x0000000000002dc0 vaddr 0x0000000000003dc0 paddr 0x0000000000003dc0 align 2**3         filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags rw-    NOTE off    0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3         filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--    NOTE off    0x0000000000000358 vaddr 0x0000000000000358 paddr 0x0000000000000358 align 2**2         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--0x6474e553 off    0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3         filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--EH_FRAME off    0x0000000000002010 vaddr 0x0000000000002010 paddr 0x0000000000002010 align 2**2         filesz 0x000000000000004c memsz 0x000000000000004c flags r--   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-   RELRO off    0x0000000000002db0 vaddr 0x0000000000003db0 paddr 0x0000000000003db0 align 2**0

如果程序正在运行时,可在/proc/PID/maps里获取uprobetrace.txt文档

获取USDT断点地址

trace loop1
1、首先运行./tick程序kylin@local:~/桌面/trace/ticks$ ./tick tick: 1tick: 2tick: 3tick: 42、获取tick的pidroot@local:/home/kylin/桌面/trace/ticks# ps -ef|grep ./tickkylin      58087   57183  0 17:22 pts/0    00:00:00 ./tick3、打开另一个终端使用gdb查看main函数的汇编代码root@local:/home/kylin/桌面/trace/ticks# gdb -p 58087GNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.Type "show copying" and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:    <http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word".Attaching to process 58087Reading symbols from /home/kylin/桌面/trace/ticks/tick...Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.31.so...Reading symbols from /lib64/ld-linux-x86-64.so.2...(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)0x00007f6f2c6f4334 in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0,     flags=flags@entry=0, req=req@entry=0x7ffe1bc60330, rem=rem@entry=0x7ffe1bc60330)    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:7878	../sysdeps/unix/sysv/linux/clock_nanosleep.c: 没有那个文件或目录.(gdb) disas mainDump of assembler code for function main:   0x000055bb29563169 <+0>:	endbr64    0x000055bb2956316d <+4>:	push   %rbp   0x000055bb2956316e <+5>:	mov    %rsp,%rbp   0x000055bb29563171 <+8>:	sub    $0x20,%rsp   0x000055bb29563175 <+12>:	mov    %edi,-0x14(%rbp)   0x000055bb29563178 <+15>:	mov    %rsi,-0x20(%rbp)   0x000055bb2956317c <+19>:	addl   $0x1,-0x4(%rbp)   0x000055bb29563180 <+23>:	nop   0x000055bb29563181 <+24>:	movzwl 0x2e8a(%rip),%eax        # 0x55bb29566012 <tick_loop2_semaphore>   0x000055bb29563188 <+31>:	movzwl %ax,%eax   0x000055bb2956318b <+34>:	test   %rax,%rax   0x000055bb2956318e <+37>:	je     0x55bb29563191 <main+40>   0x000055bb29563190 <+39>:	nop   0x000055bb29563191 <+40>:	mov    -0x4(%rbp),%eax   0x000055bb29563194 <+43>:	mov    %eax,%esi   0x000055bb29563196 <+45>:	lea    0xe67(%rip),%rdi        # 0x55bb29564004   0x000055bb2956319d <+52>:	mov    $0x0,%eax   0x000055bb295631a2 <+57>:	callq  0x55bb29563060 <printf@plt>   0x000055bb295631a7 <+62>:	mov    $0x5,%edi   0x000055bb295631ac <+67>:	callq  0x55bb29563070 <sleep@plt>   0x000055bb295631b1 <+72>:	jmp    0x55bb2956317c <main+19>End of assembler dump.(gdb) qA debugging session is active.	Inferior 1 [process 58087] will be detached.Quit anyway? (y or n) yDetaching from program: /home/kylin/桌面/trace/ticks/tick, process 58087[Inferior 1 (process 58087) detached]4、获取loop1的地址(Location: 0x0000000000001180)root@local:/home/kylin/桌面/trace/ticks# readelf -n ./tickDisplaying notes found in: .note.gnu.property  所有者            Data size 	Description  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0      Properties: x86 feature: IBT, SHSTKDisplaying notes found in: .note.gnu.build-id  所有者            Data size 	Description  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)    Build ID: 7c7bcacd2172936c6fd534c6471fad4a106ff060Displaying notes found in: .note.ABI-tag  所有者            Data size 	Description  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)    OS: Linux, ABI: 3.2.0Displaying notes found in: .note.stapsdt  所有者            Data size 	Description  stapsdt              0x0000002f	NT_STAPSDT (SystemTap probe descriptors)    Provider: tick    Name: loop1    Location: 0x0000000000001180, Base: 0x000000000000200e, Semaphore: 0x0000000000004010    Arguments: -4@-4(%rbp)  stapsdt              0x0000002f	NT_STAPSDT (SystemTap probe descriptors)    Provider: tick    Name: loop2    Location: 0x0000000000001190, Base: 0x000000000000200e, Semaphore: 0x0000000000004012    Arguments: -4@-4(%rbp)5、加载loop1的uprobe探针(tick二进制需要使用全路径)root@local:/home/kylin/桌面/trace/ticks# ./uprobe  "p:/home/kylin/桌面/trace/ticks/tick:0x1180"Tracing uprobe tick_0x1180 (p:tick_0x1180 /home/kylin/桌面/trace/ticks/tick:0x1180). Ctrl-C to end./sys/kernel/debug/tracing           <...>-58087 [006] d... 32059.107006: tick_0x1180: (0x55bb29563180)           <...>-58087 [006] d... 32064.107029: tick_0x1180: (0x55bb29563180)           <...>-58087 [006] d... 32069.107093: tick_0x1180: (0x55bb29563180)           <...>-58087 [006] d... 32074.107158: tick_0x1180: (0x55bb29563180)           <...>-58087 [006] d... 32079.107214: tick_0x1180: (0x55bb29563180)           <...>-58087 [006] d... 32084.107309: tick_0x1180: (0x55bb29563180)                      6、打开另一个终端用gdb查看tick现在的汇编代码root@local:/home/kylin/桌面/trace/ticks# gdb -p 58087GNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.Type "show copying" and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:    <http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word".Attaching to process 58087Reading symbols from /home/kylin/桌面/trace/ticks/tick...Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.31.so...Reading symbols from /lib64/ld-linux-x86-64.so.2...(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)0x00007f6f2c6f4334 in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0,     flags=flags@entry=0, req=req@entry=0x7ffe1bc60330, rem=rem@entry=0x7ffe1bc60330)    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:7878	../sysdeps/unix/sysv/linux/clock_nanosleep.c: 没有那个文件或目录.(gdb) disas mainDump of assembler code for function main:   0x000055bb29563169 <+0>:	endbr64    0x000055bb2956316d <+4>:	push   %rbp   0x000055bb2956316e <+5>:	mov    %rsp,%rbp   0x000055bb29563171 <+8>:	sub    $0x20,%rsp   0x000055bb29563175 <+12>:	mov    %edi,-0x14(%rbp)   0x000055bb29563178 <+15>:	mov    %rsi,-0x20(%rbp)   0x000055bb2956317c <+19>:	addl   $0x1,-0x4(%rbp)   0x000055bb29563180 <+23>:	int3      0x000055bb29563181 <+24>:	movzwl 0x2e8a(%rip),%eax        # 0x55bb29566012 <tick_loop2_semaphore>   0x000055bb29563188 <+31>:	movzwl %ax,%eax   0x000055bb2956318b <+34>:	test   %rax,%rax   0x000055bb2956318e <+37>:	je     0x55bb29563191 <main+40>   0x000055bb29563190 <+39>:	nop   0x000055bb29563191 <+40>:	mov    -0x4(%rbp),%eax   0x000055bb29563194 <+43>:	mov    %eax,%esi   0x000055bb29563196 <+45>:	lea    0xe67(%rip),%rdi        # 0x55bb29564004   0x000055bb2956319d <+52>:	mov    $0x0,%eax   0x000055bb295631a2 <+57>:	callq  0x55bb29563060 <printf@plt>   0x000055bb295631a7 <+62>:	mov    $0x5,%edi   0x000055bb295631ac <+67>:	callq  0x55bb29563070 <sleep@plt>   0x000055bb295631b1 <+72>:	jmp    0x55bb2956317c <main+19>End of assembler dump.

对比程序的反汇编代码左图为加载了uprobe探针后的代码,可以发现在加载uprobe后原本的nop变成了int3

image-20220110173312435

事实上,使用bcc或者bpftrace前段工具来进行USDT探测会更好,使用ftrace作为例子是因为ftrace的步骤显示出来更完整,能够帮助我们更好的理解USDT的工作原理。如果对此感兴趣可以参照Brendan Gregg’s 的博客Hacking Linux USDT with FtraceLinux uprobe: User-Level Dynamic Tracing两篇文章。

基于bcc的USDT探针(eBPF+uprobe)

参照Brendan Gregg’s Blog - Linux bcc/BPF Node.js USDT Tracing

Node.js 编译了USDT探针,让性能分析和debug更加容易,bcc提供了一个nodejs_http_server.py分析的例子,演示如何编写bcc脚本来跟踪内置节点提供的USDT事件。js实现。

可编程USDT

​ USDT只实现了静态探针,它依赖于在运行时加载ELF notes部分的注释。libstapsdt利用了动态库的优势,创建了一个带有ELF node和链接的小库,并且在运行时加载。通过这种方式,大多数现有工具也可以正常运行。

libstapsdt

它通过USDT动态生成共享对象来工作

image-20220111140936205

下面介绍它是如何工作的

kylin@local:/usr/local/libstapsdt-master/libstapsdt-master$ ./demo PROVIDER_NAME PROBE_NAME

查看libstapsd的demo程序可以看到它在运行的时候在/tmp目录下生成了动态库

image-20220111143546291

readefl -n 查看动态库

image-20220111145317805

使用bcc工具查看USDT追踪点位置和格式

image-20220111145730942

libstapsdt是用C语言编写的,这使得它几乎可以移植到任何语言。大多数动态语言提供了一种包装C代码的方法。

例如node.js wrapper

const USDT = require("usdt");const provider = new USDT.USDTProvider("nodeProvider");const probe1 = provider.addProbe("firstProbe", "int", "char *");provider.enable();let countdown = 10;function waiter() {console.log("Firing probe...");if(countdown <= 0) {console.log("Disable provider");provider.disable();}probe1.fire(function() {console.log("Probe fired!");countdown = countdown - 1;return [countdown, "My little string"];});}setInterval(waiter, 750);

可以使用bcc工具进行追踪

image-20220111153022668

/proc/$(pgrep node)/maps

image-20220111153053368

image-20220111153112477

tplist

image-20220111153126575

结尾

本文介绍了以下与LInux tracing有关内容

  • LInux追踪基本概念
  • Linux跟踪堆栈,包括:事件源、跟踪框架和前端
  • USDT
  • 以编程的方式启动USDT

Brendan Gregg’s Blog提供了很多关于LInux性能追踪的方法和讨论,如果想要深入了解或学习关于linux追踪,这里将是最好的途径。

附录

ftrace

ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为,以便进行故障调试或性能分析。

最早 ftrace 是一个 function tracer,仅能够记录内核的函数调用流程。如今 ftrace 已经成为一个 framework,采用 plugin 的方式支持开发人员添加更多种类的 trace 功能。

Ftrace 由 RedHat 的 Steve Rostedt 负责维护。到 2.6.30 为止,已经支持的 tracer 包括:

Function tracerFunction graph tracer: 跟踪函数调用。

Schedule switch tracer: 跟踪进程调度情况。

Wakeup tracer:跟踪进程的调度延迟,即高优先级进程从进入 ready 状态到获得 CPU 的延迟时间。该 tracer 只针对实时进程。

Irqsoff tracer:当中断被禁止时,系统无法相应外部事件,比如键盘和鼠标,时钟也无法产生 tick 中断。这意味着系统响应延迟,irqsoff 这个 tracer 能够跟踪并记录内核中哪些函数禁止了中断,对于其中中断禁止时间最长的,irqsoff 将在 log 文件的第一行标示出来,从而使开发人员可以迅速定位造成响应延迟的罪魁祸首。

Preemptoff tracer:和前一个 tracer 类似,preemptoff tracer 跟踪并记录禁止内核抢占的函数,并清晰地显示出禁止抢占时间最长的内核函数。

Preemptirqsoff tracer: 同上,跟踪和记录禁止中断或者禁止抢占的内核函数,以及禁止时间最长的函数。

Branch tracer: 跟踪内核程序中的 likely/unlikely 分支预测命中率情况。 Branch tracer 能够记录这些分支语句有多少次预测成功。从而为优化程序提供线索。

Hardware branch tracer:利用处理器的分支跟踪能力,实现硬件级别的指令跳转记录。在 x86 上,主要利用了 BTS 这个特性。

Initcall tracer:记录系统在 boot 阶段所调用的 init call 。

Mmiotrace tracer:记录 memory map IO 的相关信息。

Power tracer:记录系统电源管理相关的信息。

Sysprof tracer:缺省情况下,sysprof tracer 每隔 1 msec 对内核进行一次采样,记录函数调用和堆栈信息。

Kernel memory tracer: 内存 tracer 主要用来跟踪 slab allocator 的分配情况。包括 kfree,kmem_cache_alloc 等 API 的调用情况,用户程序可以根据 tracer 收集到的信息分析内部碎片情况,找出内存分配最频繁的代码片断,等等。

Workqueue statistical tracer:这是一个 statistic tracer,统计系统中所有的 workqueue 的工作情况,比如有多少个 work 被插入 workqueue,多少个已经被执行等。开发人员可以以此来决定具体的 workqueue 实现,比如是使用 single threaded workqueue 还是 per cpu workqueue.

Event tracer: 跟踪系统事件,比如 timer,系统调用,中断等。

这里还没有列出所有的 tracer,ftrace 是目前非常活跃的开发领域,新的 tracer 将不断被加入内核。

使用传统的 ftrace 需要如下几个步骤:

  • 选择一种 tracer
  • 使能 ftrace
  • 执行需要 trace 的应用程序,比如需要跟踪 ls,就执行 ls
  • 关闭 ftrace
  • 查看 trace 文件

用户通过读写 debugfs 文件系统中的控制文件完成上述步骤,通常debugfs挂载在/sys/kernel/debug/tracing下。如果没有挂载 debugfs,首先要挂载她。命令如下:

# mkdir /debug  # mount -t debugfs nodev /debug

Ftrace的控制接口就是tracing下的目录

Ftrace和Uprobe

Linux uprobe: User-Level Dynamic Tracing

使能Ftrace
echo 1 > tracing_on

ftrace 的输出信息主要保存在 3 个文件中。

  • Trace,该文件保存 ftrace 的输出信息,其内容可以直接阅读。
  • latency_trace,保存与 trace 相同的信息,不过组织方式略有不同。主要为了用户能方便地分析系统中有关延迟的信息。
  • trace_pipe 是一个管道文件,主要为了方便应用程序读取 trace 内容。算是扩展接口吧。
添加探针函数
root@local:/sys/kernel/debug/tracing#  cat /proc/`pgrep zsh`/maps |grep /bin/zsh |grep r-xp  56281973f000-5628197d4000 r-xp 00017000 08:03 262358                     /usr/bin/zshroot@local:/sys/kernel/debug/tracing# objdump -T /bin/zsh | grep -w zfree0000000000067c60 g    DF .text	0000000000000009  Base        zfreeroot@local:/sys/kernel/debug/tracing# cd /sys/kernel/debug/tracing/root@local:/sys/kernel/debug/tracing# echo 'p:zfree_entry /bin/zsh:0x67c60 %ip %ax' > uprobe_events root@local:/sys/kernel/debug/tracing# echo 'r:zfree_exit /bin/zsh:0x67c60 %ip %ax' >> uprobe_events root@local:/sys/kernel/debug/tracing# echo 1 > events/uprobes/enable 
Ftrace输出
# tracer: nop## entries-in-buffer/entries-written: 2072/2072   #P:8##                              _-----=> irqs-off#                             / _----=> need-resched#                            | / _---=> hardirq/softirq#                            || / _--=> preempt-depth#                            ||| /     delay#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION#              | |       |   ||||       |         |   SecuClientAPP-11686 [000] .... 17325.724594: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)   SecuClientAPP-11686 [000] .... 17325.724644: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)   SecuClientAPP-11686 [000] .... 17325.724648: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)   SecuClientAPP-11686 [000] .... 17325.724657: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)   SecuClientAPP-11686 [000] .... 17330.725377: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)   SecuClientAPP-11686 [000] .... 17330.725427: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)   SecuClientAPP-11686 [000] .... 17330.725432: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)   SecuClientAPP-11686 [000] .... 17330.725441: tcp_sendmsg: (tcp_sendmsg+0x0/0x50)             zsh-14514 [002] d... 17333.797662: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797670: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0             zsh-14514 [002] d... 17333.797672: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797676: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0             zsh-14514 [002] d... 17333.797678: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797681: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0             zsh-14514 [002] d... 17333.797683: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797687: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0             zsh-14514 [002] d... 17333.797688: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797692: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0             zsh-14514 [002] d... 17333.797693: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797697: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0             zsh-14514 [002] d... 17333.797698: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797702: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0             zsh-14514 [002] d... 17333.797703: zfree_entry: (0x56281978fc60) arg1=0x56281978fc60 arg2=0x56281aa5ab40             zsh-14514 [002] d... 17333.797707: zfree_exit: (0x7f7f18ee7f68 <- 0x56281978fc60) arg1=0x7f7f18ee7f68 arg2=0x0.....

trace-cmd

首先安装trace-cmd

trace-cmd是Ftrace的前端,通过trace-cmd可以更方便的使用ftrace

追踪指定模块

确定模块已经加载

root@local:/tmp/test# lsmod |grep kvmkvm_intel             286720  0kvm                   663552  1 kvm_intel

再次运行 trace-cmd,使用 list 参数,并从输出结果中,grep 查找以 ] 结尾的行。这将过滤掉内核模块。然后 grep 内核模块 kvm_intel ,你应该看到所有与该内核模块有关的函数。

root@local:/tmp/test# trace-cmd list -f | grep ]$  | grep kvm|moreack_flush [kvm]kvm_get_kvm [kvm]kvm_disable_largepages [kvm]kvm_vcpu_mmap [kvm]kvm_device_mmap [kvm]kvm_io_bus_sort_cmp [kvm]vm_stat_get_per_vm [kvm]vm_stat_clear_per_vm [kvm]vcpu_stat_get_per_vm [kvm]vcpu_load [kvm]kvm_sched_out [kvm]vcpu_stat_fops_open [kvm]....

使用perf工具练习

uprobe

root@local:/home/kylin/桌面/trace/perf/uprobe# perf probe -x /bin/bash 'readline%return +0($retval):string'Added new event:  probe_bash:readline__return (on readline%return in /usr/bin/bash with +0($retval):string)You can now use it in all perf tools, such as:	perf record -e probe_bash:readline__return -aR sleep 1root@local:/home/kylin/桌面/trace/perf/uprobe# perf list|grep bash  probe_bash:readline__return                        [Tracepoint event]收集perf数据root@local:/home/kylin/桌面/trace/perf/uprobe# perf record -e probe_bash:readline__return -a^C[ perf record: Woken up 1 times to write data ][ perf record: Captured and wrote 2.069 MB perf.data (4 samples) ]

perf script 打印结果

image-20220111155340767

可以看到perf打印出了bash进程中调用readline函数的进程和返回值

内核态动态追踪

image-20220111173626289

用户态动态追踪

为libc中的堆内存分配函数malloc添加探针

image-20220113153007177

perf list 查看探针

image-20220113153115299

perf record 收集信息perf report 查看情况

image-20220113153415086

就可以看到perf收集的数据了

image-20220113162000172

perf工具架构图

image-20220111175139262

Dtrace

dtrace4linux doc

参考文献

https://blog.openresty.com.cn/cn/dynamic-tracing/

https://www.brendangregg.com/blog/

https://www.brendangregg.com/

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值