There are a couple of reports concerning (huge) slow downs when using minidump on x86_64. Some even happen during game play with > 25s of slugginess. See also https://bugs.winehq.org/show_bug.cgi?id=55798
This is the first part for addressing this issue: - update minidumpapiset.h to latest defines, - add a bunch of tests for minidump generation / reloading.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- include/minidumpapiset.h | 698 ++++++++++++++++++++++++++++++++------- 1 file changed, 575 insertions(+), 123 deletions(-)
diff --git a/include/minidumpapiset.h b/include/minidumpapiset.h index fed3e4cac03..98fe12fd2f0 100644 --- a/include/minidumpapiset.h +++ b/include/minidumpapiset.h @@ -30,6 +30,7 @@ extern "C" {
typedef DWORD RVA; typedef ULONG64 RVA64; +#define RVA_TO_ADDR(map,rva) ((void *)((ULONG_PTR)(map) + (rva)))
typedef enum _MINIDUMP_TYPE { @@ -48,129 +49,24 @@ typedef enum _MINIDUMP_TYPE MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, - MiniDumpValidTypeFlags = 0x01ffffff, -} MINIDUMP_TYPE; - -typedef enum _MINIDUMP_CALLBACK_TYPE -{ - ModuleCallback, - ThreadCallback, - ThreadExCallback, - IncludeThreadCallback, - IncludeModuleCallback, - MemoryCallback, -} MINIDUMP_CALLBACK_TYPE; - -typedef struct _MINIDUMP_THREAD_CALLBACK -{ - ULONG ThreadId; - HANDLE ThreadHandle; -#if defined(__aarch64__) - ULONG Pad; -#endif - CONTEXT Context; - ULONG SizeOfContext; - ULONG64 StackBase; - ULONG64 StackEnd; -} MINIDUMP_THREAD_CALLBACK, *PMINIDUMP_THREAD_CALLBACK; - -typedef struct _MINIDUMP_THREAD_EX_CALLBACK -{ - ULONG ThreadId; - HANDLE ThreadHandle; -#if defined(__aarch64__) - ULONG Pad; -#endif - CONTEXT Context; - ULONG SizeOfContext; - ULONG64 StackBase; - ULONG64 StackEnd; - ULONG64 BackingStoreBase; - ULONG64 BackingStoreEnd; -} MINIDUMP_THREAD_EX_CALLBACK, *PMINIDUMP_THREAD_EX_CALLBACK; - -typedef struct _MINIDUMP_INCLUDE_THREAD_CALLBACK -{ - ULONG ThreadId; -} MINIDUMP_INCLUDE_THREAD_CALLBACK, *PMINIDUMP_INCLUDE_THREAD_CALLBACK; - -typedef enum _THREAD_WRITE_FLAGS -{ - ThreadWriteThread = 0x0001, - ThreadWriteStack = 0x0002, - ThreadWriteContext = 0x0004, - ThreadWriteBackingStore = 0x0008, - ThreadWriteInstructionWindow = 0x0010, - ThreadWriteThreadData = 0x0020, - ThreadWriteThreadInfo = 0x0040 -} THREAD_WRITE_FLAGS; - -typedef struct _MINIDUMP_MODULE_CALLBACK -{ - PWCHAR FullPath; - ULONG64 BaseOfImage; - ULONG SizeOfImage; - ULONG CheckSum; - ULONG TimeDateStamp; - VS_FIXEDFILEINFO VersionInfo; - PVOID CvRecord; - ULONG SizeOfCvRecord; - PVOID MiscRecord; - ULONG SizeOfMiscRecord; -} MINIDUMP_MODULE_CALLBACK, *PMINIDUMP_MODULE_CALLBACK; - -typedef struct _MINIDUMP_INCLUDE_MODULE_CALLBACK -{ - ULONG64 BaseOfImage; -} MINIDUMP_INCLUDE_MODULE_CALLBACK, *PMINIDUMP_INCLUDE_MODULE_CALLBACK; - -typedef enum _MODULE_WRITE_FLAGS -{ - ModuleWriteModule = 0x0001, - ModuleWriteDataSeg = 0x0002, - ModuleWriteMiscRecord = 0x0004, - ModuleWriteCvRecord = 0x0008, - ModuleReferencedByMemory = 0x0010, - ModuleWriteTlsData = 0x0020, - ModuleWriteCodeSegs = 0x0040, -} MODULE_WRITE_FLAGS; - -typedef struct _MINIDUMP_CALLBACK_INPUT -{ - ULONG ProcessId; - HANDLE ProcessHandle; - ULONG CallbackType; - union - { - MINIDUMP_THREAD_CALLBACK Thread; - MINIDUMP_THREAD_EX_CALLBACK ThreadEx; - MINIDUMP_MODULE_CALLBACK Module; - MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; - MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; - } DUMMYUNIONNAME; -} MINIDUMP_CALLBACK_INPUT, *PMINIDUMP_CALLBACK_INPUT;
-typedef struct _MINIDUMP_CALLBACK_OUTPUT -{ - union - { - ULONG ModuleWriteFlags; - ULONG ThreadWriteFlags; - struct - { - ULONG64 MemoryBase; - ULONG MemorySize; - } DUMMYSTRUCTNAME; - } DUMMYUNIONNAME; -} MINIDUMP_CALLBACK_OUTPUT, *PMINIDUMP_CALLBACK_OUTPUT; - -typedef BOOL (WINAPI* MINIDUMP_CALLBACK_ROUTINE)(PVOID, const PMINIDUMP_CALLBACK_INPUT, PMINIDUMP_CALLBACK_OUTPUT); + MiniDumpWithoutAuxiliaryState = 0x00004000, + MiniDumpWithFullAuxiliaryState = 0x00008000, + MiniDumpWithPrivateWriteCopyMemory = 0x00010000, + MiniDumpIgnoreInaccessibleMemory = 0x00020000, + MiniDumpWithTokenInformation = 0x00040000, + MiniDumpWithModuleHeaders = 0x00080000, + MiniDumpFilterTriage = 0x00100000, + MiniDumpWithAvxXStateContext = 0x00200000, + MiniDumpWithIptTrace = 0x00400000, + MiniDumpValidTypeFlags = 0x007fffff, +} MINIDUMP_TYPE;
-typedef struct _MINIDUMP_CALLBACK_INFORMATION +typedef enum _MINIDUMP_SECONDARY_FLAGS { - MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; - void *CallbackParam; -} MINIDUMP_CALLBACK_INFORMATION, *PMINIDUMP_CALLBACK_INFORMATION; + MiniSecondaryWithoutPowerInfo = 0x00000001, + MiniSecondaryValidFlags = 0x00000001, +} MINIDUMP_SECONDARY_FLAGS;
typedef struct _MINIDUMP_LOCATION_DESCRIPTOR { @@ -208,6 +104,14 @@ typedef struct _MINIDUMP_EXCEPTION_INFORMATION BOOL ClientPointers; } MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
+typedef struct _MINIDUMP_EXCEPTION_INFORMATION64 +{ + DWORD ThreadId; + ULONG64 ExceptionRecord; + ULONG64 ContextRecord; + BOOL ClientPointers; +} MINIDUMP_EXCEPTION_INFORMATION64, *PMINIDUMP_EXCEPTION_INFORMATION64; + typedef struct MINIDUMP_EXCEPTION_STREAM { ULONG32 ThreadId; @@ -256,8 +160,23 @@ typedef struct _MINIDUMP_MEMORY64_LIST MINIDUMP_MEMORY_DESCRIPTOR64 MemoryRanges[1]; /* FIXME: 0-sized array not supported */ } MINIDUMP_MEMORY64_LIST, *PMINIDUMP_MEMORY64_LIST;
-#define MINIDUMP_MISC1_PROCESS_ID 0x00000001 -#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002 +typedef struct _XSTATE_CONFIG_FEATURE_MSC_INFO +{ + ULONG32 SizeOfInfo; + ULONG32 ContextSize; + ULONG64 EnabledFeatures; + XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES]; +} XSTATE_CONFIG_FEATURE_MSC_INFO, *PXSTATE_CONFIG_FEATURE_MSC_INFO; + +#define MINIDUMP_MISC1_PROCESS_ID 0x00000001 +#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002 +#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004 +#define MINIDUMP_MISC3_PROCESS_INTEGRITY 0x00000010 +#define MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS 0x00000020 +#define MINIDUMP_MISC3_TIMEZONE 0x00000040 +#define MINIDUMP_MISC3_PROTECTED_PROCESS 0x00000080 +#define MINIDUMP_MISC4_BUILDSTRING 0x00000100 +#define MINIDUMP_MISC5_PROCESS_COOKIE 0x00000200
typedef struct _MINIDUMP_MISC_INFO { @@ -269,6 +188,87 @@ typedef struct _MINIDUMP_MISC_INFO ULONG32 ProcessKernelTime; } MINIDUMP_MISC_INFO, *PMINIDUMP_MISC_INFO;
+typedef struct _MINIDUMP_MISC_INFO_2 +{ + ULONG32 SizeOfInfo; + ULONG32 Flags1; + ULONG32 ProcessId; + ULONG32 ProcessCreateTime; + ULONG32 ProcessUserTime; + ULONG32 ProcessKernelTime; + ULONG32 ProcessorMaxMhz; + ULONG32 ProcessorCurrentMhz; + ULONG32 ProcessorMhzLimit; + ULONG32 ProcessorMaxIdleState; + ULONG32 ProcessorCurrentIdleState; +} MINIDUMP_MISC_INFO_2, *PMINIDUMP_MISC_INFO_2; + +typedef struct _MINIDUMP_MISC_INFO_3 +{ + ULONG32 SizeOfInfo; + ULONG32 Flags1; + ULONG32 ProcessId; + ULONG32 ProcessCreateTime; + ULONG32 ProcessUserTime; + ULONG32 ProcessKernelTime; + ULONG32 ProcessorMaxMhz; + ULONG32 ProcessorCurrentMhz; + ULONG32 ProcessorMhzLimit; + ULONG32 ProcessorMaxIdleState; + ULONG32 ProcessorCurrentIdleState; + ULONG32 ProcessIntegrityLevel; + ULONG32 ProcessExecuteFlags; + ULONG32 ProtectedProcess; + ULONG32 TimeZoneId; + TIME_ZONE_INFORMATION TimeZone; +} MINIDUMP_MISC_INFO_3, *PMINIDUMP_MISC_INFO_3; + +typedef struct _MINIDUMP_MISC_INFO_4 +{ + ULONG32 SizeOfInfo; + ULONG32 Flags1; + ULONG32 ProcessId; + ULONG32 ProcessCreateTime; + ULONG32 ProcessUserTime; + ULONG32 ProcessKernelTime; + ULONG32 ProcessorMaxMhz; + ULONG32 ProcessorCurrentMhz; + ULONG32 ProcessorMhzLimit; + ULONG32 ProcessorMaxIdleState; + ULONG32 ProcessorCurrentIdleState; + ULONG32 ProcessIntegrityLevel; + ULONG32 ProcessExecuteFlags; + ULONG32 ProtectedProcess; + ULONG32 TimeZoneId; + TIME_ZONE_INFORMATION TimeZone; + WCHAR BuildString[MAX_PATH]; + WCHAR DbgBldStr[40]; +} MINIDUMP_MISC_INFO_4, *PMINIDUMP_MISC_INFO_4; + +typedef struct _MINIDUMP_MISC_INFO_5 +{ + ULONG32 SizeOfInfo; + ULONG32 Flags1; + ULONG32 ProcessId; + ULONG32 ProcessCreateTime; + ULONG32 ProcessUserTime; + ULONG32 ProcessKernelTime; + ULONG32 ProcessorMaxMhz; + ULONG32 ProcessorCurrentMhz; + ULONG32 ProcessorMhzLimit; + ULONG32 ProcessorMaxIdleState; + ULONG32 ProcessorCurrentIdleState; + ULONG32 ProcessIntegrityLevel; + ULONG32 ProcessExecuteFlags; + ULONG32 ProtectedProcess; + ULONG32 TimeZoneId; + TIME_ZONE_INFORMATION TimeZone; + WCHAR BuildString[MAX_PATH]; + WCHAR DbgBldStr[40]; + XSTATE_CONFIG_FEATURE_MSC_INFO XStateData; + ULONG32 ProcessCookie; +} MINIDUMP_MISC_INFO_5, *PMINIDUMP_MISC_INFO_5; + typedef struct _MINIDUMP_MODULE { ULONG64 BaseOfImage; @@ -369,7 +369,7 @@ typedef struct _MINIDUMP_USER_STREAM
typedef struct _MINIDUMP_USER_STREAM_INFORMATION { - ULONG UserStreamCount; + ULONG32 UserStreamCount; PMINIDUMP_USER_STREAM UserStreamArray; } MINIDUMP_USER_STREAM_INFORMATION, *PMINIDUMP_USER_STREAM_INFORMATION;
@@ -442,6 +442,458 @@ typedef struct _MINIDUMP_UNLOADED_MODULE_LIST ULONG32 NumberOfEntries; } MINIDUMP_UNLOADED_MODULE_LIST, *PMINIDUMP_UNLOADED_MODULE_LIST;
+typedef struct _MINIDUMP_FUNCTION_TABLE_DESCRIPTOR +{ + ULONG64 MinimumAddress; + ULONG64 MaximumAddress; + ULONG64 BaseAddress; + ULONG32 EntryCount; + ULONG32 SizeOfAlignPad; +} MINIDUMP_FUNCTION_TABLE_DESCRIPTOR, *PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR; + +typedef struct _MINIDUMP_FUNCTION_TABLE_STREAM +{ + ULONG32 SizeOfHeader; + ULONG32 SizeOfDescriptor; + ULONG32 SizeOfNativeDescriptor; + ULONG32 SizeOfFunctionEntry; + ULONG32 NumberOfDescriptors; + ULONG32 SizeOfAlignPad; +} MINIDUMP_FUNCTION_TABLE_STREAM, *PMINIDUMP_FUNCTION_TABLE_STREAM; + +typedef struct _MINIDUMP_MEMORY_INFO +{ + ULONG64 BaseAddress; + ULONG64 AllocationBase; + ULONG32 AllocationProtect; + ULONG32 __alignment1; + ULONG64 RegionSize; + ULONG32 State; + ULONG32 Protect; + ULONG32 Type; + ULONG32 __alignment2; +} MINIDUMP_MEMORY_INFO, *PMINIDUMP_MEMORY_INFO; + +typedef struct _MINIDUMP_MEMORY_INFO_LIST +{ + ULONG SizeOfHeader; + ULONG SizeOfEntry; + ULONG64 NumberOfEntries; +} MINIDUMP_MEMORY_INFO_LIST, *PMINIDUMP_MEMORY_INFO_LIST; + +typedef struct _MINIDUMP_THREAD_NAME +{ + ULONG ThreadId; + RVA64 RvaOfThreadName; +} MINIDUMP_THREAD_NAME, *PMINIDUMP_THREAD_NAME; + +typedef struct _MINIDUMP_THREAD_NAME_LIST +{ + ULONG NumberOfThreadNames; + MINIDUMP_THREAD_NAME ThreadNames[1]; /* FIXME: 0-sized array not supported */ +} MINIDUMP_THREAD_NAME_LIST, *PMINIDUMP_THREAD_NAME_LIST; + +typedef struct _MINIDUMP_TOKEN_INFO_HEADER +{ + ULONG32 TokenSize; + ULONG32 TokenId; + ULONG64 TokenHandle; +} MINIDUMP_TOKEN_INFO_HEADER, *PMINIDUMP_TOKEN_INFO_HEADER; + +typedef struct _MINIDUMP_TOKEN_INFO_LIST +{ + ULONG32 TokenListSize; + ULONG32 TokenListEntries; + ULONG32 ListHeaderSize; + ULONG32 ElementHeaderSize; +} MINIDUMP_TOKEN_INFO_LIST, *PMINIDUMP_TOKEN_INFO_LIST; + +typedef struct _MINIDUMP_PROCESS_VM_COUNTERS_1 +{ + USHORT Revision; + ULONG PageFaultCount; + ULONG64 PeakWorkingSetSize; + ULONG64 WorkingSetSize; + ULONG64 QuotaPeakPagedPoolUsage; + ULONG64 QuotaPagedPoolUsage; + ULONG64 QuotaPeakNonPagedPoolUsage; + ULONG64 QuotaNonPagedPoolUsage; + ULONG64 PagefileUsage; + ULONG64 PeakPagefileUsage; + ULONG64 PrivateUsage; +} MINIDUMP_PROCESS_VM_COUNTERS_1, *PMINIDUMP_PROCESS_VM_COUNTERS_1; + +#define MINIDUMP_PROCESS_VM_COUNTERS 0x0001 +#define MINIDUMP_PROCESS_VM_COUNTERS_VIRTUALSIZE 0x0002 +#define MINIDUMP_PROCESS_VM_COUNTERS_EX 0x0004 +#define MINIDUMP_PROCESS_VM_COUNTERS_EX2 0x0008 +#define MINIDUMP_PROCESS_VM_COUNTERS_JOB 0x0010 + +typedef struct _MINIDUMP_PROCESS_VM_COUNTERS_2 +{ + USHORT Revision; + USHORT Flags; + ULONG PageFaultCount; + ULONG64 PeakWorkingSetSize; + ULONG64 WorkingSetSize; + ULONG64 QuotaPeakPagedPoolUsage; + ULONG64 QuotaPagedPoolUsage; + ULONG64 QuotaPeakNonPagedPoolUsage; + ULONG64 QuotaNonPagedPoolUsage; + ULONG64 PagefileUsage; + ULONG64 PeakPagefileUsage; + ULONG64 PeakVirtualSize; + ULONG64 VirtualSize; + ULONG64 PrivateUsage; + ULONG64 PrivateWorkingSetSize; + ULONG64 SharedCommitUsage; + + ULONG64 JobSharedCommitUsage; + ULONG64 JobPrivateCommitUsage; + ULONG64 JobPeakPrivateCommitUsage; + ULONG64 JobPrivateCommitLimit; + ULONG64 JobTotalCommitLimit; +} MINIDUMP_PROCESS_VM_COUNTERS_2, *PMINIDUMP_PROCESS_VM_COUNTERS_2; + +typedef struct _MINIDUMP_SYSTEM_BASIC_INFORMATION +{ + ULONG TimerResolution; + ULONG PageSize; + ULONG NumberOfPhysicalPages; + ULONG LowestPhysicalPageNumber; + ULONG HighestPhysicalPageNumber; + ULONG AllocationGranularity; + ULONG64 MinimumUserModeAddress; + ULONG64 MaximumUserModeAddress; + ULONG64 ActiveProcessorsAffinityMask; + ULONG NumberOfProcessors; +} MINIDUMP_SYSTEM_BASIC_INFORMATION, *PMINIDUMP_SYSTEM_BASIC_INFORMATION; + +typedef struct _MINIDUMP_SYSTEM_FILECACHE_INFORMATION +{ + ULONG64 CurrentSize; + ULONG64 PeakSize; + ULONG PageFaultCount; + ULONG64 MinimumWorkingSet; + ULONG64 MaximumWorkingSet; + ULONG64 CurrentSizeIncludingTransitionInPages; + ULONG64 PeakSizeIncludingTransitionInPages; + ULONG TransitionRePurposeCount; + ULONG Flags; +} MINIDUMP_SYSTEM_FILECACHE_INFORMATION, *PMINIDUMP_SYSTEM_FILECACHE_INFORMATION; + +typedef struct _MINIDUMP_SYSTEM_BASIC_PERFORMANCE_INFORMATION +{ + ULONG64 AvailablePages; + ULONG64 CommittedPages; + ULONG64 CommitLimit; + ULONG64 PeakCommitment; +} MINIDUMP_SYSTEM_BASIC_PERFORMANCE_INFORMATION, *PMINIDUMP_SYSTEM_BASIC_PERFORMANCE_INFORMATION; + +typedef struct _MINIDUMP_SYSTEM_PERFORMANCE_INFORMATION { + ULONG64 IdleProcessTime; + ULONG64 IoReadTransferCount; + ULONG64 IoWriteTransferCount; + ULONG64 IoOtherTransferCount; + ULONG IoReadOperationCount; + ULONG IoWriteOperationCount; + ULONG IoOtherOperationCount; + ULONG AvailablePages; + ULONG CommittedPages; + ULONG CommitLimit; + ULONG PeakCommitment; + ULONG PageFaultCount; + ULONG CopyOnWriteCount; + ULONG TransitionCount; + ULONG CacheTransitionCount; + ULONG DemandZeroCount; + ULONG PageReadCount; + ULONG PageReadIoCount; + ULONG CacheReadCount; + ULONG CacheIoCount; + ULONG DirtyPagesWriteCount; + ULONG DirtyWriteIoCount; + ULONG MappedPagesWriteCount; + ULONG MappedWriteIoCount; + ULONG PagedPoolPages; + ULONG NonPagedPoolPages; + ULONG PagedPoolAllocs; + ULONG PagedPoolFrees; + ULONG NonPagedPoolAllocs; + ULONG NonPagedPoolFrees; + ULONG FreeSystemPtes; + ULONG ResidentSystemCodePage; + ULONG TotalSystemDriverPages; + ULONG TotalSystemCodePages; + ULONG NonPagedPoolLookasideHits; + ULONG PagedPoolLookasideHits; + ULONG AvailablePagedPoolPages; + ULONG ResidentSystemCachePage; + ULONG ResidentPagedPoolPage; + ULONG ResidentSystemDriverPage; + ULONG CcFastReadNoWait; + ULONG CcFastReadWait; + ULONG CcFastReadResourceMiss; + ULONG CcFastReadNotPossible; + ULONG CcFastMdlReadNoWait; + ULONG CcFastMdlReadWait; + ULONG CcFastMdlReadResourceMiss; + ULONG CcFastMdlReadNotPossible; + ULONG CcMapDataNoWait; + ULONG CcMapDataWait; + ULONG CcMapDataNoWaitMiss; + ULONG CcMapDataWaitMiss; + ULONG CcPinMappedDataCount; + ULONG CcPinReadNoWait; + ULONG CcPinReadWait; + ULONG CcPinReadNoWaitMiss; + ULONG CcPinReadWaitMiss; + ULONG CcCopyReadNoWait; + ULONG CcCopyReadWait; + ULONG CcCopyReadNoWaitMiss; + ULONG CcCopyReadWaitMiss; + ULONG CcMdlReadNoWait; + ULONG CcMdlReadWait; + ULONG CcMdlReadNoWaitMiss; + ULONG CcMdlReadWaitMiss; + ULONG CcReadAheadIos; + ULONG CcLazyWriteIos; + ULONG CcLazyWritePages; + ULONG CcDataFlushes; + ULONG CcDataPages; + ULONG ContextSwitches; + ULONG FirstLevelTbFills; + ULONG SecondLevelTbFills; + ULONG SystemCalls; + + ULONG64 CcTotalDirtyPages; + ULONG64 CcDirtyPageThreshold; + + LONG64 ResidentAvailablePages; + ULONG64 SharedCommittedPages; +} MINIDUMP_SYSTEM_PERFORMANCE_INFORMATION, *PMINIDUMP_SYSTEM_PERFORMANCE_INFORMATION; + +#define MINIDUMP_SYSMEMINFO1_FILECACHE_TRANSITIONREPURPOSECOUNT_FLAGS 0x0001 +#define MINIDUMP_SYSMEMINFO1_BASICPERF 0x0002 +#define MINIDUMP_SYSMEMINFO1_PERF_CCTOTALDIRTYPAGES_CCDIRTYPAGETHRESHOLD 0x0004 +#define MINIDUMP_SYSMEMINFO1_PERF_RESIDENTAVAILABLEPAGES_SHAREDCOMMITPAGES 0x0008 + +typedef struct _MINIDUMP_SYSTEM_MEMORY_INFO_1 +{ + USHORT Revision; + USHORT Flags; + + MINIDUMP_SYSTEM_BASIC_INFORMATION BasicInfo; + MINIDUMP_SYSTEM_FILECACHE_INFORMATION FileCacheInfo; + MINIDUMP_SYSTEM_BASIC_PERFORMANCE_INFORMATION BasicPerfInfo; + MINIDUMP_SYSTEM_PERFORMANCE_INFORMATION PerfInfo; +} MINIDUMP_SYSTEM_MEMORY_INFO_1, *PMINIDUMP_SYSTEM_MEMORY_INFO_1; + +typedef MINIDUMP_SYSTEM_MEMORY_INFO_1 MINIDUMP_SYSTEM_MEMORY_INFO_N; +typedef MINIDUMP_SYSTEM_MEMORY_INFO_N *PMINIDUMP_SYSTEM_MEMORY_INFO_N; + +typedef enum _MINIDUMP_CALLBACK_TYPE +{ + ModuleCallback, + ThreadCallback, + ThreadExCallback, + IncludeThreadCallback, + IncludeModuleCallback, + MemoryCallback, + CancelCallback, + WriteKernelMinidumpCallback, + KernelMinidumpStatusCallback, + RemoveMemoryCallback, + IncludeVmRegionCallback, + IoStartCallback, + IoWriteAllCallback, + IoFinishCallback, + ReadMemoryFailureCallback, + SecondaryFlagsCallback, + IsProcessSnapshotCallback, + VmStartCallback, + VmQueryCallback, + VmPreReadCallback, + VmPostReadCallback, +} MINIDUMP_CALLBACK_TYPE; + +typedef struct _MINIDUMP_THREAD_CALLBACK +{ + ULONG ThreadId; + HANDLE ThreadHandle; +#if defined(__aarch64__) + ULONG Pad; +#endif + CONTEXT Context; + ULONG SizeOfContext; + ULONG64 StackBase; + ULONG64 StackEnd; +} MINIDUMP_THREAD_CALLBACK, *PMINIDUMP_THREAD_CALLBACK; + +typedef struct _MINIDUMP_THREAD_EX_CALLBACK +{ + ULONG ThreadId; + HANDLE ThreadHandle; +#if defined(__aarch64__) + ULONG Pad; +#endif + CONTEXT Context; + ULONG SizeOfContext; + ULONG64 StackBase; + ULONG64 StackEnd; + ULONG64 BackingStoreBase; + ULONG64 BackingStoreEnd; +} MINIDUMP_THREAD_EX_CALLBACK, *PMINIDUMP_THREAD_EX_CALLBACK; + +typedef struct _MINIDUMP_INCLUDE_THREAD_CALLBACK +{ + ULONG ThreadId; +} MINIDUMP_INCLUDE_THREAD_CALLBACK, *PMINIDUMP_INCLUDE_THREAD_CALLBACK; + +typedef enum _THREAD_WRITE_FLAGS +{ + ThreadWriteThread = 0x0001, + ThreadWriteStack = 0x0002, + ThreadWriteContext = 0x0004, + ThreadWriteBackingStore = 0x0008, + ThreadWriteInstructionWindow = 0x0010, + ThreadWriteThreadData = 0x0020, + ThreadWriteThreadInfo = 0x0040 +} THREAD_WRITE_FLAGS; + +typedef struct _MINIDUMP_MODULE_CALLBACK +{ + PWCHAR FullPath; + ULONG64 BaseOfImage; + ULONG SizeOfImage; + ULONG CheckSum; + ULONG TimeDateStamp; + VS_FIXEDFILEINFO VersionInfo; + PVOID CvRecord; + ULONG SizeOfCvRecord; + PVOID MiscRecord; + ULONG SizeOfMiscRecord; +} MINIDUMP_MODULE_CALLBACK, *PMINIDUMP_MODULE_CALLBACK; + +typedef struct _MINIDUMP_INCLUDE_MODULE_CALLBACK +{ + ULONG64 BaseOfImage; +} MINIDUMP_INCLUDE_MODULE_CALLBACK, *PMINIDUMP_INCLUDE_MODULE_CALLBACK; + +typedef enum _MODULE_WRITE_FLAGS +{ + ModuleWriteModule = 0x0001, + ModuleWriteDataSeg = 0x0002, + ModuleWriteMiscRecord = 0x0004, + ModuleWriteCvRecord = 0x0008, + ModuleReferencedByMemory = 0x0010, + ModuleWriteTlsData = 0x0020, + ModuleWriteCodeSegs = 0x0040, +} MODULE_WRITE_FLAGS; + +typedef struct _MINIDUMP_IO_CALLBACK +{ + HANDLE Handle; + ULONG64 Offset; + PVOID Buffer; + ULONG BufferBytes; +} MINIDUMP_IO_CALLBACK, *PMINIDUMP_IO_CALLBACK; + +typedef struct _MINIDUMP_READ_MEMORY_FAILURE_CALLBACK +{ + ULONG64 Offset; + ULONG Bytes; + HRESULT FailureStatus; +} MINIDUMP_READ_MEMORY_FAILURE_CALLBACK, *PMINIDUMP_READ_MEMORY_FAILURE_CALLBACK; + +typedef struct _MINIDUMP_VM_QUERY_CALLBACK +{ + ULONG64 Offset; +} MINIDUMP_VM_QUERY_CALLBACK, *PMINIDUMP_VM_QUERY_CALLBACK; + +typedef struct _MINIDUMP_VM_PRE_READ_CALLBACK +{ + ULONG64 Offset; + PVOID Buffer; + ULONG Size; +} MINIDUMP_VM_PRE_READ_CALLBACK, *PMINIDUMP_VM_PRE_READ_CALLBACK; + +typedef struct _MINIDUMP_VM_POST_READ_CALLBACK +{ + ULONG64 Offset; + PVOID Buffer; + ULONG Size; + ULONG Completed; + HRESULT Status; +} MINIDUMP_VM_POST_READ_CALLBACK, *PMINIDUMP_VM_POST_READ_CALLBACK; + +typedef struct _MINIDUMP_CALLBACK_INPUT +{ + ULONG ProcessId; + HANDLE ProcessHandle; + ULONG CallbackType; + union + { + HRESULT Status; + MINIDUMP_THREAD_CALLBACK Thread; + MINIDUMP_THREAD_EX_CALLBACK ThreadEx; + MINIDUMP_MODULE_CALLBACK Module; + MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; + MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; + MINIDUMP_IO_CALLBACK Io; + MINIDUMP_READ_MEMORY_FAILURE_CALLBACK ReadMemoryFailure; + ULONG SecondaryFlags; + MINIDUMP_VM_QUERY_CALLBACK VmQuery; + MINIDUMP_VM_PRE_READ_CALLBACK VmPreRead; + MINIDUMP_VM_POST_READ_CALLBACK VmPostRead; + } DUMMYUNIONNAME; +} MINIDUMP_CALLBACK_INPUT, *PMINIDUMP_CALLBACK_INPUT; + +typedef struct _MINIDUMP_CALLBACK_OUTPUT +{ + union + { + ULONG ModuleWriteFlags; + ULONG ThreadWriteFlags; + ULONG SecondaryFlags; + struct + { + ULONG64 MemoryBase; + ULONG MemorySize; + } DUMMYSTRUCTNAME; + struct + { + BOOL CheckCancel; + BOOL Cancel; + } DUMMYSTRUCTNAME; + HANDLE Handle; + struct + { + MINIDUMP_MEMORY_INFO VmRegion; + BOOL Continue; + } DUMMYSTRUCTNAME; + struct + { + HRESULT VmQueryStatus; + MINIDUMP_MEMORY_INFO VmQueryResult; + } DUMMYSTRUCTNAME; + struct + { + HRESULT VmReadStatus; + ULONG VmReadBytesCompleted; + } DUMMYSTRUCTNAME; + HRESULT Status; + } DUMMYUNIONNAME; +} MINIDUMP_CALLBACK_OUTPUT, *PMINIDUMP_CALLBACK_OUTPUT; + +typedef BOOL (WINAPI* MINIDUMP_CALLBACK_ROUTINE)(PVOID, const PMINIDUMP_CALLBACK_INPUT, PMINIDUMP_CALLBACK_OUTPUT); + +typedef struct _MINIDUMP_CALLBACK_INFORMATION +{ + MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; + void *CallbackParam; +} MINIDUMP_CALLBACK_INFORMATION, *PMINIDUMP_CALLBACK_INFORMATION; + typedef enum _MINIDUMP_STREAM_TYPE { UnusedStream = 0,
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/dbghelp/tests/Makefile.in | 1 + dlls/dbghelp/tests/minidump.c | 164 +++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 dlls/dbghelp/tests/minidump.c
diff --git a/dlls/dbghelp/tests/Makefile.in b/dlls/dbghelp/tests/Makefile.in index d4264e85c5b..7b54cfa8827 100644 --- a/dlls/dbghelp/tests/Makefile.in +++ b/dlls/dbghelp/tests/Makefile.in @@ -3,4 +3,5 @@ IMPORTS = dbghelp user32
SOURCES = \ dbghelp.c \ + minidump.c \ path.c diff --git a/dlls/dbghelp/tests/minidump.c b/dlls/dbghelp/tests/minidump.c new file mode 100644 index 00000000000..33bf6216014 --- /dev/null +++ b/dlls/dbghelp/tests/minidump.c @@ -0,0 +1,164 @@ +/* + * Copyright 2024 Eric Pouech for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <assert.h> +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windows.h" +#include "dbghelp.h" +#include "wine/test.h" +#include "winternl.h" +#include "winnt.h" +#include "wine/test.h" + +static unsigned popcount32(ULONG val) +{ + val -= val >> 1 & 0x55555555; + val = (val & 0x33333333) + (val >> 2 & 0x33333333); + return ((val + (val >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24; +} + +static inline unsigned popcount64(ULONG64 val) +{ + return popcount32(val >> 32) + popcount32(val); +} + +#define minidump_open_for_read(a) _minidump_open_for_read(__LINE__, (a)) +static MINIDUMP_HEADER *_minidump_open_for_read(unsigned line, const char *filename) +{ + MINIDUMP_HEADER *hdr; + HANDLE file, map; + BOOL ret; + + file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ok_(__FILE__, line)(file != INVALID_HANDLE_VALUE, "Couldn't reopen file\n"); + map = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL); + ok_(__FILE__, line)(map != 0, "Couldn't map file\n"); + hdr = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + ok(hdr != NULL, "Couldn't map file\n"); + + ok(hdr && hdr->Signature == MINIDUMP_SIGNATURE, "Unexpected signature\n"); + + ret = CloseHandle(map); + ok_(__FILE__, line)(ret, "Couldn't unmap file\n"); + ret = CloseHandle(file); + ok_(__FILE__, line)(ret, "Couldn't close file\n"); + + return hdr; +} + +#define minidump_close_for_read(a) _minidump_close_for_read(__LINE__, (a)) +static void _minidump_close_for_read(unsigned line, MINIDUMP_HEADER *data) +{ + BOOL ret; + + ret = UnmapViewOfFile(data); + ok_(__FILE__, line)(ret, "Couldn't unmap file\n"); +} + +static BOOL minidump_write(HANDLE proc, const WCHAR *filename, MINIDUMP_TYPE type, BOOL windows_can_fail) +{ + HANDLE file; + BOOL ret, ret2; + + file = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + ok(file != INVALID_HANDLE_VALUE, "Failed to create minidump %ls\n", filename); + + ret = MiniDumpWriteDump(proc, GetProcessId(proc), file, type, NULL, NULL, NULL); + /* using new features that are not supported on old dbghelp versions */ + ok(ret || broken(windows_can_fail), "Couldn't write minidump content\n"); + + ret2 = CloseHandle(file); + ok(ret2, "Couldn't close file\n"); + + return ret; +} + +typedef DWORD64 stream_mask_t; +#define STREAM2MASK(st) (((stream_mask_t)1) << (st)) + +#define BASIC_STREAM_MASK \ + (STREAM2MASK(ThreadListStream) | STREAM2MASK(ModuleListStream) | STREAM2MASK(MemoryListStream) | \ + STREAM2MASK(SystemInfoStream) | STREAM2MASK(MiscInfoStream) | STREAM2MASK(SystemMemoryInfoStream) | \ + STREAM2MASK(ProcessVmCountersStream)) +/* streams added in Win8 & Win10... */ +#define BASIC_STREAM_BROKEN_MASK (STREAM2MASK(SystemMemoryInfoStream) | STREAM2MASK(ProcessVmCountersStream)) +#define BASIC_STREAM_TODO_MASK (STREAM2MASK(SystemMemoryInfoStream) | STREAM2MASK(ProcessVmCountersStream)) +static void test_minidump_contents(void) +{ + static const struct minidump_streams + { + MINIDUMP_TYPE type; + stream_mask_t streams_mask; + stream_mask_t todo_wine_mask; + } + streams_table[] = + { +/* 0 */ {MiniDumpNormal, BASIC_STREAM_MASK, BASIC_STREAM_TODO_MASK}, + {MiniDumpWithDataSegs, BASIC_STREAM_MASK, BASIC_STREAM_TODO_MASK}, + {MiniDumpWithProcessThreadData, BASIC_STREAM_MASK, BASIC_STREAM_TODO_MASK}, + {MiniDumpWithThreadInfo, BASIC_STREAM_MASK | STREAM2MASK(ThreadInfoListStream), BASIC_STREAM_TODO_MASK | STREAM2MASK(ThreadInfoListStream)}, + {MiniDumpWithCodeSegs, BASIC_STREAM_MASK, BASIC_STREAM_TODO_MASK}, +/* 5 */ {MiniDumpWithTokenInformation, BASIC_STREAM_MASK | STREAM2MASK(TokenStream), BASIC_STREAM_TODO_MASK | STREAM2MASK(TokenStream)}, + {MiniDumpWithModuleHeaders, BASIC_STREAM_MASK, BASIC_STREAM_TODO_MASK}, /* requires win8 at least */ + {MiniDumpWithAvxXStateContext, BASIC_STREAM_MASK, BASIC_STREAM_TODO_MASK}, /* requires win10 at least */ + {MiniDumpWithIptTrace, BASIC_STREAM_MASK, BASIC_STREAM_TODO_MASK}, + }; + MINIDUMP_HEADER *hdr; + void *where; + ULONG size; + BOOL ret; + int i, j; + + for (i = 0; i < ARRAY_SIZE(streams_table); i++) + { + winetest_push_context("streams_table[%d]", i); + if (minidump_write(GetCurrentProcess(), L"foo.mdmp", streams_table[i].type, i >= 6)) + { + hdr = minidump_open_for_read("foo.mdmp"); + /* native keeps (likely padding) some unused streams at the end of directory, but lists them here */ + ok(hdr->NumberOfStreams >= popcount64(streams_table[i].streams_mask), "Unexpected number of streams %u <> %u\n", + hdr->NumberOfStreams, popcount64(streams_table[i].streams_mask)); + ok(hdr->Flags == streams_table[i].type, "Unexpected flags\n"); + /* start at 3, 0=unused (cf above), 1,2=reserved shall be skipped */ + for (j = 3; j < 25 /* last documented stream */; j++) + { + ret = MiniDumpReadDumpStream(hdr, j, NULL, &where, &size); + todo_wine_if(streams_table[i].todo_wine_mask & STREAM2MASK(j)) + if (streams_table[i].streams_mask & STREAM2MASK(j)) + ok((ret && where) || broken(BASIC_STREAM_BROKEN_MASK & STREAM2MASK(j)), "Expecting stream %d to be present\n", j); + else + ok(!ret, "Not expecting stream %d to be present\n", j); + } + + minidump_close_for_read(hdr); + + ret = DeleteFileA("foo.mdmp"); + ok(ret, "Couldn't delete file\n"); + } + winetest_pop_context(); + } +} + +START_TEST(minidump) +{ + test_minidump_contents(); +}
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/dbghelp/tests/minidump.c | 351 ++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+)
diff --git a/dlls/dbghelp/tests/minidump.c b/dlls/dbghelp/tests/minidump.c index 33bf6216014..901c06aa1ed 100644 --- a/dlls/dbghelp/tests/minidump.c +++ b/dlls/dbghelp/tests/minidump.c @@ -158,7 +158,358 @@ static void test_minidump_contents(void) } }
+static void minidump_check_nostream(void *data, MINIDUMP_STREAM_TYPE stream_type) +{ + void *stream; + BOOL ret; + + ret = MiniDumpReadDumpStream(data, stream_type, NULL, (void**)&stream, NULL); + ok(!ret, "Unexpected stream %u\n", stream_type); +} + +static void minidump_check_pid(void *data, DWORD pid) +{ + MINIDUMP_MISC_INFO *misc_info; + BOOL ret; + + ret = MiniDumpReadDumpStream(data, MiscInfoStream, NULL, (void**)&misc_info, NULL); + ok(ret && misc_info, "Couldn't find misc-info stream\n"); + ok(misc_info->Flags1 & misc_info->Flags1 & MINIDUMP_MISC1_PROCESS_ID, "No process-id in misc_info\n"); + ok(pid == misc_info->ProcessId, "Unexpected process id\n"); +} + +static unsigned minidump_get_number_of_threads(void *data) +{ + MINIDUMP_THREAD_LIST *thread_list; + BOOL ret; + + ret = MiniDumpReadDumpStream(data, ThreadListStream, NULL, (void**)&thread_list, NULL); + ok(ret && thread_list, "Couldn't find thread-list stream\n"); + return thread_list->NumberOfThreads; +} + +static void minidump_check_threads(void *data) +{ + MINIDUMP_THREAD_LIST *thread_list; + int i; + BOOL ret; + + ret = MiniDumpReadDumpStream(data, ThreadListStream, NULL, (void**)&thread_list, NULL); + ok(ret && thread_list, "Couldn't find thread-list stream\n"); + for (i = 0; i < thread_list->NumberOfThreads; i++) + { + const MINIDUMP_THREAD *thread = &thread_list->Threads[i]; + const CONTEXT *ctx; + + ok(thread->SuspendCount == 0, "Unexpected value\n"); + todo_wine + ok(thread->Stack.StartOfMemoryRange, "Unexpected value\n"); + todo_wine + ok(thread->Stack.Memory.DataSize, "Unexpected value\n"); + ok(thread->Teb, "Unexpected value\n"); + todo_wine + ok(thread->ThreadContext.DataSize >= sizeof(CONTEXT), "Unexpected value\n"); + ctx = RVA_TO_ADDR(data, thread->ThreadContext.Rva); + todo_wine + ok((ctx->ContextFlags & CONTEXT_ALL) == CONTEXT_ALL, "Unexpected value\n"); + } +} + +static void minidump_check_module(void *data, const WCHAR *name, DWORD64 base) +{ + MINIDUMP_MODULE_LIST *module_list; + size_t namelen = wcslen(name); + int i; + BOOL ret; + + ret = MiniDumpReadDumpStream(data, ModuleListStream, NULL, (void**)&module_list, NULL); + ok(ret && module_list, "Couldn't find module-list stream\n"); + ok(module_list->NumberOfModules > 3, "Unexpected number of modules\n"); + for (i = 0; i < module_list->NumberOfModules; i++) + { + MINIDUMP_MODULE *module = &module_list->Modules[i]; + WCHAR *ptr; + + MINIDUMP_STRING *string = RVA_TO_ADDR(data, module->ModuleNameRva); + for (ptr = string->Buffer + string->Length / sizeof(WCHAR) - 1; ptr >= string->Buffer && *ptr != L'\'; ptr--) {} + ptr++; + if (ptr + namelen == string->Buffer + string->Length / sizeof(WCHAR) && + !wcsnicmp(name, ptr, namelen) && + module->BaseOfImage == base) break; + } + ok(i < module_list->NumberOfModules, "Couldn't find module %ls in minidump\n", name); +} + +struct memory_description +{ + /* MD_SECTION, MD_DIRECTORY can be present at the same time */ + /* MD_UNMAPPED: some DLLs are present when creating the minidump, but are unloaded afterwards (native) */ + enum {MD_NONE = 0, MD_UNMAPPED = 1, MD_PE_HEADER = 2, MD_STACK = 3, MD_SECTION = 4, MD_DIRECTORY = 8, MD_UNWIND = 16} kind; + unsigned id; /* MD_STACK: thread index + * MD_DIRECTORY: directory index + * MD_UNWIND: function index in function table + */ + const char* name; /* MD_SECTION: section name */ +}; + +static struct memory_description minidump_get_memory_description(void *data, DWORD64 addr) +{ + const BYTE *addr_ptr = (void*)(ULONG_PTR)addr; + MINIDUMP_MODULE_LIST *module_list; + MINIDUMP_THREAD_LIST *thread_list; + struct memory_description md = {.kind = MD_NONE}; + BOOL ret; + int i, j, dir; + + ret = MiniDumpReadDumpStream(data, ModuleListStream, NULL, (void**)&module_list, NULL); + ok(ret && module_list, "Couldn't find module-list stream\n"); + for (i = 0; i < module_list->NumberOfModules; i++) + { + MINIDUMP_MODULE *module = &module_list->Modules[i]; + MINIDUMP_STRING *string = RVA_TO_ADDR(data, module->ModuleNameRva); + if (module->BaseOfImage <= addr && addr < module->BaseOfImage + module->SizeOfImage) + { + HMODULE module_handle; + WCHAR *module_name; + size_t module_name_len = string->Length / sizeof(WCHAR); + IMAGE_NT_HEADERS *nthdr; + + module_name = malloc((module_name_len + 1) * sizeof(WCHAR)); + if (!module_name) continue; + memcpy(module_name, string->Buffer, module_name_len * sizeof(WCHAR)); + module_name[module_name_len] = L'\0'; + module_handle = GetModuleHandleW(module_name); + if ((nthdr = RtlImageNtHeader(module_handle))) + { + for (dir = 0; dir < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; dir++) + { + ULONG dir_size; + const BYTE *dir_start = RtlImageDirectoryEntryToData(module_handle, TRUE, dir, &dir_size); + if (dir_start && dir_start <= addr_ptr && addr_ptr < dir_start + dir_size) + { + md.kind |= MD_DIRECTORY; + md.id = dir; + } + switch (dir) + { + case IMAGE_DIRECTORY_ENTRY_EXCEPTION: + if (nthdr->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) + { + const IMAGE_AMD64_RUNTIME_FUNCTION_ENTRY *func; + for (func = (const void*)dir_start; func < (const IMAGE_AMD64_RUNTIME_FUNCTION_ENTRY *)(dir_start + dir_size); func++) + { + if (RtlImageRvaToVa(nthdr, module_handle, func->UnwindData, NULL) == addr_ptr) + { + md.kind = MD_UNWIND; + md.id = func - (const IMAGE_AMD64_RUNTIME_FUNCTION_ENTRY *)dir_start; + } + } + } + break; + /* FIXME handle more areas: import/export tables... */ + } + } + if (addr < (DWORD_PTR)(IMAGE_FIRST_SECTION(nthdr) + nthdr->FileHeader.NumberOfSections)) + { + md.kind = MD_PE_HEADER; + } + for (j = 0; j < nthdr->FileHeader.NumberOfSections; j++) + { + IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nthdr) + j; + const BYTE *section_start = (BYTE*)module_handle + section->VirtualAddress; + if (section_start <= addr_ptr && addr_ptr < section_start + section->Misc.VirtualSize) + { + md.kind |= MD_SECTION; + md.name = (const char*)section->Name; + } + } + } + else md.kind = MD_UNMAPPED; + free(module_name); + return md; + } + } + ret = MiniDumpReadDumpStream(data, ThreadListStream, NULL, (void**)&thread_list, NULL); + ok(ret && thread_list, "Couldn't find thread-list stream\n"); + for (i = 0; i < thread_list->NumberOfThreads; i++) + { + MINIDUMP_THREAD *thread = &thread_list->Threads[i]; + if (thread->Stack.StartOfMemoryRange <= addr && + addr < thread->Stack.StartOfMemoryRange + thread->Stack.Memory.DataSize) + { + md.kind = MD_STACK; + md.id = i; + return md; + } + } + return md; +} + +/* modules could be load/unloaded when generating the minidump, so count the number of available modules */ +static unsigned minidump_get_number_of_available_modules(void *data) +{ + MINIDUMP_MODULE_LIST *module_list; + unsigned num_modules = 0; + BOOL ret; + int i; + ret = MiniDumpReadDumpStream(data, ModuleListStream, NULL, (void**)&module_list, NULL); + ok(ret && module_list, "Couldn't find module-list stream\n"); + for (i = 0; i < module_list->NumberOfModules; i++) + { + struct memory_description md; + md = minidump_get_memory_description(data, module_list->Modules[i].BaseOfImage); + if (md.kind != MD_UNMAPPED) + num_modules++; + } + return num_modules; +} + +struct memory_walker +{ + unsigned num_unknown; /* number of unknown memory locations */ + unsigned num_thread_stack; /* number of locations inside a thread stack */ + unsigned num_directories[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* number of locations in side the directories' content */ + unsigned num_text; /* number of locations inside .text section */ + unsigned num_unwind_info; +}; + +static void minidump_walk_memory(void *data, struct memory_walker *walker) +{ + MINIDUMP_MEMORY_LIST *memory_list; + BOOL ret; + int i; + + ret = MiniDumpReadDumpStream(data, MemoryListStream, NULL, (void**)&memory_list, NULL); + ok(ret && memory_list, "Couldn't find memory-list stream\n"); + for (i = 0; i < memory_list->NumberOfMemoryRanges; i++) + { + MINIDUMP_MEMORY_DESCRIPTOR *desc = &memory_list->MemoryRanges[i]; + struct memory_description md; + md = minidump_get_memory_description(data, desc->StartOfMemoryRange); + switch ((int)md.kind) + { + case MD_NONE: + walker->num_unknown++; + break; + case MD_UNMAPPED: + /* nothing we can do here */ + break; + case MD_STACK: + walker->num_thread_stack++; + break; + case MD_SECTION | MD_UNWIND: + walker->num_unwind_info++; + break; + case MD_PE_HEADER: + /* FIXME may change with MiniDumpWithModuleHeaders */ + ok(0, "Unexpected memory block in PE header\n"); + break; + case MD_SECTION: + case MD_SECTION | MD_DIRECTORY: + if (!strcmp(md.name, ".text")) + walker->num_text++; + if (md.kind & MD_DIRECTORY) + { + ok(md.id < ARRAY_SIZE(walker->num_directories), "Out of bounds index\n"); + walker->num_directories[md.id]++; + } + break; + default: + ok(0, "Unexpected memory description kind: %x\n", md.kind); + break; + } + } +} + +static void test_current_process(void) +{ + static const struct + { + MINIDUMP_TYPE dump_type; + } + process_tests[] = + { + { MiniDumpNormal /* = 0 */ }, + { MiniDumpWithCodeSegs }, + { MiniDumpWithDataSegs }, + { MiniDumpWithThreadInfo }, +/* requires more work + { MiniDumpWithModuleHeaders }, + { MiniDumpWithProcessThreadData }, +*/ + }; + struct memory_walker walker; + struct memory_description md, md2; + unsigned num_available_modules, num_threads; + void *data; + BOOL ret; + int i; + + for (i = 0; i < ARRAY_SIZE(process_tests); i++) + { + winetest_push_context("process_tests[%d]", i); + minidump_write(GetCurrentProcess(), L"foo.mdmp", process_tests[i].dump_type, FALSE); + + data = minidump_open_for_read("foo.mdmp"); + + num_threads = minidump_get_number_of_threads(data); + ok(num_threads > 0, "Unexpected number of threads\n"); + + minidump_check_threads(data); + md = minidump_get_memory_description(data, (DWORD_PTR)&i); + todo_wine + ok(md.kind == MD_STACK, "Couldn't find automatic variable\n"); + + md2 = minidump_get_memory_description(data, (DWORD_PTR)NtCurrentTeb()->Tib.StackBase - sizeof(void*)); + todo_wine + ok(md2.kind == MD_STACK, "Couldn't find stack bottom\n"); + ok(md.id == md2.id, "Should be on same stack\n"); + + minidump_check_pid(data, GetCurrentProcessId()); + num_available_modules = minidump_get_number_of_available_modules(data); +#define CHECK_MODULE(s) minidump_check_module(data, s, (DWORD64)(DWORD_PTR)GetModuleHandleW(s)) + CHECK_MODULE(L"ntdll.dll"); + CHECK_MODULE(L"kernelbase.dll"); + CHECK_MODULE(L"kernel32.dll"); + CHECK_MODULE(L"dbghelp.dll"); +#undef CHECK_MODULE + + memset(&walker, 0, sizeof(walker)); + minidump_walk_memory(data, &walker); + + ok(walker.num_unknown == 0, "unexpected unknown memory locations\n"); + todo_wine + ok(walker.num_thread_stack == num_threads, "Unexpected number of stacks\n"); + + if (sizeof(void*) > 4 && (process_tests[i].dump_type & MiniDumpWithModuleHeaders)) + ok(walker.num_directories[IMAGE_DIRECTORY_ENTRY_EXCEPTION] > 0, "expected unwind information\n"); + else + ok(walker.num_directories[IMAGE_DIRECTORY_ENTRY_EXCEPTION] == 0, "unexpected unwind information\n"); + if (process_tests[i].dump_type & MiniDumpWithCodeSegs) + { + todo_wine + ok(walker.num_text >= num_available_modules || + /* win7 & 8 report one less */ + broken(walker.num_text + 1 >= num_available_modules), "expected code segments %u %u\n", walker.num_text, num_available_modules); + } + else + /* native embeds some elements in code segment from ntdll */ + ok(walker.num_text < 5, "unexpected code segments %u %u\n", walker.num_text, num_available_modules); + + todo_wine_if(RtlImageNtHeader(GetModuleHandleW(NULL))->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) + ok(walker.num_unwind_info == 0, "unexpected unwind info %u\n", walker.num_unwind_info); + minidump_check_nostream(data, ExceptionStream); + + minidump_close_for_read(data); + + ret = DeleteFileA("foo.mdmp"); + ok(ret, "Couldn't delete file\n"); + winetest_pop_context(); + } +} + START_TEST(minidump) { test_minidump_contents(); + test_current_process(); }
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/dbghelp/tests/minidump.c | 185 +++++++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 4 deletions(-)
diff --git a/dlls/dbghelp/tests/minidump.c b/dlls/dbghelp/tests/minidump.c index 901c06aa1ed..738c9789bcb 100644 --- a/dlls/dbghelp/tests/minidump.c +++ b/dlls/dbghelp/tests/minidump.c @@ -72,7 +72,8 @@ static void _minidump_close_for_read(unsigned line, MINIDUMP_HEADER *data) ok_(__FILE__, line)(ret, "Couldn't unmap file\n"); }
-static BOOL minidump_write(HANDLE proc, const WCHAR *filename, MINIDUMP_TYPE type, BOOL windows_can_fail) +static BOOL minidump_write(HANDLE proc, const WCHAR *filename, MINIDUMP_TYPE type, BOOL windows_can_fail, + MINIDUMP_CALLBACK_INFORMATION *cbi) { HANDLE file; BOOL ret, ret2; @@ -82,7 +83,7 @@ static BOOL minidump_write(HANDLE proc, const WCHAR *filename, MINIDUMP_TYPE typ
ok(file != INVALID_HANDLE_VALUE, "Failed to create minidump %ls\n", filename);
- ret = MiniDumpWriteDump(proc, GetProcessId(proc), file, type, NULL, NULL, NULL); + ret = MiniDumpWriteDump(proc, GetProcessId(proc), file, type, NULL, NULL, cbi); /* using new features that are not supported on old dbghelp versions */ ok(ret || broken(windows_can_fail), "Couldn't write minidump content\n");
@@ -131,7 +132,9 @@ static void test_minidump_contents(void) for (i = 0; i < ARRAY_SIZE(streams_table); i++) { winetest_push_context("streams_table[%d]", i); - if (minidump_write(GetCurrentProcess(), L"foo.mdmp", streams_table[i].type, i >= 6)) + + /* too old for dbghelp in Win7 & 8 */ + if (minidump_write(GetCurrentProcess(), L"foo.mdmp", streams_table[i].type, i >= 6, NULL)) { hdr = minidump_open_for_read("foo.mdmp"); /* native keeps (likely padding) some unused streams at the end of directory, but lists them here */ @@ -154,6 +157,7 @@ static void test_minidump_contents(void) ret = DeleteFileA("foo.mdmp"); ok(ret, "Couldn't delete file\n"); } + else win_skip("Skipping not supported feature (too old dbghelp version)\n"); winetest_pop_context(); } } @@ -448,7 +452,7 @@ static void test_current_process(void) for (i = 0; i < ARRAY_SIZE(process_tests); i++) { winetest_push_context("process_tests[%d]", i); - minidump_write(GetCurrentProcess(), L"foo.mdmp", process_tests[i].dump_type, FALSE); + minidump_write(GetCurrentProcess(), L"foo.mdmp", process_tests[i].dump_type, FALSE, NULL);
data = minidump_open_for_read("foo.mdmp");
@@ -508,8 +512,181 @@ static void test_current_process(void) } }
+struct cb_info +{ + /* input */ + unsigned module_flags; + unsigned thread_flags; + /* output */ + DWORD64 mask_types; + unsigned num_modules; + unsigned num_threads; + unsigned num_include_modules; + unsigned num_include_threads; +}; + +static BOOL CALLBACK test_callback_cb(void *pmt, MINIDUMP_CALLBACK_INPUT *input, MINIDUMP_CALLBACK_OUTPUT *output) +{ + struct cb_info *cb = pmt; + + ok(input->CallbackType < sizeof(cb->mask_types) * 8, "Too small mask\n"); + cb->mask_types |= (DWORD64)1u << input->CallbackType; + + if (input->CallbackType == WriteKernelMinidumpCallback || input->CallbackType == IoStartCallback) + { + ok(input->ProcessId == 0, "Unexpected pid %lu %lu\n", input->ProcessId, input->CallbackType); + ok(input->ProcessHandle == NULL, "Unexpected process handle %p %lu\n", input->ProcessHandle, input->CallbackType); + } + else if (input->CallbackType == IsProcessSnapshotCallback || input->CallbackType == VmStartCallback) + { + ok(input->ProcessId == 0, "Unexpected pid %lu %lu\n", input->ProcessId, input->CallbackType); + ok(input->ProcessHandle == GetCurrentProcess(), "Unexpected process handle %p %lu\n", input->ProcessHandle, input->CallbackType); + } + else + { + ok(input->ProcessId == GetCurrentProcessId(), "Unexpected pid %lu %lu\n", input->ProcessId, input->CallbackType); + ok(input->ProcessHandle == GetCurrentProcess(), "Unexpected process handle %p %lu\n", input->ProcessHandle, input->CallbackType); + } + + switch (input->CallbackType) + { + case ModuleCallback: + ok(output->ModuleWriteFlags == cb->module_flags, "Unexpected module flags %lx\n", output->ModuleWriteFlags); + cb->num_modules++; + break; + case IncludeModuleCallback: + ok(output->ModuleWriteFlags == cb->module_flags, "Unexpected module flags %lx\n", output->ModuleWriteFlags); + cb->num_include_modules++; + break; + case ThreadCallback: + case ThreadExCallback: + ok(output->ThreadWriteFlags == cb->thread_flags, "Unexpected thread flags %lx\n", output->ThreadWriteFlags); + cb->num_threads++; + break; + case IncludeThreadCallback: + ok(output->ThreadWriteFlags == cb->thread_flags, "Unexpected thread flags %lx\n", output->ThreadWriteFlags); + cb->num_include_threads++; + break; + case MemoryCallback: + case RemoveMemoryCallback: + ok(output->MemoryBase == 0, "Unexpected memory info\n"); + ok(output->MemorySize == 0, "Unexpected memory info\n"); + break; + case CancelCallback: + ok(!output->Cancel, "Unexpected value\n"); + ok(!output->CheckCancel, "Unexpected value\n"); + break; + case WriteKernelMinidumpCallback: + ok(output->Handle == NULL, "Unexpected value\n"); + break; + /* case KernelMinidumpStatusCallback: */ + /* case IncludeVmRegionCallback: */ + case IoStartCallback: + ok(output->Status == E_NOTIMPL, "Unexpected value %lx\n", output->Status); + /* TODO check the output->Vm* fields */ + break; + /* case IoWriteAllCallback: + * case IoFinishCallback: + */ + case ReadMemoryFailureCallback: + /* TODO check the rest */ + break; + case SecondaryFlagsCallback: + ok(input->SecondaryFlags == 0x00, "Unexpected value %lx\n", input->SecondaryFlags); + ok(input->SecondaryFlags == output->SecondaryFlags, "Unexpected value %lx\n", output->SecondaryFlags); + break; + case IsProcessSnapshotCallback: + break; + case VmStartCallback: + break; + /* + case VmQueryCallback: + case VmPreReadCallback: + case VmPostReadCallback: + */ + default: + ok(0, "Unexpected callback type %lu\n", input->CallbackType); + break; + } + return TRUE; +} + +static void test_callback(void) +{ + static const struct + { + MINIDUMP_TYPE dump_type; + unsigned module_flags; + unsigned thread_flags; + } + callback_tests[] = + { + { + MiniDumpNormal /* = 0 */, + ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord, + ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | ThreadWriteInstructionWindow, + }, + { + MiniDumpWithCodeSegs, + ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord | ModuleWriteCodeSegs, + ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | ThreadWriteInstructionWindow, + }, + { + MiniDumpWithDataSegs, + ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord | ModuleWriteDataSeg, + ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | ThreadWriteInstructionWindow, + }, + { + MiniDumpWithThreadInfo, + ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord, + ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | ThreadWriteInstructionWindow | ThreadWriteThreadInfo, + }, + }; +#define X(a) ((stream_mask_t)1u << (a)) + static const stream_mask_t mask_types = + X(ModuleCallback) | X(ThreadCallback) | /* ThreadExCallback */ X(IncludeThreadCallback) | + X(IncludeModuleCallback) | X(MemoryCallback) | X(CancelCallback) | X(WriteKernelMinidumpCallback) | + /* KernelMinidumpStatusCallback) */ X(RemoveMemoryCallback) | /* IncludeVmRegionCallback */ X(IoStartCallback) | + /* IoWriteAllCallback IoFinishCallback ReadMemoryFailureCallback */ X(SecondaryFlagsCallback) | + X(IsProcessSnapshotCallback) | X(VmStartCallback) /* VmQueryCallback VmPreReadCallback */ + /* VmPostReadCallback */; + static const stream_mask_t mask_types_too_old_dbghelp = X(IsProcessSnapshotCallback) | X(VmStartCallback); +#undef X + struct cb_info cb_info; + MINIDUMP_CALLBACK_INFORMATION cbi = {.CallbackRoutine = test_callback_cb, .CallbackParam = &cb_info}; + BOOL ret; + int i; + + for (i = 0; i < ARRAY_SIZE(callback_tests); i++) + { + winetest_push_context("callback_tests[%d]", i); + + memset(&cb_info, 0, sizeof(cb_info)); + cb_info.module_flags = callback_tests[i].module_flags; + cb_info.thread_flags = callback_tests[i].thread_flags; + minidump_write(GetCurrentProcess(), L"foo.mdmp", callback_tests[i].dump_type, FALSE, &cbi); + + todo_wine + ok(cb_info.mask_types == mask_types || + broken(cb_info.mask_types == (mask_types & ~mask_types_too_old_dbghelp)), + "Unexpected mask for callback types %I64x (%I64x)\n", cb_info.mask_types, mask_types); + ok(cb_info.num_modules > 5, "Unexpected number of modules\n"); + /* native reports several threads... */ + ok(cb_info.num_threads >= 1, "Unexpected number of threads\n"); + todo_wine + ok(cb_info.num_modules == cb_info.num_include_modules, "Unexpected number of include modules\n"); + todo_wine + ok(cb_info.num_threads == cb_info.num_include_threads, "Unexpected number of include threads\n"); + + ret = DeleteFileA("foo.mdmp"); + ok(ret, "Couldn't delete file\n"); + winetest_pop_context(); + } +} + START_TEST(minidump) { test_minidump_contents(); test_current_process(); + test_callback(); }
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/dbghelp/tests/minidump.c | 208 +++++++++++++++++++++++++++++++--- 1 file changed, 194 insertions(+), 14 deletions(-)
diff --git a/dlls/dbghelp/tests/minidump.c b/dlls/dbghelp/tests/minidump.c index 738c9789bcb..4ae0e9d44e3 100644 --- a/dlls/dbghelp/tests/minidump.c +++ b/dlls/dbghelp/tests/minidump.c @@ -73,7 +73,7 @@ static void _minidump_close_for_read(unsigned line, MINIDUMP_HEADER *data) }
static BOOL minidump_write(HANDLE proc, const WCHAR *filename, MINIDUMP_TYPE type, BOOL windows_can_fail, - MINIDUMP_CALLBACK_INFORMATION *cbi) + MINIDUMP_EXCEPTION_INFORMATION *mei, MINIDUMP_CALLBACK_INFORMATION *cbi) { HANDLE file; BOOL ret, ret2; @@ -83,7 +83,7 @@ static BOOL minidump_write(HANDLE proc, const WCHAR *filename, MINIDUMP_TYPE typ
ok(file != INVALID_HANDLE_VALUE, "Failed to create minidump %ls\n", filename);
- ret = MiniDumpWriteDump(proc, GetProcessId(proc), file, type, NULL, NULL, cbi); + ret = MiniDumpWriteDump(proc, GetProcessId(proc), file, type, mei, NULL, cbi); /* using new features that are not supported on old dbghelp versions */ ok(ret || broken(windows_can_fail), "Couldn't write minidump content\n");
@@ -134,7 +134,7 @@ static void test_minidump_contents(void) winetest_push_context("streams_table[%d]", i);
/* too old for dbghelp in Win7 & 8 */ - if (minidump_write(GetCurrentProcess(), L"foo.mdmp", streams_table[i].type, i >= 6, NULL)) + if (minidump_write(GetCurrentProcess(), L"foo.mdmp", streams_table[i].type, i >= 6, NULL, NULL)) { hdr = minidump_open_for_read("foo.mdmp"); /* native keeps (likely padding) some unused streams at the end of directory, but lists them here */ @@ -192,29 +192,33 @@ static unsigned minidump_get_number_of_threads(void *data) return thread_list->NumberOfThreads; }
-static void minidump_check_threads(void *data) +static void minidump_check_threads(void *data, unsigned todo_flags) { MINIDUMP_THREAD_LIST *thread_list; + ULONG stream_size; int i; BOOL ret;
- ret = MiniDumpReadDumpStream(data, ThreadListStream, NULL, (void**)&thread_list, NULL); + ret = MiniDumpReadDumpStream(data, ThreadListStream, NULL, (void**)&thread_list, &stream_size); ok(ret && thread_list, "Couldn't find thread-list stream\n"); + ok(stream_size == sizeof(thread_list->NumberOfThreads) + thread_list->NumberOfThreads * sizeof(thread_list->Threads[0]), + "Unexpected size\n"); for (i = 0; i < thread_list->NumberOfThreads; i++) { const MINIDUMP_THREAD *thread = &thread_list->Threads[i]; const CONTEXT *ctx;
+ todo_wine_if(todo_flags & 4) ok(thread->SuspendCount == 0, "Unexpected value\n"); - todo_wine - ok(thread->Stack.StartOfMemoryRange, "Unexpected value\n"); - todo_wine - ok(thread->Stack.Memory.DataSize, "Unexpected value\n"); + todo_wine_if(todo_flags & 1) + ok(thread->Stack.StartOfMemoryRange, "Unexpected value %I64x\n", thread->Stack.StartOfMemoryRange); + todo_wine_if(todo_flags & 1) + ok(thread->Stack.Memory.DataSize, "Unexpected value %x\n", thread->Stack.Memory.DataSize); ok(thread->Teb, "Unexpected value\n"); - todo_wine + todo_wine_if(todo_flags & 8) ok(thread->ThreadContext.DataSize >= sizeof(CONTEXT), "Unexpected value\n"); ctx = RVA_TO_ADDR(data, thread->ThreadContext.Rva); - todo_wine + todo_wine_if(todo_flags & 2) ok((ctx->ContextFlags & CONTEXT_ALL) == CONTEXT_ALL, "Unexpected value\n"); } } @@ -452,14 +456,14 @@ static void test_current_process(void) for (i = 0; i < ARRAY_SIZE(process_tests); i++) { winetest_push_context("process_tests[%d]", i); - minidump_write(GetCurrentProcess(), L"foo.mdmp", process_tests[i].dump_type, FALSE, NULL); + minidump_write(GetCurrentProcess(), L"foo.mdmp", process_tests[i].dump_type, FALSE, NULL, NULL);
data = minidump_open_for_read("foo.mdmp");
num_threads = minidump_get_number_of_threads(data); ok(num_threads > 0, "Unexpected number of threads\n");
- minidump_check_threads(data); + minidump_check_threads(data, 11); md = minidump_get_memory_description(data, (DWORD_PTR)&i); todo_wine ok(md.kind == MD_STACK, "Couldn't find automatic variable\n"); @@ -664,7 +668,7 @@ static void test_callback(void) memset(&cb_info, 0, sizeof(cb_info)); cb_info.module_flags = callback_tests[i].module_flags; cb_info.thread_flags = callback_tests[i].thread_flags; - minidump_write(GetCurrentProcess(), L"foo.mdmp", callback_tests[i].dump_type, FALSE, &cbi); + minidump_write(GetCurrentProcess(), L"foo.mdmp", callback_tests[i].dump_type, FALSE, NULL, &cbi);
todo_wine ok(cb_info.mask_types == mask_types || @@ -684,9 +688,185 @@ static void test_callback(void) } }
+static void test_exception(void) +{ + static const struct + { + unsigned exception_code; + unsigned exception_flags; + unsigned num_args; + BOOL with_child; + } + exception_tests[] = + { + { 0x1234, 0, 0, FALSE }, + { 0x1234, 0, 0, TRUE }, + { 0x1234, 0, 5, FALSE }, + { 0x1234, 0, 5, TRUE }, + { EXCEPTION_BREAKPOINT, 0, 1, TRUE }, + { EXCEPTION_ACCESS_VIOLATION, 0, 2, TRUE }, + }; + ULONG_PTR args[EXCEPTION_MAXIMUM_PARAMETERS]; + MINIDUMP_EXCEPTION_STREAM *except_info; + ULONG size; + void *data; + BOOL ret; + int i, j; + + for (i = 0; i < ARRAY_SIZE(args); i++) args[i] = 0x666000 + i; + for (i = 0; i < ARRAY_SIZE(exception_tests); i++) + { + PROCESS_INFORMATION pi; + MINIDUMP_EXCEPTION_INFORMATION mei; + EXCEPTION_POINTERS ep; + DEBUG_EVENT ev; + /* for local access */ + EXCEPTION_RECORD er; + CONTEXT ctx; + CONTEXT *mctx; + + winetest_push_context("test_exceptions[%d]", i); + + if (exception_tests[i].with_child) + { + BOOL first_exception = TRUE; + STARTUPINFOA si; + char buffer[MAX_PATH]; + char **argv; + + winetest_get_mainargs(&argv); + snprintf(buffer, ARRAY_SIZE(buffer), "%s minidump exception %x;%x;%u", + argv[0], exception_tests[i].exception_code, exception_tests[i].exception_flags, + exception_tests[i].num_args); + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "CreateProcess failed, last error %#lx.\n", GetLastError()); + + while ((ret = WaitForDebugEvent(&ev, 2000))) + { + if (!first_exception && ev.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) break; + if (first_exception && ev.dwDebugEventCode == EXCEPTION_DEBUG_EVENT && + ev.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + first_exception = FALSE; + ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); + }; + ok(ret, "Couldn't get debug event\n"); + ok(ev.dwThreadId == pi.dwThreadId, "Unexpected value\n"); + mei.ThreadId = ev.dwThreadId; + mei.ExceptionPointers = &ep; + mei.ClientPointers = FALSE; + ep.ExceptionRecord = &ev.u.Exception.ExceptionRecord; + ep.ContextRecord = &ctx; + ctx.ContextFlags = CONTEXT_FULL; + ret = GetThreadContext(pi.hThread, &ctx); + ok(ret, "Couldn't get thread context\n"); + CloseHandle(pi.hThread); + } + else + { + mei.ThreadId = GetCurrentThreadId(); + mei.ExceptionPointers = &ep; + mei.ClientPointers = FALSE; + ep.ExceptionRecord = &er; + ep.ContextRecord = &ctx; + memset(&ctx, 0xA5, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_FULL; + er.ExceptionCode = exception_tests[i].exception_code; + er.ExceptionFlags = exception_tests[i].exception_flags; + er.ExceptionAddress = (void *)(DWORD_PTR)0xdeadbeef; + er.NumberParameters = exception_tests[i].num_args; + for (j = 0; j < exception_tests[i].num_args; j++) + er.ExceptionInformation[j] = args[j]; + pi.hProcess = GetCurrentProcess(); + } + minidump_write(pi.hProcess, L"foo.mdmp", MiniDumpNormal, FALSE, &mei, NULL); + + data = minidump_open_for_read("foo.mdmp"); + ret = MiniDumpReadDumpStream(data, ExceptionStream, NULL, (void *)&except_info, &size); + ok(ret, "Couldn't find exception stream\n"); + ok(except_info->ThreadId == mei.ThreadId, "Unexpected value\n"); + ok(except_info->ExceptionRecord.ExceptionCode == exception_tests[i].exception_code, "Unexpected value %x %x\n", except_info->ExceptionRecord.ExceptionCode, exception_tests[i].exception_code); + /* windows 11 starts adding EXCEPTION_SOFTWARE_ORIGINATE flag */ + ok((except_info->ExceptionRecord.ExceptionFlags & ~EXCEPTION_SOFTWARE_ORIGINATE) == exception_tests[i].exception_flags, "Unexpected value\n"); + /* yes native does a signed conversion to DWORD64 when running on 32bit... */ + ok(except_info->ExceptionRecord.ExceptionAddress == (DWORD_PTR)ep.ExceptionRecord->ExceptionAddress + || broken(except_info->ExceptionRecord.ExceptionAddress == (LONG_PTR)ep.ExceptionRecord->ExceptionAddress), "Unexpected value\n"); + if (except_info->ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + { + /* number of parameters depend on machine, wow64... */ + ok(except_info->ExceptionRecord.NumberParameters, "Unexpected value %x\n", except_info->ExceptionRecord.NumberParameters); + } + else if (except_info->ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) + { + ok(except_info->ExceptionRecord.NumberParameters == exception_tests[i].num_args, "Unexpected value\n"); + } + else + { + ok(except_info->ExceptionRecord.NumberParameters == exception_tests[i].num_args, "Unexpected value\n"); + for (j = 0; j < exception_tests[i].num_args; j++) + ok(except_info->ExceptionRecord.ExceptionInformation[j] == args[j], "Unexpected value\n"); + } + ok(except_info->ThreadContext.Rva, "Unexpected value\n"); + mctx = RVA_TO_ADDR(data, except_info->ThreadContext.Rva); + ok(!memcmp(mctx, &ctx, sizeof(ctx)), "Unexpected value\n"); + minidump_check_threads(data, exception_tests[i].with_child ? 2 : (sizeof(void*) == 4 ? 7 : 6)); + minidump_close_for_read(data); + winetest_pop_context(); + if (exception_tests[i].with_child) + { + TerminateProcess(pi.hProcess, 0); + CloseHandle(pi.hProcess); + } + } +} + +static void generate_child_exception(const char *arg) +{ + DWORD code, flags; + unsigned num_args; + + if (sscanf(arg, "%lx;%lx;%u", &code, &flags, &num_args) == 3) + { + switch (code) + { + case EXCEPTION_BREAKPOINT: + DbgBreakPoint(); + break; + case EXCEPTION_ACCESS_VIOLATION: + { + /* volatile to silence gcc warning */ + char * volatile crashme = (char *)(DWORD_PTR)0x12; + *crashme = 2; + } + break; + default: + { + DWORD_PTR my_args[EXCEPTION_MAXIMUM_PARAMETERS]; + int i; + + for (i = 0; i < ARRAY_SIZE(my_args); i++) + my_args[i] = 0x666000 + i; + RaiseException(code, flags, num_args, my_args); + } + break; + } + } +} + START_TEST(minidump) { + int argc; + char **argv; + argc = winetest_get_mainargs(&argv); + if (argc == 4 && !strcmp(argv[2], "exception")) + { + generate_child_exception(argv[3]); + return; + } + test_minidump_contents(); test_current_process(); test_callback(); + test_exception(); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=144018
Your paranoid android.
=== debian11b (64 bit WoW report) ===
d3dx10_34: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 0000000001239730. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011B6120. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 0000000001239730. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 0000000001194A80. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 0000000001239730.
d3dx10_35: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 00000000011C37D0. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011C3580. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 00000000011B5D90. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011C3750. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011BBCD0.
d3dx10_36: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 0000000001239D20. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011C3C30. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 00000000011B6E80. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011B7050. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011B7220.
d3dx10_37: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 00000000011E3D90. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011E39A0. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 00000000011E39A0. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011B5D90. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011D4950.
d3dx10_38: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 00000000011B6030. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011BCAB0. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 00000000011A4690. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011A4B40. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011958C0.
d3dx10_39: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 0000000001239E60. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011939B0. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 0000000001239E60. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011E3800. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011B5D90.
d3dx10_40: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 00000000011D5860. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011B5E50. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 00000000011B5E50. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011C3440. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011C3610.
d3dx10_41: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 00000000011C37D0. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011C3580. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 00000000011B5D90. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011C3750. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011BBCD0.
d3dx10_42: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 00000000011E39D0. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 00000000011D3F40. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 00000000011D3F40. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 00000000011E3C60. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011D3F40.
d3dx10_43: d3dx10.c:4380: Test succeeded inside todo block: Got unexpected effect 0000000001239D40. d3dx10.c:4470: Test succeeded inside todo block: Got unexpected effect 0000000001239F10. d3dx10.c:4480: Test succeeded inside todo block: Got unexpected effect 0000000001239D40. d3dx10.c:4589: Test succeeded inside todo block: Got unexpected effect 0000000001239F10. d3dx10.c:4599: Test succeeded inside todo block: Got unexpected effect 00000000011B20B0.