KernelShark调试笔记

KernelShark是Linux系统上的一个优化工具,最初是由红帽公司开发的,主要开发者是Steven Rostedt。

根据内核代码中的签名信息,Steven在2007年接手内核的ftrace模块,他可能是在开发ftrace的过程中,出于测试的需要,开发了建立在ftrace功能上的两个工具程序,一个是命令行形式的trace-cmd,另一个便是图形界面的KernelShark。

Steven在1998年读硕士时便参与Linux内核开发,在2006年加入RedHat,一直工作2016年。正是在RedHat工作时,Steven开发了ftrace和KernelShark。最初版本的KernelShark大约是在2010年时对外发布的。

KernelShark调试笔记_第1张图片Steven在Linux基金会官网上的照片 (来自LF官网)

我一直很喜欢KernelShark工具,常常使用这个工具,也总在讲Linux内核调试工具时介绍这个工具。

不知道是不是因为这个工具源自RedHat,在Ubuntu上,这个工具总是有些问题。比如在几年前的Ubuntu 16上,从Ubuntu仓库下载安装的的KernelShark菜单缺少重要的Record项。

KernelShark调试笔记_第2张图片

近日,在较新的Ubuntu 20.04上安装KernelShark,使用Ubuntu仓库安装,结果又是Record功能不工作。

KernelShark调试笔记_第3张图片

于是我只好又用几年前的老办法,自己下载代码,自己build。

和上次一样,我到KernelShark的官网下载了最新的代码。

但是构建时,发现构建方法变了,不如以前简单方便了。但装了一些依赖后,还是构建通过了。 

但运行时,启动Record功能,立刻出了问题。

gebox@gebox-VirtualBox:~/kernel-shark-kernelshark-v2.1.0/bin$ ./kshark-su-record 

QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'

free(): invalid pointer

./kshark-su-record: line 5: 13167 Aborted                 (core dumped) pkexec kshark-record -o ${PWD}/trace.dat

看起来是释放无效指针,glibc发起abort信号,紧急停车。

于是上调试器,追查。

有了调试器,很快定位到发起错误free的代码,即KsCaptureDialog.cpp的230行。

KernelShark调试笔记_第4张图片

找到出问题的函数,其代码如下:

KernelShark调试笔记_第5张图片

看来出问题的free释放的是来自kshark_tracecmd_local_plugins的字符串数组,是由tracefs_tracers返回的。

/** Get an array of available tracer plugins. */

char **kshark_tracecmd_local_plugins()

{

    return tracefs_tracers(tracefs_tracing_dir());

}

tracefs_tracers是libtracefs的接口函数,tracefs是ftrace的文件系统接口,主要开发者也是Steven。

根据librtracefs的文档,必须使用tracefs_list_free函数来释放tracefs_tracers返回的字符串数组。

The tracefs_tracers() function returns array of strings with the names of supported tracer
       plugins, located in the given tracing_dir directory. This could be NULL or the location of
       the tracefs mount point for the trace systems of the local machine, or it may be a path to
       a copy of the tracefs directory from another machine. The last entry in the array as a
       NULL pointer. The array must be freed with tracefs_list_free() API.

如此看来,这里直接使用free来释放,是个大bug,如果glibc不检查到,那么就可能把堆搞乱,导致更难发现的bug。

纠正了free问题后,重新编译,再运行,这下Record功能可以工作了,可以抓到事件和产生trace.dat文件了。但是打开文件分析时,KernelShark的界面挂住了,长时间没有更新,也没有反应。

KernelShark调试笔记_第6张图片

我是昨晚做上面的安装和调试工作的,改掉了一个bug后,没想到又出来一个。因为今天还要讲课,所以我想放弃新版本,改用老版本了。但是在要关闭电脑的时候,大脑中又有了继续战斗的念头。

于是,再次上调试器,切换UI线程,查找挂死原因。仔细一看,居然和白天讲课时讲的案例一模一样,UI线程在等待互斥量。

查找互斥量的拥有者线程,再找到等待这个互斥量的函数,查看源代码,发现了一个很明显的bug。

KernelShark调试笔记_第7张图片

789行获取锁,然后调用tracecmd_read_at函数,判断函数返回值,如果返回值为空,794行直接返回了,忘记释放锁。唉,这也太草率了。

在794行前面增加释放锁的代码,再次编译运行,这下终于可以正常工作了,熟悉的界面再次出现。

KernelShark调试笔记_第8张图片

需要说明的是,上面两个比较明显、也比较严重的Showstopper级别的bug并不是出自Steven之手,Steven在2016年时就离开RedHat,加入Vmware,在Vmware工作四年后,目前在Google工作。

在今晚准备写这个文章时,顺着Steven的Linkedin动态找到trace-cmd 3.0的发布说明,又到trace-cmd的git网页。

https://git.kernel.org/pub/scm/utils/trace-cmd/kernel-shark.git/commit/

偶然中看到一个7天前的补丁信息:

Branch Commit message Author Age

kernelshark kernelshark: Release input_mutex on not finding record Steven Rostedt (Google) 7 days

看详细描述:

KernelShark调试笔记_第9张图片

居然就是上面的第二个bug。看来Steven已经发现了这个问题。

查看git.kernel.org上第一个bug对应的源代码,bug还是在的。

KernelShark调试笔记_第10张图片

如此看来,git.kernel.org上的代码与kernelshark.org上的代码相比,fix掉了第二个bug,更新一些。

第一个bug也许与libtracefs的实现有关,也就是与发行版有关,或许在某些实现中,返回的字符串数组可以直接使用free来释放。但仔细看了Steven本人写的tracefs的实现,在注释里也明确说了要使用专门的函数释放,言外之意是不可以使用free。

https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/tree/src/tracefs-events.c

KernelShark调试笔记_第11张图片

如此看来,第一个bug确实是bug无疑。

参考资料:

1. Steven的Linkedin页面

https://www.linkedin.com/in/steven-rostedt-0159437a/

2. Steven的个人网站

http://goodmis.org/ 

(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)

*************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

KernelShark调试笔记_第12张图片

也欢迎关注格友公众号

KernelShark调试笔记_第13张图片

你可能感兴趣的:(内核,java,linux,python,编程语言)