Linux 调试之 TRACE

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

文章目录

前言

在Linux的整个历史中人们一直希望在内核中添加静态跟踪点即记录内核中特定位置的数据以供以后检索的函数。与Ftrace函数跟踪程序不同跟踪点可以记录的不仅仅是输入的函数。跟踪点可以记录函数的局部变量。随着时间的推移已经尝试了各种添加跟踪点的策略TRACE_EVENT宏是添加内核跟踪点的最新方法。

一、TRACE_EVENT简介

trace markers添加一个非常低的开销 tracer hook 尽管trace markers通过使用精心制作的宏解决了性能问题但跟踪标记将记录的信息以printf格式嵌入核心内核中的位置但是这让核心内核代码看起来像是分散在各处的调试代码。

tracepoints跟踪点在内核代码中包含一个函数调用当启用该函数时将调用一个回调函数将跟踪点的参数传递给该函数就像使用这些参数调用回调函数一样。这比trace markers要好得多因为它允许传递回调函数可以取消引用的类型转换指针而不是标记接口trace markers需要回调函数解析字符串。使用 tracepoints 回调函数可以有效地从结构中获取所需的任何内容。

尽管这是对跟踪标记的改进但对于开发人员来说为他们想要添加的每个跟踪点创建回调以便跟踪程序输出其数据仍然太过乏味。内核需要一种更自动化的方式来将跟踪程序连接到跟踪点。这将需要自动创建回调并格式化其数据就像跟踪标记所做的那样但应该在回调中完成而不是在内核代码中的跟踪点位置。

为了解决自动跟踪点的问题TRACE_EVENT宏应运而生该宏专门用于允许开发人员向其子系统添加跟踪点并使Ftrace能够自动跟踪它们。开发人员不需要了解Ftrace的工作原理他们只需要使用TRACE_EVENT宏创建跟踪点。此外他们需要遵循一些指南来创建头文件这样他们就可以完全访问Ftrace跟踪程序。TRACE_EVENT宏设计的另一个目的是不将其耦合到Ftrace或任何其他跟踪器。

ftrace、perf、SystemTapebpf和LTTng等都使用了TRACE_EVENT。

二、TRACE_EVENT() 结构

2.1 TRACE_EVENT简介

自动跟踪点具有必须满足的各种要求
1必须创建一个可以放置在内核代码中的跟踪点。
2必须创建一个可以挂接到该跟踪点的回调函数。
3回调函数必须能够以最快的方式将传递给它的数据记录到跟踪环缓冲区中。
4必须创建一个函数该函数可以解析记录到环形缓冲区中的数据并将其转换为跟踪程序可以向用户显示的人类可读格式。

为了实现上述要求TRACE_EVENT宏被分为六个组件它们对应于宏的参数

#define TRACE_EVENT(name, proto, args, struct, assign, print)	\
	DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))

name要创建的跟踪点的名称。
prototype跟踪点回调的原型
args与原型匹配的参数。
struct跟踪程序可以使用但不需要存储传递到跟踪点的数据的结构。
assign已类似于 C-like 的方式 将数据分配给结构。
print以可读的ASCII格式输出结构的方法。

比如sched_switch的跟踪点。

2.2 trace_sched_switch示例

下面将使用该定义来描述TRACE_EVENT宏的每个部分。

// linux-3.10/include/trace/events/sched.h

/*
 * Tracepoint for task switches, performed by the scheduler:
 */
