Ftrace源码分析系列之objtrace

原创 Linux阅码场 2022-06-21 08:00


Ftrace训练营火热报名中:Ftrace训练营:站在设计者的角度来理解ftrace(限50人)。训练营第一期报名已圆满成功,好评如潮。第二期报名正在火爆进行中(咨询小月微信:linuxer2016)。


ARM安全架构训练营火热报名中:阅码场训练营:ARM安全架构之Trustzone-TEE实战。报名咨询客服(小月微信:linuxer2016)。


ARM架构与调优调试训练营火热报名中:阅码场训练营:ARM架构与调试调优。报名咨询客服(小月微信:linuxer2016)。



作者介绍:
陈松,毕业于天津大学精密仪器专业。先后在索尼爱立信,意法爱立信和华为技术有限公司任软件工程师,软件架构师等职务,主要负责手机内核和应用的开发工作。2019年加入麒麟,现任装备事业部软件工程师,主要负责实时内核研发和新技术预研等工作。


1、objtrace和Ftrace的关系


我们在内核代码调试过程中,经常会遇到需要跟踪函数参数值变化的情况,objtrace是一个非常有创造性的工具,它可以将我们从笨重的调试工作中解脱出来,让我们能够很简洁,直观而且优雅的找到我们期望在代码中发现的内容。


Kprobe和function tracer共建于ftrace的基础设施,objtrace将二者巧妙的结合起来,在开发者设置了kprobe的时候,objtrace打开了function tracer,并注册了回调函数。


当代码运行到kprobe关联的函数的时候,objtrace将开发者所设定的参数的地址,偏移量,大小等信息保存起来。


之后,当内核代码运行到任何一个function tracer可以跟踪的函数的时候,objtrace都会将该函数的参数依次取出来,与已经存储的,需要跟踪的参数的地址进行比对,如果发现符合条件的话,就将参数的值打印到ftrace中。


2、如何获取objtrace源代码


Objtrace的源码最早由阅码场资深用户Jeff Xie提交给内核社区,大家可以在github上找到,大家可以下载,编译,试用,很便捷。目前objtrace应该已经到了v12,大家找到v12的分支即可。作者之前一直在v10上调试,下载代码的命令:git clone -b objtrace-v10 https://github.com/x-lugoo/linux.git


3、功能描述


我们在开发和调试代码的过程中,经常会遇到一个场景,变量在函数之间传递的过程中,我们需要追踪这个变量的值变化的过程,比如下面这个例子中的bio:


bio_add_page(struct bio *bio ...) -->__bio_try_merge_page(bio ...) -->         bio_flagged(bio...)   __bio_add_page(bio...)...


通常,我们会增加很多printk,然后编译安装内核,重启。如果有的函数分支忽略掉的话,还需要再增加printk,再次编译安装内核和重启,是不是很笨重?


内核中的kprobe提供了可以不用修改代码而直接查看函数参数值的方法:


cd /sys/kernel/debug/tracingecho 'p bio_add_page arg1=$arg1 arg2=$arg2' > ./kprobe_eventsecho 1 > events/kprobes/p_bio_add_page_0/enablecat your_file > /dev/nullcat trace
jbd2/sda5-8-220 [000] ...1. 55531.677782: p_bio_add_page_0: (bio_add_page+0x0/0x90) arg1=0xffff8ecac98aba00 arg2=0xfffff58a0122a400 jbd2/sda5-8-220 [000] ...1. 55531.677786: p_bio_add_page_0: (bio_add_page+0x0/0x90) arg1=0xffff8ecac98abc00 arg2=0xfffff58a04141140 jbd2/sda5-8-220 [000] ...1. 55531.677790: p_bio_add_page_0: (bio_add_page+0x0/0x90) arg1=0xffff8ecac98ab100 arg2=0xfffff58a02e9f880 jbd2/sda5-8-220 [000] ...1. 55531.677794: p_bio_add_page_0: (bio_add_page+0x0/0x90) arg1=0xffff8ecac98ab300 arg2=0xfffff58a041d1b00 jbd2/sda5-8-220 [000] ...1. 55531.677798: p_bio_add_page_0: (bio_add_page+0x0/0x90) arg1=0xffff8ecac98ab600 arg2=0xfffff58a04141280...


