The documentation of the MiniDumpWriteDump function表示
如果可能的话,应该从一个单独的进程调用
MiniDumpWriteDump
,而不是从被转储的目标进程中调用。{/ p>
所以我写了一个小的MFC崩溃处理程序,就是这样做的。我遵循Hans Passant在this SO answer中的建议,即我将异常指针的值从崩溃程序传递给崩溃处理程序,即使异常指针在崩溃处理程序的上下文中无效。当我在调试版本中运行测试时,这很有效,但是当我切换到版本构建时,崩溃处理程序崩溃,在MiniDumpWriteDump
函数内发生访问冲突。
我很难过。为什么这应该在调试版本中工作,而不是在发布版本中?这很令人抓狂,因为访问违规通常是访问无效指针的指标,而我在崩溃处理程序中收到的异常指针确实无效 - 但另一方面,我被告知这应该无关紧要,MiniDumpWriteDump
是在崩溃过程的上下文中解释指针(从指针发起的位置开始)。
任何想法我可能做错了什么?
旁观:在他的回答中,汉斯提出了一个解决方案,其中看门狗进程是预先启动的,然后进入睡眠状态并在崩溃过程触发时唤醒。我的解决方案略有不同:我只是在崩溃发生时启动崩溃处理程序,然后通过命令行参数将崩溃程序中的必要信息传递给崩溃处理程序。我仔细检查了传递的信息是否正确,特别是异常指针。
答案 0 :(得分:0)
我在类似的问题上挣扎,现在终于发现了什么是错的。
MINIDUMP_EXCEPTION_INFORMATION的MSDN文档指出,如果ClientPointers
地址来自目标进程而非本地进程,则TRUE
字段必须为ExceptionPointers
。
正确设置此字段后,我可以简单地从崩溃过程中传递ThreadId
和ExceptionPointers
,在转储写入过程中将它们填充到MINIDUMP_EXCEPTION_INFORMATION
,并且它可以正常工作。
答案 1 :(得分:0)
我改变了方法,以便最终解决方案看起来像Hans Passant建议的那样:看门狗进程是预先启动的,然后进入睡眠状态并在崩溃过程触发时唤醒。崩溃过程会对EXCEPTION_POINTERS
结构进行深层复制,并将该信息传递给监视程序进程。
这是制作深拷贝的代码。正如对问题的评论中提到的那样,主要问题是"是EXCEPTION_RECORD
,这是一个可能无限大小的链接列表。
// The maximum number of nested exception that we can handle. The value we
// use for this constant is an arbitrarily chosen number that is, hopefully,
// sufficiently high to support all realistic and surrealistic scenarios.
//
// sizeof(CrashInfo) for a maximum of 1000 = ca. 80 KB
const int MaximumNumberOfNestedExceptions = 1000;
// Structure with information about the crash that we can pass to the
// watchdog process
struct CrashInfo
{
EXCEPTION_POINTERS exceptionPointers;
int numberOfExceptionRecords;
// Contiguous area of memory that can easily be processed by memcpy
EXCEPTION_RECORD exceptionRecords[MaximumNumberOfNestedExceptions];
CONTEXT contextRecord;
};
// The EXCEPTION_POINTERS parameter is the original exception pointer
// that we are going to deep-copy.
// The CrashInfo parameter receives the copy.
void FillCrashInfoWithExceptionPointers(CrashInfo& crashInfo, EXCEPTION_POINTERS* exceptionPointers)
{
// De-referencing creates a copy
crashInfo.exceptionPointers = *exceptionPointers;
crashInfo.contextRecord = *(exceptionPointers->ContextRecord);
int indexOfExceptionRecord = 0;
crashInfo.numberOfExceptionRecords = 0;
EXCEPTION_RECORD* exceptionRecord = exceptionPointers->ExceptionRecord;
while (exceptionRecord != 0)
{
if (indexOfExceptionRecord >= MaximumNumberOfNestedExceptions)
{
// Yikes, maximum number of nested exceptions reached
break;
}
// De-referencing creates a copy
crashInfo.exceptionRecords[indexOfExceptionRecord] = *exceptionRecord;
++indexOfExceptionRecord;
++crashInfo.numberOfExceptionRecords;
exceptionRecord = exceptionRecord->ExceptionRecord;
}
}
当我们在监视程序进程中收到CrashInfo
结构时,我们现在遇到一个问题:EXCEPTION_RECORD
引用指向无效的内存地址,即指向仅在崩溃进程中有效的内存地址。以下函数(必须在监视程序进程中运行)修复了这些引用。
// The CrashInfo parameter is both in/out
void FixExceptionPointersInCrashInfo(CrashInfo& crashInfo)
{
crashInfo.exceptionPointers.ContextRecord = &(crashInfo.contextRecord);
for (int indexOfExceptionRecord = 0; indexOfExceptionRecord < crashInfo.numberOfExceptionRecords; ++indexOfExceptionRecord)
{
if (0 == indexOfExceptionRecord)
crashInfo.exceptionPointers.ExceptionRecord = &(crashInfo.exceptionRecords[indexOfExceptionRecord]);
else
crashInfo.exceptionRecords[indexOfExceptionRecord - 1].ExceptionRecord = &(crashInfo.exceptionRecords[indexOfExceptionRecord]);
}
}
现在我们已准备好将&(crashInfo.exceptionPointers)
传递给MiniDumpWriteDump
函数。
注意:显然这不是一个完整的解决方案。您可能希望将更多信息从崩溃过程传递到监视程序进程。 CrashInfo
结构是保存此信息的候选者。此处未显示进程如何相互通信的方式。在我的例子中,我使用了Hans Passant提出的解决方案,该解决方案在问题开头链接:使用事件进行同步(CreateEvent + SetEvent)和内存映射文件(CreateFileMapping + MapViewOfFile)来从一个进程中混洗信息到下一个。事件的(唯一)名称和内存映射文件由主进程确定,并通过命令行参数传递给监视程序进程。