考查课的英文翻译英语怎么说-日加华
2023年4月19日发(作者:少儿英语班加盟)Ftrace简介
1. How to implement ftrace in Kernel
config MACH_****
select TRACING
select TRACING_SUPPORT
select FTRACE
select ENABLE_DEFAULT_TRACERS
select GENERIC_TRACER
select FUNCTION_TRACER
#make menuconfig
Kernel hacking ---> Tracers ---> select all
2. Priciple of Ftrace
2.1两种实现机制:
Mcount:
本质上是一种静态代码插装技术,即在每一个函数入口处通过编译器选项,自动插入对
mcount的调用:call_mcount
How to implement it:
#gcc –pg
gcc 的 -pg 选项将在每个函数入口处加入对 mcount 的调用代码。比如下面的 C 代码。
清单 1. test.c
void foo(void)
{
printf( “ foo ” );
}
用 gcc 编译:
arm-eabi-gcc – S test.c
清单 2. test.c 不加入 pg 选项的汇编代码
foo:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
stmfd sp!, {fp, lr}
add fp, sp, #4
ldr r0, .L3
bl printf
1 / 14
ldmfd sp!, {fp, pc}
再加入 -gp 选项编译:
arm-eabi-gcc – pg – S test.c
得到的汇编如下:
清单 3. test.c 加入 pg 选项后的汇编代码
foo:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
stmfd sp!, {fp, lr}
add fp, sp, #4
push {lr}
bl __gnu_mcount_nc
ldr r0, .L3
bl printf
ldmfd sp!, {fp, pc}
由编译器自动插装,原本mcount由libc实现,但内核不会连接libc库,因此,ftrace编写了
自己的mcount函数,并借此实现trace功能。传统的tracer主要以这种机制实现。
Tracepoint:
静态编译进内核,提供一个钩子函数,当tracepoint打开时,会去调用probe函数,通过声
明并注册这个probe函数来实现我们的trace功能。只能在所关心的重要函数中手动插入,
插入量有限。例如:
static inline void prepare_task_switch(struct rq *rq, struct task_struct *prev,
struct task_struct *next)
{
…
trace_sched_switch(prev, next);
}
trace_sched_switch()相当于
if(key) do_tracer;
else back;
注册probe函数,无非有就是利用它现有的接口注册函数,将do_tracer的函数指针指向已定
义的函数。
内核源码中已经设置好一些tracepoint,我们只要声明并注册相应的probe函数与其对应,并
打开tracep关河令 周邦彦 oint即可实现我们的trace功能。event_tracer正是以这种机制实现。
具体说明请见, tracepoint如何hook probe函数请见附录。
2.2具体实现方法:
Mcount:
mcount 干了哪些事情:
mcount由汇编写成,会调用一些C函数,并通过r0和r1寄存器传递ip和parent_ip(子父
函数的pc值)给ftrace_trace_funtion,执行完成后返回原函数。
2 / 14
Step1, if(ftrace_trace_function!=ftrace_stub) goto Step2; else go back;
Step2,do trace:ftrace_trace_function(ip,parent_ip);
Step3,go back;
ftrace_trace_function做的事:
让ftrace_trace_function指向不同的trcer_function即可实现不同的tracer功能。
主要trace工作在ring_buffer写操作中进行。
Tracepoint:
以sched_wakeup为例,源码中已在ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int
wake_flags)函数中插入trace_sched_wakeup(p, true)钩子函数即tracepoint。
Tracepoint机制已有相应的接口函数提供给我们调用,所以我们所要做的工作就是:
1、声明一个probe函数
2、通过接口函数注册这个probe函数与tracepoint对应
3、将这个tracepoint turn on
所以,重点关注的也是这个probe函数做了哪些事。在我们的这个ftrace中,其主要工作也
是往ring_buffer中写些东西。
上述两种机制归根结底都是往ring_buffer中写东西,那么写了哪些东西?ring_buffer又是怎
样一种结构?
首先,写了哪些东西?
主要数据来源:current_thread_info()->task,即task_struct结构体中的内容,以及每种tracer
所需相关信息。
写的内容主要包括:时间戳信息(time_delta)、数据长度(type_len)、数据类型(type)、当前
进程的pid、当前进程的抢占位计数PC(current_thread_info()->preempt_count)、CPU标志
(flags)、padding等组成的数据块。上述存储内容为基本存储项,根据不同的tracer可对存
储数据进行不同的扩充,如trace_sched_wakeup()除须存储以上数据外,还得存储curr->prio、
curr->state以及将要被唤醒进程的task_struct中的部分内容等。
其中flags包含很多信息,具体如下:
第一位:表示中断禁用位,禁用为1,启用为0
第二位:表示当前平台是否支持irq flag的检测,不支持此位置1,否则为0
3 / 14
第三位:表示当前是否需要重调度,是为1,否则为0
第四位:表示是否处于硬中断坏境(响应外部设备的中断),是为1,否则为0
第五位:表示是否处于软中断环境,是为1,否则为0
type的作用:上述框架只提供了一个基本的头部,每个tracer还有自己的数据扩充,那怎么
知道后面扩充的是怎样的数据呢? type就可以派上用场了,根据不同的type存储不同的内
容,也可以凭借它找到它所对应的输出规则。不同的tracer无非就是标记不同type,写不同
内容到相应的trace_entry结构体中。在读取数据时,首先获取type的值,根据type的不同,
打印不同的输出内容。
存储结构如下:
struct trace_array global_trace {
struct ring_buffer *buffer;
unsigned long entries;
int cpu;
cycle_t time_start;
struct task_struct *waiter;
struct trace_array_cpu *data[NR_CPUS];
};
global_trace是live tracing的缓冲区描述符。每个CPU都包含一个存储跟踪条目的页链表。
这些页的在内存中的描述符,通过链接页描述符的LRU项目,来取得每个CPU在buffer
中的页。对于每个活动的CPU,有一个保存着这个CPU缓冲区的页的数据区域,每个CPU
有相同数量的缓冲区的页面。
struct trace_array global_trace –>
struct ring_buffer –>
struct ring_buffer_per_cpu –>
struct buffer_page –>
struct buffer_data_page –>
struct ring_buffer_event –>
struct ftrace_entry
具体存贮结构请见Data format in ring_
2.3具体函数操作:
ring_buffer的写操作:
简单概述如下:根据不同的tracer标志,在ring_buffer的存储页中获取不同大小和格式的存
储单元event,其中event包含数据长度、时间delay和实际数据;然后按照一定的格式,往
实际数据段写内容;最近进行提交,更新时间戳信息等。
详细实现步骤如下:
Step1: 根据标记的写位置,从ring_buffer的tail_page中获取的event结构体的位置
struct ring_buffer_event *trace_buffer_lock_reserve(struct ring_buffer *buffer,int type,unsigned
long len,unsigned long flags, int pc)
返回值:return tail_page->page->data + tail;
4 / 14
Step2: 根据不同的event->type_len,从event结构体中获取entry结构体(实际数据)的位置
entry= ring_buffer_event_data(event);
返回值:if (event->type_len)
return (void *)&event->array[0];
return (void *)&event->array[1];
Step3:根据不同的type,填入所需数据
struct task_struct *tsk = current;
entry->preempt_count= pc & 0xff;
entry->pid= (tsk) ? tsk->pid : 0;
entry->padding= 0;
entry->flags =
entry->type = type;
entry->ip=ip;
entry->parent_ip=parent_ip;
…
Step4: 提交信息,更新写时间戳。
ring_buffer_unlock_commit(buffer, event);
详细内容请见ring_buffer写.xmind
ring_buffer读操作:
简单概述如下:根据标记的读位置,获取存储单元event的位置,然后根据不同的type按照
不同的格式输出内容。其中,type存储在event的开始处。
详细实现步骤如下:
Step1: 返回event结构体的位置,并更新读时间戳。
ring_buffer_peek(struct ring_buffer *buffer, int cpu, u64 *ts,unsigned long *lost_events)
返回值:return cpu_buffer->reader_page->page->data + cpu_buffer->reader_page->read;
Step2: 根据不同的event->type_len,从event结构体中获取entry结构体指针
ring_buffer_event_data(event)
返回值:if (event->type_len)
return (void *)&event->array[0];
return (void *)&event->array[1];
Step3: 根据不同的type,将entry结构体中的内容写入到iter->seq结构体中缓存
trace_seq_printf(&iter->seq,...)
Step4: 将iter->seq结构体中内容写入指定的文件,会根据不同的tracer标志(即type),输出
不同的内容
trace_print_seq(m, &iter->seq);
5 / 14
详细内容请见ring_buffer读.xmind
3. Ftrace架构
X:不支持ARM架构
传统的tracer一般通过mcount这种机制来实现,而event_tracer则主要通过tracepoint这种
方式的来实现。
Ftrace的框架结构
tracer的注册过程:
early_initcall(tracer_alloc_buffers):
分配ring_buffer内存,初始化global_trace(tracer用它来存放信息),并设置current_tracer
为nop
fs_initcall(tracer_init_debugfs):
初始化debugfs,提供用户接口
device_initcall(init_function_trace):
会调用register_tracer()函数,注册各种传统tracer。根据提供的tracer名称,遍历trace_types
链表若无同名tracer则进行注册,并清空global_trace的ring_buffer,将current_tracer设置为
该tracer, 然后调用selftest进行自检,接着将current_tracer恢复原值,再将ring buffer中的
数据清空。如果自检成功,将tracer加入到trace_types链表。
fs_initcall(event_trace_init);
6 / 14
注册各种event_tracer
device_initcall(init_events);
…
Ftrace的详细启动和注册过程请见
4. How to use it
4.1传统tracer的使用(通过debugfs接口)
Step1>选择一种 tracer
echo function > /data/d/tracing/current_tracer
Step2>使能 ftrace
echo 1 > /data/d/tracing/tacing_enabled (to enabled ftrace)
echo 1 > /data/d/tracing/tacing_on (to enabled ring_buffer)
Step3>执行需要 trace 的应用程序,比如需要跟踪 ls,就执行 ls
Step4>关闭 ftrace
echo 0 > /data/d/tracing/tacing_enabled (to disabled ftrace)
Step5>查看 trace 文件
cat /data/d/tracing/trace (or trace_pipe )
支持的传统tracer种类:
root@android:/data/d/tracing # cat available_tracers
blk wakeup_rt wakeup preemptirqsoff preemptoff irqsoff function nop
nop:
什么都不做
function:跟踪函数调用
输出的内容格式如下:
# tracer: function
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
rb_producer-39 [000] 127.844788: msm_gpt_read <-sched_clock
rb_producer-39 [000] 127.844788: msm_read_timer_count <-msm_gpt_read
rb_producer-39 [000] 127.844788: msm_gpt_read <-sched_clock
包括进程名、PID、CPU、时间戳(us,进入函数的时间点)、函数信息(函数名<-父函数名)
irqoff:跟踪并记录内核中哪些函数禁止了中断
输出内容:
7 / 14
# tracer: irqsoff
#
# _------=> CPU#:CPU ID
# / _-----=> irqs-off:’d’中断被disabled,’.’:中断没有关闭
# | / _----=> need-resched:‘N’:need_resched被设置
# || / _---=> hardirq/softirq:H:softirq中发生了硬件中断,h:硬件中断,s:softirq
# ||| / _--=> preempt-depth:表示preempt_disabled的级别
# |||| /
# ||||| delay: \'!\' - greater than preempt_mark_thresh (default 100)
||||| | \'+\' - greater than 1 microsecond
||||| | \' \' - less than or equal to 1 microsecond.
||||| |
# cmd pid | | | | | time | caller
# / | | | | | | /
Preemptoff:跟踪并记录禁止内核抢占的函数
preemptirqsoff:跟踪并记录禁止中断或者禁止抢占内核的函数
wakeup:跟踪进程的调度延迟,即高优先级进程从进入ready状态到获得CPU的延迟进程,
只针对实时进程。
高级功能:
①可以通过trace_options控制输出的内容和格式。
#cat /data/d/trace/trace_options
print-parent nosym-offset nosym-addr noverbose noraw nohex nobin noblock nostacktrace
trace_printk noftrace_preempt nobranch annotate nouserstacktrace nosym-userobj
noprintk-msg-only context-info nolatency-format sleep-time graph-time record-cmd overwrite
not端午节的故事由来 est_nop_accept notest_nop_refuse
eg:
#echo noprint-parent >trace_options (to disable print the calling)
#echo print-parent > trace_options (to enable print the calling)
②cat trace_pipe 动态打印trace的内容
③可以设置ftrace_dump_on_oops=1或2,当kernel panic或die的时候,自动通过串口终端
打印出ring_buffer中记录的内容以供分析。
更多内容请见
4.2 Non-Tracer Tracer的使用(通过debugfs接口)
8 / 14
Max Stack Tracer:记录内核函数的堆栈使用情况
使能:echo 1 > /proc/sys/kernel/stack_tracer_enabled
输出:root@android:/data/d/tracing # cat stack_trace
Depth Size Location (2 entries)
----- ---- --------
0) 2156 8 ftrace_test_stop_func+0x20/0x2c
1) 2148 2148 __gnu_mcount_nc+0x34/0x3c
Event tracer:不间断地记录内核中的重要事件
支持的Event有:
#cat /data/d/tracing/available_events
…
支持的子系统有:
#cd /data/d/tracing/events
#ls
block、compaction、enable、ext4、ftrace、gpio、header_event、header_page、irq、jbd2、kgsl、
kmem、module、napi、net、power、regulator、sched、signal、skb、timer、vmscan、workqueue、
writeback
Step1,将current_tracer设为nop,否则cat trace的信息均是current_tracer记录的信息
Step2,打开event tracer
eg:
# echo sched_switch >> /data/d/tracing/set_event
or
# echo 1 > /data/d/tracing/events/sched/sched_switch/enable
root@android:/data/d/tracing # cat trace
# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | 关于雨的古诗有哪些 |
sh-4609 [000] 19355.141327: sched_switch: prev_comm=sh prev_pid=4609
prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=3820 next_prio=120
adbd-3820 [000] 19355.141785: sched_switch: prev_comm=adbd prev_pid=3820
prev_prio=120 prev_state=R ==> next_comm=adbd next_pid=3823 next_prio=120
adbd-3823 [000] 19355.142120: sched_switch: prev_comm=adbd prev_pid=3823
prev_prio=120 prev_state=S ==> next_comm=LocationService next_pid=1262 next_prio=120
你也可以使能全部event: 1 > /data/d/tracing/events/enable
4.3 不通过debugfs设置和使能上述tracer
传统的tracer:
在源码中,
使用tracing_set_tracer()设置想要的传统tracer
使用trace_set_clr_event()设置想要的event_tracer
9 / 14
tracing_on()使能ring_buffer
tracing_start()使能tracer
同样也可以在源码中设置ftrace_dump_on_oops=1或2(1:dump buffers of all CPU,2:one
dump the buffer of the CPU that triggered the oops)
注:若启用event_tracer,tracing_set_tracer必须设置为nop否则,ring_buffer内的内容记录的
全是传司马徽 统tracer的内容。
5. Request
①能不能定义自己的event,具体该怎么做?
能够定义自己的event,按照原ftrace的方法:a.在你所关心的代码中插入tracepoint,再
定义和注册相应的probe函数与其对应,来记录你想要的数据到ring_buffer即可;
b.增添或修改传统的tracer,通过mcount来实现,会在每个函数入口处插入,通过定义自
己的type,既可记录和输出不同的内容。两种方法的具体操作办法如下:
方法一:利用内核中现有的tracepoint机制
Step1: 插入tracepoint
void foo(void)
{
…
trace_foo();//插入的tracepoint
…
}
Step2:定义probe函数,记录自己想要的数据
void probe_foo()
{
…
}
Step3:根据自己记录的数据,标记相应的type与其对应,在输出函数中添加此type的输
出格式。
Step4:注册probe函数,并与tracepoint形成连接
tracepoint_probe_register(“foo”, probe_foo(), NULL);
此方法适用于监测个别函数,具有针对性。
详细实施方法请见patch1
方法二:利用ftrace的mcount机制
Step1:定义新的struct tracer test_trace
{
.name=
.init=
10 / 14
.reset=
.start=
}
Step2:定义新的trace_ops与test_对应
static struct ftrace_test_ops trace_ops __read_mostly =
{
.func = test_trace_call,
.flags = FTRACE_OPS_FL_GLOBAL,
};
Step3:定义test_trace_call,调用新定义的trace_test函数,在里面实现记录自定义数据的函
数
Step4:添加type和相应的print函数,用于标记存储空间大小,以及如何解析数据
Step5:注册这个tracer,并将其使能
register_tracer(&test_trace);
tracing_set_tracer(test_trace);
详细实施方法请见patch
此方法适用于普遍监测所有函数。
②能不能固定ring_buffer的存储地址?
可以,ring_buffer的内存分配都是通过调用_get_free_page()函数来实现的,我们只需要
重新定义这段函数,让其分配到固定的物理地址,并将这段物理地址移除内核内存管理
区域即可。
③RAMDUMP之后如何解析ring_buffer内的内容?
利用crash工具,extend 之后,即可使用trace show 命令,自动解析出ring_buffer
中的内容,无需手动解析
④不通过debugfs接口,在源码中设置和使能tracer的示例:
static int __init late(void)
{
extern int tracing_set_tracer(const char *buf);
tracing_update_buffers();//将ring_buffer大小设置为默认大小,若需自定义大小,自行修
改内部调用函数的参数即可
trace_set_clr_event(\"irq\", \"irq_handler_entry\", 1);
trace_set_clr_event(\"irq\", \"irq_handler_exit\", 1);//设置event_tracer
…
or
err = tracing_set_tracer(function);//设置传统的tracer
11 / 14
if (err)
printk(\"Unable to enable function tracen\");
…
ftrace_dump_on_oops=1;
//可根据需要进行设置,在系统oops的时候,自动dump堆栈信息到月在回廊新月如钩 输出终端
//1:dump buffers of all CPU,2:one dump the buffer of the CPU that triggered the oops
//tracing_on();
//tracing_start();
return 0;
}
late_initcall(late);
这样即可不通过debugfs接口,使能我们的tracer了,tracing_on()和tracing_start()默认是打
开的,所以此处无需设置。其余诸如过滤输出内容以及自定义输出格式等,不是重点,在此
不作阐述。
⑤在实践中的应用
6. 附录—(关于tracepoint的说明)
1) Tracepoint的设置:
eg:
static inline void
prepare_task_switch(struct rq *rq, struct task_struct *prev,
struct task_struct *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);
trace_sched_switch(prev, next);//加入的tracepoint
}
2) Tracepoint以及probe注册函数的定义:
#define TRACE_EVENT(name, proto, args, struct, assign, print)
DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))
#define DECLARE_TRACE(name, proto, args)
__DECLARE_TRACE(name, PARAMS(proto), PARAMS(args), 1,
PARAMS(void *__data, proto),
PARAMS(__data, args))
12 / 14
#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args)
extern struct tracepoint __tracepoint_##name;
static inline void trace_##name(proto)
{
if (static_branch(&__tracepoint_##))
__DO_TRACE(&__tracepoint_##name,
TP_PROTO(data_proto),
TP_ARGS(data_args),
TP_CONDITION(cond));
}
static inline int
register_trace_##name(void (*probe)(data_proto), void *data)
{
return tracepoint_probe_r逝者如斯夫不舍昼夜感悟 egister(#name, (void *)probe,
data);
}
static inline int
unregister_trace_##name(void (*probe)(data_proto), void *data)
{
return tracepoint_probe_unregister(#name, (void *)probe,
data);
}
static inline void
check_trace_callback_type_##name(void (*cb)(data_proto))
{
}
#define __DO_TRACE(tp, proto, args, cond)
do {
struct tracepoint_func *it_func_ptr;
void *it_func;
void *__data;
if (!(cond))
return;
rcu_read_lock_sched_notrace();
it_func_ptr = rcu_dereference_sched((tp)->funcs);
if (it_func_ptr) {
do {
it_func = (it_func_ptr)->func;
__data = (it_func_ptr)->data;
((void(*)(proto))(it_func))(args);
} while ((++it_func_ptr)->func);
13 / 14
}
rcu_read_unlock_sched_notrace();
} while (0)
3)tracepoint_func结构体
struct tracepoint_func{
void *func;
void *data;
}
14 / 14
性灵的英文译语怎么说-oops
更多推荐
tracer是什么意思cer在线翻译读音例句
发布评论