崩溃报告程序

概述

崩溃报告程序是一个用于记录和管理应用程序崩溃数据的子系统。

虽然该子系统被称为崩溃报告程序,但将其视为进程转储管理器更有助于理解。这是因为该子系统的核心实际上是管理进程转储文件,而这些文件不仅在进程崩溃时创建,还可能在挂起和其他异常事件时创建。

崩溃报告程序子系统由多个协同工作的部件组成。

Breakpad

Breakpad 是一个库和工具集,用于简化收集进程信息(特别是崩溃转储)的过程。Breakpad 是一个第三方项目(最初由 Google 开发),导入到代码树中。

转储文件

Breakpad 生成名为转储文件的文件,其中包含进程数据(堆栈、堆数据等)。

崩溃报告程序客户端

崩溃报告程序客户端是一个独立的可执行文件,启动后用于处理转储文件。此应用程序可以选择性地将崩溃提交到 Mozilla(或配置的服务器)。

Minidump 分析器

Minidump 分析器是一个独立的可执行文件,由崩溃报告程序客户端或浏览器本身启动,用于从崩溃期间生成的转储文件中提取堆栈跟踪。它将堆栈跟踪附加到与崩溃转储关联的 .extra 文件中。

Ping 发送器

Ping 发送器是一个独立的可执行文件,由崩溃报告程序客户端启动,用于向我们的遥测服务器发送崩溃 Ping。Ping 发送器用于加快崩溃 Ping 的发送速度,否则必须等到 Firefox 重新启动才能发送。

主进程崩溃处理工作原理

崩溃处理程序在 Gecko 进程生命周期的早期阶段就被挂钩。这一切都始于 XREMain::XRE_mainInit(),来自 nsAppRunner.cpp。假设启用了崩溃报告,此启动函数将为进程注册异常处理程序,并告诉崩溃报告程序子系统有关基本元数据的信息,例如应用程序名称和版本。

崩溃报告程序异常处理程序的注册也作为崩溃报告程序本身的初始化。这发生在 CrashReporter::SetExceptionHandler() 中,来自 nsExceptionHandler.cpp。崩溃报告程序确定要使用哪个应用程序来报告转储的崩溃以及在磁盘上存储这些转储文件的位置。Breakpad 异常处理程序(实际上只是一种转储进程状态的机制)在此函数中初始化。Breakpad 异常处理程序是一个 google_breakpad::ExceptionHandler 实例,并将其存储为 gExceptionHandler

随着应用程序的运行,各种其他系统可能会向崩溃报告程序写入注释备注,以指示应用程序的状态,帮助确定当前或未来崩溃的可能原因等。这些操作通过 CrashReporter::RecordAnnotation*()CrashReporter::RegisterAnnotation*() 函数和 CrashReporter::AppendAppNotesToCrashReport()nsExceptionHandler.h 中执行。

对于正常运行的应用程序,这就是所有发生的事情。但是,如果发生崩溃或类似的异常事件(例如挂起),我们需要编写崩溃报告。

当发生值得写入转储的事件时,将调用 Breakpad 异常处理程序,Breakpad 将执行其操作。Breakpad 完成后,它将回调到 CrashReporter::MinidumpCallback(),来自 nsExceptionHandler.cpp,以告诉崩溃报告程序已写入的内容。

MinidumpCallback() 在写入转储后执行许多操作。它写入一个包含崩溃时间的文件,以便其他系统可以轻松确定上次崩溃的时间。它使用包含 Mozilla 特定元数据的extra 文件补充转储文件。此数据包括通过 CrashReporter::AnnotateCrashReport() 设置的注释,以及自上次崩溃以来的时间、崩溃时垃圾回收是否处于活动状态、内存统计信息等。

如果启用了崩溃报告程序客户端MinidumpCallback() 将调用它。它只是尝试使用写入的 minidump 文件的路径作为参数创建一个新的崩溃报告程序客户端进程(例如crashreporter.exe)。

