Application crashes in Electron

If the exception is only caused by Javascript, you can listen for the relevant Error type event in the application logic and handle it. By listening for crashed related events, you can listen for and respond to application crashes within the application.

1
2
3
4
app.on('gpu-process-crashed', (_event, killed) => {});
app.on('renderer-process-crashed', (_e, _w, killed) => {});
mainWindow.webContents.on('plugin-crashed', (_ev, name, version) => {});
mainWindow.webContents.on('crashed', (ev, killed) => {});

The above code can only listen to and handle crashed events, but it can’t tell what caused the crash; to analyze the details, you need to get the minidump file where the crash occurred and debug it.

Application crash files in Electron

Only when crashReporter.start is executed in the Electron application will the crash-related directory and file information be generated. The following sample code is referenced from the official documentation.

1
2
3
4
5
6
7
8
const { crashReporter } = require('electron');
 
crashReporter.start({
  productName: 'YourName',
  companyName: 'YourCompany',
  submitURL: 'https://your-domain.com/url-to-submit',
  uploadToServer: true
});

Create a crash:

1
process.crash();

Find crash files.

1
2
3
4
5
6
7
8
const app = require('electron').remote.app;
 
// 对于支持 crashDumps 的版本,获取崩溃文件路径
app.getPath('crashDumps');
 
// 不支持 getPath('crashDumps') 的 Electron 版本
// 从 temp 目录下查找 <productName>-Crashes 目录
app.getPath('temp');

By actually testing the above code, the crashed file was found in the temporary directory under the electron@5.x version with the .dmp suffix. As shown in the figure.

Electron crash file analysis

What is the application symbols file (Symbols)

The .dmp file obtained above is a minidump file, which is a file in the crash-to-storage technology format developed by Microsoft. The minidump file contains only the most necessary information to recover the call stack of all threads of the failed process and to see the values of local variables at the moment of failure.

To analyze the minidump file specifically, you need to use the symbols file of the Electron application.

The application symbols file is similar to the sourceMap file in the front-end, which allows debugging tools to map the crash information in the dump file to the original code and parse it. The format of symbols file varies from platform to platform, Windows platform applications are generally in .pdb format.

Google breakpad is a cross-platform crash dump and analysis framework and tools collection, it provides tools to support the conversion of crash files under all platforms to Symobol format as defined by Breakpad. The Symobol format is very simple, it is actually a plain text file, one record per line, with a space separating the fields in each record and the first field of each record indicating what type of record the line is.

If you are using the official Electron package, you can download the corresponding symbols file from github at

If it is a self-programmed application, it will output a symbolic file with the same name in the output directory of the executable. For example electron.exe -> electron.exe.pdb .

Debugging Electron with Windbg

For the minidump file, you can use Windbg to analyze it under windows. It is included in the windows SDK and you can install the windows 10 SDK directly.

A brief step-by-step procedure is roughly as follows (please consult the official documentation for a more detailed approach).

  • Install the windows 10 SDK
  • Search for Windbg and open it
  • Press the ctrl+E shortcut and select the electron.exe application file
  • At the bottom run .reload /f /i electron.exe to load it. This will look for its corresponding symbol file, and will report an error if it is not found. You can place the corresponding symbol file in any directory in the error message, and then re-execute
  • Press ctrl+s shortcut key to select minidump file to load.
  • Just analyze it according to the loading situation

Related references:

Analysis using the Sentry platform

Electron provides the official @sentry-cli and @sentry/electron toolkits available through Sentry. By installing and configuring them, you can integrate Sentry in your Electron application, which can help collect and analyze crash files.

Installation and Integration Sentry

Installing @sentry/electron.

1
2
yarn add -D @sentry/cli
yarn add @sentry/electron

Integrating Sentry in Electron Applications.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 用于main 主进程应用中
import { init } from '@sentry/electron/dist/main';
// 用于浏览器渲染进程中
import { init } from '@sentry/electron/dist/renderer';
 