Kprobe利用了ftrace中function tracer的架构,将代码段中__fentry__(或__mcount)替换为kprobe_ftrace_handler,kprobe_ftrace_handler将函数名称和参数值打印到了ftraceringbuffer之中。


但还有一个问题,kprobe只能追踪变量在bio_add_page内的值,不能跟踪变量在其之后的变化。


国内社区的jeff老师近期开发了objtrace可以实现变量持续跟踪的功能,objtrace结合使用了kprobe和function tracer的功能,持续跟踪一个或多个变量,同时还可以设置偏移量,获取变量结构内部元素的值。


目前objtrace正在评审中,很快就会进入到内核代码树的主线。


4、使用方法


1)进入debugfs的目录:


cd /sys/kernel/debug/tracing


2)设置kprobe


echo 'p bio_add_page arg1=$arg1 arg2=$arg2' > ./kprobe_events查看kprobe:ls events/kprobes/ -al里面增加了p_bio_add_page_0cat events/kprobes/p_bio_add_page_0/trigger# Available triggers:# traceon traceoff snapshot stacktrace enable_event disable_event enable_hist disable_hist hist objtrace
可以看到除了原有的traceon traceoff snapshot stacktrace之外,增加了一个新的触发器objtrace


3)设置objtrace


echo 'objtrace:add:arg1,0x28:u32:5 if comm == "cat"' > ./events/kprobes/p_bio_add_page_0/trigger表示在kprobe:bio_add_page需要跟踪第一个参数,也就是结构bio偏移量为0x28的变量的值。cat events/kprobes/p_bio_add_page_0/triggerobjtrace:add:arg1,0x28:u32:count=0 if comm == "cat"


4)触发


cat your_file > /dev/null  //读取一个大文件,触发bio_add_page,也就是内核需要调用到bio_add_page函数


5)查看日志


cat trace# tracer: nop## entries-in-buffer/entries-written: 309/401   #P:4##                                _-----=> irqs-off/BH-disabled#                               / _----=> need-resched#                              | / _---=> hardirq/softirq#                              || / _--=> preempt-depth#                              ||| / _-=> migrate-disable#                              |||| /     delay#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION#              | |         |   |||||     |         |    kworker/3:1H-79      [003] ...1.   248.076143: wbt_issue <-__rq_qos_issue object:0xffffa0c886cc8700 value:0x1000    kworker/3:1H-79      [003] ...1.   248.076143: blk_add_timer <-blk_mq_start_request object:0xffffa0c886cc8700 value:0x1000##### CPU 1 buffer started ####          -0       [001] .Ns2.   248.080898: bio_endio <-blk_update_request object:0xffffa0c886cc8700 value:0x0          -0       [001] .Ns2.   248.080900: __rq_qos_done_bio <-bio_endio object:0xffffa0c886cc8700 value:0x0          -0       [001] .Ns2.   248.080900: bio_uninit <-bio_endio object:0xffffa0c886cc8700 value:0x0          -0       [001] .Ns2.   248.080901: bio_uninit <-bio_endio object:0xffffa0c886cc8700 value:0x0          -0       [001] .Ns2.   248.080901: __rcu_read_lock <-bio_uninit object:0xffffa0c886cc8700 value:0x0          -0       [001] .Ns2.   248.080901: __rcu_read_lock <-bio_uninit object:0xffffa0c886cc8700 value:0x0



5、代码分析


5.1初始化


初始化就是让内核的ftrace架构知道有objtrace这样的一个trigger(触发器),当开发者创建一个kprobe的时候,在这个kprobetrigger目录下,会出现objtrace的选项。代码流程如下:


