c++ 可以通过 dump 或 core 调试分析出哪个线程修改了变量吗?

2020-08-13 09:58:03 +08:00
 goforwardv2

多个线程对共享变量可修改,如果共享变量是一个指针, 而由于程序 bug, 锁没有用好, 导致一个线程把指针置为 nullptr 后, 其他线程用此指针时程序崩溃,产生 dump 或 core, 那么通过 windbg 或 gdb 对 dump 或 core 进行分析, 可分析出指针被哪个线程置为 nullptr 了吗

3856 次点击
所在节点    C++
20 条回复
jonah
2020-08-13 10:01:00 +08:00
haozhang
2020-08-13 10:40:19 +08:00
你完全可以打日志或者直接 print 找出来是哪个函数把变量设置成 null,或者单步调试,看哪个 thread 把他弄成了 null,然后看调用栈最上面是到哪个函数,关键点在于找出出错的函数。然后看里面用到的信号量或者互斥锁有没有出错。
fengjianxinghun
2020-08-13 10:43:21 +08:00
@haozhang 单步往往就没有 race 了
goforwardv2
2020-08-13 10:45:28 +08:00
@jonah 面试的问道的一个问题, 不让加条件断点或 watch 查看, 所以我没想出来答案
dearmymy
2020-08-13 10:55:15 +08:00
这个够呛。dump 只保留当时的环境。之前谁把他置 null 应该找不到了
goforwardv2
2020-08-13 11:03:40 +08:00
@dearmymy 是的,有的说修改指针的线程堆栈内存里会残留指针的地址&p, 但试了下, 并没有
429839446
2020-08-13 11:12:50 +08:00
申请内存的时候整页拿,然后要释放的时候不要归还,直接把页面设置成不可访问,然后等 segv ?
abutter
2020-08-13 11:29:23 +08:00
以个人经验来看,被写成 0 更多的是 memset 之类的,不一定是锁写的不好。

第一步,先判断出错是否总是跟共享变量走。在变量前添加一些无用的空间,或者调整共享变量的最终链接地址,看现象是否依然。如果不出现,或者出现在别的地方,可以倾向非锁造成的。

第二步,如果是 memset 之类的造成的,一般会造成前后的区域都被改写,那么就看看这段区域是否有啥特征,可以进行保护。如果是变量的问题,那么看代码,尤其是异常分支,或者 lint 工具可能会更快的,还有就是共享变量变成 atomic 访问,逐个的去掉锁看看有什么情况发生。
lbmjsls1
2020-08-13 11:50:03 +08:00
首先,你的调查方向就错了,我们遇到太多这样的问题了,尤其是涉及到网络多线程线上崩溃,自己测试数据少还触发不了。
这种问题解决,一般是编译 d 版本,附带调试信息,增加崩溃写入 dump 的功能。在崩溃的时候找到崩溃的地方,这个地方信息很重要,一般可以知道调用堆栈和崩溃的变量,然后就是打日志,就是与这个变量有关的,比如你说的访问了 nullptr 的指针,那么,这个指针进来的时候不应该是 nullptr,那么你应该在这个指针使用的时候判断,如果是 nullptr,就打印出来,并且把设置为 nullptr 的地方打印出来。这样一步步的上溯,找到最终出问题的地方


按照你的描述,应该是:1.初始化的变量,没有复制就使用了 2.释放的变量,没有标记,又使用了 3.逻辑错误,把正常的数据复制成了 nullpt 或是释放错了对象
lbmjsls1
2020-08-13 11:52:40 +08:00
还有一点就是,有时候会出现莫名其妙的问题,比如网络接收消息,你也不知道网络是否正常,或是收到一半就断了,你首先要解决人为引起这个问题的 bug,然后在这个地方增加逻辑判断,如果是 nullptr,应该怎么出错处理
newmlp
2020-08-13 12:21:17 +08:00
建议在修改的地方打日志,core 是看不出来的
jonah
2020-08-13 12:55:48 +08:00
这题目出的有点稀里糊涂,不知道面试官想考察啥。首先指针置为 nullptr,跟锁没用好没啥关系,用好了也可能置为 nullptr 。
再举个极端点的例子:某个线程 7 天前把指针置为 nullptr 后继续执行其它逻辑,7 天后程序挂了,不可能仅靠 core 就能把这个线程揪出来,这个信息已经丢失了。
boyhailong
2020-08-13 15:39:53 +08:00
@jonah 如果是考 debug 能力不仅仅体现在 gdb 调试技巧吧,从代码、日志找到问题解决就行了。同没看出面试目的
feelapi
2020-08-13 18:00:55 +08:00
这么问是 zb 。dump 数据是快照。多线程修改数据,时间点都过去了,dump 也抓不住啊。
justforlook44444
2020-08-13 20:15:27 +08:00
debug hacks 第四章?
hardwork
2020-08-13 20:35:51 +08:00
你都知道崩溃当时是空指针了,肯定是某个线程改了,这个看代码就可以解决了吧。coredump 应该是崩溃时的虚拟内存映射。
laminux29
2020-08-13 20:41:18 +08:00
dump 调试是想干嘛?觉得人生太漫长?直接源码+条件断点+日志来调试不行吗?
abutter
2020-08-14 08:21:05 +08:00
可能面试官的意思是,别的线程恰好刚刚修改成空指针或者修改之后还没有来得及做更复杂的事情就产生 core dump 了,然后看看其他线程里面是否有对应的寄存器保存有数据跟共享变量的地址一致或者接近,然后进行分析。

或许他 /她只是考考你的思路,或许他 /她最近刚好用这种办法结果过这样的问题,或者最得意的事情是用这种办法解决了问题。如果是第一种,那么无可厚非,如果是第二 /三种,那么这种说法无异于守株待兔。这是我的猜想,不过不重要。
goforwardv2
2020-08-17 11:36:17 +08:00
@abutter 或许是你说的思路, 但我自己写代码试过, 线程对共享的指针修改时(比如赋值 nullptr), 反汇编代码是直接写死的指针的地址值, 没有留下一丝丝指针地址的迹象, 大牛如果有相关的经验, 可分享下
abutter
2020-08-23 07:55:05 +08:00
@goforwardv2 只要是共享地址空间,多人协作的 C/C++ 项目都会有类似的问题。根据我的经验,判定这种问题的第一个步骤是重现,找出重现规律,第二是看越界 /覆盖是根据特定的变量走,还是随机特定的地址,然后利用 debug 工具来跟踪特定的地址访问。同时,lint 工具检查不可少,代码 review 也有神效。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/697865

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX