458 lines
19 KiB
JavaScript
458 lines
19 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.readMinidump = readMinidump;
|
|
var _fs = _interopRequireDefault(require("fs"));
|
|
var _util = _interopRequireDefault(require("util"));
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
/* eslint-disable no-console */
|
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
|
|
const exceptionTypes = {
|
|
C0000005: 'EXCEPTION_ACCESS_VIOLATION',
|
|
'80000002': 'EXCEPTION_DATATYPE_MISALIGNMENT',
|
|
'80000003': 'EXCEPTION_BREAKPOINT',
|
|
'80000004': 'EXCEPTION_SINGLE_STEP',
|
|
C000008C: 'EXCEPTION_ARRAY_BOUNDS_EXCEEDED',
|
|
C000008D: 'EXCEPTION_FLT_DENORMAL_OPERAND',
|
|
C000008E: 'EXCEPTION_FLT_DIVIDE_BY_ZERO',
|
|
C000008F: 'EXCEPTION_FLT_INEXACT_RESULT',
|
|
C0000090: 'EXCEPTION_FLT_INVALID_OPERATION',
|
|
C0000091: 'EXCEPTION_FLT_OVERFLOW',
|
|
C0000092: 'EXCEPTION_FLT_STACK_CHECK',
|
|
C0000093: 'EXCEPTION_FLT_UNDERFLOW',
|
|
C0000094: 'EXCEPTION_INT_DIVIDE_BY_ZERO',
|
|
C0000095: 'EXCEPTION_INT_OVERFLOW',
|
|
C0000096: 'EXCEPTION_PRIV_INSTRUCTION',
|
|
C0000006: 'EXCEPTION_IN_PAGE_ERROR',
|
|
C000001D: 'EXCEPTION_ILLEGAL_INSTRUCTION',
|
|
C0000025: 'EXCEPTION_NONCONTINUABLE_EXCEPTION',
|
|
C00000FD: 'EXCEPTION_STACK_OVERFLOW',
|
|
C0000026: 'EXCEPTION_INVALID_DISPOSITION',
|
|
'80000001': 'EXCEPTION_GUARD_PAGE',
|
|
C0000008: 'EXCEPTION_INVALID_HANDLE'
|
|
};
|
|
class FileReader {
|
|
utf16Decoder = new _util.default.TextDecoder('utf-16');
|
|
static promiseFs = Object.freeze({
|
|
open: _util.default.promisify(_fs.default.open),
|
|
read: _util.default.promisify(_fs.default.read),
|
|
close: _util.default.promisify(_fs.default.close)
|
|
});
|
|
constructor(path, bufferSize = 2048) {
|
|
this.handle = FileReader.promiseFs.open(path, 'r');
|
|
this.buffer = new Uint8Array(bufferSize);
|
|
}
|
|
async read(u32toReadCount, position = null) {
|
|
const byteSize = u32toReadCount * 4;
|
|
await this.readCore(byteSize, position);
|
|
return new ReadResult(this.buffer.buffer.slice(0, byteSize));
|
|
}
|
|
async readMinidumpString(rva) {
|
|
if (rva === 0) {
|
|
return '';
|
|
}
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_string
|
|
await this.readCore(4, rva);
|
|
const length = this.buffer[0] | this.buffer[1] << 8 | this.buffer[2] << 16 | this.buffer[3] << 24;
|
|
await this.readCore(Math.min(length, this.buffer.byteLength), rva + 4);
|
|
return this.utf16Decoder.decode(this.buffer.slice(0, length));
|
|
}
|
|
async readCore(byteLength, position = null) {
|
|
if (byteLength > this.buffer.byteLength) {
|
|
throw new Error(`Requested nuber of bytes ${byteLength} exceeds buffer size ${this.buffer.byteLength}.`);
|
|
}
|
|
if (this.handle == null) {
|
|
throw new Error('Cannot use FileReader once closed.');
|
|
}
|
|
const handle = await this.handle;
|
|
const readResult = await FileReader.promiseFs.read(handle, this.buffer, 0, byteLength, position);
|
|
if (readResult.bytesRead < byteLength) {
|
|
throw new Error(`FileReader failed to read enough bytes: 0x${readResult.bytesRead}`);
|
|
}
|
|
}
|
|
async close() {
|
|
if (this.handle == null) {
|
|
return;
|
|
}
|
|
const handle = await this.handle;
|
|
this.handle = null;
|
|
await FileReader.promiseFs.close(handle);
|
|
}
|
|
}
|
|
class ReadResult {
|
|
index = 0;
|
|
// irl, this should likely take the uint8 and work from there, but this is fine for us.
|
|
// Maybe just steal my other impl here https://github.com/jlennox/WebWad/blob/main/wad.ts#L8
|
|
// but we likely don't want to load the entire file at once. But who knows, maybe the system
|
|
// call reduction is better and/or it irl doesn't matter either way.
|
|
constructor(buffer) {
|
|
this.u8 = new Uint8Array(buffer);
|
|
this.u16 = new Uint16Array(buffer);
|
|
this.u32 = new Uint32Array(buffer);
|
|
}
|
|
seek(index) {
|
|
this.index = index;
|
|
}
|
|
readuint32() {
|
|
const val = this.u32[this.index / 4];
|
|
this.index += 4;
|
|
return val;
|
|
}
|
|
readuint16() {
|
|
const val = this.u16[this.index / 2];
|
|
this.index += 2;
|
|
return val;
|
|
}
|
|
readByteArray(count) {
|
|
const val = Array.from(this.u8.slice(this.index, this.index + count));
|
|
this.index += count;
|
|
return val;
|
|
}
|
|
readuint64() {
|
|
const u32Index = this.index / 4;
|
|
const val = BigInt(this.u32[u32Index]) | BigInt(this.u32[u32Index + 1]) << BigInt(32);
|
|
this.index += 8;
|
|
return val;
|
|
}
|
|
}
|
|
function isMinidumpFilename(filename) {
|
|
return /\.dmp$/i.test(filename);
|
|
}
|
|
var MinidumpStreamType; // https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_header
|
|
(function (MinidumpStreamType) {
|
|
MinidumpStreamType[MinidumpStreamType["UnusedStream"] = 0] = "UnusedStream";
|
|
MinidumpStreamType[MinidumpStreamType["ReservedStream0"] = 1] = "ReservedStream0";
|
|
MinidumpStreamType[MinidumpStreamType["ReservedStream1"] = 2] = "ReservedStream1";
|
|
MinidumpStreamType[MinidumpStreamType["ThreadListStream"] = 3] = "ThreadListStream";
|
|
MinidumpStreamType[MinidumpStreamType["ModuleListStream"] = 4] = "ModuleListStream";
|
|
MinidumpStreamType[MinidumpStreamType["MemoryListStream"] = 5] = "MemoryListStream";
|
|
MinidumpStreamType[MinidumpStreamType["ExceptionStream"] = 6] = "ExceptionStream";
|
|
MinidumpStreamType[MinidumpStreamType["SystemInfoStream"] = 7] = "SystemInfoStream";
|
|
MinidumpStreamType[MinidumpStreamType["ThreadExListStream"] = 8] = "ThreadExListStream";
|
|
MinidumpStreamType[MinidumpStreamType["Memory64ListStream"] = 9] = "Memory64ListStream";
|
|
MinidumpStreamType[MinidumpStreamType["CommentStreamA"] = 10] = "CommentStreamA";
|
|
MinidumpStreamType[MinidumpStreamType["CommentStreamW"] = 11] = "CommentStreamW";
|
|
MinidumpStreamType[MinidumpStreamType["HandleDataStream"] = 12] = "HandleDataStream";
|
|
MinidumpStreamType[MinidumpStreamType["FunctionTableStream"] = 13] = "FunctionTableStream";
|
|
MinidumpStreamType[MinidumpStreamType["UnloadedModuleListStream"] = 14] = "UnloadedModuleListStream";
|
|
MinidumpStreamType[MinidumpStreamType["MiscInfoStream"] = 15] = "MiscInfoStream";
|
|
MinidumpStreamType[MinidumpStreamType["MemoryInfoListStream"] = 16] = "MemoryInfoListStream";
|
|
MinidumpStreamType[MinidumpStreamType["ThreadInfoListStream"] = 17] = "ThreadInfoListStream";
|
|
MinidumpStreamType[MinidumpStreamType["HandleOperationListStream"] = 18] = "HandleOperationListStream";
|
|
MinidumpStreamType[MinidumpStreamType["TokenStream"] = 19] = "TokenStream";
|
|
MinidumpStreamType[MinidumpStreamType["JavaScriptDataStream"] = 20] = "JavaScriptDataStream";
|
|
MinidumpStreamType[MinidumpStreamType["SystemMemoryInfoStream"] = 21] = "SystemMemoryInfoStream";
|
|
MinidumpStreamType[MinidumpStreamType["ProcessVmCountersStream"] = 22] = "ProcessVmCountersStream";
|
|
MinidumpStreamType[MinidumpStreamType["IptTraceStream"] = 23] = "IptTraceStream";
|
|
MinidumpStreamType[MinidumpStreamType["ThreadNamesStream"] = 24] = "ThreadNamesStream";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamNull"] = 32768] = "ceStreamNull";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamSystemInfo"] = 32769] = "ceStreamSystemInfo";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamException"] = 32770] = "ceStreamException";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamModuleList"] = 32771] = "ceStreamModuleList";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamProcessList"] = 32772] = "ceStreamProcessList";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamThreadList"] = 32773] = "ceStreamThreadList";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamThreadContextList"] = 32774] = "ceStreamThreadContextList";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamThreadCallStackList"] = 32775] = "ceStreamThreadCallStackList";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamMemoryVirtualList"] = 32776] = "ceStreamMemoryVirtualList";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamMemoryPhysicalList"] = 32777] = "ceStreamMemoryPhysicalList";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamBucketParameters"] = 32778] = "ceStreamBucketParameters";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamProcessModuleMap"] = 32779] = "ceStreamProcessModuleMap";
|
|
MinidumpStreamType[MinidumpStreamType["ceStreamDiagnosisList"] = 32780] = "ceStreamDiagnosisList";
|
|
MinidumpStreamType[MinidumpStreamType["LastReservedStream"] = 65535] = "LastReservedStream";
|
|
})(MinidumpStreamType || (MinidumpStreamType = {}));
|
|
class MINIDUMP_HEADER {
|
|
static U32_SIZE = 4;
|
|
constructor(reader) {
|
|
this.signature = reader.readuint32();
|
|
this.version = reader.readuint32();
|
|
this.numberOfStreams = reader.readuint32();
|
|
this.streamDirectoryOffset = reader.readuint32();
|
|
}
|
|
static async read(reader, position) {
|
|
return new MINIDUMP_HEADER(await reader.read(MINIDUMP_HEADER.U32_SIZE, position));
|
|
}
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_directory
|
|
class MINIDUMP_DIRECTORY {
|
|
static U32_SIZE = 3;
|
|
constructor(reader) {
|
|
this.streamType = reader.readuint32();
|
|
this.dataSize = reader.readuint32();
|
|
this.dataOffset = reader.readuint32();
|
|
}
|
|
static async read(reader, position) {
|
|
return new MINIDUMP_DIRECTORY(await reader.read(MINIDUMP_DIRECTORY.U32_SIZE, position));
|
|
}
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_exception_stream
|
|
class MINIDUMP_EXCEPTION_STREAM {
|
|
static U32_SIZE = 8;
|
|
constructor(reader) {
|
|
this.threadId = reader.readuint32();
|
|
this.alignment = reader.readuint32();
|
|
this.exceptionCode = reader.readuint32();
|
|
this.exceptionFlags = reader.readuint32();
|
|
this.exceptionRecord = reader.readuint64();
|
|
this.exceptionAddress = reader.readuint64();
|
|
}
|
|
static async read(reader, position) {
|
|
return new MINIDUMP_EXCEPTION_STREAM(await reader.read(MINIDUMP_EXCEPTION_STREAM.U32_SIZE, position));
|
|
}
|
|
getExceptionCodeString() {
|
|
const exceptionCode = this.exceptionCode.toString(16).toUpperCase().padStart(8, '0');
|
|
const exceptionString = exceptionTypes[exceptionCode] ?? exceptionCode;
|
|
return exceptionString;
|
|
}
|
|
}
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_location_descriptor
|
|
class MINIDUMP_LOCATION_DESCRIPTOR {
|
|
constructor(reader) {
|
|
this.dataSize = reader.readuint32();
|
|
this.rva = reader.readuint32();
|
|
}
|
|
}
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_module_list
|
|
class MINIDUMP_MODULE_LIST {
|
|
static U32_SIZE = 1;
|
|
constructor(reader) {
|
|
this.numberOfModules = reader.readuint32();
|
|
}
|
|
static async read(reader, position) {
|
|
return new MINIDUMP_MODULE_LIST(await reader.read(MINIDUMP_MODULE_LIST.U32_SIZE, position));
|
|
}
|
|
}
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_module
|
|
class MINIDUMP_MODULE {
|
|
// sizeof(MINIDUMP_MODULE) 108
|
|
// sizeof(VS_FIXEDFILEINFO) 52
|
|
// sizeof(MINIDUMP_LOCATION_DESCRIPTOR) 8
|
|
// sizeof(RVA) 4
|
|
static U32_SIZE = 108 / 4;
|
|
constructor(reader) {
|
|
this.baseOfImage = reader.readuint64();
|
|
this.sizeOfImage = reader.readuint32();
|
|
this.checkSum = reader.readuint32();
|
|
this.timeDateStamp = reader.readuint32();
|
|
this.moduleNameRva = reader.readuint32();
|
|
this.versionInfo = new VS_FIXEDFILEINFO(reader);
|
|
this.cvRecord = new MINIDUMP_LOCATION_DESCRIPTOR(reader);
|
|
this.miscRecord = new MINIDUMP_LOCATION_DESCRIPTOR(reader);
|
|
this.reserved0 = reader.readuint64();
|
|
this.reserved1 = reader.readuint64();
|
|
}
|
|
static async read(reader, position) {
|
|
return new MINIDUMP_MODULE(await reader.read(MINIDUMP_MODULE.U32_SIZE, position));
|
|
}
|
|
containsAddress(address) {
|
|
const endAddress = this.baseOfImage + BigInt(this.sizeOfImage);
|
|
return this.baseOfImage <= address && endAddress > address;
|
|
}
|
|
async getModuleFileName(reader) {
|
|
const moduleName = await reader.readMinidumpString(this.moduleNameRva);
|
|
let dirPos = moduleName.lastIndexOf('\\');
|
|
if (dirPos === -1) {
|
|
dirPos = moduleName.lastIndexOf('/');
|
|
}
|
|
dirPos = dirPos === -1 ? 0 : dirPos + 1;
|
|
return moduleName.slice(dirPos);
|
|
}
|
|
async getCVInfoIdString(reader) {
|
|
return (await CV_INFO.read(reader, this.cvRecord.rva)).getIdString();
|
|
}
|
|
getCodeIdString() {
|
|
return (this.timeDateStamp.toString(16).padStart(8, '0') + this.sizeOfImage.toString(16)).toUpperCase();
|
|
}
|
|
}
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
|
|
class VS_FIXEDFILEINFO {
|
|
constructor(reader) {
|
|
this.dwSignature = reader.readuint32();
|
|
this.dwStrucVersion = reader.readuint32();
|
|
this.dwFileVersionMS = reader.readuint32();
|
|
this.dwFileVersionLS = reader.readuint32();
|
|
this.dwProductVersionMS = reader.readuint32();
|
|
this.dwProductVersionLS = reader.readuint32();
|
|
this.dwFileFlagsMask = reader.readuint32();
|
|
this.dwFileFlags = reader.readuint32();
|
|
this.dwFileOS = reader.readuint32();
|
|
this.dwFileType = reader.readuint32();
|
|
this.dwFileSubtype = reader.readuint32();
|
|
this.dwFileDateMS = reader.readuint32();
|
|
this.dwFileDateLS = reader.readuint32();
|
|
}
|
|
getVersionString() {
|
|
const first = this.dwProductVersionMS >> 16 & 0xffff;
|
|
const second = this.dwProductVersionMS & 0xffff;
|
|
const third = this.dwProductVersionLS >> 16 & 0xffff;
|
|
const fourth = this.dwProductVersionLS & 0xffff;
|
|
return `${first}.${second}.${third}.${fourth}`;
|
|
}
|
|
}
|
|
|
|
// This isn't spec'ed on the minidump page (from what I could tell):
|
|
// https://github.com/rust-minidump/rust-minidump/blob/main/minidump/src/minidump.rs#L267
|
|
// This returns the ID string that is used by the Mozilla tools for PDB lookup.
|
|
class CV_INFO {
|
|
static async read(reader, position) {
|
|
if (position === 0) {
|
|
return new CV_INFO_UNKNOWN(0);
|
|
}
|
|
|
|
// 6 is the largest.
|
|
const readResult = await reader.read(6, position);
|
|
const cvSignature = readResult.readuint32();
|
|
switch (cvSignature) {
|
|
case CV_INFO_PDB20.SIGNATURE:
|
|
return new CV_INFO_PDB20(readResult);
|
|
case CV_INFO_PDB70.SIGNATURE:
|
|
return new CV_INFO_PDB70(readResult);
|
|
case CV_INFO_ELF.SIGNATURE:
|
|
return new CV_INFO_ELF();
|
|
default:
|
|
return new CV_INFO_UNKNOWN(cvSignature);
|
|
}
|
|
}
|
|
}
|
|
class CV_INFO_PDB20 {
|
|
static SIGNATURE = 0x3031424e;
|
|
constructor(reader) {
|
|
this.cvOffset = reader.readuint32();
|
|
this.signature = reader.readuint32();
|
|
this.age = reader.readuint32();
|
|
}
|
|
getIdString() {
|
|
// TODO: Probably uncommon at this point.
|
|
return 'CV_INFO_PDB20';
|
|
}
|
|
}
|
|
class GUID {
|
|
// fixed 8 length.
|
|
|
|
constructor(reader) {
|
|
this.data1 = reader.readuint32();
|
|
this.data2 = reader.readuint16();
|
|
this.data3 = reader.readuint16();
|
|
this.data4 = reader.readByteArray(8);
|
|
}
|
|
toString() {
|
|
if (this.data4.length !== 8) {
|
|
return 'Invalid';
|
|
}
|
|
return this.data1.toString(16).padStart(4, '0') + this.data2.toString(16).padStart(2, '0') + this.data3.toString(16).padStart(2, '0') + this.data4.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
}
|
|
class CV_INFO_PDB70 {
|
|
static SIGNATURE = 0x53445352;
|
|
constructor(reader) {
|
|
this.signature = new GUID(reader);
|
|
this.age = reader.readuint32();
|
|
}
|
|
getIdString() {
|
|
// https://randomascii.wordpress.com/2013/03/09/symbols-the-microsoft-way/
|
|
return (this.signature.toString() + this.age.toString(16)).toUpperCase();
|
|
}
|
|
}
|
|
class CV_INFO_ELF {
|
|
static SIGNATURE = 0x4270454c;
|
|
getIdString() {
|
|
// TODO: Would be needed for macOS/linux support presumably?
|
|
return 'CV_INFO_ELF';
|
|
}
|
|
}
|
|
class CV_INFO_UNKNOWN {
|
|
constructor(cvSignature) {
|
|
this.cvSignature = cvSignature;
|
|
}
|
|
getIdString() {
|
|
return 'CV_INFO_UNKNOWN:' + this.cvSignature.toString(16).padStart(4, '0');
|
|
}
|
|
}
|
|
async function readMinidump(file) {
|
|
if (file == null || !isMinidumpFilename(file)) return null;
|
|
let reader = null;
|
|
const info = {};
|
|
try {
|
|
reader = new FileReader(file);
|
|
const header = await MINIDUMP_HEADER.read(reader, 0);
|
|
if (header.signature !== 0x504d444d) {
|
|
console.log(`readMinidump Bad signature: 0x${header.signature.toString(16)}`);
|
|
return null;
|
|
}
|
|
|
|
// Arbitrary number. Just a sanity check.
|
|
if (header.numberOfStreams > 0x100) {
|
|
console.log(`readMinidump Bad numberOfStreams: 0x${header.numberOfStreams.toString(16)}`);
|
|
return null;
|
|
}
|
|
const streamLookup = {};
|
|
|
|
// First create a lookup because we want to process the entries in a specific order.
|
|
for (let i = 0; i < header.numberOfStreams; ++i) {
|
|
const streamOffset = header.streamDirectoryOffset + i * 12;
|
|
const entry = await MINIDUMP_DIRECTORY.read(reader, streamOffset);
|
|
|
|
// We only care about a limited amount of stream types, so lets avoid some of the overhead.
|
|
switch (entry.streamType) {
|
|
case MinidumpStreamType.ExceptionStream:
|
|
case MinidumpStreamType.ModuleListStream:
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
streamLookup[entry.streamType] = entry;
|
|
}
|
|
const exceptionStreamEntry = streamLookup[MinidumpStreamType.ExceptionStream];
|
|
if (exceptionStreamEntry == null) {
|
|
console.log(`readMinidump: No ExceptionStream found.`);
|
|
return null;
|
|
}
|
|
const exceptionStream = await MINIDUMP_EXCEPTION_STREAM.read(reader, exceptionStreamEntry.dataOffset);
|
|
info.exceptionString = exceptionStream.getExceptionCodeString();
|
|
const exceptionAddrString = exceptionStream.exceptionAddress.toString(16);
|
|
console.log(`readMinidump exceptionCode: ${info.exceptionString}, exceptionAddress ${exceptionAddrString}`);
|
|
const moduleStreamEntry = streamLookup[MinidumpStreamType.ModuleListStream];
|
|
// Skip if `exceptionAddress` is 0 since there will be no crashing module.
|
|
if (moduleStreamEntry != null && exceptionStream.exceptionAddress !== BigInt(0)) {
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_module_list
|
|
|
|
// Do not read them all at once. This 1) allows us to exit early, 2) would require a variable sized buffer
|
|
// instead of our smaller fixed size buffer.
|
|
const moduleList = await MINIDUMP_MODULE_LIST.read(reader, moduleStreamEntry.dataOffset);
|
|
// Sanity check, this number is arbitrary.
|
|
if (moduleList.numberOfModules > 0x200) {
|
|
console.log(`readMinidump ModuleListstream Bad numberOfModules: 0x${moduleList.numberOfModules.toString(16)}`);
|
|
return info;
|
|
}
|
|
let moduleEntryOffset = moduleStreamEntry.dataOffset + 4;
|
|
for (let i = 0; i < moduleList.numberOfModules; ++i) {
|
|
const module = await MINIDUMP_MODULE.read(reader, moduleEntryOffset);
|
|
moduleEntryOffset += MINIDUMP_MODULE.U32_SIZE * 4;
|
|
if (module.containsAddress(exceptionStream.exceptionAddress)) {
|
|
info.exceptionModuleName = await module.getModuleFileName(reader);
|
|
info.exceptionModuleVersion = module.versionInfo.getVersionString();
|
|
info.relativeCrashAddress = (exceptionStream.exceptionAddress - module.baseOfImage).toString(16);
|
|
info.exceptionModuleCodeId = module.getCodeIdString();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.log(`readMinidump exception: ${e} ${e === null || e === void 0 ? void 0 : e.stack}`);
|
|
return null;
|
|
} finally {
|
|
var _reader;
|
|
(_reader = reader) === null || _reader === void 0 ? void 0 : _reader.close();
|
|
}
|
|
console.log(`readMinidump result ${JSON.stringify(info)}`);
|
|
return info;
|
|
} |