start_kernel -->trace_init -->trace_event_init -->event_trace_enable -->register_trigger_cmds -->register_trigger_object_cmd -- > register_event_command(&trigger_object_cmd); 注册trigger_object_cmdtrigger_object_cmd包含了event_command所需要的函数,如event_object_trigger_parse就是工程是输入objtrace:add...之后,将这个字符串解析的过程。static struct event_command trigger_object_cmd = {     .name           = "objtrace",     .trigger_type       = ETT_TRACE_OBJECT,     .flags          = EVENT_CMD_FL_NEEDS_REC,     .parse          = event_object_trigger_parse,     .reg            = register_trigger,     .unreg          = unregister_trigger,     .get_trigger_ops    = objecttrace_get_trigger_ops,     .set_filter     = set_trigger_filter, };
此外,start_kernel还有一个路径初始化objtrace的另一部分start_kernel -->early_trace_init -->tracer_alloc_buffers -->allocate_objtrace_dataallocate_objtrace_data中申请并初始化了一个结构体objtrace_data *obj_data,其中最重要的是obj_data->fops->func = trace_object_events_call,函数trace_object_events_call负责跟踪参数值并打印到ftrace的ring buffer中,可以说trace_object_events_call是objtrace中最重要的一个函数。注意,此时trace_object_events_call只是放到了obj_data->fops->func中,并没有开始使用。


5.2设置 “echo 'objtrace:add:arg1,0x28:u32:5 if comm == "cat"' > ./events/kprobes/p_bio_add_page_0/trigger”


当开发者输入这个命令的时候,系统需要将 'objtrace:add:arg1,0x28:u32:5 if comm == "cat"' 解析出来,并调用event_trigger_register注册到ftrace中,这样当内核走到bio_add_page这个函数的时候,就会触发kprobe。函数调用过程如下:我在event_trigger_set_filter中增加了一个WARN_ONCE,可以看到调用栈,同时在event_object_trigger_parse中增加了一个printk,看到一些变量。通常我都是使用function_grath来看代码调用的流程,但ftrace本身的函数不能使用ftrace来观测,这样会造成递归调用,很不幸不是,只能使用这种笨办法了:[  180.024670] chensong: event_object_trigger_parse :param:add:arg1,0x28:u32:5, objtrace_cmd:add[  180.024685] ------------[ cut here ]------------[  180.024688] chensong: set_trigger_filter, filter_str:if comm == "cat"[  180.024698] WARNING: CPU: 3 PID: 4332 at kernel/trace/trace_events_trigger.c:1056 set_trigger_filter+0x13d/0x170...[  180.024844] CPU: 3 PID: 4332 Comm: bash Not tainted 5.18.0-rc2-objtrace-v10+ #2[  180.024848] Hardware name: LENOVO YangTianT4900v-00/, BIOS FCKT65AUS 01/12/2015[  180.024851] RIP: 0010:set_trigger_filter+0x13d/0x170[  180.024856] Code: 83 c4 18 44 89 e0 5b 41 5c 41 5d 5d c3 48 89 fa 48 c7 c6 e0 15 23 84 48 c7 c7 15 e4 5a 84 c6 05 27 84 94 01 01 e8 d2 f7 9c 00 <0f> 0b 48 8b 45 d0 e9 f6 fe ff ff e8 73 57 a3 00 48 8b 7b 20 41 bc[  180.024859] RSP: 0018:ffffa28003a77d60 EFLAGS: 00010286...[  180.024879] CR2: 00007fbeb41fa020 CR3: 000000000a0f6003 CR4: 00000000001706e0[  180.024882] Call Trace:[  180.024884]  [  180.024888]  event_trigger_set_filter+0x22/0x30[  180.024894]  event_object_trigger_parse.cold+0x313/0x345[  180.024904]  trigger_process_regex+0xc1/0x110[  180.024908]  event_trigger_write+0x70/0xd0...[  180.024913]  vfs_write+0xc0/0x2b0[  180.024920]  ksys_write+0x67/0xe0[  180.024926]  __x64_sys_write+0x1a/0x20[  180.024931]  do_syscall_64+0x3b/0x90[  180.024937]  entry_SYSCALL_64_after_hwframe+0x44/0xae


event_object_trigger_parse首先解析objtrace:add:arg1,0x28:u32:5,并将结果保存到变量trigger_data中,最后调用event_trigger_register,过程如下:


