一文学会kprobe的基础用法
  oLu30ap4trcD 2023年11月13日 18 0

1.kprobe是什么?

kprobe是一种动态探测技术,不用重新编译内核,在运行系统上插入模块方式,或者基于ftrace方式,动态的添加探测点(实现自己的回调函数),可以探测内核函数的参数和返回值。

kprobes的特点与使用限制: (1)kprobes允许在同一个被探测位置注册多个kprobe;

(2)一般情况下,可以探测内核中的任何函数,包括中断处理函数。不过实现kprobes自身的函数不允许被探测, 另外还有do_page_fault和notifier_call_chain;

(3)对于内敛函数,kprobes无法保证所有实例都注册探测点;由于gcc可能自动将某些函数优化为内联函数,因此有些函数无法达到用户预期;

(4)一个探测点的回调函数,可以修改被探测函数的上下文; 例如可以修改内核的数据结构,或者直接修改struct pt_regs结构体中的寄存器。利用这点,kprobes可以用来安装bug修复代码或者注入故障测试代码;

(5)kprobes探测回调函数,不会递归执行; 例如在printk()函数上注册了探测点,则在它的回调函数中可能再次调用printk函数,此时将不再触发printk探测点的回调,仅仅时增加了kprobe结构体中nmissed字段的数值;

(6)在kprobes的注册和注销过程中,不能睡眠,不会使用mutex锁和动态的申请内存;

(7)kprobes回调函数的运行期间是关闭内核抢占的,同时也可能在关闭中断的情况下执行,具体要视CPU架构而定。因此在回调函数中不能放弃CPU(如使用信号量、mutex锁等);

(8)kretprobe通过替换返回地址为预定义的trampoline的地址来实现,因此栈回溯和gcc内嵌函数__builtin_return_address()调用将返回trampoline的地址而不是真正的被探测函数的返回地址;

(9)如果一个函数的调用次数和返回次数不相等,则在类似这样的函数上注册kretprobe将可能不会达到预期的效果,例如do_exit()函数会存在问题,而do_execve()函数和do_fork()函数不会;

(10)如果当在进入和退出一个函数时,CPU运行在非当前任务所有的栈上,那么往该函数上注册kretprobe可能会导致不可预料的后果,因此,kprobes不支持在X86_64的结构下为__switch_to()函数注册kretprobe,将直接返回-EINVAL。

2.kprobe原理

(1)当用户注册一个探测点后,kprobe首先备份被探测点的对应指令,然后将原始指令的入口点替换为断点指令,该指令是CPU架构相关的,如i386和x86_64是int3,arm是设置一个未定义指令(目前的x86_64架构支持一种跳转优化方案Jump Optimization,内核需开启CONFIG_OPTPROBES选项,该种方案使用跳转指令来代替断点指令);

(2)当CPU流程执行到探测点的断点指令时,就触发了一个trap,在trap处理流程中会保存当前CPU的寄存器信息并调用对应的trap处理函数,该处理函数会设置kprobe的调用状态并调用用户注册的pre_handler回调函数,kprobe会向该函数传递注册的struct kprobe结构地址以及保存的CPU寄存器信息;

(3)随后kprobe单步执行前面所拷贝的被探测指令,具体执行方式各个架构不尽相同,arm会在异常处理流程中使用模拟函数执行,而x86_64架构则会设置单步调试flag并回到异常触发前的流程中执行;

(4)在单步执行完成后,kprobe执行用户注册的post_handler回调函数;

(5)最后,执行流程回到被探测指令之后的正常流程继续执行。

3.kprobe用法

kprobe按实现方式,有两种类型:一个是直接向内核动态插入探测点;一个是基于Ftrace框架实现;

2.1 原生态的kprobe动态探测

编写一个模块,动态加载进内核,从而实现动态探测; 内核提供了一个struct kprobe结构体,以及相关API接口,可以用过这些接口实现kprobe结构,并注册到内核的kprobe子系统;

2.1.1kprobe结构体分析:

struct kprobe {
	struct hlist_node hlist; 	 ///被用于kprobe全局hash,索引值为被探测点的地址;

	/* list of kprobes for multi-handler support */
	struct list_head list;   	 ///用于链接同一被探测点的不同探测kprobe;

