/*++ Copyright (c) 1991-2002 Microsoft Corporation Module Name: savedump.c Abstract: This module contains the code to recover a dump from the system paging file. Environment: Kernel mode Revision History: --*/ #include // Flag for testing behavior. BOOL g_Test = FALSE; // MachineCrash key information. ULONG g_McTempDestination; WCHAR g_McDumpFile[MAX_PATH]; // CrashControl key information. ULONG g_CcLogEvent; ULONG g_CcSendAlert; ULONG g_CcOverwrite; ULONG g_CcReportMachineDump; WCHAR g_CcMiniDumpDir[MAX_PATH]; WCHAR g_CcDumpFile[MAX_PATH]; // Dump information. DUMP_HEADER g_DumpHeader; WCHAR g_DumpBugCheckString[256]; WCHAR g_MiniDumpFile[MAX_PATH]; PWSTR g_FinalDumpFile; HRESULT FrrvToStatus(EFaultRepRetVal Frrv) { switch(Frrv) { case frrvOk: case frrvOkManifest: case frrvOkQueued: case frrvOkHeadless: return S_OK; default: return LAST_HR(); } } HRESULT GetRegStr(HKEY Key, PWSTR Value, PWSTR Buffer, ULONG BufferChars, PWSTR Default) { ULONG Length; ULONG Error; ULONG Type; HRESULT Status; // // We want to only return valid, terminated strings // that fit in the given buffer. If the registry value // is not a string, has a bad size or fills the buffer // without termination it can't be returned. // Length = BufferChars * sizeof(WCHAR); Error = RegQueryValueEx(Key, Value, NULL, &Type, (LPBYTE)Buffer, &Length); if (Error != ERROR_SUCCESS) { Status = HRESULT_FROM_WIN32(Error); } else if ((Type != REG_SZ && Type != REG_EXPAND_SZ) || (Length & (sizeof(WCHAR) - 1)) || (Length == BufferChars * sizeof(WCHAR) && Buffer[Length / sizeof(WCHAR) - 1] != 0)) { Status = E_INVALIDARG; } else { if (Length < BufferChars * sizeof(WCHAR)) { // Ensure that the string is terminated. Buffer[Length / sizeof(WCHAR)] = 0; } Status = S_OK; } if (Status != S_OK) { // Set to default. if (!Default || wcslen(Default) >= BufferChars) { return E_NOINTERFACE; } StringCchCopy(Buffer, BufferChars, Default); Status = S_OK; } return Status; } HRESULT ExpandRegStr(HKEY Key, PWSTR Value, PWSTR Buffer, ULONG BufferChars, PWSTR Default, PWSTR ExpandBuffer, ULONG ExpandChars) { HRESULT Status; ULONG Length; if ((Status = GetRegStr(Key, Value, Buffer, BufferChars, Default)) != S_OK) { return Status; } Length = ExpandEnvironmentStrings(Buffer, ExpandBuffer, ExpandChars); return Length > 0 && Length <= ExpandChars ? S_OK : E_INVALIDARG; } HRESULT GetRegWord32(HKEY Key, PWSTR Value, PULONG Word, ULONG Default, BOOL CanDefault) { ULONG Length; ULONG Error; ULONG Type; HRESULT Status; Length = sizeof(*Word); Error = RegQueryValueEx(Key, Value, NULL, &Type, (LPBYTE)Word, &Length); if (Error != ERROR_SUCCESS) { Status = HRESULT_FROM_WIN32(Error); } else if (Type != REG_DWORD || Length != sizeof(*Word)) { Status = E_INVALIDARG; } else { Status = S_OK; } if (Status != S_OK) { // Set to default. if (!CanDefault) { return E_NOINTERFACE; } *Word = Default; Status = S_OK; } return Status; } HRESULT GetRegMachineCrash(void) { ULONG Error; HKEY Key; Error = RegOpenKey(HKEY_LOCAL_MACHINE, SUBKEY_CRASH_CONTROL L"\\MachineCrash", &Key); if (Error != ERROR_SUCCESS) { // If the key doesn't exist we just go with the defaults. return S_OK; } GetRegWord32(Key, L"TempDestination", &g_McTempDestination, 0, TRUE); GetRegStr(Key, L"DumpFile", g_McDumpFile, RTL_NUMBER_OF(g_McDumpFile), L""); RegCloseKey(Key); return S_OK; } HRESULT GetRegCrashControl(void) { HRESULT Status; ULONG Error; HKEY Key; WCHAR TmpPath[MAX_PATH]; PWSTR Scan; Error = RegOpenKey(HKEY_LOCAL_MACHINE, SUBKEY_CRASH_CONTROL, &Key); if (Error != ERROR_SUCCESS) { // If the key doesn't exist we just go with the defaults. return S_OK; } GetRegWord32(Key, L"LogEvent", &g_CcLogEvent, 0, TRUE); GetRegWord32(Key, L"SendAlert", &g_CcSendAlert, 0, TRUE); GetRegWord32(Key, L"Overwrite", &g_CcOverwrite, 0, TRUE); GetRegWord32(Key, L"ReportMachineDump", &g_CcReportMachineDump, 0, TRUE); if ((Status = ExpandRegStr(Key, L"MiniDumpDir", TmpPath, RTL_NUMBER_OF(TmpPath), L"%SystemRoot%\\Minidump", g_CcMiniDumpDir, RTL_NUMBER_OF(g_CcMiniDumpDir))) != S_OK) { g_CcMiniDumpDir[0] = 0; goto Exit; } // Remove any trailing slash on the directory name. Scan = g_CcMiniDumpDir + wcslen(g_CcMiniDumpDir); if (Scan > g_CcMiniDumpDir && *(Scan - 1) == L'\\') { *--Scan = 0; } if ((Status = ExpandRegStr(Key, L"DumpFile", TmpPath, RTL_NUMBER_OF(TmpPath), L"%SystemRoot%\\MEMORY.DMP", g_CcDumpFile, RTL_NUMBER_OF(g_CcDumpFile))) != S_OK) { g_CcDumpFile[0] = 0; goto Exit; } Status = S_OK; Exit: RegCloseKey(Key); return Status; } HRESULT GetDumpInfo(void) { HANDLE File; ULONG Bytes; BOOL Succ; HRESULT Status; if (!g_McDumpFile[0]) { return E_NOINTERFACE; } File = CreateFile(g_McDumpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (File == INVALID_HANDLE_VALUE) { return E_NOINTERFACE; } Succ = ReadFile(File, &g_DumpHeader, sizeof(g_DumpHeader), &Bytes, NULL); CloseHandle(File); if (Succ && Bytes == sizeof(g_DumpHeader) && g_DumpHeader.Signature == DUMP_SIGNATURE && g_DumpHeader.ValidDump == DUMP_VALID_DUMP) { #ifdef _WIN64 Status = StringCchPrintf(g_DumpBugCheckString, RTL_NUMBER_OF(g_DumpBugCheckString), L"0x%08x (0x%016I64x, 0x%016I64x, 0x%016I64x, 0x%016I64x)", g_DumpHeader.BugCheckCode, g_DumpHeader.BugCheckParameter1, g_DumpHeader.BugCheckParameter2, g_DumpHeader.BugCheckParameter3, g_DumpHeader.BugCheckParameter4); #else Status = StringCchPrintf(g_DumpBugCheckString, RTL_NUMBER_OF(g_DumpBugCheckString), L"0x%08x (0x%08x, 0x%08x, 0x%08x, 0x%08x)", g_DumpHeader.BugCheckCode, g_DumpHeader.BugCheckParameter1, g_DumpHeader.BugCheckParameter2, g_DumpHeader.BugCheckParameter3, g_DumpHeader.BugCheckParameter4); #endif // This check and message are here just to make // it easy to catch cases where the message outgrows // the buffer. It is highly unlikely that this will happen. if (Status != S_OK) { KdPrint(("SAVEDUMP: g_DumpBugCheckString too small\n")); Status = S_OK; } } else { ZeroMemory(&g_DumpHeader, sizeof(g_DumpHeader)); Status = E_NOINTERFACE; } return Status; } HRESULT SetSecurity(HANDLE FileHandle) { PSID LocalSystemSid = NULL; PSID AdminSid = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY; PSECURITY_DESCRIPTOR SecurityDescriptor; BYTE SdBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH]; PACL Acl; BYTE AclBuffer[1024]; HANDLE Token = NULL; PTOKEN_OWNER TokOwner; ULONG TryLen = 256; ULONG RetLen; NTSTATUS NtStatus; NtStatus = RtlAllocateAndInitializeSid(&NtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &LocalSystemSid); if (!NT_SUCCESS(NtStatus)) { goto Exit; } NtStatus = RtlAllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminSid); if (!NT_SUCCESS(NtStatus)) { goto Exit; } SecurityDescriptor = (PSECURITY_DESCRIPTOR)SdBuffer; // // You can be fancy and compute the exact size, but since the // security descriptor capture code has to do that anyway, why // do it twice? // Acl = (PACL)AclBuffer; NtStatus = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); if (!NT_SUCCESS(NtStatus)) { goto Exit; } NtStatus = RtlCreateAcl(Acl, sizeof(AclBuffer), ACL_REVISION); if (!NT_SUCCESS(NtStatus)) { goto Exit; } // // Current user, Administrator and System have full control // if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &Token) && !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token)) { NtStatus = STATUS_ACCESS_DENIED; goto Exit; } for (;;) { TokOwner = (PTOKEN_OWNER)malloc(TryLen); if (!TokOwner) { NtStatus = STATUS_NO_MEMORY; goto Exit; } if (GetTokenInformation(Token, TokenOwner, TokOwner, TryLen, &RetLen)) { NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION, GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER, TokOwner->Owner); break; } else if (RetLen <= TryLen) { NtStatus = STATUS_ACCESS_DENIED; break; } free(TokOwner); TryLen = RetLen; } free(TokOwner); if (!NT_SUCCESS(NtStatus)) { goto Exit; } NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION, GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER, AdminSid); if (!NT_SUCCESS(NtStatus)) { goto Exit; } NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION, GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER, LocalSystemSid); if (!NT_SUCCESS(NtStatus)) { goto Exit; } NtStatus = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Acl, FALSE); if (!NT_SUCCESS(NtStatus)) { goto Exit; } NtStatus = RtlSetOwnerSecurityDescriptor(SecurityDescriptor, AdminSid, FALSE); if (!NT_SUCCESS(NtStatus)) { goto Exit; } NtStatus = NtSetSecurityObject(FileHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor); Exit: if (AdminSid) { RtlFreeSid(AdminSid); } if (LocalSystemSid) { RtlFreeSid(LocalSystemSid); } if (Token) { CloseHandle(Token); } return NT_SUCCESS(NtStatus) ? S_OK : HRESULT_FROM_NT(NtStatus); } HRESULT CreateMiniDumpFile(PHANDLE MiniFileHandle) { INT i; SYSTEMTIME Time; HRESULT Status; HANDLE FileHandle; if (!g_CcMiniDumpDir[0]) { // Bad minidump directory. return E_INVALIDARG; } // // If directory does not exist, create it. Ignore errors here because // they will be picked up later when we try to create the file. // CreateDirectory(g_CcMiniDumpDir, NULL); // // Format is: Mini-MM_DD_YY_HH_MM.dmp // GetLocalTime(&Time); for (i = 1; i < 100; i++) { if ((Status = StringCchPrintf(g_MiniDumpFile, RTL_NUMBER_OF(g_MiniDumpFile), L"%s\\Mini%2.2d%2.2d%2.2d-%2.2d.dmp", g_CcMiniDumpDir, (int)Time.wMonth, (int)Time.wDay, (int)Time.wYear % 100, (int)i)) != S_OK) { g_MiniDumpFile[0] = 0; return Status; } FileHandle = CreateFile(g_MiniDumpFile, GENERIC_WRITE | WRITE_DAC, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (FileHandle != INVALID_HANDLE_VALUE) { break; } } if (FileHandle == INVALID_HANDLE_VALUE) { // We failed to create a suitable file name. g_MiniDumpFile[0] = 0; return E_FAIL; } if ((Status = SetSecurity(FileHandle)) != S_OK) { CloseHandle(FileHandle); DeleteFile(g_MiniDumpFile); g_MiniDumpFile[0] = 0; return Status; } *MiniFileHandle = FileHandle; return S_OK; } #define COPY_CHUNK (1024 * 1024) HRESULT CopyAndSecureFile(PWSTR Source, PWSTR Dest, HANDLE DestHandle, BOOL Overwrite, BOOL DeleteDest) { HRESULT Status; HANDLE SourceHandle = INVALID_HANDLE_VALUE; PUCHAR Buffer = NULL; Buffer = (PUCHAR)malloc(COPY_CHUNK); if (!Buffer) { Status = E_OUTOFMEMORY; goto Exit; } SourceHandle = CreateFile(Source, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (SourceHandle == INVALID_HANDLE_VALUE) { Status = LAST_HR(); goto Exit; } if (DestHandle == INVALID_HANDLE_VALUE) { DestHandle = CreateFile(Dest, GENERIC_WRITE | WRITE_DAC, 0, NULL, Overwrite ? CREATE_ALWAYS : CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (DestHandle == INVALID_HANDLE_VALUE) { Status = LAST_HR(); goto Exit; } DeleteDest = TRUE; if ((Status = SetSecurity(DestHandle)) != S_OK) { goto Exit; } } for (;;) { ULONG Req, Done; if (!ReadFile(SourceHandle, Buffer, COPY_CHUNK, &Done, NULL)) { Status = LAST_HR(); break; } else if (Done == 0) { // End-of-file. Status = S_OK; break; } Req = Done; if (!WriteFile(DestHandle, Buffer, Req, &Done, NULL)) { Status = LAST_HR(); break; } else if (Done < Req) { Status = HRESULT_FROM_WIN32(ERROR_HANDLE_DISK_FULL); break; } } Exit: if (DeleteDest) { CloseHandle(DestHandle); if (Status != S_OK) { DeleteFile(Dest); } } if (SourceHandle != INVALID_HANDLE_VALUE) { CloseHandle(SourceHandle); } free(Buffer); return Status; } HRESULT MoveDumpFile(void) { HRESULT Status; if (g_DumpHeader.Signature != DUMP_SIGNATURE) { // Dump file is not present or invalid, so there's nothing to do. return S_OK; } // // If the dump file needs to be copied, copy it now. // if (!g_McTempDestination) { g_FinalDumpFile = g_McDumpFile; } else { if (!g_Test) { // // Set the priority class of this application down to the Lowest // priority class to ensure that copying the file does not overload // everything else that is going on during system initialization. // // We do not lower the priority in test mode because it just // wastes time. // SetPriorityClass (GetCurrentProcess(), IDLE_PRIORITY_CLASS); SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_LOWEST); } if (g_DumpHeader.DumpType == DUMP_TYPE_FULL || g_DumpHeader.DumpType == DUMP_TYPE_SUMMARY) { if (!g_CcDumpFile[0]) { // Invalid dump file registry entry. return E_INVALIDARG; } if ((Status = CopyAndSecureFile(g_McDumpFile, g_CcDumpFile, INVALID_HANDLE_VALUE, g_CcOverwrite ? TRUE : FALSE, TRUE)) != S_OK) { return Status; } g_FinalDumpFile = g_CcDumpFile; } else if (g_DumpHeader.DumpType == DUMP_TYPE_TRIAGE) { HANDLE MiniFile; if ((Status = CreateMiniDumpFile(&MiniFile)) != S_OK) { return Status; } if ((Status = CopyAndSecureFile(g_McDumpFile, NULL, MiniFile, FALSE, TRUE)) != S_OK) { g_MiniDumpFile[0] = 0; return Status; } g_FinalDumpFile = g_MiniDumpFile; } DeleteFile(g_McDumpFile); g_McDumpFile[0] = 0; } return S_OK; } HRESULT ConvertDumpFile(void) { HRESULT Status; IDebugClient4 *DebugClient; IDebugControl *DebugControl; HANDLE MiniFile; // // Produce a minidump by conversion if necessary. // if (!g_FinalDumpFile || (g_DumpHeader.DumpType != DUMP_TYPE_FULL && g_DumpHeader.DumpType != DUMP_TYPE_SUMMARY)) { // No dump or not a convertable dump. return S_OK; } if ((Status = CreateMiniDumpFile(&MiniFile)) != S_OK) { return Status; } if ((Status = DebugCreate(__uuidof(IDebugClient4), (void **)&DebugClient)) != S_OK) { goto EH_File; } if ((Status = DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl)) != S_OK) { goto EH_Client; } if ((Status = DebugClient->OpenDumpFileWide(g_FinalDumpFile, 0)) != S_OK) { goto EH_Control; } if ((Status = DebugControl-> WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) != S_OK) { goto EH_Control; } Status = DebugClient-> WriteDumpFileWide(NULL, (ULONG_PTR)MiniFile, DEBUG_DUMP_SMALL, DEBUG_FORMAT_DEFAULT, NULL); EH_Control: DebugControl->Release(); EH_Client: DebugClient->EndSession(DEBUG_END_PASSIVE); DebugClient->Release(); EH_File: CloseHandle(MiniFile); if (Status != S_OK) { DeleteFile(g_MiniDumpFile); g_MiniDumpFile[0] = 0; } return Status; } VOID LogEvent(ULONG Id, WORD StringCount, PWSTR* Strings) { HANDLE LogHandle; BOOL Retry; DWORD Retries; // // Attempt to register the event source. // Savedump runs early in the startup process so // it's possible that the event service hasn't started // yet. If it appears that the service hasn't started, // wait a bit until it comes around. If it hasn't // come around after a reasonable amount of time bail out. // Retries = 0; do { LogHandle = RegisterEventSource(NULL, L"Save Dump"); // // Retry on specific failures that indicate the event // service hasn't started yet. // if (LogHandle == NULL && Retries < 20 && (GetLastError () == RPC_S_SERVER_UNAVAILABLE || GetLastError () == RPC_S_UNKNOWN_IF)) { Sleep(1500); Retry = TRUE; } else { Retry = FALSE; } Retries++; } while (LogHandle == NULL && Retry); if (!LogHandle) { KdPrint(("SAVEDUMP: Unable to register event source, %d\n", GetLastError())); return; } if (!ReportEvent(LogHandle, EVENTLOG_INFORMATION_TYPE, 0, Id, NULL, StringCount, 0, (LPCWSTR *)Strings, NULL)) { KdPrint(("SAVEDUMP: Unable to report event, %d\n", GetLastError())); } DeregisterEventSource(LogHandle); } void LogCrashDumpEvent(void) { LPWSTR StringArray[2]; WORD StringCount; DWORD EventId; // // Set up the parameters based on how much information // is available. // StringCount = 0; if (g_DumpBugCheckString[0]) { StringArray[StringCount++] = g_DumpBugCheckString; } if (g_FinalDumpFile) { StringArray[StringCount++] = g_FinalDumpFile; } // // Report the appropriate event. // if (g_FinalDumpFile) { EventId = EVENT_BUGCHECK_SAVED; } else if (g_DumpBugCheckString[0]) { EventId = EVENT_BUGCHECK; } else { EventId = EVENT_UNKNOWN_BUGCHECK; } LogEvent(EventId, StringCount, StringArray); } void SendCrashDumpAlert(void) { PADMIN_OTHER_INFO AdminInfo; DWORD AdminInfoSize; DWORD Length; DWORD i; ULONG Error; UCHAR VariableInfo[4096]; // // Set up the administrator information variables for processing the // buffer. // AdminInfo = (PADMIN_OTHER_INFO)VariableInfo; AdminInfo->alrtad_numstrings = 0; AdminInfoSize = sizeof(*AdminInfo); // // Format the bugcheck information into the appropriate message format. // if (g_DumpBugCheckString[0]) { Length = (wcslen(g_DumpBugCheckString) + 1) * sizeof(WCHAR); if (AdminInfoSize + Length > sizeof(VariableInfo)) { goto Error; } RtlCopyMemory((PCHAR)AdminInfo + AdminInfoSize, g_DumpBugCheckString, Length); AdminInfo->alrtad_numstrings++; AdminInfoSize += Length; } // // Set up the administrator alert information according to the type of // dump that was taken. // if (g_FinalDumpFile) { Length = (wcslen(g_FinalDumpFile) + 1) * sizeof(WCHAR); if (AdminInfoSize + Length > sizeof(VariableInfo)) { goto Error; } AdminInfo->alrtad_errcode = ALERT_BugCheckSaved; RtlCopyMemory((PCHAR)AdminInfo + AdminInfoSize, g_FinalDumpFile, Length); AdminInfo->alrtad_numstrings++; AdminInfoSize += Length; } else { AdminInfo->alrtad_errcode = ALERT_BugCheck; } // // Get the name of the computer and insert it into the buffer. // Length = (sizeof(VariableInfo) - AdminInfoSize) / sizeof(WCHAR); if (!GetComputerName((LPWSTR)((PCHAR)AdminInfo + AdminInfoSize), &Length)) { goto Error; } Length = (Length + 1) * sizeof(WCHAR); AdminInfo->alrtad_numstrings++; AdminInfoSize += Length; // // Raise the alert. // i = 0; do { Error = NetAlertRaiseEx(ALERT_ADMIN_EVENT, AdminInfo, AdminInfoSize, L"SAVEDUMP"); if (Error) { if (Error == ERROR_FILE_NOT_FOUND) { if (i++ > 20) { break; } if ((i & 3) == 0) { KdPrint(("SAVEDUMP: Waiting for alerter...\n")); } Sleep(15000); } } } while (Error == ERROR_FILE_NOT_FOUND); if (Error != ERROR_SUCCESS) { goto Error; } return; Error: KdPrint(("SAVEDUMP: Unable to raise alert\n")); } VOID __cdecl wmain(int Argc, PWSTR Argv[]) { int Arg; BOOL Report = TRUE; for (Arg = 1; Arg < Argc; Arg++) { if (Argv[Arg][0] == L'-' || Argv[Arg][0] == L'/') { switch (Argv[Arg][1]) { case L't': case L'T': #if DBG g_Test = TRUE; #endif break; default: break; } } } if (GetRegMachineCrash() != S_OK || GetRegCrashControl() != S_OK) { LogEvent(EVENT_UNABLE_TO_READ_REGISTRY, 0, NULL); } GetDumpInfo(); if (MoveDumpFile() != S_OK) { LogEvent(EVENT_UNABLE_TO_MOVE_DUMP_FILE, 0, NULL); } if (ConvertDumpFile() != S_OK) { LogEvent(EVENT_UNABLE_TO_CONVERT_DUMP_FILE, 0, NULL); } //if (WatchdogEventHandler(TRUE) == S_OK) //{ // // Note: Bugcheck EA will be reported in the watchdog code since // // we need to send minidump and wdl files together and we want to have // // a single pop-up only. // Report = FALSE; //} if (Report) { // The default behavior is to report a minidump // even if the machine dump was not a minidump in // order to minimize the amount of data sent. // If the system is configured to report the // machine dump go ahead and send it regardless // of what kind of dump it is. PWSTR ReportDumpFile = g_CcReportMachineDump ? g_FinalDumpFile : g_MiniDumpFile; if (ReportDumpFile && ReportDumpFile[0]) { // Report bugcheck to Microsoft Error Reporting. if (FrrvToStatus(ReportEREvent(eetKernelFault, ReportDumpFile, NULL)) != S_OK) { LogEvent(EVENT_UNABLE_TO_REPORT_BUGCHECK, 0, NULL); } } } // // Knock down reliability ShutdownEventPending flag. We must always try // to do this since somebody can set this flag and recover later on // (e.g. watchdog's EventFlag cleared). With this flag set savedump // will always run and we don't want that. // // Note: This flag is shared between multiple components. // Only savedump is allowed to clear this flag, all other // components are only allowed to set it to trigger // savedump run at next logon. // HKEY Key; if (RegOpenKey(HKEY_LOCAL_MACHINE, SUBKEY_RELIABILITY, &Key) == ERROR_SUCCESS) { RegDeleteValue(Key, L"ShutdownEventPending"); RegCloseKey(Key); } // // If there was a dump produced we may need to log an event // and send an alert. // We delay these time consuming opertaions till the end. We had the // case where SendCrashDumpAlert delayed PC Health pop-ups few minutes. // BOOL HaveCrashData = g_McDumpFile[0] || g_DumpBugCheckString[0] || g_FinalDumpFile; if (HaveCrashData && g_CcLogEvent) { LogCrashDumpEvent(); } // // This function will fill the BugCheckString for DirtyShutdown UI based // on the flag set by EventLog service during startup time, in some case // Eventlog service might start after savedump, so we will need to run this // function after the first event was logged by savedump. // if g_CcLogEvent == FALSE, it is OK not set the string since the user // are not interested about the BugCheck info at all. // if (DirtyShutdownEventHandler(TRUE) != S_OK) { LogEvent(EVENT_UNABLE_TO_REPORT_DIRTY_SHUTDOWN, 0, NULL); } if (HaveCrashData && g_CcSendAlert) { SendCrashDumpAlert(); } }