static int event_object_trigger_parse(struct event_command *cmd_ops,... {     struct event_trigger_data *trigger_data;     struct objtrace_trigger_data *obj_data;           ...     obj_data->field = field;                                             --- (1)     obj_data->obj_offset = offset;     obj_data->obj_value_type_size = obj_value_type_size;     obj_data->tr = file->tr;     snprintf(obj_data->objtrace_cmd, OBJTRACE_CMD_LEN, objtrace_cmd);
trigger_data = event_trigger_alloc(cmd_ops, cmd, param, obj_data); ---(2) ... ret = event_trigger_set_filter(cmd_ops, file, filter, trigger_data); ---(3) if (ret < 0) goto out_free;
ret = event_trigger_register(cmd_ops, file, glob, trigger_data); ---(4) ... } int event_trigger_register(struct event_command *cmd_ops, { return cmd_ops->reg(glob, trigger_data, file); ---(5) } int event_trigger_set_filter(struct event_command *cmd_ops, { if (param && cmd_ops->set_filter) return cmd_ops->set_filter(param, trigger_data, file); ---(6) return 0; }
static struct event_command trigger_object_cmd = { --- (7) .name = "objtrace", .trigger_type = ETT_TRACE_OBJECT, .flags = EVENT_CMD_FL_NEEDS_REC, .parse = event_object_trigger_parse, .reg = register_trigger, .unreg = unregister_trigger, .get_trigger_ops = objecttrace_get_trigger_ops, .set_filter = set_trigger_filter, };


  • (1)解析的结果放到objtrace_trigger_data中。

  • (2)同时,将objtrace_trigger_data放置到event_trigger_data中。

  • (3)注册filter,也就是if comm == "cat"

  • (4)注册trigger。

  • (5)event_trigger_register就是调用trigger_object_cmd中的.reg,也就是register_trigger

  • (6)event_trigger_set_filter就是调用trigger_object_cmd中的.set_filter,也就是set_trigger_filter

  • (7)trigger_object_cmd的定义。


objtrace还有一个重要的工作,就是要跟踪变量的流向,这个就超出了kprobe的功能范围,需要借助function tracer了,所以kprobe部分还有一个重要的工作,就是使能function tracer。我花了很多时间来找如何使能function tracer的,一直没有找到。但如果你了解function tracer的话,会知道在使能function tracer,必然会调用ftrace_replace_code来替换代码段,将__fentry__(或__mcount)替换为ftrace_call,那么我就在ftrace_replace_code中增加一个WARN,看一下调用栈:


增加调试代码:arch/x86/kernel/ftrace.c  +194  ftrace_replace_code    +static int counter = 0; void ftrace_replace_code(int enable) {                rec = ftrace_rec_iter_record(iter);+               WARN_ONCE(1, "chensong: ip:%pS \n", rec->ip);+               counter++;+               if (counter%100 == 0) +                 printk("chensong:%s, ip:%pS\n", __FUNCTION__, rec->ip);输入echo 'objtrace:add:arg1,0x28:u32:5 if comm == "cat"' > ./events/kprobes/p_bio_add_page_0/trigger后,得到调用栈[  168.674941] chensong: ip:__traceiter_initcall_level+0x0/0x40 [  168.674954] WARNING: CPU: 3 PID: 3032 at arch/x86/kernel/ftrace.c:205 ftrace_replace_code+0x10b/0x1d0...[  168.675108] CPU: 3 PID: 3032 Comm: bash Not tainted 5.18.0-rc2-objtrace-v10+ #6[  168.675112] Hardware name: LENOVO YangTianT4900v-00/, BIOS FCKT65AUS 01/12/2015[  168.675115] RIP: 0010:ftrace_replace_code+0x10b/0x1d0[  168.675121] Code: 89 fe 89 c7 e8 f7 97 b4 00 5b 41 5c 41 5d 41 5e 41 5f 5d c3 48 8b 30 48 c7 c7 78 c5 b8 a5 c6 05 f0 6e ab 01 01 e8 4f f3 b3 00 <0f> 0b eb 80 e8 bc d1 13 00 31 db 45 85 f6 49 c7 c6 30 32 49 a6 0f[  168.675124] RSP: 0018:ffffb8a90299fc78 EFLAGS: 00010286[  168.675128] RAX: 0000000000000000 RBX: ffffffffa6493230 RCX: 0000000000000000...[  168.675149] Call Trace:[  168.675151]  [  168.675156]  ftrace_modify_all_code+0xcc/0x160[  168.675165]  arch_ftrace_update_code+0x9/0x10[  168.675170]  ftrace_run_update_code+0x1a/0x70[  168.675176]  ftrace_startup_enable+0x2e/0x50[  168.675181]  ftrace_startup+0xa8/0x120[  168.675185]  register_ftrace_function+0x25/0x70[  168.675189]  event_object_trigger_init+0x32/0x50[  168.675194]  register_trigger+0x69/0xd0[  168.675200]  event_trigger_register+0x1c/0x20[  168.675204]  event_object_trigger_parse+0x323/0x390[  168.675211]  trigger_process_regex+0xc1/0x110[  168.675216]  event_trigger_write+0x70/0xd0[  168.675221]  vfs_write+0xc0/0x2b0[  168.675228]  ksys_write+0x67/0xe0[  168.675234]  __x64_sys_write+0x1a/0x20[  168.675240]  do_syscall_64+0x3b/0x90[  168.675246]  entry_SYSCALL_64_after_hwframe+0x44/0xae


这部分代码也是从register_trigger调用过来的,最终调用了


register_trigger -->event_object_trigger_init --> register_ftrace_function -->ftrace_modify_all_code其中,register_ftrace_function还将&tr->obj_data->fops注册到function tracer的框架中,那么函数调用function tracer时,function tracer会调用tr->obj_data->fops中的回调函数func,也就是在allocate_objtrace_data中赋值的trace_object_events_call函数,此时trace_object_events_call开始工作。


至此,function tracer被打开,以后内核中所有可以trace的函数,都将要走过function tracer这条路径,也就是每一个函数调用之后,都会运行到trace_object_events_call中。


5.3触发 “cat /home/kylin/workspace/opensource/linux-objtrace-jeff/kernel/sched/core.c > /dev/null”


本节是objtrace中最重要的一个环节,当kprobe和objtrace都设置好了,一旦内核调用到bio_add_page,就要开始跟踪bio这个变量了,objtrace是如何在原有的kprobe的基础上实现这个功能的呢?


我在阅读代码的时候,将这个过程分为两部分,一个是kprobe,另一个是function tracer,我们先来看看当kprobe被触发的时候,objtrace做了什么工作:


同样的,我还是使用了printk和WARN_ONCE来进行调试,我首先在函数set_trace_object中增加了一个WARN_ONCE,得到的调用栈如下:[  248.069971] chensong:set_trace_object, call stack of set trace object[  248.069980] WARNING: CPU: 3 PID: 4343 at kernel/trace/trace_object.c:162 trace_object_trigger+0x184/0x1a0....[  248.070450] Hardware name: LENOVO YangTianT4900v-00/, BIOS FCKT65AUS 01/12/2015[  248.070455] RIP: 0010:trace_object_trigger+0x184/0x1a0[  248.070462] Code: 7a ae 48 c7 c6 2d 69 3d ad e8 28 82 fe ff eb bc 48 c7 c6 60 05 43 ae 48 c7 c7 80 ce 7a ae c6 05 2c 11 95 01 01 e8 db 84 9d 00 <0f> 0b eb 9e 48 85 db 0f 84 16 ff ff ff 48 c7 c0 80 96 bc ae e9 d1[  248.070467] RSP: 0018:ffffb122007d36a8 EFLAGS: 00010086....[  248.070515] Call Trace:[  248.070520]  [  248.070533]  trace_object_count_trigger+0x1e/0x30[  248.070541]  event_triggers_call+0x5d/0xe0
[ 248.070556] trace_event_buffer_commit+0x1a2/0x260[ 248.070573] kprobe_trace_func+0x1ad/0x2a0[ 248.070602] kprobe_dispatcher+0x42/0x70

[ 248.070610] ? bio_add_page+0x1/0x90[ 248.070622] kprobe_ftrace_handler+0x168/0x1f0[ 248.070629] ? bio_add_page+0x5/0x90[ 248.070636] ? __bio_try_merge_page+0x140/0x140[ 248.070648] arch_ftrace_ops_list_func+0xe5/0x160[ 248.070655] ? submit_bh_wbc+0xca/0x140[ 248.070677] ftrace_regs_call+0x5/0x52[ 248.070689] RIP: 0010:bio_add_page+0x1/0x90....[ 248.070781] ? bio_add_page+0x5/0x90[ 248.070789] ? submit_bh_wbc+0xca/0x140



我们可以看到,当bio_add_page被触发之后,进入和kprobe的调用路径:


bio_add_page -->ftrace_regs_call -->arch_ftrace_ops_list_func-->kprobe_ftrace_handler -->kprobe_dispatcher -->kprobe_trace_func -->event_triggers_call --> trace_object_count_trigger --> set_trace_object


其中trace_object_count_trigger也是trigger_object_cmd中定义的回调函数,在trigger条件符合被触发的时候被调用,我在set_trace_object中还增加了一个printk,打印obj的地址,也就是需要跟踪的变量的地址。


   static void set_trace_object(void *obj, int obj_offset,             int obj_value_type_size, struct trace_array *tr){     unsigned long flags;     struct object_instance *obj_ins;     struct objtrace_data *obj_data;
... obj_ins = &obj_data->traced_obj[atomic_read(&obj_data->num_traced_obj)]; obj_ins->obj = obj; --- (1) printk("chensong:%s, obj:0x%lx", __FUNCTION__, obj); obj_ins->obj_value_type_size = obj_value_type_size; obj_ins->obj_offset = obj_offset; obj_ins->tr = tr; /* make sure the num_traced_obj update always appears after traced_obj update */ smp_wmb(); atomic_inc(&obj_data->num_traced_obj);
//WARN_ONCE(1, "chensong:%s, call stack of set trace object\n", __FUNCTION__); out: raw_spin_unlock_irqrestore(&obj_data->obj_data_lock, flags); }


(1) 这个时候内核已经走到了bio_add_page,所以已经知道了bio的地址了,所以需要将bio的地址保存到object_instance的obj中,object_instance中其余的元素保存偏移量,变量大小等信息。


接下来是function tracer的部分,在上一部分讲过,开发者运行命令“echo 'objtrace:add:arg1,0x28:u32:5 if comm == "cat"' > ./events/kprobes/p_bio_add_page_0/trigger”之后,function tracer被打开,以后内核中所有可以trace的函数,都将要走过function tracer这条路径,每一个函数调用之后,都会运行到trace_object_events_call中,trace_object_events_call的代码如下:


static voidtrace_object_events_call(unsigned long ip, unsigned long parent_ip,        struct ftrace_ops *op, struct ftrace_regs *fregs){    struct pt_regs *pt_regs = ftrace_get_regs(fregs);    int cpu, n;        for (n = 0; n < max_args_num; n++) {            obj = regs_get_kernel_argument(pt_regs, n);                            --- (1)            if (object_exist((void *)obj, tr)) {                                       --- (2)         ...                if (get_object_value(&val, (void *)(obj + obj_offset), val_type_size))  --- (3)                    continue;
submit_trace_object(ip, parent_ip, obj, val, tr); ---(4) }...}static inline unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset){ return *(unsigned long *)((unsigned long)regs + offset); --- (5)}
static bool object_exist(void *obj, struct trace_array *tr){ ... for (i = 0; i < max; i++) { if (obj_data->traced_obj[i].obj == obj) --- (6) return true; } return false;}


  • (1)Function tracer使能后,每个可以trace的函数都会调用到trace_object_events_call中,调用栈如下:


Any kernel tracable function -->ftrace_regs_call -->    arch_ftrace_ops_list_func -->          __ftrace_ops_list_func -->              op->func(ip, parent_ip, op, fregs); -->                    trace_object_events_call -->                           get_object_value


trace_object_events_call首先调用regs_get_kernel_argument,依次获得函数的参数,本例中为bio_add_page的参数。

  • (2)调用object_exist来检查是不是需要跟踪的变量,这个变量的地址在开发者输入cat file > /dev/null之后, 内核代码走到了bio_add_pagebio就会在set_trace_object中保存到了object_instanceobj中,所以在这个时候,后续函数如果使用了bioobject_exist返回为TRUE。如果内核代码没有走到过bio_add_page或者该函数的参数中没有bioobject_exist会返回FALSE

  • (3)获取变量值,保存到val中。

  • (4)调用submit_trace_object将value打印到ftrace的ringbuffer中。

  • (5)regs_get_kernel_argument最终会调用到regs_get_register中,返回指定参数的地址。

  • (6)object_exist通过比较地址来判断,当前函数的参数中是不是包含需要跟踪的变量。


好了,到此为止,objtrace的大部分工作都结束了,找到了变量,获得了它的值,并打印到ringbuffer中,很巧妙,避免了大量的printk和WARN这样笨重的方法。


但objtrace打开了所有函数的function tracer,每个函数都要去调用trace_object_events_call,还有调用object_exist进行变量的比对,会增加一定的系统开销,所以objtrace最好不用和性能调试的trace一起工作。


5.4打印 cat trace


从debugfs的trace文件中打印到中断,显示给开发者,调用栈如下:


[ 1617.394055] chensong:trace_object_print[ 1617.394073] WARNING: CPU: 2 PID: 4761 at kernel/trace/trace_output.c:1567 trace_object_print+0x73/0x90
[ 1617.394733] RIP: 0010:trace_object_print+0x73/0x90[ 1617.394749] Code: c6 83 b5 fa b8 e8 9d 0d 00 00 4c 89 e7 be 0a 00 00 00 e8 d0 0a 00 00 48 c7 c6 30 f5 c2 b8 48 c7 c7 90 b5 fa b8 e8 ec a6 9e 00 <0f> 0b 4c 89 e7 e8 23 37 ff ff 5b 41 5c 5d c3 0f 0b eb 97 66 2e 0f
[ 1617.394841] Call Trace:[ 1617.394850] [ 1617.394864] print_trace_line+0x25e/0x570[ 1617.394878] ? ring_buffer_iter_advance+0x32/0x40[ 1617.394902] s_show+0x4c/0x160[ 1617.394919] seq_read_iter+0x2a3/0x450[ 1617.394947] seq_read+0xee/0x120[ 1617.394983] vfs_read+0xa7/0x1a0[ 1617.395006] ksys_read+0x67/0xe0[ 1617.395029] __x64_sys_read+0x1a/0x20[ 1617.395042] do_syscall_64+0x3b/0x90[ 1617.395056] entry_SYSCALL_64_after_hwframe+0x44/0xae


精华文章:【精华】Linux阅码场原创精华文章汇总



阅码场付费会员专业交流群

会员招募:各专业群会员费为88元/季度,权益包含群内提问,线下活动8折,全年不定期群技术分享(普通用户直播免费,分享后每次点播价为19元/次),有意加入请私信客服小月(小月微信号:linuxer2016)


专业群介绍:

彭伟林-阅码场内核性能与稳定性
本群定位内核性能与稳定性技术交流,覆盖云/网/车/机/芯领域资深内核专家,由阅码场资深讲师彭伟林主持。


甄建勇-性能优化与体系结构

本群定位Perf、cache和CPU架构技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师甄建勇主持。


李春良-Xenomai与实时优化

本群定位Xenomai与实时优化技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师李春良和彭伟林共同主持。


周贺贺-Tee和ARM架构

本群定位Tee和ARM架构技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师周贺贺主持。


谢欢-Linux tracers

本群定位Linux tracers技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师谢欢主持。




Linux阅码场 专业的Linux技术社区和Linux操作系统学习平台,内容涉及Linux内核,Linux内存管理,Linux进程管理,Linux文件系统和IO,Linux性能调优,Linux设备驱动以及Linux虚拟化和云计算等各方各面.
评论
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 157浏览
  • 国际标准IPC 标准:IPC-A-600:规定了印刷电路板制造过程中的质量要求和验收标准,涵盖材料、外观、尺寸、焊接、表面处理等方面。IPC-2221/2222:IPC-2221 提供了用于设计印刷电路板的一般原则和要求,IPC-2222 则针对高可靠性电子产品的设计提供了进一步的指导。IPC-6012:详细定义了刚性基板和柔性基板的要求,包括材料、工艺、尺寸、层次结构、特征等。IPC-4101:定义了印刷电路板的基板材料的物理和电气特性。IPC-7351:提供了元件封装的设计规范,包括封装尺寸
    Jeffreyzhang123 2025-01-02 16:50 198浏览
  • 车身域是指负责管理和控制汽车车身相关功能的一个功能域,在汽车域控系统中起着至关重要的作用。它涵盖了车门、车窗、车灯、雨刮器等各种与车身相关的功能模块。与汽车电子电气架构升级相一致,车身域发展亦可以划分为三个阶段,功能集成愈加丰富:第一阶段为分布式架构:对应BCM车身控制模块,包含灯光、雨刮、门窗等传统车身控制功能。第二阶段为域集中架构:对应BDC/CEM域控制器,在BCM基础上集成网关、PEPS等。第三阶段为SOA理念下的中央集中架构:VIU/ZCU区域控制器,在BDC/CEM基础上集成VCU、
    北汇信息 2025-01-03 16:01 166浏览
  • 本文继续介绍Linux系统查看硬件配置及常用调试命令,方便开发者快速了解开发板硬件信息及进行相关调试。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。查看系统版本信息查看操作系统版本信息root@ido:/# cat /etc/*releaseDISTRIB_ID=UbuntuDISTRIB_RELEASE=20.04DISTRIB_CODENAME=focalDIS
    Industio_触觉智能 2025-01-03 11:37 137浏览
  • 在测试XTS时会遇到修改产品属性、SElinux权限、等一些内容,修改源码再编译很费时。今天为大家介绍一个便捷的方法,让OpenHarmony通过挂载镜像来修改镜像内容!触觉智能Purple Pi OH鸿蒙开发板演示。搭载了瑞芯微RK3566四核处理器,树莓派卡片电脑设计,支持开源鸿蒙OpenHarmony3.2-5.0系统,适合鸿蒙开发入门学习。挂载镜像首先,将要修改内容的镜像传入虚拟机当中,并创建一个要挂载镜像的文件夹,如下图:之后通过挂载命令将system.img镜像挂载到sys
    Industio_触觉智能 2025-01-03 11:39 112浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 160浏览
  • 【工程师故事】+半年的经历依然忧伤,带着焦虑和绝望  对于一个企业来说,赚钱才是第一位的,对于一个人来说,赚钱也是第一位的。因为企业要活下去,因为个人也要活下去。企业打不了倒闭。个人还是要吃饭的。企业倒闭了,打不了从头再来。个人失业了,面对的不仅是房贷车贷和教育,还有找工作的焦虑。企业说,一个公司倒闭了,说明不了什么,这是正常的一个现象。个人说,一个中年男人失业了,面对的压力太大了,焦虑会摧毁你的一切。企业说,是个公司倒闭了,也不是什么大的问题,只不过是这些公司经营有问题吧。
    curton 2025-01-02 23:08 284浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 117浏览
  • Matter加持:新世代串流装置如何改变智能家居体验?随着现在智能家庭快速成长,串流装置(Streaming Device,以下简称Streaming Device)除了提供更卓越的影音体验,越来越多厂商开始推出支持Matter标准的串流产品,使其能作为智能家庭中枢,连结多种智能家电。消费者可以透过Matter的功能执行多样化功能,例如:开关灯、控制窗帘、对讲机开门,以及操作所有支持Matter的智能家电。此外,再搭配语音遥控器与语音助理,打造出一个更加智能、便捷的居家生活。支持Matter协议
    百佳泰测试实验室 2025-01-03 10:29 136浏览
  • 影像质量应用于多个不同领域,无论是在娱乐、医疗或工业应用中,高质量的影像都是决策的关键基础。清晰的影像不仅能提升观看体验,还能保证关键细节的准确传达,例如:在医学影像中,它对诊断结果有着直接的影响!不仅如此,影像质量还影响了:▶ 压缩技术▶ 存储需求▶ 传输效率随着技术进步,影像质量的标准不断提高,对于研究与开发领域,理解并提升影像质量已成为不可忽视的重要课题。在图像处理的过程中,硬件与软件除了各自扮演着不可或缺的基础角色,有效地协作能够确保图像处理过程既高效又具有优异的质量。软硬件各扮演了什么
    百佳泰测试实验室 2025-01-03 10:39 132浏览
  • 前言近年来,随着汽车工业的快速发展,尤其是新能源汽车与智能汽车领域的崛起,汽车安全标准和认证要求日益严格,应用范围愈加广泛。ISO 26262和ISO 21448作为两个重要的汽车安全标准,它们在“系统安全”中扮演的角色各自不同,但又有一定交集。在智能网联汽车的高级辅助驾驶系统(ADAS)应用中,理解这两个标准的区别及其相互关系,对于保障车辆的安全性至关重要。ISO 26262:汽车功能安全的基石如图2.1所示,ISO 26262对“功能安全”的定义解释为:不存在由于电子/电气系统失效引起的危害
    广电计量 2025-01-02 17:18 214浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 155浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