前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >谢欢《Linux内核tracers的实现原理与应用》课程精彩答疑

谢欢《Linux内核tracers的实现原理与应用》课程精彩答疑

作者头像
Linux阅码场
发布2021-12-21 15:43:52
1.2K0
发布2021-12-21 15:43:52
举报
文章被收录于专栏:LINUX阅码场LINUX阅码场

作者简介

谢欢,大家可以叫我Jeff, 我目前就职于某国际知名linux发行版开源公司, 热衷于linux内核。我平时把linux内核源码当小说一样阅读学习,也一直把能给linux社区贡献更多有质量的代码而努力。

今年10月中旬,我向Linux内核社区提交了一个关于tracing 的patchset. tracing 的 Maintainer steve 和kprobe的maintainer Masami都非常感兴趣。

学员:Jeff老师,请教一下,您课程的理论和方法在arm+android环境下适用吗,实验是不是可以玩

Jeff老师:都可以玩,具体实现会跟架构不同有所区别,x86搞会了,其他的架构可以去套

学员:function graph trace挂的第二个钩子函数 怎么返回到 parent函数的呢,这个返回值不是被覆盖了吗这块没交代

Jeff老师:是的,这个地方没有交代,因为你会在kretprobe章节那里看到类似的逻辑,我当时是考虑从全局看不想讲两次一样的,如果想了解的话,parent被覆盖之前已经被保存了,在调用第二个钩子函数(return_to_handler)之后,会返回到parent处,代码如下:

代码语言:javascript
复制
SYM_FUNC_START(return_to_handler)
       subq  $24, %rsp 
       /* Save the return values */
       movq %rax, (%rsp)
        movq %rdx, 8(%rsp)
       movq %rbp, %rdi 
       call ftrace_return_to_handler 
       movq %rax, %rdi   <rax是ftrace_return_to_handler的返回值,也是parent的地址>
       movq 8(%rsp), %rdx
       movq (%rsp), %rax
       addq $24, %rsp
       JMP_NOSPEC rdi

Jeff老师与学员日常对话截取:

Jeff老师:其实讲解基本的使用不太费时间的,主要是讲解底层原理,确实要花不少时间,我自己也是一个刨根问底的人

学员:基本使用网上也可以搜到一大堆教程,但是原理的东西才是难得地方。有老师带着学,我们可以事半功倍,搞懂了原理,再去使用工具,更加得心应手了

老师如果有精力,也可以出一些内核子系统的实现讲解,就像ftrace这种讲法就挺好

Jeff老师:我是准备之后讲解block子系统的,从文件系统到多队列调度器以及块设备驱动,一个个来

学员:jeff老师,我想打印这个函数里面的vq->last_used_index,用kprobe可以吗?

代码语言:javascript
复制
void *virtqueue_get_buf_ctx(struct virtqueue*_vq, unsigned int *len,
      void **ctx)
{
structvring_virtqueue *vq = to_vvq(_vq);
…
last_used =(vq->last_used_idx & (vq->vring.num - 1));
…
}

Jeff老师:可以,和函数参数相关的变量,直接用kprobe_event就可以

借助gdb 算偏移还算挺方便,如果你觉得麻烦,可以自己写一个kprobe模块插进去 。

学员:怎样用trace如何打出io从insert到complete中的函数栈

Jeff老师:这个有挺多方法,就看怎么灵活运用,例如:

代码语言:javascript
复制
echo 0 > ./tracing_on 
echo 1 >./events/block/block_bio_queue/enable
echo traceon >./events/block/block_bio_queue/trigger
echo 1 >./events/block/block_rq_insert/enable
echo traceon >./events/block/block_rq_insert/trigger 
echo 1 >./events/block/block_rq_complete/enable
echo traceoff >  ./events/block/block_rq_complete/trigger
echo function >  current_tracer 

Jeff老师主动拉高学员的学习热情和讨论欲:

Jeff老师我最近想到一个idea,不再局限于跟踪函数,跟踪struct page,跟踪struct bio,跟踪struct task_struct,任何对象都可以动态跟踪,正在准备提个大补丁到社区去,看能不能合并进去。

学员甲:这个厉害了

学员乙:这个功能overhead高吗?

学员丙:比较有意思。大致原理是啥,访问内存的时候触发异常?

Jeff老师:到时候看我补丁,你会觉得简单的想哭

最新补丁进展:

https://lore.kernel.org/all/20211129164951.220511-1-xiehuan09@gmail.com/

学员自悟一刻:

“看到trace_event 有点晕了,我理解trace_event是不是就是一种静态的预埋的点, 当enable + filter后,输出信息到buffer中去?本身的实现和之前的ftrace其实没有关系?然后 老师说的 trace_event要多次展开, 各种unset 和define, 为何要多次呢?这里有点晕啊。

其实第一节课的内容能听懂,后面就都好懂了, 第四课的trace_event 我后来自己搜了一些文章看了一遍,再结合视频又听了一遍,能大致听懂 trace_event 的实现原理和逻辑,只是还是有刚才上面的疑问。本质上还是自己基础不扎实。

阅码场有人投稿写了trace_event的,我也搜到了,《LinuxTraceEvent - 我见过的史上最长宏定义

还是偏源码解析的,就是上来给你一坨代码,讲一遍过程,但是为何要这么做,就像老师讲的kprobe,原来是走int3,现在利用ftrace的5个nop,可以更方便使用,这个有前后关系的,其实就很好理解。

我好像悟了, TRACE_EVENT这一系列undef和define, 就是事先 给你用宏定义了好了若干个代码模板, 然后你用一个 宏定义 -TRACE_EVENT, 在经过不同模块的(子系统时),帮你生成对应逻辑的代码, 或者说,是内核约定了一个套路,各个模块 都按这个套路写, 然后帮你生成代码,说穿了就是一个静态代码生成器。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux阅码场 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档