TRACE_EVENT(sched_switch,

	TP_PROTO(struct task_struct *prev,
		 struct task_struct *next),

	TP_ARGS(prev, next),

	TP_STRUCT__entry(
		__array(	char,	prev_comm,	TASK_COMM_LEN	)
		__field(	pid_t,	prev_pid			)
		__field(	int,	prev_prio			)
		__field(	long,	prev_state			)
		__array(	char,	next_comm,	TASK_COMM_LEN	)
		__field(	pid_t,	next_pid			)
		__field(	int,	next_prio			)
	),

	TP_fast_assign(
		memcpy(__entry->next_comm, next->comm, TASK_COMM_LEN);
		__entry->prev_pid	= prev->pid;
		__entry->prev_prio	= prev->prio;
		__entry->prev_state	= __trace_sched_switch_state(prev);
		memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);
		__entry->next_pid	= next->pid;
		__entry->next_prio	= next->prio;
	),

	TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d",
		__entry->prev_comm, __entry->prev_pid, __entry->prev_prio,
		__entry->prev_state & (TASK_STATE_MAX-1) ?
		  __print_flags(__entry->prev_state & (TASK_STATE_MAX-1), "|",
				{ 1, "S"} , { 2, "D" }, { 4, "T" }, { 8, "t" },
				{ 16, "Z" }, { 32, "X" }, { 64, "x" },
				{ 128, "K" }, { 256, "W" }, { 512, "P" }) : "R",
		__entry->prev_state & TASK_STATE_MAX ? "+" : "",
		__entry->next_comm, __entry->next_pid, __entry->next_prio)
);

除第一个参数外的所有参数都用另一个宏TP_PROTO、TP_ARGS、TP_STRUCT__entry、TP_fast_assign和TP_printk封装。这些宏提供了更多的处理控制并且允许在TRACE_EVENT宏中使用逗号。

2.2.1 Name

Name第一个参数是名称。

