Better tool for symbolication of crash reports
This commit is contained in:
parent
c82ce8f15f
commit
6d816d01ad
2 changed files with 110 additions and 31 deletions
110
ts/scripts/symbolicate-crash-reports.ts
Normal file
110
ts/scripts/symbolicate-crash-reports.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { tmpdir } from 'os';
|
||||
import { execFile as rawExecFile } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { wrapEventEmitterOnce } from '../util/wrapEventEmitterOnce';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
|
||||
const execFile = promisify(rawExecFile);
|
||||
|
||||
const TARGET = 'https://symbols.electronjs.org';
|
||||
|
||||
const proxyServer = http
|
||||
.createServer(({ method, url = '/' }, res) => {
|
||||
console.log(`Proxy server got request ${method} ${url}`);
|
||||
if (method !== 'GET') {
|
||||
throw new Error('Unsupported');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const patchedURL = url.replace(/signal-desktop-[^\/.]+/g, 'electron');
|
||||
|
||||
https.get(`${TARGET}${patchedURL}`, remoteRes => {
|
||||
res.writeHead(remoteRes.statusCode ?? 500, remoteRes.headers);
|
||||
|
||||
remoteRes.pipe(res);
|
||||
});
|
||||
})
|
||||
.unref();
|
||||
|
||||
async function symbolicate(
|
||||
outDir: string,
|
||||
fileName: string,
|
||||
proxyPort: number
|
||||
): Promise<void> {
|
||||
const tmpDir = await fs.mkdtemp(path.join(tmpdir(), 'signal-crashe'));
|
||||
await fs.mkdir(tmpDir, { recursive: true });
|
||||
|
||||
const encoded = await fs.readFile(fileName);
|
||||
const { reports } = Proto.CrashReportList.decode(encoded);
|
||||
|
||||
const { name: prefix } = path.parse(fileName);
|
||||
|
||||
await Promise.all(
|
||||
reports.map(async ({ filename: reportName, content }) => {
|
||||
if (!reportName || !content) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { base, name, ext } = path.parse(reportName);
|
||||
if (ext !== '.dmp') {
|
||||
console.log(`Ignoring ${reportName}, wrong extension`);
|
||||
return;
|
||||
}
|
||||
|
||||
const dumpFile = path.join(tmpDir, `${prefix}-${base}`);
|
||||
console.log(`Extracting to ${dumpFile}`);
|
||||
await fs.writeFile(dumpFile, content);
|
||||
|
||||
const outFile = path.join(outDir, `${prefix}-${name}.txt`);
|
||||
|
||||
await execFile('minidump-stackwalk', [
|
||||
'--symbols-url',
|
||||
`http://127.0.0.1:${proxyPort}`,
|
||||
'--output-file',
|
||||
outFile,
|
||||
dumpFile,
|
||||
]);
|
||||
console.log(`Symbolicating ${dumpFile} to ${outFile}`);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function main(
|
||||
outDir: string,
|
||||
fileNames: ReadonlyArray<string>
|
||||
): Promise<void> {
|
||||
await fs.mkdir(outDir, { recursive: true });
|
||||
|
||||
proxyServer.listen(0);
|
||||
|
||||
await wrapEventEmitterOnce(proxyServer, 'listening');
|
||||
const addr = proxyServer.address();
|
||||
strictAssert(
|
||||
typeof addr === 'object' && addr !== null,
|
||||
'Address has to be an object'
|
||||
);
|
||||
|
||||
const { port: proxyPort } = addr;
|
||||
|
||||
console.log(`Proxy server listening on ${proxyPort}`);
|
||||
|
||||
await Promise.all(
|
||||
fileNames.map(fileName => symbolicate(outDir, fileName, proxyPort))
|
||||
);
|
||||
|
||||
proxyServer.close();
|
||||
}
|
||||
|
||||
main(process.argv[2], process.argv.slice(3)).catch(error => {
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
|
||||
async function main(fileName: string, outDir: string) {
|
||||
await fs.mkdir(outDir, { recursive: true });
|
||||
|
||||
const encoded = await fs.readFile(fileName);
|
||||
const { reports } = Proto.CrashReportList.decode(encoded);
|
||||
|
||||
await Promise.all(
|
||||
reports.map(async ({ filename, content }) => {
|
||||
if (!filename || !content) {
|
||||
return;
|
||||
}
|
||||
|
||||
const outFile = path.join(outDir, path.basename(filename));
|
||||
console.log(`Extracting to ${outFile}`);
|
||||
await fs.writeFile(outFile, content);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
main(process.argv[2], process.argv[3]).catch(error => {
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
Loading…
Add table
Reference in a new issue