	/*count the number of times this probe was temporarily disarmed */
	unsigned long nmissed;       ///probe调用的计数(保证一个probe不递归调用)

	/* location of the probe point */
	kprobe_opcode_t *addr; 		 ///被探测点的地址;

	/* Allow user to indicate symbol name of the probe point */
	const char *symbol_name;	 ///被探测函数的名字;

	/* Offset into the symbol */
	unsigned int offset;   		 ///被探测点在函数内部的偏移,用于探测函数内部的指令,如果该值为0表示函数的入口;

	/* Called before addr is executed. */
	kprobe_pre_handler_t pre_handler; ///在被探测点指令执行之前调用的回调函数;

	/* Called after addr is executed, unless... */
	kprobe_post_handler_t post_handler; ///在被探测指令执行之后调用的回调函数;

	/*
	 * ... called if executing addr causes a fault (eg. page fault).
	 * Return 1 if it handled fault, otherwise kernel will see it.
	 */
	kprobe_fault_handler_t fault_handler; ///在执行pre_handler、post_handler或单步执行被探测指令时出现内存异常则会调用该回调函数;

	/* Saved opcode (which has been replaced with breakpoint) */
	kprobe_opcode_t opcode;

	/* copy of the original instruction */
	struct arch_specific_insn ainsn;

	/*
	 * Indicates various status flags.
	 * Protected by kprobe_mutex after this kprobe is registered.
	 */
	u32 flags;
};

2.1.2几个常用的回调函数原型:

typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *);
typedef void (*kprobe_post_handler_t) (struct kprobe *, struct pt_regs *,
				       unsigned long flags);
typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *,
				       int trapnr);
typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
				    struct pt_regs *);

2.1.3kprobe相关API:

int register_kprobe(struct kprobe *p);    ///向内核注册kprobe探测点
void unregister_kprobe(struct kprobe *p); ///卸载kprobe探测点
int register_kprobes(struct kprobe **kps, int num);    ///注册探测函数向量,包含多个探测点
void unregister_kprobes(struct kprobe **kps, int num); ///卸载探测函数向量,包含多个探测点

int register_kretprobe(struct kretprobe *rp);               ///向内核注册kretprobe探测点
void unregister_kretprobe(struct kretprobe *rp);			///卸载kretprobe探测点
int register_kretprobes(struct kretprobe **rps, int num);   ///注册kretprobe函数向量,包含多个探测点
void unregister_kretprobes(struct kretprobe **rps, int num);///卸载探测函数向量,包含多个探测点

int disable_kprobe(struct kprobe *kp);  ///临时暂停指定探测点的探测
int enable_kprobe(struct kprobe *kp);   ///使能指定探测点的探测

2.1.3 内核提供案例演示:

内核提供了一个非常简单的案例,samples/kprobes/kprobe_example.c,实现探测kernel_clone函数;

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Here's a sample kernel module showing the use of kprobes to dump a
 * stack trace and selected registers when kernel_clone() is called.
 *
 * For more information on theory of operation of kprobes, see
 * Documentation/trace/kprobes.rst
 *
 * You will see the trace data in /var/log/messages and on the console
 * whenever kernel_clone() is invoked to create a new process.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>

#define MAX_SYMBOL_LEN	64
static char symbol[MAX_SYMBOL_LEN] = "kernel_clone";       ///探测函数名
module_param_string(symbol, symbol, sizeof(symbol), 0644);

/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp = {
	.symbol_name	= symbol,
};

