做人呢,最紧要就系开心啦

一文学会kprobe的基础用法

2,326次阅读
没有评论

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 原理

一文学会 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->
一文学会 kprobe 的基础用法

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

一文学会 kprobe 的基础用法

[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/ 目录下生成对应名字的目录
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

正文完
 
admin
版权声明:本站原创文章,由 admin 2022-04-28发表,共计15844字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)