mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
66 lines
3.3 KiB
JavaScript
66 lines
3.3 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import process from 'node:process'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
// Read only exception/module/thread-name metadata; this is not a stack unwinder.
|
|
export function inspectFirefoxMinidump(bytes) {
|
|
const region = (offset, size) => {
|
|
assert(Number.isSafeInteger(offset) && Number.isSafeInteger(size) && offset >= 0 && size >= 0 && offset + size <= bytes.length, 'Truncated minidump region')
|
|
return bytes.subarray(offset, offset + size)
|
|
}
|
|
const u32 = offset => region(offset, 4).readUInt32LE()
|
|
const u64 = offset => region(offset, 8).readBigUInt64LE()
|
|
const hex = value => `0x${value.toString(16)}`
|
|
const string = (offset) => {
|
|
const size = u32(offset)
|
|
assert.equal(size % 2, 0, 'Invalid UTF-16 minidump string')
|
|
return region(offset + 4, size).toString('utf16le')
|
|
}
|
|
assert.equal(region(0, 4).toString('ascii'), 'MDMP', 'Invalid minidump signature')
|
|
const count = u32(8)
|
|
assert(count <= 4096, 'Excessive minidump stream count')
|
|
const streams = Array.from({ length: count }, (_, i) => {
|
|
const at = u32(12) + i * 12
|
|
const stream = { type: u32(at), size: u32(at + 4), rva: u32(at + 8) }
|
|
region(stream.rva, stream.size)
|
|
return stream
|
|
})
|
|
const stream = (type, minimum) => {
|
|
const matches = streams.filter(item => item.type === type)
|
|
assert(matches.length === 1 && matches[0].size >= minimum, `Missing, duplicated or truncated stream ${type}`)
|
|
return matches[0]
|
|
}
|
|
const exceptionStream = stream(6, 168)
|
|
const e = exceptionStream.rva
|
|
const parameters = u32(e + 32)
|
|
assert(parameters <= 15, 'Invalid exception parameter count')
|
|
const exception = { threadId: u32(e), code: hex(u32(e + 8)), flags: u32(e + 12), address: hex(u64(e + 24)), parameters: Array.from({ length: parameters }, (_, i) => hex(u64(e + 40 + i * 8))) }
|
|
const moduleStream = stream(4, 4)
|
|
const moduleCount = u32(moduleStream.rva)
|
|
assert(moduleCount * 108 + 4 <= moduleStream.size, 'Truncated module list')
|
|
const modules = Array.from({ length: moduleCount }, (_, i) => {
|
|
const at = moduleStream.rva + 4 + 108 * i
|
|
return { name: string(u32(at + 20)), base: hex(u64(at)), size: u32(at + 8), codeView: region(u32(at + 80), u32(at + 76)).toString('hex') }
|
|
})
|
|
const faultModule = modules.find(module => BigInt(module.base) <= BigInt(exception.address) && BigInt(exception.address) < BigInt(module.base) + BigInt(module.size))
|
|
let threadName
|
|
if (streams.some(item => item.type === 24)) {
|
|
const names = stream(24, 4)
|
|
const count = u32(names.rva)
|
|
assert(count * 12 + 4 <= names.size, 'Truncated thread name list')
|
|
for (let i = 0; i < count; i++) {
|
|
const at = names.rva + 4 + i * 12
|
|
if (u32(at) === exception.threadId)
|
|
threadName = string(Number(u64(at + 4)))
|
|
}
|
|
}
|
|
return { exception, threadName, faultModule, moduleOffset: faultModule && hex(BigInt(exception.address) - BigInt(faultModule.base)), moduleCount, stackResolved: false }
|
|
}
|
|
|
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
assert.equal(process.argv.length, 3, 'Usage: node refactor/scripts/firefox-minidump.mjs <dump>')
|
|
console.log(JSON.stringify(inspectFirefoxMinidump(fs.readFileSync(process.argv[2])), null, 2))
|
|
}
|