提高Firefox在Linux上的稳定性的方法

269 阅读7分钟

大约一年前,在Mozilla,我们开始努力提高Firefox在Linux上的稳定性。这项工作很快就成为FOSS项目之间良好协同作用的一个例子。

每次火狐浏览器崩溃时,用户可以给我们发送一份崩溃报告,我们用它来分析问题并希望能解决它。

A screenshot of a tab that justc crashed

这份报告包含了,除其他外,一个minidump:在崩溃时进程内存的一个小快照。这包括处理器寄存器的内容,以及每个线程堆栈的数据。

这通常是什么样子的。

如果你熟悉核心转储,那么迷你转储本质上是它们的缩小版。minidump格式最初是在微软设计的,Windows有一个写出minidump的本地方法。在Linux上,我们使用Breakpad来完成这项任务。Breakpad起源于Google,用于他们的软件(Picasa、Google Earth等),但我们已经分叉了,为我们的目的进行了大量的修改,最近部分地用Rust重写了它。

一旦用户提交了崩溃报告,我们有一个服务器端的组件--叫做Socorro--来处理它,并从minidump中提取堆栈跟踪。然后,这些报告会根据崩溃线程的堆栈跟踪中的顶级方法名称进行聚类。当一个新的崩溃被发现时,我们把它归为一个bug并开始处理。请看下面的图片,这是一个关于崩溃如何分组的例子。

The snapshot of a stack trace as displayed on crash-stats.mozilla.com

为了从minidump中提取有意义的堆栈跟踪,还需要两样东西:解卷信息和符号。解开信息是一组指令,描述如何在堆栈中找到给定的指令指针的各种框架。符号信息包含对应于给定地址范围的函数名称,以及它们来自的源文件和给定指令所对应的行号。

在常规的Firefox版本中,我们从构建文件中提取这些信息,并以Breakpad标准格式将其存储到符号文件中。有了这些信息,Socorro可以产生一个人类可读的堆栈跟踪。整个流程见下文。

A graphicsl representation of our crash reporting flow, from the capture on the client to processing on the server

下面是一个适当的堆栈跟踪的例子。

A fully symbolicated stack trace

如果Socorro不能访问适当的崩溃符号文件,那么产生的跟踪只包含地址,没有什么帮助。

A stack trace showing raw addresses instead of symbols

当涉及到Linux时,事情的发展与其他平台不同:我们的大多数用户不安装我们的构建,他们安装的是为他们最喜欢的发行版打包的Firefox版本。

在处理Linux上的稳定性问题时,这构成了一个重要的问题:对于我们大多数的崩溃报告,我们无法产生高质量的堆栈跟踪,因为我们没有所需的符号信息。提交报告的Firefox构建不是由我们完成的。更糟的是,Firefox依赖于许多第三方软件包(如GTK、Mesa、FFmpeg、SQLite等)。如果崩溃发生在这些包中而不是Firefox本身,我们就无法得到好的堆栈痕迹,因为我们也没有它们的符号。

为了解决这个问题,我们开始从多个发行版的软件包库中收集Firefox构建及其依赖的调试信息。Arch, Debian, Fedora, OpenSUSE 和 Ubuntu。由于每个发行版的做法都有点不同,我们不得不编写针对发行版的脚本,以浏览其仓库中的软件包列表,并找到相关的调试信息(这些脚本可在此获得)。然后将这些数据输入一个工具,从调试信息中提取符号文件并上传到我们的符号服务器。

有了这些信息,我们就能够分析我们从Linux用户那里收到的超过99%的崩溃报告,而以前只有不到20%。下面是一个从一个发行版的火狐浏览器中提取的高质量跟踪的例子。我们还没有建立任何相关的库,但函数名是存在的,受影响代码的文件和行号也是存在的。

A fully symbolicated stack trace including external code

这一点的重要性怎么估计都不为过。Linux用户往往更懂技术,也更有可能帮助我们解决问题,所以所有这些报告都是一个宝库,即使对其他操作系统(Windows、Mac、Android等)来说,也能提高稳定性。特别是,我们经常首先发现Linux上的Fission错误

这种新发现的检查Linux崩溃的能力的第一个效果是,它大大加快了我们对Linux特定问题的反应时间,并且经常让我们在Firefox的Nightly和Beta版本中的问题到达发布渠道的用户之前就能发现它们。

我们还能迅速发现WebRenderWebGPUWayland和VA-API视频加速等前沿组件的问题;通常在引发问题的变化发生后几天内就能提供修复。

我们并没有止步于此:我们现在可以识别特定发行版的问题和退步。这使我们能够通知软件包维护者这些问题,并使它们迅速得到解决。例如,我们能够在一个Debian特有的问题出现两周后就发现它,并立即将其修复。崩溃的原因是Debian对Firefox的一个依赖项进行了修改,可能会导致启动时的崩溃,如果你对细节感到好奇,它被归入了Bug1679430

另一个很好的例子来自Fedora:他们一直在使用他们自己的崩溃报告系统(ABRT)来捕捉Firefox构建中的崩溃,但鉴于我们这边的改进,他们开始把Firefox的崩溃发送给我们

我们也终于可以确定我们的依赖关系中的回归和问题。这使我们能够与上游沟通这些问题,有时甚至贡献出修复方案,使我们的用户和他们的用户都受益。

例如,在某些时候,Debian通过回传上游对内存泄漏的修复来更新fontconfig软件包。不幸的是,这个修复包含了一个会使火狐浏览器崩溃的错误,可能也会使其他软件崩溃。我们在这个改动进入Debian源代码的六天后就发现了这个新的崩溃,而在几周后,这个问题在上游和Debian中都得到了修复。我们也向其他项目发送了报告和修复方法,包括Mesa、GTK、glibPCSC、SQLite等等。

火狐的夜间版本还包括一个发现安全敏感问题的工具:概率堆检查器。这个工具随机填充一些内存分配,以检测缓冲区溢出和自由使用后访问。当它检测到其中一个问题时,它会向我们发送一份非常详细的崩溃报告。鉴于Firefox在Linux上的庞大用户群,这使我们能够在上游项目中发现一些难以捉摸的问题并报告它们。

这也暴露了我们用于崩溃分析的工具的一些局限性,所以我们决定在Rust中重写这些工具,主要是依靠Sentry开发的优秀的crack。由此产生的工具比我们以前的工具要快得多,只用了一小部分内存,并且产生了更准确的结果。代码的流动是双向的:我们为他们的工具箱(和他们的依赖关系)做出了改进,而他们则扩展了他们的API以解决我们的新用例,并修复了我们发现的问题。

这项工作的另一个令人愉快的副作用是,Thunderbird现在也受益于我们为Firefox所作的改进。

这继续表明自由和开放源码软件项目之间的合作不仅有利于他们的用户,而且最终改善了整个生态系统和依赖它的更广泛的社区。

特别感谢Calixte Denizet、Nicholas Nethercote、Jan Auer和所有其他为这项工作做出贡献的人。

The postImproving Firefox stability on Linuxappeared first onMozilla Hacks - the Web developer blog.