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

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

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

因此,我认为分段错误是由于 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/"

附加信息

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

测试于:

GNU Wget 1.18 built on solaris2.10.

-cares +digest -gpgme +https +ipv6 -iri +large-file -metalink -nls 
+ntlm +opie -psl +ssl/openssl 

GNU Wget 1.18 built on linux-gnu.

-cares +digest -gpgme +https +ipv6 +iri +large-file -metalink +nls 
+ntlm +opie +psl +ssl/gnutls

GNU Wget 1.16 built on linux-gnueabihf.

+digest +https +ipv6 +iri +large-file +nls +ntlm +opie +psl +ssl/gnutls

答案1

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

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

为此,请使用以下命令:

$ gdb wget core

这将在二进制文件上启动调试器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) _

此时,您可以使用该命令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 ()

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

(gdb) q

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

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

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

$ sudo apt-get install wget-dbgsym

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

$ sudo apt-get install libc6-amd64-dbgsym

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

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

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

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

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

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

相关内容