KGTP中增加对GDB命令“set trace-buffer-size”的支持 - Week 6

CSDN开源夏令营 - 第六周工作总结


这一周是参加夏令营以来收获最大的一周了,因为终于找到了实现“set trace-buffer-size”的关键所在,并且成功实现了这个feature的simple mode(重新分配Ring Buffer后,不进行数据迁移,直接舍弃),而进行数据迁移的我把它叫做normal mode,这个之后有时间了再做。下面从Ring Buffer开始说起,穿插一些在整个代码实现过程中需要注意的点,以及KGTP中关于Linux内核需要学习的东西。

1. KGTP中的Ring Buffer

嗯,是不是又想起了unified trace ring buffer之争,KGTP中的Ring Buffer实现也有三种策略,上周总结提到过,我们再简单回顾一下。

GTP_FRAME_SIMPLE,作为调试使用,直接使用vmalloc分配一块内存区域;GTP_FTRACE_RING_BUFFER,使用Ftrace提供的Ring Buffer的实现;GTP_RB则是KGTP自行实现的一套Ring Buffer,作为默认选项。

具体实现可以参考,gtp_rb.c,ring_buffer.c和ring_buffer.h,由于GTP_FRAME_SIMPLE较简单,因此直接在gtp.c中实现了相关操作。

2. GDBRSP再回顾

GDBRSP从一开始就成为一种阅读代码的障碍,而到现在则是阅读代码的一种线索了,我们再看看这个数据流, GDB ——>   GDBRSP ——> SYSFS ——> KGTP ——> Kernel。GDB的任何指令都需要被转换成Packet,通过RSP协议发送到remote(KGTP中是一个文件,即gtp),然后由stub解析,之后完成相应的trace工作,把trace frame存在Ring Buffer中,等待GDB通过tfind类指令查看。

这里最核心的一个函数则是gtp.c中的gtp_write,它作为一个driver function,完成包解析的工作,并调用相应的handler进行具体的处理。

关于GDBRSP一个比较好的参考在[1],网上有个中文翻译[2]。官方的文档也可以看[3]。另外,为了看懂KGTP的代码,对GDB的包格式也要有一个清晰的了解,参考以下链接,Packets[4],Stop Reply Packets[5],General Query Packets[6],Tracepoint Packets[7],Trace Experiments[8]。

通过Google搜索,发现除了官方文档,相关的post不太多,因此老实看官方文档就好了。

3. Linux内核中的内存分配

想搞懂KGTP中的Ring Buffer是如何被分配的,则需要对Linux Kernel的内存分配有一定的掌握,让我们看一下核心的代码:

