CSDN开源夏令营 - 第八周工作总结&&结题总结
转眼,暑期两个月已经过去了,虽然严格意义上讲,我是没有暑假的。参加首届CSDN开源夏令营,自己感觉还是非常开心的,尤其是能够做自己感兴趣的方向,偏底层开发的KGTP,这属于Linux Kernel开发的范畴了。能够选T大作为mentor,是一件让我兴奋的事情,从Hello GCC 2012开始认识到KGTP,就对这个项目产生了向往,也对身为GDB开发者的T大由衷佩服。作为国内开源工具链workshop的发起者,能够组织来自全国,包括台湾的众多工具链高手聚集一堂,这不是简简单单就能做到的。
虽然最后的结果不那么令人满意,但是本着学习的心态接触这个项目,一开始是没有太多自信能够完成的,只能是做一步算一步,走一步学一步,跌跌撞撞走来,自己对Linux Kernel的开发算是略窥门径,对于如何入门,如何查阅资料,如何在看不懂source code的时候去补充相应的理论知识有了一定的心得。这两个月的项目期,一直在用evernote记录自己的进度,整理问题并且寻找总结方案。从初始KGTP,开始知道Linux Tracing是何物,了解到FTrace,perf,LTTng,知道了unified Ring Buffer之争;到知晓GDBRSP,再到了解Linux Kernel中的内存分配,锁,SMP支持,模块加载和释放等等,这一路收获颇丰。最后虽然勉强实现了“set trace-buffer-size”的feature,但还有很多需要改进,比如如何支持数据的迁移,如何规范代码风格,如何优雅地设计将自己的feature融入到KGTP中。
1. 初始KGTP
记得Hello GCC 2012上,自己刚研究生入学,本科的时候玩过几天嵌入式Linux,读研后加入了开源软件协会,有机会能够听到Hello GCC,因为这个活动正是我们协会承办的,自己是志愿者。正是当天会议上,听到了T大的KGTP,当时就感觉,不明觉厉,这玩意太神奇了!当时就暗暗决心,下来要研读一下KGTP的代码,好好学习!无奈水平实在有限,不了了之。
2013年的Hello GCC,依然由我们协会承办,只是那时候我已经是开源协会的会长,当天也做了一些志愿者的工作,有幸和Hello GCC的几个发起者以及台湾的一些开源人士坐在一起吃饭。
2014年的Hello GCC,将在下周六举行,此时不再担任开源协会会长,但是开源之心早已坚定。
正是和Hello GCC的几次接触,埋下了参加此次开源夏令营,并且选择KGTP的种子。
2. Linux Tracing
接触KGTP之前,从没搞过Kernel开发,唯一的积累是看了一本书,叫做《Linux内核修炼之道》,算是对一些基本概念有了一些了解。开始正式做KGTP的项目之后,才知道Tracing作为Kernel的一个infrastructure是多么的重要!Tracing和Debugging不分家,强烈大家看一下[1]和[2]。其中[1]是一篇paper,名为Ptrace, Utrace, Uprobes: Lightweight, Dynamic Tracing of User Apps,由于ptrace过多的overhead,以及不是POSIX标准接口,带来了跨平台的麻烦,出现了utrace这个framework,相当于一个unified layer,ptrace可以作为其client,基于utrace,作者开发了uprobes,用于trace user space application,后面主要讲了uprobes的实现及未来。[2]是对Kernel中的trace的一个综述,可以从这里了解一下Kernel中的trace大概是个什么东西。
提到Tracing,则需要知道probe,什么是probe point呢?
Probe point is a generic term for the following three Streams Debugger concepts:
* Break points used for suspending execution.
* Trace points for tracing data flowing into or out of an operator.
* Inject points for inserting new tuples and punctuations into the application's data flow at a specified port.
等大家对Tracing有了基本认识之后,可以再看看[3]这个slides,作为一个回顾,这里对比了Ftrace,perf,LTTng,是一个非常好的参考资料。
3. GDBRSP
这个是阅读KGTP代码的第一个障碍,只有知道了GDBRSP是如何工作的,才能了解到GDB和KGTP是如何通信的。再把那个data flow拿出来,
GDB ——> GDBRSP ——> SYSFS ——> KGTP ——> Kernel
Kerne的Tracingl需要相应地debug info,因此需要配置内核如下:
General setup —>
[*] Kprobes
[*] Enable loadable module support —>
Kernel hacking —>
[*] Debug Filesystem
[*] Compile the kernel with debug info
如果是Distro,需要安装Linux内核调试镜像和Linux内核源码包和开发包。只有这样,Kernel才能被GDB加载调试。
GDBRSP则是Remote Serial Protocol,用于GDB远程调试发包,但是也支持File I/O和Console,而KGTP正是利用了GDBRSP对于File I/O的支持,完成GDB和KGTP的通信,在接受到GDB发送的Packet后,KGTP进行解析。而通信的介质则是通过debugfs或者其他kernel space和user space进行数据交换的接口进行([4])。KGTP接收到了GDB的指令后,基于Kprobes和Uprobes进行内核和用户应用程序的trace。
关于GDBRSP更加具体的信息,可以参考之前的周总结。GDB的工作原理,可以看看[5]。
4. Ring Buffer
这个看看那个unified Ring Buffer之争[6],就可以很清楚了。再参考下Week 5的总结吧[7],这里不再重复了。
5. 实现set trace-buffer-size的feature
让我们顺着GDB和KGTP的通信分析。首先,GDB的Query Packet[8]:
Packets starting with ‘q’ are general query packets; packets starting with ‘Q’ are general set packets. General query and set packets are a semi-unified form for retrieving and sending information to and from the stub.
然后,KGTP跟GDB的通信分析:
driver函数gtp_write,具体处理函数为gtp_gdbrsp_**,比如:
gtp_gdbrsp_QT 处理QT的packet
gtp_gdbrsp_qtstart Start Trace Experiments,注册kprobe,uprobe以及watchpoints,hardware breakpoints等,并分配存储空间
gtp_qdbrsp_qtstop Stop Trace Experiments,flush work queue,tasklet_kill,以及unregister在qtstart注册的所有probe points和一些回调函数
而添加“set trace-buffer-size”的支持,对应的命令为:
set trace-buffer-size n
set trace-buffer-size unlimited
Request that the target use a trace buffer of n bytes. Not all targets will honor the request; they may have a compiled-in size for the trace buffer, or some other limitation. Set to a value of unlimited or -1 to let the target use whatever size it likes. This is also the default.
对应的Trace Packet为:
‘QTBuffer:size:size’
This packet directs the target to make the trace buffer be of size size if possible. A value of -1 tells the target to use whatever size it prefers.
而对应KGTP,目前已经支持了:
set circular-trace-buffer on
set circular-trace-buffer off
Choose whether a tracing run should use a linear or circular buffer for trace data. A linear buffer will not lose any trace data, but may fill up prematurely, while a circular buffer will discard old trace data, but it will have always room for the latest tracepoint hits.
show circular-trace-buffer
Show the current choice for the trace buffer. Note that this may not match the agent’s current buffer handling, nor is it guaranteed to match the setting that might have been in effect during a past run, for instance if you are looking at frames from a trace file.
对应的Trace Packet为:
‘QTBuffer:circular:value’
This packet directs the target to use a circular trace buffer if value is 1, or a linear buffer if the value is 0.
可以参考gtp.c中的函数gtp_gdbrsp_qtbuffer,按照以上分析对相应的Packet进行解析处理即可。
6. 总结&&感谢
两个月的时间匆匆过去了,很感谢T大的支持,自己一撇了Linux Kernel开发的大门,用到的知识点大体总结如下:
(1)Linux内核的同步机制(锁,信号量等)
(2)字符驱动程序的实现原理,主要是GDB和KGTP的通信需要用到
(3)Linux内核module的编写,因此KGTP是以一个module的形式存在的
(4)Linux内核的tasklet和workqueue,KGTP的后台进程是一个守护进程gtpd
(5)Ring buffer的实现,Linux内核trace的RB的实现以及KGTP自身RB的实现
(6)GDB和GDBRSP
(7)Linux的SMP
(8)Linux内核的内存分配
其它的就是一些工具的使用了,Git,Python,Shell,当然还有Linux系统!
路漫漫其修远兮,希望自己的水平上去之后,能够再投入到KGTP的开发中去,随着这个项目一起成长!感谢CSDN,开源夏令营对于国内的开源事业的发展很有意义;感谢T大,Hello GCC开源工具链大会对国内开源技术氛围的增长功不可没!
Hello Open Source World!
7. 参考链接
[1] http://www.linuxsymposium.org/archives/OLS/Reprints-2007/keniston-Reprint.pdf
[2] http://elinux.org/Kernel_Trace_Systems
[3] http://events.linuxfoundation.org/sites/events/files/lcjp13_zannoni.pdf
[4] http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto.html#toc1
[5] http://www.slideshare.net/libfetion/gdb-principle
[6] http://lwn.net/Articles/388978/
[7] http://blog.csdn.net/calmdownba/article/details/38659317
[8] https://www.sourceware.org/gdb/onlinedocs/gdb/General-Query-Packets.html