程序会异常断掉。用 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
augustheart 2019-08-14 17:39:34 +08:00
这错误日志……
你可真是个天才…… 如果是这一行,不用想,只可能是 rsi 为空 |
2
misaka19000 2019-08-14 17:42:07 +08:00
我不会 C++ 但我觉得把源码放出来是个很合适的方式
|
3
Mirana 2019-08-14 17:46:56 +08:00 1
memcopy 挂了 而且栈是空的。。。
在代码里的 memcpy 前加些 log 看看把 |
4
gaokevin163 2019-08-14 17:49:41 +08:00
第一直接上代码,第二用 DEBUG 编译一下,再看错误
|
5
tengtengking OP @augustheart 能麻烦详细说下吗,rsi 为空一般什么原因
|
6
TomStark 2019-08-14 18:10:41 +08:00 1
打印全部堆栈确定调用 memcpy 的位置,贴一点具体代码更好找原因
|
7
sylxjtu 2019-08-14 18:12:16 +08:00 via Android 1
编译时加上 sanitizer 再复现问题,就能知道错哪儿了
|
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 的参数吧。你这个根本看不出来。 |
9
lcdtyph 2019-08-14 18:24:05 +08:00
这个 bt 结果很奇怪啊,为什么起始 rip 是 0x0000000000000000 呢,感觉 binary 或者 core 也有问题
|
10
augustheart 2019-08-14 18:25:10 +08:00
@tengtengking 我不懂 linux 调试,你这个肯定要想办法打印出栈到调用的上下文的,你既然是 linux 开发应该比我熟。memcpy 这种函数到处都是,只看这一处根本不知道是哪个地方调用的吧。
|
11
lcdtyph 2019-08-14 18:30:18 +08:00
@augustheart
这个程序是在进入 main 之后崩溃的么,根据你贴出来的这点信息,好像连 main 函数都没进去就崩了 |
12
nicebird 2019-08-14 18:45:34 +08:00
空指针
|
13
tengtengking OP @sylxjtu 请问 sanitizer 是 g++的参数吗, 搜了下没太明白是什么意思
|
14
tengtengking OP @lcdtyph 进入 main 函数了, 运行几小时才会出这个问题。根据生成的 coredump 文件,堆栈看不到东西
|
15
lcdtyph 2019-08-14 19:46:33 +08:00 1
@tengtengking
那么可能是哪里的内存越界把堆栈破坏了所以 gdb 没法回溯这里的返回地址,也可能是你的 libc 或者 libpthread 没有 unwind 符号。这种情况下 gdb 几乎没什么作用,需要其他信息。 有日志吗?从日志内容能推出大致什么地方出的错么?是私有项目么? |
16
tengtengking OP @lcdtyph 应该是破坏了堆栈。日志都是业务相关的,对排错没太大帮助。正准备把所有的 memcpy 都打印一遍
|
17
Mirana 2019-08-14 19:55:55 +08:00
rsi (source index)源变址寄存器,与 rds 段寄存器联用,可以访问数据段中的任一个存储单元 函数调用时的第 2 个参数
查了下 rsi 估计是 memcpy 的源地址指针是空 |
18
bookit 2019-08-14 19:57:03 +08:00
这种可以上 function call log
进入退出每一个函数都记录一遍 |
19
lcdtyph 2019-08-14 19:59:53 +08:00
@tengtengking #16
你可以在编译选项里加上 `-fsanitize=address -fno-omit-frame-pointer` 让 gcc 插入 sanitizer 的代码,再复现问题看看报错 |
20
Mirana 2019-08-14 20:01:05 +08:00 1
提供个方案
可以自己写一个 memcpy 函数,编个 lib.a,把堆栈打印出来,把 src 打印出来,然后调用真正的 memcpy 启动 binary 的时候这个样子 LD_PRELOAD=lib.a ./binray |
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 等强静态检查工具尝试找出可疑代码 |
22
LitostCheng 2019-08-14 20:36:28 +08:00 1
[backtrace 了解一下]
在 Linux 中如何利用 backtrace 信息解决问题: https://blog.csdn.net/jxgz_leo/article/details/53458366 |
23
tengtengking OP @nvioue 非常感谢。 方法 1 已经尝试过了 , 发现 用上 valgrind 后运行变得十分缓慢,生产环境影响比价大,没能复现。
|
24
tengtengking OP @nvioue 我去学习下你的这几个编译参数,尝试一下
|
25
laminux29 2019-08-14 21:57:02 +08:00 1
我建议题主应该学点 java 软件工程的方法,这样就不至于那么难堪,居然要搬出 gdb 对着一堆啥玩意来调试。
第一步,建议题主找一款靠谱的日志框架,先把日志框架搭起来。 第二步,通过日志判断出问题的大概位置,然后对这区域的代码,进行源码调试。 |
26
boywhp 2019-08-14 22:05:21 +08:00
写一个 memcpy 封装函数, 每次调用时 printf 参数到屏幕或日志, 尤其注意检查 len 是否异常
|
27
lingxi27 2019-08-14 22:51:15 +08:00
这种问题试试日志配合单元测试
|
28
SPACELAN 2019-08-15 00:16:35 +08:00
sse 指令,,看看栈和超操作的地址有没有对齐 16 字节
|
29
Chenamy2017 2019-08-15 09:08:31 +08:00 1
1.dmesg 看程序出错的位置
2.objdump -d xxx 找到这个地址,基本能确定是哪个函数的那行代码了 |
30
tengtengking OP @laminux29 日志用的 glog,出错的地方也没什么信息
|
31
eliteYang 2019-08-15 09:37:49 +08:00
至少吧完整栈和最后一行可读代码贴出来啊
|
32
whi147 2019-08-15 09:56:25 +08:00
可能是指针生命周期结束了
|
33
tengtengking OP @eliteYang 我也想定位到你说的这两个地方。可是定位不到呀
|
34
hsuehsen 2019-08-15 10:35:51 +08:00 1
bt 这样的信息,说明跑的不是 debug 版; 编译个 debug 版本,然后尝试重现问题
最重要的问题是, 1. 重现问题 2. 重现问题 3. 重现问题 若无法重现,那只能是过代码。通过楼上一些提供的方法,确定初步位置,然后前后过一边所有 memcpy 相关的操作。大概率呢,是两种可能, 1. 内存越界(这种比较坑) 但是可怀疑的地方也好确定 2. 空指针 这个大概率是某个流程处理的问题,比如某个判断失败还是继续往下走 |
35
tengtengking OP @hsuehsen 这是 debug 版本的
|
36
hsuehsen 2019-08-15 14:33:11 +08:00
@tengtengking
你这 bt 的符号都没有,明显不是 debug 版,或者部分以来的库或子模块是没有调试符号的。要么就是把符号都去掉了 |
37
tengtengking OP @Chenamy2017 dmesg 看到看到的 ip, 在 objdump 的输出里看不到,这是怎么回事? dmesg 显示错在 libc-2.12.so
|
38
Chenamy2017 2019-08-16 17:19:07 +08:00
恰巧我也刚遇到了在 C 库的错误,我的问题是空指针引起。
|
39
peiqing9003ah 2020-01-10 15:46:34 +08:00
@tengtengking 兄弟, 能不能告诉我你的解决方案 是怎么用 valgrind attach 到服务程序的, 这是运行的服务进程呐!
|
40
tengtengking OP @peiqing9003ah 直接用 valgrind 运行程序。valgrind --leak-check=full --show-reachable=yes --trace-children=yes --log-file=./valgrind.log ./a.out
|