崩溃报告程序客户端执行许多角色。这里发生了很多事情,因此您可能需要查看 main(),位于 crashreporter.cpp 中。首先,通过minidump 分析器工具从转储中提取堆栈跟踪。生成的跟踪与 minidump 文件的 SHA256 哈希一起附加到崩溃的 .extra 文件中。完成此操作后,将组装一个崩溃 Ping,其中包含与 `CrashManager` 生成的 Ping 相同的信息,并通过Ping 发送器程序发送到遥测服务器。然后,Ping 的 UUID 将存储在 extra 文件中;`CrashManager` 稍后将获取它并生成具有相同 UUID 的新 Ping,以便遥测服务器可以对这两个 Ping 进行重复数据删除。然后,崩溃报告程序客户端验证转储数据是否正常。如果不是(例如缺少必需的元数据),则忽略转储数据。如果转储数据看起来正常,则将转储数据移动到配置的数据目录的pending 目录中(通过 MOZ_CRASHREPORTER_DATA_DIRECTORY 环境变量或从 UI 中定义)。完成此操作后,将通过 UIShowCrashUI() 显示主要的崩溃报告程序 UI。崩溃报告程序 UI 是特定于平台的:Windows、OS X 和各种 *NIX 演示风格(如 GTK)都有单独的版本。基本要点是向用户显示一个对话框,用户有机会将此转储数据提交到远程服务器。

如果通过崩溃报告程序提交了转储,则原始转储文件将从pending 目录中删除,并在submitted 目录中创建一个文件,其中包含已提交转储的远程服务器上的崩溃 ID。

如果用户选择不在崩溃报告程序 UI 中提交转储,则删除转储文件。

这就是编写崩溃/转储时发生的大致情况!

插件和子进程崩溃

插件和子进程中的崩溃也由崩溃报告程序子系统管理。

子进程崩溃由定义在 dom/ipc 中的 mozilla::dom::CrashReporterParent 类处理。当子进程崩溃时,顶级 IPDL actor 应通过在其 ActorDestroy 方法中调用 TakeMinidump 来检查它:请参阅 mozilla::plugins::PluginModuleParent::ActorDestroymozilla::plugins::PluginModuleParent::ProcessFirstMinidump。该方法负责使用特定于崩溃的适当崩溃注释调用 mozilla::dom::CrashReporterParent::GenerateCrashReportForMinidump。所有子进程崩溃都用 ProcessType 注释进行注释,例如“content”或“plugin”。

生成 minidump 文件后,将通知 mozilla::dom::CrashReporterHost 崩溃。它将首先尝试使用minidump 分析器从 minidump 文件中提取堆栈跟踪。然后,堆栈跟踪将与其余崩溃注释一起存储在 extra 文件中,最后通过调用 `CrashService.addCrash()` 记录崩溃。此最后一步将崩溃添加到 `CrashManager` 数据库中,并自动发送包含有关崩溃信息的崩溃 Ping。

子进程崩溃的提交由应用程序代码处理。此代码提示用户在上下文相关的 UI 中提交崩溃,然后使用 CrashSubmit.sys.mjs 提交崩溃。

内存报告

当进程检测到其内存不足时,将保存内存报告。如果进程崩溃,内存报告将与崩溃报告一起包含在内。 nsThread::SaveMemoryReportNearOOM() 最多每 30 秒检查一次进程是否内存不足,并且最多每 3 分钟保存一次报告。由于子进程实际上无法保存到硬盘,因此它会通知其父进程,由父进程保存报告。如果确实发生了崩溃,则内存报告将与其他转储数据一起移动到pending 目录中,并添加注释以指示报告的存在。这发生在 nsExceptionHandler.cpp 中,但具体发生在哪些函数中取决于崩溃的进程。当主进程崩溃时,这发生在 MinidumpCallback() 中。当子进程崩溃时,它发生在 OnChildProcessDumpRequested() 中,注释是在 WriteExtraData() 中添加的。

插件挂起

插件挂起作为崩溃报告进行处理。如果插件在 60 秒后未响应 IPC 消息,则插件 IPC 代码将获取所有相关进程的 minidump,然后杀死插件。

