Wget segfault——我如何知道是哪个网站造成的?

nin*_*ing 5 linux command-line networking kernel wget

我正在尝试在本地镜像网站。但是,我在下载过程中的某个一致点遇到了分段错误,该错误位于与我的目标站点不同的域上(可能是由于--page-requisites)。

2018-04-09 04:58:32 (346 KB/s) - './not-website.com/2017/06/28/xyz/index.html' saved [145810]

29247 Segmentation Fault      (core dumped) wget --directory-prefix="${DL_ROOT}" --recursive --page-requisites --span-hosts --tries="${TRIES_NUM}" --timeout="${TIMEOUT_NUM}" --reject="*.tar" --convert-links --adjust-extension --continue --no-check-certificate "http://website.com/"
Run Code Online (Sandbox Code Playgroud)

因此,我认为分段错误是由于 wget 尝试下载特定网站但失败造成的。

但是,错误消息似乎没有告诉我 wget 在哪个地址上失败。它只告诉我最后一次成功的下载。我如何找出 wget 因该段错误而失败的位置/原因?

core错误似乎引用了一个 55M 的文件(core dumped),但它不是纯文本形式。里面有我需要的信息吗?我该如何提取这些信息?

我已经跨发行版(Solaris、Debian、Raspbian)对此进行了测试,并且此段错误是一致的,并且始终位于相同的地址之后(not-website.com/...在上面的错误消息中)。

我正在使用命令:

$ wget \
    --directory-prefix="${DL_ROOT}" \
    --recursive \
    --page-requisites \
    --span-hosts \
    --tries="${TRIES_NUM}" \
    --timeout="${TIMEOUT_NUM}" \
    --reject="*.tar" \
    --convert-links \
    --adjust-extension \
    --continue \
    --no-check-certificate \
  "http://website.com/"
Run Code Online (Sandbox Code Playgroud)

附加信息

这是一个很大的网站,有很多媒体。故障时,下载的目录大小约为252M。

测试于:

GNU Wget 1.18 built on solaris2.10.

-cares +digest -gpgme +https +ipv6 -iri +large-file -metalink -nls 
+ntlm +opie -psl +ssl/openssl 
Run Code Online (Sandbox Code Playgroud)

GNU Wget 1.18 built on linux-gnu.

-cares +digest -gpgme +https +ipv6 +iri +large-file -metalink +nls 
+ntlm +opie +psl +ssl/gnutls
Run Code Online (Sandbox Code Playgroud)

GNU Wget 1.16 built on linux-gnueabihf.

+digest +https +ipv6 +iri +large-file +nls +ntlm +opie +psl +ssl/gnutls
Run Code Online (Sandbox Code Playgroud)

fil*_*den 3

分段错误意味着程序(在本例中为 wget)尝试访问无效的内存地址,因此被内核终止。这通常是由于程序错误而发生的,因此虽然它很可能是由特定网站或网页触发的(考虑到您似乎能够在多个平台上的同一点上非常一致地重现它),但它您仍然可能暴露了 wget 本身的错误。

为了找到 wget 中发生分段错误的位置,您可以使用程序gdb(GNU 调试器)来获取 wget 在崩溃时的堆栈跟踪,这是可能的,因为您有一个core文件。(核心转储是正在运行的程序由于无效操作(例如分段错误)而终止时的映像副本。)

为此,请使用以下命令:

$ gdb wget core
Run Code Online (Sandbox Code Playgroud)

这将在二进制文件上启动调试器wget(从路径)并将core文件(在当前目录中)恢复为正在运行的程序的映像。

gdb然后会打印一些有关该程序的信息并给你一个提示:

$ gdb wget core
GNU gdb (GDB) 7.9
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
...
Core was generated by `wget --directory-prefix=... --recursive --page-requisites --span-hosts --tries=... --timeout=... --reject=*.tar --convert-links --adjust-extension --continue --no-check-certificate http://website.com/'.
Program terminated with signal SIGSEGV, Segmentation Fault.
(gdb) _
Run Code Online (Sandbox Code Playgroud)

此时,您可以使用该命令bt(“backtrace”的缩写)来显示程序崩溃时正在执行的内容。这通常是开始寻找错误的好地方。

例如,您可能会看到这样的内容:

(gdb) bt
#0  0x00007f5371206363 in __select_nocancel () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x0000559e5acbf21c in select_fd ()
#2  0x0000559e5acf0bde in wgnutls_poll ()
#3  0x0000559e5acbf3a2 in poll_internal ()
#4  0x0000559e5acbf6ed in fd_peek ()
#5  0x0000559e5ace423d in fd_read_hunk ()
#6  0x0000559e5acd5ef9 in gethttp ()
#7  0x0000559e5acd9b26 in http_loop ()
#8  0x0000559e5ace53c8 in retrieve_url ()
#9  0x0000559e5ace273b in retrieve_tree ()
#10 0x0000559e5acbe67d in main ()
Run Code Online (Sandbox Code Playgroud)

然后您可以gdb使用q(“quit”)命令退出:

(gdb) q
Run Code Online (Sandbox Code Playgroud)

如果您安装了“调试符号”,通常会很有帮助。这些是编译器生成的用于调试二进制文件的信息,通常会在系统上安装的二进制文件中删除这些信息,因此它们的大小较小。该信息可以保存到另一个位置(通常在 下),在尝试调试二进制文件时/usr/lib/debug可以找到该位置。gdb

有了这些信息,您的回溯通常会附加更多信息,例如所有内部函数的名称。

在 Debian 上,您可以使用以下命令安装 wget 的调试信息:

$ sudo apt-get install wget-dbgsym
Run Code Online (Sandbox Code Playgroud)

您可能还想安装 glibc 的调试符号:

$ sudo apt-get install libc6-amd64-dbgsym
Run Code Online (Sandbox Code Playgroud)

话虽如此,在您开始查看 wget 崩溃的原因之前,您可能想尝试一下 wget 的最新版本,它似乎是版本 1.9.4,您可以在此处下载。这是一个源代码包,因此您可能需要从源代码进行构建才能使其在您的系统中工作。

这是因为分段错误通常是由错误引起的,并且该错误很可能已在 wget 中修复,并且最新版本中存在该修复。

如果您在最新版本中遇到相同的问题,请考虑获取核心文件并使用 gdb 来获取回溯,然后将错误报告给 wget 维护人员,以便他们有机会解决它。

如果它已在最新的 wget 1.9.4 上修复,但它存在于您正在使用的 Debian 版本中,请考虑向 Debian 报告此问题,以便他们有机会将该补丁向后移植到其 wget 版本。

还有一个名为wget2的新项目,看起来他们正在尝试用新的代码库替换 wget 。你可能想检查一下它是否有效...似乎最近 Debian 以“wget2”的名称发布了它。

我希望这些指示也能有所帮助!