最近我自己开发一个小项目librr玩,是一个本地索引文件夹的工具,在这个项目里面我用到了eventmachine,用来同时跑一个http服务器,监控两个进程。
开发过程中遇到一个bug:很多时候会发生CPU冲高的现象,并且内存消耗会快速变大。 这个问题可以重现,所以是好bug, 但是没有找到办法绕过去,如果不解决,这个项目就废了。
这种没有数据提供的问题一般相对难解一些, 如果是报错,就可以定位到问题所在, 如果数据结果有问题,可以二分法定位出错位置。 为了能够调查原因,我研究了如何在ruby下调试内存泄漏。 用这个方法,我发现在出现问题的进程里面,创建了无数的Proc对象:
1 2 3 4 5 6 |
|
采样几个Proc,用proc.source_location
,定位到了是eventmachine里面run_deferred_callbacks
中的一个地方,但是为什么会发生这种现象?我走到了死胡同里面。
run_deferred_callbacks
是eventmachine的核心方法,用来处理回调方法,我于是修改了librr的代码,
一个个去掉回调,看看哪里有问题,结果发现,所有的回调都去掉了,还是有这样的问题发生,
那么应该是跑evenmachine的主线有问题了。
然后我用二分法隔离代码,终于发现问题的所在点,这是一个最小重现代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
在异常捕捉里面跑eventmachine,同时调用回调,就会出现问题。修改之后,问题不再发生。
如果有时间的话,我可能会研究一下eventmachine源码,定位到核心地点,这个bug才算真正解决掉。 但是现在我要先把librr做完,先提交了一个bug,如果他们没有受理的话,我再继续寻找核心问题所在。
总结一下学到了什么:
- 二分法是定位bug的核心方法。
- 这个bug消耗了我半周的时间,占据了开发过程中很大一部分比例,bug是项目的时间黑洞。
- debug需要的是逻辑思考,数据越多,脑子越清醒,思路越开阔,bug解决就越快。
- 各种debug工具,比如gdb,perftools.rb,帮助了解状况,验证猜想,极大地提升效率。