如何从核心转储文件中识别导致崩溃的完整命令

Posted

技术标签:

【中文标题】如何从核心转储文件中识别导致崩溃的完整命令【英文标题】:How to identify the full command that caused the crash from the core dump file 【发布时间】:2018-05-28 17:48:47 【问题描述】:

使用 gdb 从核心转储文件中识别完整命令时出现问题 崩溃的命令本身可能很长​​p>

myCommand -f log/SlaRunTimeReport.rep -I input/myFile.txt -t output/myFile.txt

但是当使用 gdb 来识别“Core was generated by”位置中的命令时

即通过执行

gdb -c core.56536

输出:

GNU gdb (GDB) Red Hat Enterprise Linux 7.10-20.el7

….

Core was generated by `myCommand -f log/SlaRunTimeReport.rep -I 
input/myFile.t'.

可以看到中间切掉了完整的命令(可执行文件+参数)

‘myCommand -f log/SlaRunTimeReport.rep -I input/myFile.t'

另外使用strings命令,也无助于识别完整命令

strings core.56536 | grep PMRunTimeReport

输出:

myCommand 

myCommand -f log/SlaRunTimeReport.rep -I input/myFile.t

有什么方法可以从 coredump 文件中获取导致失败的完整命令

提前致谢

【问题讨论】:

你不能在包含main()的堆栈帧中打印argv的内容吗? 请尝试正确格式化问题。 我会提供非常短的文件名来规避输出中的长度限制。如果只有长参数会导致问题,请考虑这是对您的错误的提示。 这可能是存储覆盖的症状,它覆盖了 argv 列表的一部分......为了证明,在程序开始时打印出 argv 参数列表的内容......然后当它转储时,检查 GDB 显示的值是否与程序开始时打印的值相同......如果不是,那么你知道发生了存储覆盖。希望你用过 valgrind。 【参考方案1】:

有什么方法可以从 coredump 文件中获取导致失败的完整命令

有多种方式,但运行strings错误方式。

如果您使用调试信息构建程序,您应该能够简单地执行up 命令直到到达main,然后检查argv[0]argv[argc-1]

如果您的main 不是使用调试信息构建的,或者它不使用argcargv,您应该能够从__libc_argc__libc_argv 变量中恢复该信息。示例:

$ ./a.out foo bar baz $(python -c 'print "a" * 500')
Aborted (core dumped)

$ gdb -q ./a.out core
Core was generated by `./a.out foo bar baz aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'.

请注意,“生成者”已被截断——它来自struct prpsinfo 内部的固定长度数组,保存在NT_PRPSINFO ELF 注释中core

Program terminated with signal SIGABRT, Aborted.
#0  0x00007fab38cfcf2b in raise () from /lib64/libc.so.6
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.27-15.fc28.x86_64

(gdb) p (int)__libc_argc
$1 = 5
(gdb) p ((char**)__libc_argv)[0]@5
$2 = 0x7ffede43289f "./a.out", 0x7ffede4328a7 "foo", 0x7ffede4328ab "bar",
  0x7ffede4328af "baz", 
  0x7ffede4328b3 'a' <repeats 200 times>...

最后一行实际上是个谎言——我们知道'a' 重复了 500 次。

我们可以这样修复它:

(gdb) set print elem 0
(gdb) p ((char**)__libc_argv)[0]@5
$3 = 0x7ffede43289f "./a.out", 0x7ffede4328a7 "foo", 0x7ffede4328ab "bar",
  0x7ffede4328af "baz", 
  0x7ffede4328b3 'a' <repeats 500 times>

瞧:我们现在有了完整的命令。

最后,如果您为 GLIBC 安装调试信息,您可以简单地查看 __libc_start_main(称为您的 main):

(gdb) set backtrace past-main
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007fab38ce7561 in __GI_abort () at abort.c:79
#2  0x00000000004004ef in main () at foo.c:3
#3  0x00007fab38ce918b in __libc_start_main (main=0x4004e6 <main>, argc=5, argv=0x7ffede431118, 
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffede431108)
    at ../csu/libc-start.c:308
#4  0x000000000040042a in _start ()

在这里您可以清楚地看到第 3 帧中的 argcargv,并且可以像这样检查 那个 argv

(gdb) fr 3
#3  0x00007fab38ce918b in __libc_start_main (main=0x4004e6 <main>, argc=5, argv=0x7ffede431118, 
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffede431108)
    at ../csu/libc-start.c:308
308       result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);

(gdb) p argv[0]@5
$1 = 0x7ffede43289f "./a.out", 0x7ffede4328a7 "foo", 0x7ffede4328ab "bar",
  0x7ffede4328af "baz", 
  0x7ffede4328b3 'a' <repeats 500 times>

【讨论】:

我曾尝试运行:gdb -q ./myCommand -c core.56590,然后:p (int)__libc_argc 结果为:$1 = 7 然后运行下一个:set print elem 0 然后:p ((char**)__libc_argv)[0]@7 No success , got : Cannot access memory at address 0x3003ff68 我也尝试了其他建议,但我假设我没有 glibc 的调试信息,我运行:set backtrace Past-main,然后运行: bt 然后我查找 __libc_start_main 并在我的情况下运行:fr 2,但我得到的输出中没有 argc:#2 0x00007fbda6d46b35 in __libc_start_main () **从 /lib64/libc.so.6 ,然后我运行:p argc ** 并得到:**No symbol "argc" in current context。 @AlexOstar “我没有 glibc 的调试信息”是一个可以解决的问题:安装它。

以上是关于如何从核心转储文件中识别导致崩溃的完整命令的主要内容,如果未能解决你的问题,请参考以下文章

从附加到 ddd/dbx 的崩溃进程生成核心转储

如何在 linux 中强制使具有 's' 权限的命令崩溃?

movsql 调用导致核心转储

如何使用参数自动从 Azure webapp 获取完整内存转储

识别导致分段错误(核心转储)的错误

JVM崩溃。如何获取错误日志或核心转储