init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  debug: process.env.APP_ENV !== 'prod',
  environment: process.env.APP_ENV,
  release: 'release@' + pkg.version,
  beforeSend: data => {
    const exception = data.exception;
    if (exception && exception.values) {
      // TODO,可以做一些修正累的处理等
    }
    return data;
  },
});

Configurator file download and Sentry upload

1
2
3
4
5
npm install -g @sentry/wizard
# 登陆 Sentry 并获取相关项目配置信息
sentry-wizard --integration electron --url "https://mysentry.lzw.me"
# or 手动填写相关项目配置信息
sentry-wizard --integration electron --skip-connect

Follow the prompts and when you are done, a sentry-symbols.js file will be generated. Run it after each Electron update to download the corresponding symbols from the Electron Github Release address and upload them to your configured Sentry server.

For more specific information, please refer to the official documentation at

SourceMap of JavaScript files uploaded to Sentry

Integrating in webpack, uploading SourceMap to the Sentry server.

1
yarn add -D @sentry/webpack-plugin

To configure the plugin in webpack.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const SentryWebpackPlugin = require("@sentry/webpack-plugin");
 
module.exports = {
  // other configuration
  configureWebpack: {
    plugins: [
      new SentryWebpackPlugin({
        // sentry-cli configuration
        authToken: process.env.SENTRY_AUTH_TOKEN,
        org: "example-org",
        project: "example-project",
        release: process.env.SENTRY_RELEASE,
 
        // webpack specific configuration
        include: ".",
        ignore: ["node_modules", "webpack.config.js"],
      }),
    ],
  },
};

Alternatively, you can use @sentry/cli to upload a SourceMap, for which the official documentation can be found at

Compile your own symbols file for Electron

If it is a self-coded application after modifying the Electron source code, the .pdb type file in the build output directory is the symbols symbol file, such as electron.exe.pdb, etc.

The Sentry platform supports symbol files in Breakpad format. In addition, the Sentry platform also supports Native-style pdb files, but does not support pdb files generated under the .NET platform.

We can use the dump_syms.exe utility to convert pdb format symbol files to Breakpad format. The dump_syms.exe file is in the Electron compilation directory at the following path third_party\breakpad\breakpad\src\tools\windows\binaries\dump_syms.exe. It can also be downloaded at the following address.

Example of electron.exe.sym file generated using dump_syms.exe tool.

1
..\..\third_party\breakpad\breakpad\src\tools\windows\binaries\dump_syms.exe out\Release\electron.exe > electron.exe.sym

Open the electron.exe.sym file and take a look. It is actually a SourceMap-like text file containing the original filenames and the mapping of variables, functions, methods and so on. The first line contains the system platform, Breakpadid and the corresponding pdb file name.

Sentry upload of self-coded Electron symbol files

The official symbol file downloaded from Electron contains the following information, which we can use to generate a package of the same format and provide to Sentry for upload. The paragraph after the electron.exe.pbd path is Breakpadid, which can be obtained from the first line of the symbol file.

In fact, the main logic in the sentry-symbols.js file is to download the corresponding symbols according to the currently installed version of Electron and upload them using @sentry/cli. We can also write a similar upload script. Example.

We refer to the electron-<version>-win32-x64-symbols.zip file downloaded from the official website as an example, and generate and compress a zip file of the same structure locally into the .electron-symbols/custom directory.

Generate Symbols symbols files from the Electron compile output directory, main example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const path = require('path');
const fs = require('fs');
const { execSync } = require('child_process');
const getArgv = ()  => {
    return process.argv.slice(2).map(d => d.replace(/^--/, '')).reduce((res, d) => {
        if (d.includes('=')) {
            const [key, value] = d.split('=');
            res[key] = value;
        } else res[d] = true;
        return res;
    }, {});
};
const argv = getArgv();
 