#if defined(GTP_FTRACE_RING_BUFFER)         \
     && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39))    \
     && !defined(GTP_SELF_RING_BUFFER)
     if (gtp_frame && gtp_circular_is_changed) {
         ring_buffer_free(gtp_frame);
         gtp_frame = NULL;
     }
     gtp_circular_is_changed = 0;
 #endif

 #ifdef GTP_RB
     if (GTP_RB_PAGE_IS_EMPTY) {
         if (gtp_rb_page_alloc(gtp_trace_buffer_size) != 0) {
             ret = -ENOMEM;
             goto out;
         }
 #endif
 #if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER)
     if (!gtp_frame) {
 #ifdef GTP_FRAME_SIMPLE
         gtp_frame = vmalloc(gtp_trace_buffer_size);
 #endif
 #ifdef GTP_FTRACE_RING_BUFFER
         gtp_frame = ring_buffer_alloc(gtp_trace_buffer_size,
                           gtp_circular ? RB_FL_OVERWRITE
                                  : 0);
 #endif
         if (!gtp_frame) {
             ret = -ENOMEM;
             goto out;
         }
 #endif

跟踪下去,我们需要了解以下函数:

kmalloc/kfree
vmalloc/vfree
kzalloc
kcalloc

不熟的话,自行查阅资料吧,这篇文章[9]可以简单参考。


4. Linux内核中的工作队列

KGTP开启一个内核线程gtpd,来搞定所有的相关工作,看这里:

gtp_wq = create_singlethread_workqueue("gtpd");
     if (gtp_wq == NULL)
         goto out;

     {
         struct task_struct  *p;

         /* Get the task of "gtpd".  */
         gtpd_task = NULL;
         for_each_process (p) {
             if (strcmp(p->comm, "gtpd") == 0) {
                 if (gtpd_task != NULL)
                     printk(KERN_WARNING "KGTP: system have more than one gtpd.\n");
                 gtpd_task = p;
             }
         }
         if (gtpd_task == NULL) {
             printk(KERN_WARNING "KGTP: cannot get gtpd task.\n");
             goto out;
         }
         gtp_current_pid = gtpd_task->pid;
     }

这里比较好玩的是for_each_process这个宏,大家可以看下Linux源码。另外,create_singlethread_workqueue值得深入研究,一篇不错的参考文章[10]。

5. Linux内核中的等待队列

上面说到了work queue,那么如何通知它有哪些工作需要做呢?通过wait queue实现,看代码:

 static DECLARE_WAIT_QUEUE_HEAD(gtpframe_wq);

static void
 gtp_wq_add_work(unsigned long data)
 {
     /* Same with prev function, queue_work will wake up sometimes.  */
     queue_work(gtp_wq, (struct work_struct *)data);
 }

具体Tracepoint的信息的收集,需要理解一个核心的数据结构gtp_entry,

struct gtp_entry {
     union gtp_entry_u {
 #ifdef CONFIG_KPROBES
         /* For gtp_entry_kprobe.  */
         struct gtp_kp {
             struct kretprobe    kpret;
             struct tasklet_struct   stop_tasklet;
             struct work_struct  stop_work;
         } kp;
 #endif

 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) && defined CONFIG_UPROBES
         /* For gtp_entry_uprobe.  */
         struct gtp_up {
             struct inode        *inode;
             loff_t          offset;
             struct uprobe_consumer  uc;
         } up;
 #endif

         /* For gtp_entry_watch_static and gtp_entry_watch_dynamic.  */
         struct {
             int type;
             int size;
         } watch;
     } u;
     unsigned long       flags;
     enum gtp_entry_type type;
     ULONGEST        num;
     ULONGEST        addr;

     /* The actions that set $current help tracepoint get right regs.  */
     struct action       *get_regs;
     /* The actions for the condition.  */
     struct action       *cond;
     struct list_head    action_list;
     int         step;
     struct list_head    step_action_list;

     atomic_t        current_pass;
     struct gtpsrc       *printk_str;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
     /* This is to enable and disable an tracepoint.  */
     struct tasklet_struct   enable_tasklet;
     struct work_struct  enable_work;
     struct tasklet_struct   disable_tasklet;
     struct work_struct  disable_work;
 #endif
     enum gtp_stop_type  reason;

     struct gtp_entry    *next;

     /* Pid for this tracepoint.
        KGTP will use this pid to select Kprobe or Uprobe.
        And use this pid to get the inode for Uprobe if need.
        In default, this pid will bet set to gtp_current_pid.  */
     pid_t           pid;

     int         disable;
     ULONGEST        pass;
     struct gtpsrc       *src;
     /* Sometime, it will not same with action
        because action will be deleted.  */
     struct gtpsrc       *action_cmd;
 };

这里又涉及到了tasklet,代码中的体现,

tasklet_init(&tpe->u.kp.stop_tasklet, gtp_wq_add_work,
                  (unsigned long)&tpe->u.kp.stop_work);

if (enable && tpe->disable)
                 tasklet_schedule(>s->tpe->enable_tasklet);
             else if (!enable && !tpe->disable)
                 tasklet_schedule(>s->tpe->disable_tasklet);

关于tasklet的东西我还没有完全想清楚,接下来需要仔细研究一下。

6. set trace-buffer-size的代码实现

目前实现了simple mode,代码就不贴了,大家直接看这里的pull request[11]或者这个commit[12]。接下来时间允许的话,需要实现normal mode,即考虑trace frame数据的迁移,另外KGTP中很多核心的代码,比如涉及到SMP,内核锁,原子操作,Kprobe,Uprobe等等,都需要继续学习。

Fighting!!!

7. 参考链接

[1] http://www.huihoo.org/mirrors/pub/embed/document/debugger/ew_GDB_RSP.pdf
[2] http://blog.csdn.net/hmsiwtv/article/details/8759129
[3] https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html
[4] https://sourceware.org/gdb/current/onlinedocs/gdb/Packets.html
[5] https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets
[6] https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html
[7] https://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoint-Packets.html
[8] https://sourceware.org/gdb/current/onlinedocs/gdb/Starting-and-Stopping-Trace-Experiments.html
[9] http://www.cnblogs.com/dubingsky/archive/2010/04/20/1716158.html
[10] http://bgutech.blog.163.com/blog/static/18261124320116181119889/
[11] https://github.com/teawater/kgtp/pull/36
[12] https://code.csdn.net/Calmdownba/kgtp/commit/51bdefa458678b8d9b679d8188149f4d663f4415
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值