本周工作中,我花了整整一周的时间来尝试调试一个段错误。我以前从来没有这样做过,我花了很长时间才弄清楚其中涉及的一些基本事情(获得核心转储、找到导致段错误的行号)。于是便有了这篇博客来解释如何做那些事情!
夏邑网站制作公司哪家好,找创新互联公司!从网页设计、网站建设、微信开发、APP开发、成都响应式网站建设公司等网站项目制作,到程序开发,运营维护。创新互联公司从2013年创立到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联公司。
在看完这篇博客后,你应该知道如何从“哦,我的程序出现段错误,但我不知道正在发生什么”到“我知道它出现段错误时的堆栈、行号了! ”。
“段错误segmentation fault”是指你的程序尝试访问不允许访问的内存地址的情况。这可能是由于:
0
);这个“C++ 虚表指针”是我的程序发生段错误的情况。我可能会在未来的博客中解释这个,因为我最初并不知道任何关于 C++ 的知识,并且这种虚表查找导致程序段错误的情况也是我所不了解的。
但是!这篇博客后不是关于 C++ 问题的。让我们谈论的基本的东西,比如,我们如何得到一个核心转储?
我发现找出为什么我的程序出现段错误的最简单的方式是使用 valgrind
:我运行
valgrind -v your-program
这给了我一个故障时的堆栈调用序列。 简洁!
但我想也希望做一个更深入调查,并找出些 valgrind
没告诉我的信息! 所以我想获得一个核心转储并探索它。
核心转储core dump是您的程序内存的一个副本,并且当您试图调试您的有问题的程序哪里出错的时候它非常有用。
当您的程序出现段错误,Linux 的内核有时会把一个核心转储写到磁盘。 当我最初试图获得一个核心转储时,我很长一段时间非常沮丧,因为 - Linux 没有生成核心转储!我的核心转储在哪里?
这就是我最终做的事情:
ulimit -c unlimited
sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t
ulimit -c
设置核心转储的***尺寸。 它往往设置为 0,这意味着内核根本不会写核心转储。 它以千字节为单位。 ulimit
是按每个进程分别设置的 —— 你可以通过运行 cat /proc/PID/limit
看到一个进程的各种资源限制。
例如这些是我的系统上一个随便一个 Firefox 进程的资源限制:
$ cat /proc/6309/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 30571 30571 processes
Max open files 1024 1048576 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 30571 30571 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
内核在决定写入多大的核心转储文件时使用软限制soft limit(在这种情况下,max core file size = 0
)。 您可以使用 shell 内置命令 ulimit
(ulimit -c unlimited
) 将软限制增加到硬限制hard limit。
kernel.core_pattern
是一个内核参数,或者叫 “sysctl 设置”,它控制 Linux 内核将核心转储文件写到磁盘的哪里。
内核参数是一种设定您的系统全局设置的方法。您可以通过运行 sysctl -a
得到一个包含每个内核参数的列表,或使用 sysctl kernel.core_pattern
来专门查看 kernel.core_pattern
设置。
所以 sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t
将核心转储保存到目录 /tmp
下,并以 core
加上一系列能够标识(出故障的)进程的参数构成的后缀为文件名。
如果你想知道这些形如 %e
、%p
的参数都表示什么,请参考 man core。
有一点很重要,kernel.core_pattern
是一个全局设置 —— 修改它的时候***小心一点,因为有可能其它系统功能依赖于把它被设置为一个特定的方式(才能正常工作)。
默认情况下在 ubuntu 系统中,kernel.core_pattern
被设置为下面的值:
$ sysctl kernel.core_pattern
kernel.core_pattern = |/usr/share/apport/apport %p %s %c %d %P
这引起了我的迷惑(这 apport 是干什么的,它对我的核心转储做了什么?)。以下关于这个我了解到的:
kernel.core_pattern=|/usr/share/apport/apport %p %s %c %d %P
意味着核心转储将被通过管道送给 apport
程序。/var/log/apport.log
中。我最终只是跳过了 apport,并把 kernel.core_pattern
重新设置为 sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t
,因为我在一台开发机上,我不在乎 apport 是否工作,我也不想尝试让 apport 把我的核心转储留在磁盘上。
好的,现在我们了解了 ulimit
和 kernel.core_pattern
,并且实际上在磁盘的 /tmp
目录中有了一个核心转储文件。太好了!接下来干什么?我们仍然不知道该程序为什么会出现段错误!
下一步将使用 gdb
打开核心转储文件并获取堆栈调用序列。
你可以像这样用 gdb
打开一个核心转储文件:
$ gdb -c my_core_file
接下来,我们想知道程序崩溃时的堆栈是什么样的。在 gdb
提示符下运行 bt
会给你一个调用序列backtrace。在我的例子里,gdb
没有为二进制文件加载符号信息,所以这些函数名就像 “??????”。幸运的是,(我们通过)加载符号修复了它。
下面是如何加载调试符号。
symbol-file /path/to/my/binary
sharedlibrary
这从二进制文件及其引用的任何共享库中加载符号。一旦我这样做了,当我执行 bt
时,gdb 给了我一个带有行号的漂亮的堆栈跟踪!
如果你想它能工作,二进制文件应该以带有调试符号信息的方式被编译。在试图找出程序崩溃的原因时,堆栈跟踪中的行号非常有帮助。:)
通过以下方式在 gdb
中获取每个线程的调用栈!
thread apply all bt full
如果你有一个带调试符号的核心转储以及 gdb
,那太棒了!您可以上下查看调用堆栈(LCTT 译注:指跳进调用序列不同的函数中以便于查看局部变量),打印变量,并查看内存来得知发生了什么。这是***的。
如果您仍然正在基于 gdb 向导来工作上,只打印出栈跟踪与bt也可以。 ????
另一种搞清楚您的段错误的方法是使用 AddressSanitizer 选项编译程序(“ASAN”,即 $CC -fsanitize=address
)然后运行它。 本文中我不准备讨论那个,因为本文已经相当长了,并且在我的例子中打开 ASAN 后段错误消失了,可能是因为 ASAN 使用了一个不同的内存分配器(系统内存分配器,而不是 tcmalloc)。
在未来如果我能让 ASAN 工作,我可能会多写点有关它的东西。(LCTT 译注:这里指使用 ASAN 也能复现段错误)
这个博客听起来很多,当我做这些的时候很困惑,但说真的,从一个段错误的程序中获得一个堆栈调用序列不需要那么多步骤:
valgrind
如果那没用,或者你想要拿到一个核心转储来调查:
ulimit
和 kernel.core_pattern
;gdb
调试核心转储了,加载符号并运行 bt
;我可以使用 gdb
弄清楚有个 C++ 的虚表条目指向一些被破坏的内存,这有点帮助,并且使我感觉好像更懂了 C++ 一点。也许有一天我们会更多地讨论如何使用 gdb
来查找问题!
标题名称:在Linux上如何得到一个段错误的核心转储
网页链接:http://www.shufengxianlan.com/qtweb/news14/226464.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联