Better processing of minidump files
This commit is contained in:
parent
de33410be1
commit
8719b296cf
1 changed files with 106 additions and 21 deletions
|
@ -10,24 +10,58 @@ import z from 'zod';
|
|||
import type { LoggerType } from '../ts/types/Logging';
|
||||
import * as Errors from '../ts/types/errors';
|
||||
import { isProduction } from '../ts/util/version';
|
||||
import { isNotNil } from '../ts/util/isNotNil';
|
||||
import OS from '../ts/util/os/osMain';
|
||||
|
||||
const dumpSchema = z
|
||||
.object({
|
||||
crashing_thread: z
|
||||
.object({
|
||||
// See https://github.com/rust-minidump/rust-minidump/blob/main/minidump-processor/json-schema.md
|
||||
const dumpString = z.string().or(z.null()).optional();
|
||||
const dumpNumber = z.number().or(z.null()).optional();
|
||||
|
||||
const threadSchema = z.object({
|
||||
thread_name: dumpString,
|
||||
frames: z
|
||||
.object({
|
||||
registers: z.unknown(),
|
||||
offset: dumpString,
|
||||
module: dumpString,
|
||||
module_offset: dumpString,
|
||||
})
|
||||
.passthrough()
|
||||
.array()
|
||||
.or(z.null())
|
||||
.optional(),
|
||||
});
|
||||
|
||||
const dumpSchema = z.object({
|
||||
crash_info: z
|
||||
.object({
|
||||
type: dumpString,
|
||||
crashing_thread: dumpNumber,
|
||||
address: dumpString,
|
||||
})
|
||||
.passthrough()
|
||||
.optional()
|
||||
.or(z.null()),
|
||||
crashing_thread: threadSchema.or(z.null()).optional(),
|
||||
threads: threadSchema.array().or(z.null()).optional(),
|
||||
modules: z
|
||||
.object({
|
||||
filename: dumpString,
|
||||
debug_file: dumpString,
|
||||
debug_id: dumpString,
|
||||
base_addr: dumpString,
|
||||
end_addr: dumpString,
|
||||
version: dumpString,
|
||||
})
|
||||
.array()
|
||||
.or(z.null())
|
||||
.optional(),
|
||||
system_info: z
|
||||
.object({
|
||||
cpu_arch: dumpString,
|
||||
os: dumpString,
|
||||
os_ver: dumpString,
|
||||
})
|
||||
.passthrough();
|
||||
.or(z.null())
|
||||
.optional(),
|
||||
});
|
||||
|
||||
async function getPendingDumps(): Promise<ReadonlyArray<string>> {
|
||||
const crashDumpsPath = await realpath(app.getPath('crashDumps'));
|
||||
|
@ -81,12 +115,43 @@ export function setup(
|
|||
}
|
||||
|
||||
const pendingDumps = await getPendingDumps();
|
||||
if (pendingDumps.length !== 0) {
|
||||
getLogger().warn(
|
||||
`crashReports: ${pendingDumps.length} pending dumps found`
|
||||
const filteredDumps = (
|
||||
await Promise.all(
|
||||
pendingDumps.map(async fullPath => {
|
||||
const content = await readFile(fullPath);
|
||||
try {
|
||||
const dump = dumpSchema.parse(
|
||||
JSON.parse(dumpToJSONString(content))
|
||||
);
|
||||
if (dump.crash_info?.type !== 'Simulated Exception') {
|
||||
return fullPath;
|
||||
}
|
||||
} catch (error) {
|
||||
getLogger().error(
|
||||
`crashReports: failed to read crash report ${fullPath} due to error`,
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
return pendingDumps.length;
|
||||
|
||||
try {
|
||||
await unlink(fullPath);
|
||||
} catch (error) {
|
||||
getLogger().error(
|
||||
`crashReports: failed to unlink crash report ${fullPath}`,
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
)
|
||||
).filter(isNotNil);
|
||||
|
||||
if (filteredDumps.length !== 0) {
|
||||
getLogger().warn(
|
||||
`crashReports: ${filteredDumps.length} pending dumps found`
|
||||
);
|
||||
}
|
||||
return filteredDumps.length;
|
||||
});
|
||||
|
||||
ipc.handle('crash-reports:write-to-log', async () => {
|
||||
|
@ -109,10 +174,30 @@ export function setup(
|
|||
const { mtime } = await stat(fullPath);
|
||||
|
||||
const dump = dumpSchema.parse(JSON.parse(dumpToJSONString(content)));
|
||||
for (const frame of dump.crashing_thread?.frames ?? []) {
|
||||
delete frame.registers;
|
||||
|
||||
if (dump.crash_info?.type === 'Simulated Exception') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
dump.modules = dump.modules?.filter(({ filename }) => {
|
||||
if (filename == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Node.js Addons are useful
|
||||
if (/\.node$/.test(filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// So is Electron
|
||||
if (/electron|signal/i.test(filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rest are not relevant
|
||||
return false;
|
||||
});
|
||||
|
||||
logger.warn(
|
||||
`crashReports: dump=${basename(fullPath)} ` +
|
||||
`mtime=${JSON.stringify(mtime)}`,
|
||||
|
|
Loading…
Reference in a new issue