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

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

  •  
  •   tengtengking · 2019-08-14 17:17:20 +08:00 · 5400 次点击
    这是一个创建于 1958 天前的主题,其中的信息可能已经有所发展或是发生改变。

    程序会异常断掉。用 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 条附言  ·  2019-08-14 17:54:00 +08:00
    代码有几万行,贴代码不太现实。
    第 2 条附言  ·  2019-08-19 11:41:36 +08:00
    尝试了各种方法之后,终于用 valgrind 的 memcheck 复现问题后找到了原因,valgrind 检测到 memcpy 处有 invalid write。感谢各位帮助
    40 条回复    2020-02-04 10:36:30 +08:00
    augustheart
        1
    augustheart  
       2019-08-14 17:39:34 +08:00
    这错误日志……
    你可真是个天才……
    如果是这一行,不用想,只可能是 rsi 为空
    misaka19000
        2
    misaka19000  
       2019-08-14 17:42:07 +08:00
    我不会 C++ 但我觉得把源码放出来是个很合适的方式
    Mirana
        3
    Mirana  
       2019-08-14 17:46:56 +08:00   ❤️ 1
    memcopy 挂了 而且栈是空的。。。
    在代码里的 memcpy 前加些 log 看看把
    gaokevin163
        4
    gaokevin163  
       2019-08-14 17:49:41 +08:00
    第一直接上代码,第二用 DEBUG 编译一下,再看错误
    tengtengking
        5
    tengtengking  
    OP
       2019-08-14 17:57:09 +08:00
    @augustheart 能麻烦详细说下吗,rsi 为空一般什么原因
    TomStark
        6
    TomStark  
       2019-08-14 18:10:41 +08:00   ❤️ 1
    打印全部堆栈确定调用 memcpy 的位置,贴一点具体代码更好找原因
    sylxjtu
        7
    sylxjtu  
       2019-08-14 18:12:16 +08:00 via Android   ❤️ 1
    编译时加上 sanitizer 再复现问题,就能知道错哪儿了
    augustheart
        8
    augustheart  
       2019-08-14 18:22:07 +08:00
    @tengtengking
    memcpy(des,src,size);
    将-0x40(%rsi)所指向的内存写入到 xmm4 (后续再将 xmm4 写入到 des )。这不只可能是指针所指向的内存访问异常么? at&t 汇编我不太熟,应该是这个样子。(就算我记反了吧反正 des 和 src 两个指针至少有一个异常了)

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

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

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

    查了下 rsi 估计是 memcpy 的源地址指针是空
    bookit
        18
    bookit  
       2019-08-14 19:57:03 +08:00
    这种可以上 function call log

    进入退出每一个函数都记录一遍
    lcdtyph
        19
    lcdtyph  
       2019-08-14 19:59:53 +08:00
    @tengtengking #16
    你可以在编译选项里加上 `-fsanitize=address -fno-omit-frame-pointer` 让 gcc 插入 sanitizer 的代码,再复现问题看看报错
    Mirana
        20
    Mirana  
       2019-08-14 20:01:05 +08:00   ❤️ 1
    提供个方案

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

    启动 binary 的时候这个样子 LD_PRELOAD=lib.a ./binray
    nvioue
        21
    nvioue  
       2019-08-14 20:23:52 +08:00   ❤️ 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 等强静态检查工具尝试找出可疑代码
    LitostCheng
        22
    LitostCheng  
       2019-08-14 20:36:28 +08:00   ❤️ 1
    [backtrace 了解一下]
    在 Linux 中如何利用 backtrace 信息解决问题:
    https://blog.csdn.net/jxgz_leo/article/details/53458366
    tengtengking
        23
    tengtengking  
    OP
       2019-08-14 21:30:04 +08:00
    @nvioue 非常感谢。 方法 1 已经尝试过了 , 发现 用上 valgrind 后运行变得十分缓慢,生产环境影响比价大,没能复现。
    tengtengking
        24
    tengtengking  
    OP
       2019-08-14 21:31:07 +08:00
    @nvioue 我去学习下你的这几个编译参数,尝试一下
    laminux29
        25
    laminux29  
       2019-08-14 21:57:02 +08:00   ❤️ 1
    我建议题主应该学点 java 软件工程的方法,这样就不至于那么难堪,居然要搬出 gdb 对着一堆啥玩意来调试。

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

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

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

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

    2. 空指针
    这个大概率是某个流程处理的问题,比如某个判断失败还是继续往下走
    tengtengking
        35
    tengtengking  
    OP
       2019-08-15 11:30:24 +08:00 via Android
    @hsuehsen 这是 debug 版本的
    hsuehsen
        36
    hsuehsen  
       2019-08-15 14:33:11 +08:00
    @tengtengking
    你这 bt 的符号都没有,明显不是 debug 版,或者部分以来的库或子模块是没有调试符号的。要么就是把符号都去掉了
    tengtengking
        37
    tengtengking  
    OP
       2019-08-15 19:44:29 +08:00
    @Chenamy2017 dmesg 看到看到的 ip, 在 objdump 的输出里看不到,这是怎么回事? dmesg 显示错在 libc-2.12.so
    Chenamy2017
        38
    Chenamy2017  
       2019-08-16 17:19:07 +08:00
    恰巧我也刚遇到了在 C 库的错误,我的问题是空指针引起。
    peiqing9003ah
        39
    peiqing9003ah  
       2020-01-10 15:46:34 +08:00
    @tengtengking 兄弟, 能不能告诉我你的解决方案 是怎么用 valgrind attach 到服务程序的, 这是运行的服务进程呐!
    tengtengking
        40
    tengtengking  
    OP
       2020-02-04 10:36:30 +08:00
    @peiqing9003ah 直接用 valgrind 运行程序。valgrind --leak-check=full --show-reachable=yes --trace-children=yes --log-file=./valgrind.log ./a.out
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5345 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 09:27 · PVG 17:27 · LAX 01:27 · JFK 04:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.