tengtengking
V2EX  ›  C++

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

  •  
  •   tengtengking · Aug 14, 2019 · 6983 views
    This topic created in 2642 days ago, the information mentioned may be changed or developed.

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

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

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

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

    查了下 rsi 估计是 memcpy 的源地址指针是空
    bookit
    bookit
    Aug 14, 2019
    18
    这种可以上 function call log

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

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

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

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

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

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

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

    2. 空指针
    这个大概率是某个流程处理的问题,比如某个判断失败还是继续往下走
    tengtengking
    tengtengking
    OP
    Aug 15, 2019 via Android
    35
    @hsuehsen 这是 debug 版本的
    hsuehsen
    hsuehsen
    Aug 15, 2019
    36
    @tengtengking
    你这 bt 的符号都没有,明显不是 debug 版,或者部分以来的库或子模块是没有调试符号的。要么就是把符号都去掉了
    tengtengking
    tengtengking
    OP
    Aug 15, 2019
    37
    @Chenamy2017 dmesg 看到看到的 ip, 在 objdump 的输出里看不到,这是怎么回事? dmesg 显示错在 libc-2.12.so
    Chenamy2017
    Chenamy2017
    Aug 16, 2019
    38
    恰巧我也刚遇到了在 C 库的错误,我的问题是空指针引起。
    peiqing9003ah
    peiqing9003ah
    Jan 10, 2020
    39
    @tengtengking 兄弟, 能不能告诉我你的解决方案 是怎么用 valgrind attach 到服务程序的, 这是运行的服务进程呐!
    tengtengking
    tengtengking
    OP
    Feb 4, 2020
    40
    @peiqing9003ah 直接用 valgrind 运行程序。valgrind --leak-check=full --show-reachable=yes --trace-children=yes --log-file=./valgrind.log ./a.out
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1574 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 85ms · UTC 08:53 · PVG 16:53 · LAX 00:53 · JFK 03:53
    ♥ Do have faith in what you're doing.