在这种情况下,只有一个 .extra 文件包含崩溃报告元数据,但将有多个转储文件:至少一个用于浏览器进程,一个用于插件进程。所有这些文件都作为一个单元一起提交。在提交之前,将链接文件的文件名

  • uuid.extra - 注释,包括包含附加 minidump 逗号分隔列表的 `additional_minidumps` 注释

  • uuid.dmp - 插件进程转储文件

  • uuid-<other>.dmp - additional_minidumps 中列出的其他进程转储文件

about:crashes

如果启用了崩溃报告程序子系统,则about:crashes 页面将与应用程序一起注册。此页面提供有关以前和已提交崩溃的信息。

也可以从about:crashes 提交崩溃。

影响崩溃报告的环境变量

可以通过设置某些环境变量来更改异常处理程序和崩溃报告程序客户端的行为,其中一些变量用于测试,但相当一部分仅供内部用户使用。

用户指定的环境变量

  • MOZ_CRASHREPORTER - 与 MOZ_CRASHREPORTER_DISABLE 相反,即使在 application.ini 中禁用,也强制启用崩溃报告。您必须使用此选项才能在调试版本中启用崩溃报告。

  • MOZ_CRASHREPORTER_DISABLE - 在非调试版本中完全禁用 Breakpad 崩溃报告。例如,如果您希望在 Windows 上使用符号服务器使用 JIT 调试器,可以使用此选项。

  • MOZ_CRASHREPORTER_FULLDUMP - 在 minidump 中存储完整的应用程序内存,以便您可以在 Microsoft 调试器中打开它。不要将其提交到服务器。(仅限 Windows。)

  • MOZ_CRASHREPORTER_NO_DELETE_DUMP - 提交崩溃报告转储文件到服务器后,不要删除该文件。小型转储文件仍将移动到“Crash Reports/pending”目录。

  • MOZ_CRASHREPORTER_NO_REPORT - 保存小型转储文件,但不要启动崩溃报告 UI 或将报告发送到服务器。小型转储文件将存储在用户的配置文件目录中,位于名为“minidumps”的子目录下。

  • MOZ_CRASHREPORTER_SHUTDOWN - 保存小型转储文件,然后强制应用程序关闭。这对于通常不会关闭 chrome(主应用程序)进程的内容崩溃很有用。此变量也会导致应用程序关闭。

  • MOZ_CRASHREPORTER_URL - 设置崩溃报告程序将报告提交到的 URL。

内部使用的环境变量

  • MOZ_CRASHREPORTER_AUTO_SUBMIT - 设置后,会导致崩溃报告客户端跳过 UI 流程并直接提交崩溃报告。

  • MOZ_CRASHREPORTER_DATA_DIRECTORY - 平台相关的 数据目录,挂起的崩溃报告将存储在此路径的子目录中。这将覆盖客户端代码生成的默认目录。

  • MOZ_CRASHREPORTER_DUMP_ALL_THREADS - 设置为 1 时,会生成所有线程的堆栈跟踪并在崩溃 ping 中发送,未设置时,只会生成崩溃线程的跟踪。

  • MOZ_CRASHREPORTER_EVENTS_DIRECTORY - 保存崩溃事件文件的目录路径。

  • MOZ_CRASHREPORTER_PING_DIRECTORY - 保存挂起的崩溃 ping 文件的目录路径。

  • MOZ_CRASHREPORTER_RESTART_ARG_<n> - 这些变量中的每一个都指定传递给应用程序的参数之一,从可执行文件后的第一个参数开始,崩溃报告客户端使用它们来重新启动应用程序。

  • MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE - 如果在启动应用程序时指定了 XUL 应用程序文件,则必须将其存储在此变量中,以便崩溃报告客户端可以重新启动应用程序。

  • MOZ_CRASHREPORTER_STRINGS_OVERRIDE - 覆盖用于加载包含崩溃报告客户端 UI 中使用的字符串的 .ini 文件的路径。

开发中使用的环境变量

在构建时设置这些变量(例如,.mozconfig 中的 ac_add_options)。

  • MOZ_CRASHREPORTER_MOCK - 设置后,会导致崩溃报告客户端模拟其与系统的接口,以便您可以测试 GUI 行为。设置此选项后,GUI 将完全不会与主机系统交互。

其他主题