/* kprobe pre_handler: called just before the probed instruction is executed */
///探测点前执行函数
static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs)
{
#ifdef CONFIG_X86
	pr_info("<%s> pre_handler: p->addr = 0x%p, ip = %lx, flags = 0x%lx\n",
		p->symbol_name, p->addr, regs->ip, regs->flags);
#endif
#ifdef CONFIG_PPC
	pr_info("<%s> pre_handler: p->addr = 0x%p, nip = 0x%lx, msr = 0x%lx\n",
		p->symbol_name, p->addr, regs->nip, regs->msr);
#endif
#ifdef CONFIG_MIPS
	pr_info("<%s> pre_handler: p->addr = 0x%p, epc = 0x%lx, status = 0x%lx\n",
		p->symbol_name, p->addr, regs->cp0_epc, regs->cp0_status);
#endif
#ifdef CONFIG_ARM64
	pr_info("<%s> pre_handler: p->addr = 0x%p, pc = 0x%lx,"
			" pstate = 0x%lx\n",
		p->symbol_name, p->addr, (long)regs->pc, (long)regs->pstate);
#endif
#ifdef CONFIG_ARM
	pr_info("<%s> pre_handler: p->addr = 0x%p, pc = 0x%lx, cpsr = 0x%lx\n",
		p->symbol_name, p->addr, (long)regs->ARM_pc, (long)regs->ARM_cpsr);
#endif
#ifdef CONFIG_RISCV
	pr_info("<%s> pre_handler: p->addr = 0x%p, pc = 0x%lx, status = 0x%lx\n",
		p->symbol_name, p->addr, regs->epc, regs->status);
#endif
#ifdef CONFIG_S390
	pr_info("<%s> pre_handler: p->addr, 0x%p, ip = 0x%lx, flags = 0x%lx\n",
		p->symbol_name, p->addr, regs->psw.addr, regs->flags);
#endif

	/* A dump_stack() here will give a stack backtrace */
	return 0;
}

/* kprobe post_handler: called after the probed instruction is executed */
static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
				unsigned long flags)
{
#ifdef CONFIG_X86
	pr_info("<%s> post_handler: p->addr = 0x%p, flags = 0x%lx\n",
		p->symbol_name, p->addr, regs->flags);
#endif
#ifdef CONFIG_PPC
	pr_info("<%s> post_handler: p->addr = 0x%p, msr = 0x%lx\n",
		p->symbol_name, p->addr, regs->msr);
#endif
#ifdef CONFIG_MIPS
	pr_info("<%s> post_handler: p->addr = 0x%p, status = 0x%lx\n",
		p->symbol_name, p->addr, regs->cp0_status);
#endif
#ifdef CONFIG_ARM64
	pr_info("<%s> post_handler: p->addr = 0x%p, pstate = 0x%lx\n",
		p->symbol_name, p->addr, (long)regs->pstate);
#endif
#ifdef CONFIG_ARM
	pr_info("<%s> post_handler: p->addr = 0x%p, cpsr = 0x%lx\n",
		p->symbol_name, p->addr, (long)regs->ARM_cpsr);
#endif
#ifdef CONFIG_RISCV
	pr_info("<%s> post_handler: p->addr = 0x%p, status = 0x%lx\n",
		p->symbol_name, p->addr, regs->status);
#endif
#ifdef CONFIG_S390
	pr_info("<%s> pre_handler: p->addr, 0x%p, flags = 0x%lx\n",
		p->symbol_name, p->addr, regs->flags);
#endif
}

/*
 * fault_handler: this is called if an exception is generated for any
 * instruction within the pre- or post-handler, or when Kprobes
 * single-steps the probed instruction.
 */
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
	pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
	/* Return 0 because we don't handle the fault. */
	return 0;
}
/* NOKPROBE_SYMBOL() is also available */
NOKPROBE_SYMBOL(handler_fault);

static int __init kprobe_init(void)
{
	int ret;

	///初始化探测函数
	kp.pre_handler = handler_pre;
	kp.post_handler = handler_post;
	kp.fault_handler = handler_fault;

	//向内核kprobe子系统注册一个探测点
	ret = register_kprobe(&kp);
	if (ret < 0) {
		pr_err("register_kprobe failed, returned %d\n", ret);
		return ret;
	}
	pr_info("Planted kprobe at %p\n", kp.addr);
	return 0;
}

