diff --git a/ts/scripts/symbolicate-crash-reports.ts b/ts/scripts/symbolicate-crash-reports.ts new file mode 100644 index 0000000000..1058358c9f --- /dev/null +++ b/ts/scripts/symbolicate-crash-reports.ts @@ -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 { + 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 +): Promise { + 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); +}); diff --git a/ts/scripts/unpack-crash-reports.ts b/ts/scripts/unpack-crash-reports.ts deleted file mode 100644 index 8144916b4f..0000000000 --- a/ts/scripts/unpack-crash-reports.ts +++ /dev/null @@ -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); -});