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

求助大神! C++程序异常挂断

  •  
  •   tengtengking · 11 天前 · 1455 次点击

    程序会异常断掉。用 gdb 运行 core 文件,通过 bt 命令定位到 movdqu 指令,原因不明。 哪位大神可以指导下,有什么手段或者方法,可以帮助定位到程序异常原因。

    core 文件 bt 输出如下:

    gdb ./a.out core.out
    
    ………………
    ………………
    
    Program terminalted with signal 11, segmentation fault.
    #0 __memmove_ssse3_back () at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:1658
    1658		movdqu -0x40(%rsi),%xmm4
    (gdb) bt
    #0 __memmove_ssse3_back () at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:1658
    #1 0x0000000000000000 in ?? ()
    (gdb) quit
    
    
    第 1 条附言  ·  11 天前
    代码有几万行,贴代码不太现实。
    第 2 条附言  ·  6 天前
    尝试了各种方法之后,终于用 valgrind 的 memcheck 复现问题后找到了原因,valgrind 检测到 memcpy 处有 invalid write。感谢各位帮助
    38 回复  |  直到 2019-08-16 17:19:07 +08:00
        1
    augustheart   11 天前
    这错误日志……
    你可真是个天才……
    如果是这一行,不用想,只可能是 rsi 为空
        2
    misaka19000   11 天前
    我不会 C++ 但我觉得把源码放出来是个很合适的方式
        3
    Mirana   11 天前   ♥ 1
    memcopy 挂了 而且栈是空的。。。
    在代码里的 memcpy 前加些 log 看看把
        4
    gaokevin163   11 天前
    第一直接上代码,第二用 DEBUG 编译一下,再看错误
        5
    tengtengking   11 天前
    @augustheart 能麻烦详细说下吗,rsi 为空一般什么原因
        6
    TomStark   11 天前   ♥ 1
    打印全部堆栈确定调用 memcpy 的位置,贴一点具体代码更好找原因
        7
    sylxjtu   11 天前 via Android   ♥ 1
    编译时加上 sanitizer 再复现问题,就能知道错哪儿了
        8
    augustheart   11 天前
    @tengtengking
    memcpy(des,src,size);
    将-0x40(%rsi)所指向的内存写入到 xmm4 (后续再将 xmm4 写入到 des )。这不只可能是指针所指向的内存访问异常么? at&t 汇编我不太熟,应该是这个样子。(就算我记反了吧反正 des 和 src 两个指针至少有一个异常了)

    要查问题你要看 memcpy 的参数吧。你这个根本看不出来。
        9
    lcdtyph   11 天前
    这个 bt 结果很奇怪啊,为什么起始 rip 是 0x0000000000000000 呢,感觉 binary 或者 core 也有问题
        10
    augustheart   11 天前
    @tengtengking 我不懂 linux 调试,你这个肯定要想办法打印出栈到调用的上下文的,你既然是 linux 开发应该比我熟。memcpy 这种函数到处都是,只看这一处根本不知道是哪个地方调用的吧。
        11
    lcdtyph   11 天前
    @augustheart
    这个程序是在进入 main 之后崩溃的么,根据你贴出来的这点信息,好像连 main 函数都没进去就崩了
        12
    nicebird   11 天前
    空指针
        13
    tengtengking   11 天前
    @sylxjtu 请问 sanitizer 是 g++的参数吗, 搜了下没太明白是什么意思
        14
    tengtengking   11 天前
    @lcdtyph 进入 main 函数了, 运行几小时才会出这个问题。根据生成的 coredump 文件,堆栈看不到东西
        15
    lcdtyph   11 天前   ♥ 1
    @tengtengking

    那么可能是哪里的内存越界把堆栈破坏了所以 gdb 没法回溯这里的返回地址,也可能是你的 libc 或者 libpthread 没有 unwind 符号。这种情况下 gdb 几乎没什么作用,需要其他信息。

    有日志吗?从日志内容能推出大致什么地方出的错么?是私有项目么?
        16
    tengtengking   11 天前
    @lcdtyph 应该是破坏了堆栈。日志都是业务相关的,对排错没太大帮助。正准备把所有的 memcpy 都打印一遍
        17
    Mirana   11 天前
    rsi (source index)源变址寄存器,与 rds 段寄存器联用,可以访问数据段中的任一个存储单元 函数调用时的第 2 个参数

    查了下 rsi 估计是 memcpy 的源地址指针是空
        18
    bookit   11 天前
    这种可以上 function call log

    进入退出每一个函数都记录一遍
        19
    lcdtyph   11 天前
    @tengtengking #16
    你可以在编译选项里加上 `-fsanitize=address -fno-omit-frame-pointer` 让 gcc 插入 sanitizer 的代码,再复现问题看看报错
        20
    Mirana   11 天前   ♥ 1
    提供个方案

    可以自己写一个 memcpy 函数,编个 lib.a,把堆栈打印出来,把 src 打印出来,然后调用真正的 memcpy

    启动 binary 的时候这个样子 LD_PRELOAD=lib.a ./binray
        21
    nvioue   11 天前   ♥ 4
    好了 楼主这个事情本身就证明了用 c/cpp 开发程序是多么的痛苦; 用 java/.net 基本不会遇到这种鬼事情. 系统及其友好的抛出完整栈给你.

    以下来自一个刚转 java 的 7 年 cpp 一线人员的排查经验

    1 先确定 core 文件和程序文件是否对应. core 文件很多,一般需要确定这个 core 文件是你的程序生成并且是最近一次的. 这一步要是弄错了你后面怎么查都没用. 是不是很刺激

    2 这个 bt 基本毫无价值, 请确认是否有多线程. info thread (好像是这个?), 如果有多线程请依次确认每个线程的 bt 是否能帮助你

    3 如果是单线程,我可以 100%告诉你这个是栈 stack 被破坏导致 gdb 的栈回溯功能失效. 这个 core 文件基本没有太大帮助信息了, 如果有其他大佬有方法欢迎分享.

    接下来说遇到这种 stack 被破坏如何解决. 基本表现为 bt 信息乱七八糟, 或者直接就是一大排问号.
    正常的栈信息最底层基本是 main 函数开始并且函数信息完整.
    除非你的代码有使用到协程,内联汇编等黑魔法.
    此类问题原因多种多样, 因为栈上的内存含有 cpp 函数栈调用信息, 一旦 rbp 的值被破坏就 GG

    因为有阵子不搞 cpp 了 , 不太记得所有招式了.
    1 用 valgrind 带程序跑, 尝试重现. 如果你知道 valgrind 是干啥的,应该能解决了, 记得用合适的参数, 不然他的输出信息多到看不完
    2 如果#1 无法重现, 寻找重现方法, 找到对应的功能代码做 review (非常难)
    3 增加编译选项 -g -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 祝你好运(我以前基本是用这个,成功率较高)
    4 使用 cpplint 等强静态检查工具尝试找出可疑代码
        22
    LitostCheng   11 天前   ♥ 1
    [backtrace 了解一下]
    在 Linux 中如何利用 backtrace 信息解决问题:
    https://blog.csdn.net/jxgz_leo/article/details/53458366
        23
    tengtengking   11 天前
    @nvioue 非常感谢。 方法 1 已经尝试过了 , 发现 用上 valgrind 后运行变得十分缓慢,生产环境影响比价大,没能复现。
        24
    tengtengking   11 天前
    @nvioue 我去学习下你的这几个编译参数,尝试一下
        25
    laminux29   11 天前   ♥ 1
    我建议题主应该学点 java 软件工程的方法,这样就不至于那么难堪,居然要搬出 gdb 对着一堆啥玩意来调试。

    第一步,建议题主找一款靠谱的日志框架,先把日志框架搭起来。

    第二步,通过日志判断出问题的大概位置,然后对这区域的代码,进行源码调试。
        26
    boywhp   11 天前
    写一个 memcpy 封装函数, 每次调用时 printf 参数到屏幕或日志, 尤其注意检查 len 是否异常
        27
    lingxi27   11 天前
    这种问题试试日志配合单元测试
        28
    SPACELAN   11 天前
    sse 指令,,看看栈和超操作的地址有没有对齐 16 字节
        29
    Chenamy2017   10 天前   ♥ 1
    1.dmesg 看程序出错的位置
    2.objdump -d xxx 找到这个地址,基本能确定是哪个函数的那行代码了
        30
    tengtengking   10 天前 via Android
    @laminux29 日志用的 glog,出错的地方也没什么信息
        31
    eliteYang   10 天前
    至少吧完整栈和最后一行可读代码贴出来啊
        32
    whi147   10 天前
    可能是指针生命周期结束了
        33
    tengtengking   10 天前
    @eliteYang 我也想定位到你说的这两个地方。可是定位不到呀
        34
    hsuehsen   10 天前   ♥ 1
    bt 这样的信息,说明跑的不是 debug 版; 编译个 debug 版本,然后尝试重现问题

    最重要的问题是,
    1. 重现问题
    2. 重现问题
    3. 重现问题

    若无法重现,那只能是过代码。通过楼上一些提供的方法,确定初步位置,然后前后过一边所有 memcpy 相关的操作。大概率呢,是两种可能,
    1. 内存越界(这种比较坑)
    但是可怀疑的地方也好确定

    2. 空指针
    这个大概率是某个流程处理的问题,比如某个判断失败还是继续往下走
        35
    tengtengking   10 天前 via Android
    @hsuehsen 这是 debug 版本的
        36
    hsuehsen   10 天前
    @tengtengking
    你这 bt 的符号都没有,明显不是 debug 版,或者部分以来的库或子模块是没有调试符号的。要么就是把符号都去掉了
        37
    tengtengking   10 天前
    @Chenamy2017 dmesg 看到看到的 ip, 在 objdump 的输出里看不到,这是怎么回事? dmesg 显示错在 libc-2.12.so
        38
    Chenamy2017   9 天前
    恰巧我也刚遇到了在 C 库的错误,我的问题是空指针引起。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2988 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 23ms · UTC 01:03 · PVG 09:03 · LAX 18:03 · JFK 21:03
    ♥ Do have faith in what you're doing.