static void __exit kprobe_exit(void)
{
	///卸载探测点
	unregister_kprobe(&kp);
	pr_info("kprobe at %p unregistered\n", kp.addr);
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

编译成模块,动态insmod进内核;相关配置; General architecture-dependent options->

Kernel hacking  --->
	Sample kernel code  --->

[root@myQEMU mnt]# insmod kprobe_example.ko 
Planted kprobe at (____ptrval____)
<kernel_clone> pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x60000005
<kernel_clone> post_handler: p->addr = 0x(____ptrval____), pstate = 0x60000005
[root@myQEMU mnt]# ls /
<kernel_clone> pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x80000005
<kernel_clone> post_handler: p->addr = 0x(____ptrval____), pstate = 0x80000005
bin      etc      linuxrc  proc     sys      tracing
dev      lib      mnt      sbin     tmp      usr
[root@myQEMU mnt]# dmesg 
<kernel_clone> pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x80000005
<kernel_clone> post_handler: p->addr = 0x(____ptrval____), pstate = 0x80000005
...
Planted kprobe at (____ptrval____)
 pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x60000005
 post_handler: p->addr = 0x(____ptrval____), pstate = 0x60000005
 pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x80000005
 post_handler: p->addr = 0x(____ptrval____), pstate = 0x80000005
 pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x80000005
 post_handler: p->addr = 0x(____ptrval____), pstate = 0x80000005
 pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x80000005
 post_handler: p->addr = 0x(____ptrval____), pstate = 0x80000005
[root@myQEMU mnt]# 

确认下探测函数:

[root@myQEMU mnt]# cat /proc/kallsyms  |grep 0xffff8000100341a0
<kernel_clone> pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x80000005
<kernel_clone> post_handler: p->addr = 0x(____ptrval____), pstate = 0x80000005
<kernel_clone> pre_handler: p->addr = 0x(____ptrval____), pc = 0xffff8000100341a0, pstate = 0x80000005
<kernel_clone> post_handler: p->addr = 0x(____ptrval____), pstate = 0x80000005
[root@myQEMU mnt]# 

2.2基于Ftrace的kprobe

这种方式用户通过/sys/kernel/debug/tracing/目录下的trace等属性文件来探测指定的函数,无需再编写内核模块,使用更为简便,但需要内核的debugfs和ftrace功能的支持。 FTRACE = y KPROBE_EVENT = y HAVE_KPROBES_ON_FTRACE = y KPROBES_ON_FTRACE = y

2.2.1主要关注的属性文件:

属性文件 表头
配置属性文件 kprobe_events
查询属性文件 trace和trace_pipe
使能属性文件 events/kprobes/<GRP>/<EVENT>/enabled
过滤属性文件 events/kprobes/<GRP>/<EVENT>/filter
格式查询属性文件 events/kprobes/<GRP>/<EVENT>/format
事件统计属性文件 kprobe_profile
(1)kprobe_events:

设置事件,即添加探测点,支持三种格式:

  p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] ——设置一个probe探测点
  r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS]            ——设置一个return probe探测点
  -:[GRP/]EVENT                                        -—删除一个探测点

各个字段的含义如下:

字段 注释
GRP Group name. 指定后会在events/kprobes目录下生成对应名字的目录,一般不设
EVENT Event name 指定后会在events/kprobes/<GRP>目录下生成对应名字的目录
MOD Module name which has given SYM 模块名,一般不设
SYM[+offs] Symbol+offset where the probe is inserted 指定被探测函数和偏移
MEMADDR Address where the probe is inserted 指定被探测的内存绝对地址
FETCHARGS Arguments. Each probe can have up to 128 args. 指定要获取的参数信息
%REG Fetch register REG 获取指定寄存器值
@ADDR Fetch memory at ADDR (ADDR should be in kernel) 获取指定内存地址的值
@SYM[+|-offs] Fetch memory at SYM +|- offs (SYM should be a data symbol) 获取全局变量的值
$stackN Fetch Nth entry of stack (N >= 0) 获取指定栈空间值,即sp寄存器+N后的位置值
$stack Fetch stack address. 获取sp寄存器值
$retval Fetch return value.(*) 获取返回值,仅用于return probe
+|-offs(FETCHARG) Fetch memory at FETCHARG +|- offs address.(**) 以下可以由于获取指定地址的结构体参数内容,可以设定具体的参数名和偏移地址
NAME=FETCHARG Set NAME as the argument name of FETCHARG
FETCHARG:TYPE Set TYPE as the type of FETCHARG. Currently, basic types 设置参数的类型,可以支持字符串和比特类型(u8/u16/u32/u64/s8/s16/s32/s64), "string" and bitfield are supported.
(2)events/kprobes/<GRP>/<EVENT>/enabled
开启探测:echo 1 > events/kprobes/<GRP>/<EVENT>/enabled
暂停探测:echo 0 > events/kprobes/<GRP>/<EVENT>/enabled
(3)events/kprobes/<GRP>/<EVENT>/filter

