经过几个月的尽力,根据Electron结构开发的新版淘宝直播推流软件总算上线了。随之而来的便是线上用户反馈的各种问题,其中最影响用户体会的当属运用溃散问题了。当运用程序呈现未 catch 的反常时就会发生溃散,本文介绍了客户端运用溃散的处理流程。
布景
主播前期花了很长时刻精心策划了一场直播,在淘宝直播推流软件开播。当开播时刻到来时,倒计时 3、2、1 后开端直播,一切都十分顺利。
但没想到刚直播了一会,忽然弹出一个 “体系溃散提醒” 弹框,提示运用溃散了:
主播立马依照提示输入钉钉号提交溃散信息,并且参加钉钉群反馈运用溃散问题。作为技能小二的我发现显现一切正常,怎样在主播端运转就遇到溃散问题了,一定是运用方法不对。
但首要关头得赶忙处理问题,并且以后要保证运用的稳定性。作为前端工程师的我测验开端学习怎么剖析和处理运用溃散问题。
在此之前,让咱们先来了解下前端处理 js error 反常的全体流程:首要 js error 反常是怎么发生的,其非必须怎么捕获上报 js error 反常,接着怎么监控并剖析 js error 反常仓库,终究是怎么运用 sourcemap 来定位到详细是哪个函数的哪一行代码引起的 error,以此来处理该反常问题。
了解完前端处理反常的流程后,咱们接下来开端了解客户端运用程序处理溃散反常的流程,下文均以 Windows 渠道叙述内容。
运用溃散是怎么发生的
咱们先来看下 Windows 体系的全体架构图,主要分为内核方式和用户方式。像常见的内核程序、设备驱动程序等运转在内核方式,而像体系服务进程、用户运用程序如淘宝直播主播工作台等运转在用户方式。
淘宝直播主播工作台地址:market.m.taobao.com/app/mtb/liv…
当 Windows 体系或运用程序调用 CreateProcess 函数发动主线程或 CreateThread 发动线程时,线程函数会在如下代码中运转(下面的代码引自《Windows 核心编程》第 25 章未处理反常和 C++ 反常):
//主线程发动函数
// CreateProcess 发动线程函数
VOID BaseProcessStart(PPROCESS_START_ROUTINE pfnSatrtAddr)
{
__try {
ExitThread((pfnSatrtAddr)());
}
__except(UnHandledExceptionFilter(GetExceptionInformation())) {
ExitProcess(GetExceptionCode());
}
}
// CreateThread 发动线程函数
VOID BaseThreadStart(PTHREAD_START_ROUTINE pfnSatrtAddr, PVOID pvParam)
{
__try {
ExitThread((pfnSatrtAddr)());
}
__except(UnHandledExceptionFilter(GetExceptionInformation())) {
ExitProcess(GetExceptionCode());
}
}
从上面的代码能够看出,当线程运转中呈现未捕获反常时,会调用 UnHandledExceptionFilter 函数来过滤反常信息,然后调用 ExitProcess 函数退出进程的运转。也便是说,当线程中呈现未 catch 的反常(类比前端 js error 未 catch)时,比如主播在运用绿幕大屏增加超大尺度图片时运用程序 memcpy 失利但未 catch 反常导致溃散、体系内核反常导致蓝屏现象,体系 / 程序就会溃散并生成 dump 文件来帮助寻找反常原因。
那 dump 文件终究是什么呢?Windows 渠道 dump 文件分为两大类:内核方式 dump 和用户方式 dump。内核方式 dump 是操作体系创立的溃散转储,最经典的便是体系蓝屏时会主动创立内核方式的 dump。用户方式 dump 进一步能够分为 fulldump 和minidump。fulldump 包含了某个进程完整的地址空间数据,以及许多用于调试的仓库、寄存器等信息。毫无疑问,这样的 fulldump 关于过后调试十分有价值,但因为文件太大(几G字节)使得经过恳求发送给开发者十分困难。而 minidump 则有许多类型,依照最常用的配置只包含了最必要的信息,用于康复毛病进程的一切线程的调用仓库,以及检查毛病时刻局部变量的值。这样的 minidump 文件一般很小(只有几K ~ 几M字节),经过恳求发送给开发者十分简单。
事实证明,minidump 已成为各个渠道的客户端溃散的常用转储文件(类比 js stack trace)。下面的代码便是界说了 pExceptionFilter 函数,当捕获到运用溃散时调用该函数生成 minidump 文件。
LONGWINAPIpExceptionFilter(struct_EXCEPTION_POINTERS*pExceptionInfo){ // create dumpfile
HANDLE hFile = ::CreateFile(m_dumpFilePathFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// set exception info
_MINIDUMP_EXCEPTION_INFORMATION info;
info.ThreadId = ::GetCurrentThreadId();
info.ExceptionPointers = pExceptionInfo;
info.ClientPointers = true;
// generate mini dump contents
::MiniDumpWriteDump(
::GetCurrentProcess(),
::GetCurrentProcessId(),
lFile,
MiniDumpNormal,
&info, NULL, NULL
);
// close file
::CloseHandle(hFile);
// return exception handled.
return EXCEPTION_EXECUTE_HANDLER;
}
怎么捕获上报运用溃散
经过上文咱们了解到,当运用程序呈现未 catch 的反常时就会发生溃散。那咱们要怎么捕获到运用程序的溃散呢?
在客户端中捕获运用溃散
经过调研发现,Google 现已开发了一个名为 Breakpad 的库及其继任者 Crashpad,它能够在各种操作体系和 CPU 架构上生成 minidump 文件。因为 Electron 运用 Crashpad 而不是 Breakpad 来捕获和上传溃散信息,因此咱们重点介绍下 Crashpad 原理。
Breakpad地址:chromium.googlesource.com/breakpad/br… Crashpad地址:chromium.googlesource.com/crashpad/cr…
Crashpad 是一个用于从客户端运用程序捕获、存储和上传溃散信息到服务器的库,旨在使客户端能够以尽或许高的保真度和掩盖范围,以最小本钱捕获溃散时的进程状态。
Crashpad 还为客户端供给了最小的东西,以运用每个进程键 / 值对方式来封装他们的溃散,并且客户端还能够经过扩展点进一步扩大溃散报告。下图是 Crashpad 全体设计图:
在 Windows 渠道,操作体系在溃散线程的上下文中调度反常。为了告诉反常处理程序,Crashpad 客户端在客户端进程中注册一个 UnhandledExceptionFilter (UEF)。当反常传递到 UEF 时,它将反常信息和溃散线程的 ID 存储在向处理程序注册的 ExceptionInformation 结构中。然后它设置一个事情句柄来告诉处理程序持续处理反常。当发生溃散时,Crashpad终究会调用 generate_dump 来生成一个包含正在运转的进程快照的 minidump 文件。
在 Electron 中捕获运用溃散
走运的是,Electron 结构现已集成了它,当主进程 / 烘托进程溃散时,内置的溃散报告器会主动创立 minidump 文件。作为开发者运用起来也很简单,只需求几行 JavaScript 代码就能够运用它来捕获溃散并把 minidump 文件上传到远程服务器中(类比 js error catch 上报),为下一步监控和剖析治理溃散问题做准备。
import{crashReporter}from'electron'
crashReporter.start({
productName: 'YourName',
companyName: 'YourCompany',
submitURL: 'https://your-domain.com/url-to-submit',
uploadToServer: true
})
到现在为止,咱们只是写了上面几行代码,只知道能够用来捕获和上传溃散文件到服务器上。但溃散文件到底是长啥样的呢?让咱们来一探终究。
因为 Electron crashReporter 底层是经过 http/https post 来上传溃散日志的恳求,于是咱们能够经过 wireshark 抓包东西抓到 Electron 捕获溃散并上传至服务器的恳求,这便是 dump 文件的二进制内容。
wireshark地址:www.wireshark.org/download.ht…
揭开庐山真面目之后,咱们仍是不认识这堆二进制内容。但至少迈进了一步,咱们现已感知到它的存在了。接下来就先把它一致监控起来吧。
怎么监控运用溃散
那要怎样把线上用户溃散问题都监控起来呢?目前供给了两个挑选:分别是为蚂蚁前端开发者打造的实时监控告警雨燕渠道(背面运用的是支付宝小程序 IDE 最佳实践计划)、阿里集团为开发者供给桌面客户端标准化处理计划(包含溃散管理)的千鸟渠道(类比 arms / JsTracker 监控渠道)。
支付宝小程序 IDE 最佳实践
支付宝小程序 IDE 最佳实践计划的全体捕获上报流程图如下:
该计划运用 Electron 自带的 crashReporter 机制来捕获运用程序溃散,然后运用阿里云 FC 来解析 dump 文件,终究将溃散结果上签到雨燕渠道进行监控。
在这个过程中,该计划根据大量的字段约定来完成信息搜集:
- 本次溃散明细 -> 雨燕监控的每一条记载可检查明细,如版别、渠道等。
- 溃散仓库 -> 从明细中能够进入检查仓库页面,或许在监控首页点击某条记载的剖析。
- 用户环境信息、程序发动参数信息,上报的原始 payload,minidump 解析报告、文件等 -> 可在 HI 文件管理中在线检查,上签到 HI 的文件名有当前用户 ID,用户自界说 ID 等,能够经过搜索来定位到详细文件。
作为开发者来说,接入本钱也很低,只需求写下面几行 JavaScript 代码并在主进程中 start 一次即可。
import { crashReporter } from 'electron'
crashReporter.start({
productName: 'TaoBaoLive',
submitURL: 'https://hpmweb.alipay.com/minidump',
uploadToServer: true,
globalExtra: {
yuyanId: '180020010101205937',
yuyanEnv: 'prod',
yuyanCode: '12',
// app_前缀的上报在appInfo.json
app_liveId: LIVE_ID,
HIPrefix: `tblive-crashreport-${USER_ID}`,
USER_IDENTIFIER: USER_ID,
},
})
千鸟渠道
千鸟渠道也是根据 Electron 的 crashReporter 模块进行了一个封装,内置了千鸟的溃散上传逻辑。作为开发者来说,接入本钱也很低,也只需求写下面几行 JavaScript 代码并在主进程中 start 一次即可。
import{startCrashReporter}from'@ali/qianniao-crash-reporter'
startCrashReporter.start({
productName: 'TaoBaoLive',
submitURL: 'https://qianniao.alibaba.com/api/open/crash/record',
globalExtra: {
userId: USER_ID,
},
})
怎么剖析运用溃散
幸好有雨燕、千鸟渠道,咱们能够很方便地把运用溃散问题监控起来,接下来就要开端剖析这些溃散日志了。这儿介绍两种剖析 dump 溃散文件的计划:雨燕渠道在线剖析溃散和 Visual Studio / WinDbg Preview 本地剖析溃散。
雨燕渠道在线剖析溃散
雨燕渠道在线剖析溃散功能是根据开源项目 electron-minidump 完成解析 minidump 文件,获取溃散反常错误的仓库信息。如下图所示,咱们能够明晰地看到溃散仓库,但只能获取到哪个模块溃散,不清楚详细是哪一行代码导致的溃散,无法进一步排查溃散原因。
electron-minidump地址:github.com/nornagon/el…
因为雨燕渠道在线剖析溃散存在局限性,因此咱们需求把 dump 文件下载到本地,持续用 Visual Studio / WinDbg Preview 等东西进一步剖析是哪一行代码导致的溃散。
Visual Studio / WinDbg Preview 本地剖析溃散
在咱们运用 Visual Studio / WinDbg Preview 等东西本地剖析溃散文件之前,咱们有必要扼要了解下符号是什么。符号是调试和其他确诊东西的基本要求,关于 Microsoft 编译器,这些是作为构建的一部分生成的 .pdb 文件。符号 (.pdb) 文件默认情况下包含以下信息(类比 js SourceMap 文件):
- 公共符号 (一切函数、静态变量和全局变量)
- 负责可执行文件中代码部分的目标文件列表
- FPO 帧指针优化信息
- 局部变量和数据结构的称号和类型信息
- 源文件和行号信息
符号地址:learn.microsoft.com/zh-cn/windo…
那为什么咱们需求符号呢?假如没有 PDB 文件,调试器无法解析函数称号、参数或任何存储在仓库上的局部变量,无法将运用程序中执行的指令与原始源代码相关联。符号关于调试十分重要,根据咱们正在调试的内容,或许需求符号来显现完整的调用仓库,并运用 Watch 窗口或 DataTips 来检查目标。假如咱们正在调试不包含堆的转储文件,调试器将需求访问原始二进制文件,以便确定要加载的正确符号文件。换句话说,假如咱们正在调试没有堆信息的转储,则需求符号途径上的相应二进制文件和符号文件。
关于调试十分重要:learn.microsoft.com/zh-cn/previ…
对应 Electron 开发的运用程序来说,一般只需求以下三种 PDB 符号文件:
-
Microsoft PDB 符号服务器:msdl.microsoft.com/download/sy…
-
Electron PDB 符号服务器:symbols.electronjs.org
-
运用程序 PDB 符号(本地 PDB 文件途径)
了解完符号文件后,咱们就能够开端用 Visual Studio / WinDbg Preview 等东西剖析调试本地溃散文件了。
Visual Studio 本地剖析溃散
首要装置 Visual Studio 后翻开 dump 文件,Visual Studio 将在屏幕上方区域显现有关溃散的一般信息,并在下方显现已加载模块的列表,包含运用程序称号及其版别。
Visual Studio地址:code.visualstudio.com/docs/?dv=wi…
接着点击 “运用仅限本机进行调试” 按钮,Visual Studio 将尽最大尽力调试 dump 文件,但没有显现任何有用的调用仓库信息。
为了获取调用仓库信息,咱们将告诉 Visual Studio 在哪里能够找到二进制文件。首要,翻开调试菜单并挑选选项。然后单击符号,把 Microsoft PDB 符号服务器、Electron PDB 符号服务器、运用程序 PDB 符号(本地 PDB 文件途径)都填进去。
现在 Visual Studio 将显现一切可用模块的列表,并能够明晰地看到调用仓库信息,以及详细是哪一行代码导致溃散。
若要持续调试溃散代码以及检查调用函数变量等信息,还需求配置源代码途径。在 Visual Studio 中翻开处理计划资源管理器,然后点击特点->调试源文件,填入源代码目录。这样就能够在溃散那一行代码断点下来检查变量值或许断点调试都行,溃散问题排查起来就很简单了。
至此已介绍完怎么运用 Visual Studio 东西剖析溃散文件的流程。
WinDbg Preview 本地剖析溃散
接下来再共享一个更轻量化的剖析溃散东西 WinDbg Preview。该东西可用于调试 Windows 内核方式和用户方式代码、剖析毛病 dump 文件以及在代码执行时检查 CPU 寄存器。
首要装置 WinDbg Preview 后翻开 dump 文件,WinDbg Preview 将在屏幕上方区域显现有关溃散的一般信息。
WinDbg Preview地址:learn.microsoft.com/en-us/windo…
为了获取调用仓库信息,咱们将告诉 WinDbg Preview 在哪里能够找到二进制文件。翻开 Settings 设置菜单并挑选调试设置选项。然后把 Microsoft PDB 符号服务器、Electron PDB 符号服务器、运用程序 PDB 符号(本地 PDB 文件途径)都填进去。
现在运转下 !analyze -v 命令,WinDbg Preview 将显现一切可用模块的列表,并能够明晰地看到调用仓库信息,以及详细是哪一行代码导致溃散。
若要持续调试溃散代码以及检查调用函数变量等信息,还需求配置源代码途径。翻开 Settings 设置菜单并挑选调试设置选项。然后填入源代码目录。这样就能够在溃散那一行代码断点下来检查变量值或许断点调试都行,溃散问题排查起来就很简单了。
至此已介绍完怎么运用 WinDbg Preview 东西剖析溃散文件的流程。
总结和展望
总结
客户端溃散处理流程
- 首要从一个 “为什么运用就溃散了” 的问题出发,追溯运用溃散到底是怎么发生的。
- 然后叙述了怎么运用集成了 Crashpad 的 Electron crashReporter 捕获运用溃散,并上签到雨燕、千鸟渠道来监控运用溃散的。
- 终究经过对雨燕渠道在线剖析溃散及 Visual Studio / WinDbg Preview 本地剖析溃散东西的认识,让咱们深入知道 “工欲善其事,必先利其器”,以此来处理线上运用溃散。
前端 js error 处理流程
假如是前端工程师第一次触摸客户端运用程序溃散处理流程或许会有点困难,为了降低了解本钱,咱们能够类比下客户端运用程序处理溃散和前端处理 js error 的流程。
- 首要客户端 dump 发生类比 js error 反常。
- 然后 dump 捕获上报类比 js error catch 上报。
- 接着雨燕 / 千鸟监控渠道类比 arms / JsTracker 监控渠道。
- 终究 dump 文件类比 js stack trace,pdb 调试符号文件类比 js SourceMap 文件。
关于前端工程师来说,咱们也要了解 js error 反常是怎么发生的,怎么进行捕获上报并完成监控,怎么剖析 js 仓库及运用 sourcemap 来定位到详细是哪个函数的哪一行代码引起的 error,以此来处理该反常问题。
了解完前端处理 js error 的全体流程后,有没有觉得跟前面介绍的客户端运用处理溃散流程基本一致呢?其实各个端上处理反常(不只限于处理反常)的流程都是相通的,学会搬迁学习才能够快速把握其他范畴的知识,是不是忽然觉得跨端门槛也没有幻想中那么高了呢?
展望
即使咱们用力浑身解数经过各种东西剖析治理运用溃散问题、及时升级 Electron 版别等,只要运用程序运转的那一刻开端,溃散问题就永久无法防止。
因此,咱们还需求完成一个独立于 Electron 主进程的看护进程。在 Electron 运用程序发动时该看护进程就能够随之发动,然后它来看护 Electron 进程。假如 Electron 进程溃散时,就由它来发动 Electron 进程,让运用程序持续运转起来。