求助大神! C++程序异常挂断 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tengtengking
V2EX    C++

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

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

    程序会异常断掉。用 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     Solana     885 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 22:30 PVG 06:30 LAX 15:30 JFK 18:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86