该属性文件用于设置过滤条件,可以减少trace中输出的信息,它支持的格式和c语言的表达式类似,支持 ==,!=,>,<,>=,<=判断,并且支持与&&,或||,还有()。

2.2.2实例:探测kernel_clone:

(1)添加探测点
 echo 'p:myprobe kernel_clone clone_flags=%x0 stack_start=%x1 stack_size=%x2 parent_tidptr=%x3 child_tidptr=+0($stack)' > /sys/kernel/debug/tracing/kprobe_events
(2)查看格式
[root@myQEMU tracing]# cat events/kprobes/myprobe/format 
name: myprobe
ID: 1333
format:
        field:unsigned short common_type;       offset:0;       size:2; signed:0;
        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
        field:int common_pid;   offset:4;       size:4; signed:1;

        field:unsigned long __probe_ip; offset:8;       size:8; signed:0;
        field:u64 clone_flags;  offset:16;      size:8; signed:0;
        field:u64 stack_start;  offset:24;      size:8; signed:0;
        field:u64 stack_size;   offset:32;      size:8; signed:0;
        field:u64 parent_tidptr;        offset:40;      size:8; signed:0;
        field:u64 child_tidptr; offset:48;      size:8; signed:0;

print fmt: "(%lx) clone_flags=0x%Lx stack_start=0x%Lx stack_size=0x%Lx parent_tidptr=0x%Lx child_tidptr=0x%Lx", REC->__probe_ip, REC->clone_flags, REC->stack_start, REC->stack_size, REC->parent_tidptr, REC->child_tidptr
[root@myQEMU tracing]# 
(3)使能探测点
echo 1 > events/kprobes/myprobe/enable
(4)查看日志
[root@myQEMU tracing]# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 4/4   #P:1
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
              sh-572     [000] d...   115.764845: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   122.796013: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   126.746282: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   137.690582: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
(5) 使能堆栈
[root@myQEMU tracing]# echo stacktrace > /sys/kernel/debug/tracing/trace_options
[root@myQEMU tracing]# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 9/9   #P:1
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
              sh-572     [000] d...   115.764845: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   122.796013: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   126.746282: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   137.690582: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   138.834681: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   246.215843: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   289.473537: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   302.665952: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012adbdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012adbe20
              sh-572     [000] d...   302.666924: <stack trace>
 => kernel_clone
 => __arm64_sys_clone
 => invoke_syscall.constprop.0
 => do_el0_svc
 => el0_svc
 => el0_sync_handler
 => el0_sync
(6)设置事件过滤

这里的格式参考前面的format;

[root@myQEMU tracing]# echo common_pid!=572 > events/kprobes/myprobe/filter

[root@myQEMU tracing]# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 6/6   #P:1
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
         linuxrc-1       [000] d...   881.572321: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff80001000bdb0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff80001000be20
         linuxrc-1       [000] d...   881.572352: <stack trace>
 => kernel_clone
 => __arm64_sys_clone
 => invoke_syscall.constprop.0
 => do_el0_svc
 => el0_svc
 => el0_sync_handler
 => el0_sync
              sh-606     [000] d...   890.274144: myprobe: (kernel_clone+0x0/0x450) clone_flags=0xffff800012823db0 stack_start=0x0 stack_size=0x0 parent_tidptr=0x0 child_tidptr=0xffff800012823e20
              sh-606     [000] d...   890.274203: <stack trace>
 => kernel_clone
 => __arm64_sys_clone
 => invoke_syscall.constprop.0
 => do_el0_svc
 => el0_svc
 => el0_sync_handler
 => el0_sync
(7)查询统计信息

后面两列分别表示命中,未命中:

[root@myQEMU tracing]# cat /sys/kernel/debug/tracing/kprobe_profile
  myprobe                                                    9               0
[root@myQEMU tracing]# 

参考链接: https://blog.csdn.net/luckyapple1028/article/details/52972315 https://blog.csdn.net/luckyapple1028/article/details/54782659

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月13日 0

暂无评论

推荐阅读
oLu30ap4trcD