function genSym() {
    const version = argv.version || '5.0.13';
    const outDirBase = 'electron-v${version}-win32-' + (argv.ia32 ? 'ia32' : 'x64') + '-symbols';
    const dumpSyms = path.resolve('../../third_party/breakpad/breakpad/src/tools/windows/binaries/dump_syms.exe');
 
    if (!fs.existsSync(dumpSyms)) {
        console.log('未找到 dump_syms.exe 文件,请将当前文件放置到构建输出目录中,如 out/Release 下。');
        console.log('NotFound dumpSymsPath:', dumpSyms);
    } else {
        if (!fs.existsSync(outDirBase)) fs.mkdirSync(outDirBase, {recursive: true});
        fs.writeFileSync(path.join(outDirBase, 'version'), version);
 
        ['electron.exe', 'libEGL.dll', 'libGLESv2.dll'].forEach(filename => {
            if (!fs.existsSync(filename)) return console.error('未发现文件:', filename);
 
            const outFilename = filename + '.sym';
            execSync(dumpSyms + ' ' + filename + ' > ' + outFilename);
 
            if (fs.existsSync(outFilename)) {
                const outFileContent = fs.readFileSync(outFilename, 'utf8');
                const firstLine = outFileContent.split('/n')[0].split(' ');
                const breakpadid = firstLine[3];
 
                if (!breakpadid) return console.log('sym 文件读取错误:', firstLine);
 
                const outFilePath = outDirBase + '/breakpad_symbols/' + breakpadid + '/' + outFilename;
                if (!fs.existsSync(path.dirname(outFilePath))) fs.mkdirSync(path.dirname(outFilePath), {recursive: true});
                fs.writeFileSync(outFilePath, outFileContent);
                console.log('生成文件:', outFilePath);
            } else {
                console.log('可能生成失败了,未发现生成的文件:', filename, '=>', outFilename);
            }
        });
 
        console.log('执行完成');
    }
}
 
genSym();

Compress the contents of the generated symbols directory into a zip file and place it in a custom directory, such as .electron-symbols/custom, then upload the symbols file to Sentry with the following main code example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
const SentryCli = require('@sentry/cli');
const argv = require('yargs').argv;
const path = require('path');
const fs = require('fs');
const zipDir = argv.dir || '.electron-symbols/custom';
 
async function doUpload() {
  const sentryCli = new SentryCli(argv.test ? './sentry.properties' : './sentry.prod.properties');
  const zipFiles = fs.readdirSync(zipDir).filter(name => name.endsWith('symbols.zip'));
 
  if (!zipFiles) {
    console.log('未发现可上传的 Symbols 文件!', zipDir);
    return;
  }
 
  for (const filename of zipFiles) {
      const zipPath = path.resolve(zipDir, filename);
      await sentryCli.execute(['upload-dif', '-t', 'breakpad', '--no-zips', zipPath], true);
  }
}
 
doUpload();

Execute the test, and if all goes well, the result will be approximately as follows.

Related references.

The default release version pdb file output by Electron compilation is too large

The electron.exe.pdb file size is close to 1.5G to 2G in the directory of the Release version of the compiled output using the default parameters. This is because the default compiled output symbol file contains more information such as source code, you can adjust the output of the symbol file by setting the compile parameter symbol_level=1. This parameter can be added to the configuration file at

1
electron/build/args/release.gn

It can also be added to the command line of the generated GN project at

1
gn gen out/Release --args="import(\"//electron/build/args/release.gn\") symbol_level=1"

After setting symbol_level=1, the output electron.exe.pdb is only a little over 100 M in size.

Failed to generate sym file using dump_syms.exe

The error message is as follows.

CoCreateInstance CLSID_DiaSource {E6756135-1E65-4D17-8576-610761398C3C} failed (msdia*.dll unregistered?) Open failed

This is because the system has not registered the msdia*.dll file. From the Visual Studio installation directory, you can search for the msdia120.dll and msdia140 .dll files. Let’s register msdia140.dll and that’s it.

Start the cmd command prompt as system administrator (shortcut win+x, click “Command Prompt (Administrator)”), and then execute the following command to register msdia140.dll: msdia140.dll.

1
regsvr32 "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\msdia140.dll"

If executed with a non-system administrator cmd, you will get a registration failure message with the error code 0x80070005.

msdia140.dll is loaded, but the call to DllRegisterServer failed with error code 0x80070005