TRACE_EVENT(sched_switch,

这是用于调用此跟踪点的名称。实际使用的跟踪点在名称前加上trace_前缀即trace_sched_switch比如

#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>

/**
 * prepare_task_switch - prepare to switch tasks
 * @rq: the runqueue preparing to switch
 * @prev: the current task that is being switched out
 * @next: the task we are going to switch to.
 *
 * This is called with the rq lock held and interrupts off. It must
 * be paired with a subsequent finish_task_switch after the context
 * switch.
 *
 * prepare_task_switch sets up locking and calls architecture specific
 * hooks.
 */
static inline void
prepare_task_switch(struct rq *rq, struct task_struct *prev,
		    struct task_struct *next)
{
	//使用 tracepointsched_switch
	trace_sched_switch(prev, next);
	sched_info_switch(prev, next);
	perf_event_task_sched_out(prev, next);
	fire_sched_out_preempt_notifiers(prev, next);
	prepare_lock_switch(rq, next);
	prepare_arch_switch(next);
}

2.2.2 Prototype

Prototype第二个参数是函数原型。

	TP_PROTO(struct task_struct *prev,
		 struct task_struct *next),

编写原型时就像直接声明跟踪点一样

trace_sched_switch(struct task_struct *prev, struct task_struct *next),

它被用作添加到内核代码中的跟踪点和回调函数的原型。请记住跟踪点调用回调函数就像在跟踪点的位置调用回调函数一样。

2.2.3 Arguments

Arguments第三个参数是函数原型使用的参数。

TP_ARGS(prev, next),

它不仅是TRACE_EVENT宏所需要的它也是下面的跟踪点基础结构所需要的。跟踪点代码在激活时将调用回调函数可以为给定的跟踪点分配多个回调。
创建跟踪点的宏必须同时访问原型和参数。以下是跟踪点宏完成此操作所需的说明

struct tracepoint_func {
	void *func;
	void *data;
};

struct tracepoint {
	const char *name;		/* Tracepoint name */
	struct static_key key;
	void (*regfunc)(void);
	void (*unregfunc)(void);
	struct tracepoint_func __rcu *funcs;
};

extern struct tracepoint __tracepoint_##name;			\
static inline void trace_##name(proto)				\
{								\
	if (static_key_false(&__tracepoint_##name.key))		\
		__DO_TRACE(&__tracepoint_##name,		\
			TP_PROTO(data_proto),			\
			TP_ARGS(data_args),			\
			TP_CONDITION(cond),,);			\
}	

2.2.4 Structure

Structure此参数描述将存储在跟踪器环形缓冲区中的数据的结构布局。

TP_STRUCT__entry(
	__array(	char,	prev_comm,	TASK_COMM_LEN	)
	__field(	pid_t,	prev_pid			)
	__field(	int,	prev_prio			)
	__field(	long,	prev_state			)
	__array(	char,	next_comm,	TASK_COMM_LEN	)
	__field(	pid_t,	next_pid			)
	__field(	int,	next_prio			)
),

此参数描述将存储在跟踪器环形缓冲区中的数据的结构布局。结构的每个元素都由另一个宏定义。这些宏用于自动创建结构与功能不同。请注意宏之间没有任何分隔符没有逗号或分号。
sched_switch跟踪点使用的宏包括
__arraytypenamelen-这定义了一个数组项相当于int name[len]其中类型为int数组的名称为array数组中的项数为len。
__fieldtypename这定义了一个普通的结构元素比如in tvar其中类型为int名称为var。
还有其他元素宏将在后面的文章中描述。sched_switch跟踪点的定义将生成如下结构

struct {
   char   prev_comm[TASK_COMM_LEN];
   pid_t  prev_pid;
   int    prev_prio;
   long   prev_state;
   char   next_comm[TASK_COMM_LEN];
   pid_t  next_pid;
   int    next_prio;
};

2.2.5 Assignment

Assignment第五个参数定义了将参数中的数据保存到环形缓冲区的方式。

TP_fast_assign(
	memcpy(__entry->next_comm, next->comm, TASK_COMM_LEN);
	__entry->prev_pid	= prev->pid;
	__entry->prev_prio	= prev->prio;
	__entry->prev_state	= __trace_sched_switch_state(prev);
	memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);
	__entry->next_pid	= next->pid;
	__entry->next_prio	= next->prio;
),

TP_fast_assign中的代码是正常的C代码。特殊变量__entry表示指向由TP_STRUCT__entry定义的结构类型的指针并直接指向环形缓冲区。TP_fast_assign用于填充TP_STRUCT__entry中创建的所有字段。然后可以使用TP_PROTO和TP_ARGS定义的参数的变量名将适当的数据分配到__entry结构中。

2.2.6 Print

Print最后一个参数定义如何使用printk打印TP_STRUCT__entry结构中的字段。

TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d",
	__entry->prev_comm, __entry->prev_pid, __entry->prev_prio,
	__entry->prev_state & (TASK_STATE_MAX-1) ?
	  __print_flags(__entry->prev_state & (TASK_STATE_MAX-1), "|",
			{ 1, "S"} , { 2, "D" }, { 4, "T" }, { 8, "t" },
			{ 16, "Z" }, { 32, "X" }, { 64, "x" },
			{ 128, "K" }, { 256, "W" }, { 512, "P" }) : "R",
	__entry->prev_state & TASK_STATE_MAX ? "+" : "",
	__entry->next_comm, __entry->next_pid, __entry->next_prio)

变量__entry再次用于引用指向包含数据的结构的指针。格式字符串与任何其他printf格式一样。__print_flags是TRACE_EVENT附带的一组帮助函数的一部分。

2.3 Format file

Format filesched_switch TRACE_EVENT宏以/sys/kernel/debug/tracking/events/sched/sched_switch/format格式生成以下格式文件

[root@localhost ~]# cat /sys/kernel/debug/tracing/events/sched/sched_switch/format
name: sched_switch
ID: 326
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:char prev_comm[16];       offset:8;       size:16;        signed:1;
        field:pid_t prev_pid;   offset:24;      size:4; signed:1;
        field:int prev_prio;    offset:28;      size:4; signed:1;
        field:long prev_state;  offset:32;      size:8; signed:1;
        field:char next_comm[16];       offset:40;      size:16;        signed:1;
        field:pid_t next_pid;   offset:56;      size:4; signed:1;
        field:int next_prio;    offset:60;      size:4; signed:1;

print fmt: "prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d", REC->prev_comm, REC->prev_pid, REC->prev_prio, REC->prev_state & (1024-1) ? __print_flags(REC->prev_state & (1024-1), "|", { 1, "S"} , { 2, "D" }, { 4, "T" }, { 8, "t" }, { 16, "Z" }, { 32, "X" }, { 64, "x" }, { 128, "K" }, { 256, "W" }, { 512, "P" }) : "R", REC->prev_state & 1024 ? "+" : "", REC->next_comm, REC->next_pid, REC->next_prio

请注意格式文件中的__entry替换为REC。第一组字段common_*不是来自TRACE_EVENT宏而是由创建此格式文件的Ftrace添加到所有事件中其他跟踪程序可以添加不同的字段。格式文件为用户空间工具提供解析包含sched_switch项的二进制输出所需的信息。

2.3.1 tracing/events/

与进程调度有关的其他 tracepoints 点

[root@localhost ~]# cat /sys/kernel/debug/tracing/events/sched/
enable                       sched_move_numa/             sched_process_free/          sched_stat_runtime/          sched_switch/
filter                       sched_pi_setprio/            sched_process_hang/          sched_stat_sleep/            sched_wait_task/
sched_kthread_stop/          sched_process_exec/          sched_process_wait/          sched_stat_wait/             sched_wake_idle_without_ipi/
sched_kthread_stop_ret/      sched_process_exit/          sched_stat_blocked/          sched_stick_numa/            sched_wakeup/
sched_migrate_task/          sched_process_fork/          sched_stat_iowait/           sched_swap_numa/             sched_wakeup_new/

2.3.2 perf

除了通过 debugfs 的 /tracing/events/ 查看 tracepoint点还可以通过 perf list 查看

[root@localhost ~]# perf list tracepoint | grep sched:
  sched:sched_kthread_stop                           [Tracepoint event]
  sched:sched_kthread_stop_ret                       [Tracepoint event]
  sched:sched_migrate_task                           [Tracepoint event]
  sched:sched_move_numa                              [Tracepoint event]
  sched:sched_pi_setprio                             [Tracepoint event]
  sched:sched_process_exec                           [Tracepoint event]
  sched:sched_process_exit                           [Tracepoint event]
  sched:sched_process_fork                           [Tracepoint event]
  sched:sched_process_free                           [Tracepoint event]
  sched:sched_process_hang                           [Tracepoint event]
  sched:sched_process_wait                           [Tracepoint event]
  sched:sched_stat_blocked                           [Tracepoint event]
  sched:sched_stat_iowait                            [Tracepoint event]
  sched:sched_stat_runtime                           [Tracepoint event]
  sched:sched_stat_sleep                             [Tracepoint event]
  sched:sched_stat_wait                              [Tracepoint event]
  sched:sched_stick_numa                             [Tracepoint event]
  sched:sched_swap_numa                              [Tracepoint event]
  sched:sched_switch                                 [Tracepoint event]
  sched:sched_wait_task                              [Tracepoint event]
  sched:sched_wake_idle_without_ipi                  [Tracepoint event]
  sched:sched_wakeup                                 [Tracepoint event]
  sched:sched_wakeup_new                             [Tracepoint event]

2.3.3 bpftrace

对于支持 ebpf 的高版本Linux内核版本可以用bpftrace查看

yl@yl-virtual-machine:~/Desktop$ uname -r
5.19.0-23-generic
yl@yl-virtual-machine:~/Desktop$ sudo bpftrace -l tracepoint:sched:*
tracepoint:sched:sched_kthread_stop
tracepoint:sched:sched_kthread_stop_ret
tracepoint:sched:sched_kthread_work_execute_end
tracepoint:sched:sched_kthread_work_execute_start
tracepoint:sched:sched_kthread_work_queue_work
tracepoint:sched:sched_migrate_task
tracepoint:sched:sched_move_numa
tracepoint:sched:sched_pi_setprio
tracepoint:sched:sched_process_exec
tracepoint:sched:sched_process_exit
tracepoint:sched:sched_process_fork
tracepoint:sched:sched_process_free
tracepoint:sched:sched_process_hang
tracepoint:sched:sched_process_wait
tracepoint:sched:sched_stat_blocked
tracepoint:sched:sched_stat_iowait
tracepoint:sched:sched_stat_runtime
tracepoint:sched:sched_stat_sleep
tracepoint:sched:sched_stat_wait
tracepoint:sched:sched_stick_numa
tracepoint:sched:sched_swap_numa
tracepoint:sched:sched_switch
tracepoint:sched:sched_wait_task
tracepoint:sched:sched_wake_idle_without_ipi
tracepoint:sched:sched_wakeup
tracepoint:sched:sched_wakeup_new
tracepoint:sched:sched_waking

2.3.4 bcc

使用bcc查看

yl@yl-virtual-machine:~/Desktop$ sudo /usr/share/bcc/tools/tplist -v "sched:*"
sched:sched_kthread_stop
    char comm[TASK_COMM_LEN];
    pid_t pid;
sched:sched_kthread_stop_ret
    int ret;
sched:sched_kthread_work_queue_work
    void * work;
    void * function;
    void * worker;
sched:sched_kthread_work_execute_start
    void * work;
    void * function;
sched:sched_kthread_work_execute_end
    void * work;
    void * function;
sched:sched_waking
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
    int target_cpu;
sched:sched_wakeup
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
    int target_cpu;
sched:sched_wakeup_new
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
    int target_cpu;
sched:sched_switch
    char prev_comm[TASK_COMM_LEN];
    pid_t prev_pid;
    int prev_prio;
    long prev_state;
    char next_comm[TASK_COMM_LEN];
    pid_t next_pid;
    int next_prio;
sched:sched_migrate_task
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
    int orig_cpu;
    int dest_cpu;
sched:sched_process_free
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
sched:sched_process_exit
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
sched:sched_wait_task
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
sched:sched_process_wait
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int prio;
sched:sched_process_fork
    char parent_comm[TASK_COMM_LEN];
    pid_t parent_pid;
    char child_comm[TASK_COMM_LEN];
    pid_t child_pid;
sched:sched_process_exec
    __data_loc char[] filename;
    pid_t pid;
    pid_t old_pid;
sched:sched_stat_wait
    char comm[TASK_COMM_LEN];
    pid_t pid;
    u64 delay;
sched:sched_stat_sleep
    char comm[TASK_COMM_LEN];
    pid_t pid;
    u64 delay;
sched:sched_stat_iowait
    char comm[TASK_COMM_LEN];
    pid_t pid;
    u64 delay;
sched:sched_stat_blocked
    char comm[TASK_COMM_LEN];
    pid_t pid;
    u64 delay;
sched:sched_stat_runtime
    char comm[TASK_COMM_LEN];
    pid_t pid;
    u64 runtime;
    u64 vruntime;
sched:sched_pi_setprio
    char comm[TASK_COMM_LEN];
    pid_t pid;
    int oldprio;
    int newprio;
sched:sched_process_hang
    char comm[TASK_COMM_LEN];
    pid_t pid;
sched:sched_move_numa
    pid_t pid;
    pid_t tgid;
    pid_t ngid;
    int src_cpu;
    int src_nid;
    int dst_cpu;
    int dst_nid;
sched:sched_stick_numa
    pid_t src_pid;
    pid_t src_tgid;
    pid_t src_ngid;
    int src_cpu;
    int src_nid;
    pid_t dst_pid;
    pid_t dst_tgid;
    pid_t dst_ngid;
    int dst_cpu;
    int dst_nid;
sched:sched_swap_numa
    pid_t src_pid;
    pid_t src_tgid;
    pid_t src_ngid;
    int src_cpu;
    int src_nid;
    pid_t dst_pid;
    pid_t dst_tgid;
    pid_t dst_ngid;
    int dst_cpu;
    int dst_nid;
sched:sched_wake_idle_without_ipi
    int cpu;

2.3.5 SystemTap

SystemTap也可以查看

[root@localhost ~]# stap -L 'kernel.trace("sched*")'
kernel.trace("sched:sched_kthread_stop") $t:struct task_struct*
kernel.trace("sched:sched_kthread_stop_ret") $ret:int
kernel.trace("sched:sched_migrate_task") $p:struct task_struct* $dest_cpu:int
kernel.trace("sched:sched_move_numa") $tsk:struct task_struct* $src_cpu:int $dst_cpu:int
kernel.trace("sched:sched_pi_setprio") $tsk:struct task_struct* $newprio:int
kernel.trace("sched:sched_process_exec") $p:struct task_struct* $old_pid:pid_t $bprm:struct linux_binprm*
kernel.trace("sched:sched_process_exit") $p:struct task_struct*
kernel.trace("sched:sched_process_fork") $parent:struct task_struct* $child:struct task_struct*
kernel.trace("sched:sched_process_free") $p:struct task_struct*
kernel.trace("sched:sched_process_hang") $tsk:struct task_struct*
kernel.trace("sched:sched_process_wait") $pid:struct pid*
kernel.trace("sched:sched_stat_blocked") $tsk:struct task_struct* $delay:u64
kernel.trace("sched:sched_stat_iowait") $tsk:struct task_struct* $delay:u64
kernel.trace("sched:sched_stat_runtime") $tsk:struct task_struct* $runtime:u64 $vruntime:u64
kernel.trace("sched:sched_stat_sleep") $tsk:struct task_struct* $delay:u64
kernel.trace("sched:sched_stat_wait") $tsk:struct task_struct* $delay:u64
kernel.trace("sched:sched_stick_numa") $tsk:struct task_struct* $src_cpu:int $dst_cpu:int
kernel.trace("sched:sched_swap_numa") $src_tsk:struct task_struct* $src_cpu:int $dst_tsk:struct task_struct* $dst_cpu:int
kernel.trace("sched:sched_switch") $prev:struct task_struct* $next:struct task_struct*
kernel.trace("sched:sched_wait_task") $p:struct task_struct*
kernel.trace("sched:sched_wake_idle_without_ipi") $cpu:int
kernel.trace("sched:sched_wakeup") $p:struct task_struct* $success:int
kernel.trace("sched:sched_wakeup_new") $p:struct task_struct* $success:int

tracepoint 是由内核开发人员在代码中设置的静态 hook 点具有稳定的 API 接口不会随着内核版本的变化而变化可以提高我们内核跟踪程序的可移植性。但是由于 tracepoint 是需要内核研发人员参数编写因此在内核代码中的数量有限并不是所有的内核函数中都具有类似的跟踪点。

三、The header file

内核中的大部分跟踪点都是定义在该目录下

linux-3.10/include/trace/events/

在这里插入图片描述
与任务调度相关的 tracepoints 点都是在该目录下

linux-3.10/include/trace/events/sched.h

#undef TRACE_SYSTEM
#define TRACE_SYSTEM sched

#if !defined(_TRACE_SCHED_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SCHED_H

#include <linux/sched.h>
#include <linux/tracepoint.h>
#include <linux/binfmts.h>

/*
 * Tracepoint for calling kthread_stop, performed to end a kthread:
 */
TRACE_EVENT(sched_kthread_stop,

	TP_PROTO(struct task_struct *t),

	TP_ARGS(t),

	TP_STRUCT__entry(
		__array(	char,	comm,	TASK_COMM_LEN	)
		__field(	pid_t,	pid			)
	),

	TP_fast_assign(
		memcpy(__entry->comm, t->comm, TASK_COMM_LEN);
		__entry->pid	= t->pid;
	),

	TP_printk("comm=%s pid=%d", __entry->comm, __entry->pid)
);

/*
 * Tracepoint for the return value of the kthread stopping:
 */
TRACE_EVENT(sched_kthread_stop_ret,

	TP_PROTO(int ret),

	TP_ARGS(ret),

	TP_STRUCT__entry(
		__field(	int,	ret	)
	),

	TP_fast_assign(
		__entry->ret	= ret;
	),

	TP_printk("ret=%d", __entry->ret)
);
......

TRACE_EVENT头中的第一行不是常规的 #ifdef _TRACE_SCHED_H 而是

#undef TRACE_SYSTEM
#define TRACE_SYSTEM sched

#if !defined(_TRACE_SCHED_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SCHED_H

此示例用于调度程序跟踪事件其他事件的 headfile 将使用sched和_trace_sched_H以外的其他内容。TRACE_HEADER_MULTI_READ测试允许多次包含此文件这对于TRACE_EVENT宏的处理非常重要。还必须为文件定义TRACE_SYSTEM并且必须在#if的保护范围之外。TRACE_SYSTEM定义文件中TRACE_EVENT宏所属的组。这也是事件将在debugfs tracing/events/ 目录中分组的目录名。此分组对Ftrace很重要因为它允许用户按组启用或禁用事件。
然后该文件包含TRACE_EVENT宏内容所需的任何 headers 。例如#include<linux/sched.h>。
The tracepoint.h file 也要求

#include <linux/tracepoint.h>

现在可以使用 TRACE_EVENT() 宏定义所有跟踪事件。请在TRACE_EVENT宏上方包含描述跟踪点的注释。查看include/trace/events/sched.h作为示例。文件结尾为

#endif /* _TRACE_SCHED_H */

/* This part must be outside protection */
#include <trace/define_trace.h>

define_trace.h是创建跟踪点的 all the magic 所在。关于这个文件如何工作的解释将留给另一篇文章。现在只要知道这个文件必须包含在跟踪头文件的底部不受#endif的保护就足够了。

要使用跟踪点必须包含 headerfile 但在包含跟踪之前一个C文件还必须定义CREATE_TRACE_POINTS。这将导致define_trace.h来创建生成跟踪事件所需的必要函数如下所示

// linux-3.10/kernel/sched/core.c

#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>

如果另一个文件需要使用跟踪文件中定义的跟踪点那么它只需要包含跟踪文件而不需要定义CREATE_TRACE_POINTS了。为同一头文件多次定义它将在生成时导致链接器错误。比如

// linux-3.10/kernel/fork.c

#include <trace/events/sched.h>

// 这里的 CREATE_TRACE_POINTS 是生成与 task 有关的 tracepoint
#define CREATE_TRACE_POINTS
#include <trace/events/task.h>

代码中使用的跟踪点与TRACE_EVENT宏中定义的一样

/**
 * prepare_task_switch - prepare to switch tasks
 * @rq: the runqueue preparing to switch
 * @prev: the current task that is being switched out
 * @next: the task we are going to switch to.
 *
 * This is called with the rq lock held and interrupts off. It must
 * be paired with a subsequent finish_task_switch after the context
 * switch.
 *
 * prepare_task_switch sets up locking and calls architecture specific
 * hooks.
 */
static inline void
prepare_task_switch(struct rq *rq, struct task_struct *prev,
		    struct task_struct *next)
{
	trace_sched_switch(prev, next);
	sched_info_switch(prev, next);
	perf_event_task_sched_out(prev, next);
	fire_sched_out_preempt_notifiers(prev, next);
	prepare_lock_switch(rq, next);
	prepare_arch_switch(next);
}

参考资料

Linux 3.10.0

https://lwn.net/Articles/379903/
https://blog.csdn.net/rikeyone/article/details/116057261

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux