V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
goforwardv2
V2EX  ›  C++

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

  •  
  •   goforwardv2 · 266 天前 · 2169 次点击
    这是一个创建于 266 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    第 1 条附言  ·  266 天前
    出自本人面试时的一个问题, 本来问我 dump 调试经验, 我就说了一个空指针访问导致崩溃的问题。排查发现是一个线程对指针访问时没有加锁, 把指针设置成 nullptr, 其他线程用到这个指针时崩溃了。 然后面试官就问, 能不能通过分析 dump 查出指针是被哪个线程修改了? 我说在线调试通过加条件断点或 watch 可以查出。纯 dump 查出来。 但是当时看面试官的表情是, 有办法, 我结束后应该问问是啥办法, 最后也没问。 所在在这里问问各位大牛!
    20 条回复    2020-08-23 07:55:05 +08:00
    jonah
        1
    jonah   266 天前
    haozhang
        2
    haozhang   266 天前 via Android
    你完全可以打日志或者直接 print 找出来是哪个函数把变量设置成 null,或者单步调试,看哪个 thread 把他弄成了 null,然后看调用栈最上面是到哪个函数,关键点在于找出出错的函数。然后看里面用到的信号量或者互斥锁有没有出错。
    fengjianxinghun
        3
    fengjianxinghun   266 天前
    @haozhang 单步往往就没有 race 了
    goforwardv2
        4
    goforwardv2   266 天前
    @jonah 面试的问道的一个问题, 不让加条件断点或 watch 查看, 所以我没想出来答案
    dearmymy
        5
    dearmymy   266 天前
    这个够呛。dump 只保留当时的环境。之前谁把他置 null 应该找不到了
    goforwardv2
        6
    goforwardv2   266 天前
    @dearmymy 是的,有的说修改指针的线程堆栈内存里会残留指针的地址&p, 但试了下, 并没有
    429839446
        7
    429839446   266 天前
    申请内存的时候整页拿,然后要释放的时候不要归还,直接把页面设置成不可访问,然后等 segv ?
    abutter
        8
    abutter   266 天前   ❤️ 1
    以个人经验来看,被写成 0 更多的是 memset 之类的,不一定是锁写的不好。

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

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


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

    或许他 /她只是考考你的思路,或许他 /她最近刚好用这种办法结果过这样的问题,或者最得意的事情是用这种办法解决了问题。如果是第一种,那么无可厚非,如果是第二 /三种,那么这种说法无异于守株待兔。这是我的猜想,不过不重要。
    goforwardv2
        19
    goforwardv2   262 天前
    @abutter 或许是你说的思路, 但我自己写代码试过, 线程对共享的指针修改时(比如赋值 nullptr), 反汇编代码是直接写死的指针的地址值, 没有留下一丝丝指针地址的迹象, 大牛如果有相关的经验, 可分享下
    abutter
        20
    abutter   257 天前
    @goforwardv2 只要是共享地址空间,多人协作的 C/C++ 项目都会有类似的问题。根据我的经验,判定这种问题的第一个步骤是重现,找出重现规律,第二是看越界 /覆盖是根据特定的变量走,还是随机特定的地址,然后利用 debug 工具来跟踪特定的地址访问。同时,lint 工具检查不可少,代码 review 也有神效。
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2829 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 01:07 · PVG 09:07 · LAX 18:07 · JFK 21:07
    ♥ Do have faith in what you're doing.