1488 lines
38 KiB
C++
1488 lines
38 KiB
C++
/*++
|
|
|
|
Copyright (c) 1991-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
erwatch.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to report pending watchdog timeout
|
|
events at logon after dirty reboot.
|
|
|
|
Author:
|
|
|
|
Michael Maciesowicz (mmacie) 29-May-2001
|
|
|
|
Environment:
|
|
|
|
User mode at logon.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "savedump.h"
|
|
|
|
|
|
HANDLE
|
|
CreateWatchdogEventFile(
|
|
IN PWSTR FileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates watchdog event report file.
|
|
|
|
Arguments:
|
|
|
|
FileName - Points to the watchdog event file name.
|
|
|
|
Return Value:
|
|
|
|
File handle if successful, INVALID_HANDLE_VALUE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR Buffer[256];
|
|
HANDLE FileHandle;
|
|
BOOL Status;
|
|
SYSTEMTIME Time;
|
|
TIME_ZONE_INFORMATION TimeZone;
|
|
|
|
//
|
|
// Create watchdog event file.
|
|
//
|
|
|
|
FileHandle = CreateFile(FileName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == FileHandle)
|
|
{
|
|
return FileHandle;
|
|
}
|
|
|
|
//
|
|
// Write header info.
|
|
//
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, L"//\r\n// Watchdog Event Log File\r\n//\r\n\r\n");
|
|
|
|
if (TRUE == Status)
|
|
{
|
|
Status = WriteWatchdogEventFile(FileHandle, L"LogType: Watchdog\r\n");
|
|
}
|
|
|
|
if (TRUE == Status)
|
|
{
|
|
GetLocalTime(&Time);
|
|
|
|
swprintf(Buffer,
|
|
L"Created: %d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d\r\n",
|
|
Time.wYear,
|
|
Time.wMonth,
|
|
Time.wDay,
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond);
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, Buffer);
|
|
}
|
|
|
|
if (TRUE == Status)
|
|
{
|
|
GetTimeZoneInformation(&TimeZone);
|
|
|
|
swprintf(Buffer,
|
|
L"TimeZone: %d - %s\r\n",
|
|
TimeZone.Bias,
|
|
TimeZone.StandardName);
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, Buffer);
|
|
}
|
|
|
|
if (TRUE == Status)
|
|
{
|
|
swprintf(Buffer, L"WindowsVersion: XP\r\n");
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, Buffer);
|
|
}
|
|
|
|
if (TRUE == Status)
|
|
{
|
|
Status = WriteWatchdogEventFile(FileHandle, L"EventType: 0xEA - Thread Stuck in Device Driver\r\n");
|
|
}
|
|
|
|
if (FALSE == Status)
|
|
{
|
|
CloseHandle(FileHandle);
|
|
FileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return FileHandle;
|
|
}
|
|
|
|
BOOL
|
|
CreateWatchdogEventFileName(
|
|
OUT PWSTR FileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine generates watchdog event report full path file name.
|
|
|
|
Arguments:
|
|
|
|
FileName - Points to the storage for generated file name (MAX_PATH
|
|
size assumed).
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT Retry;
|
|
WCHAR DirName[MAX_PATH];
|
|
DWORD Size;
|
|
DWORD ReturnedSize;
|
|
SYSTEMTIME Time;
|
|
|
|
ASSERT(NULL != FileName);
|
|
|
|
//
|
|
// Create %SystemRoot%\LogFiles\Watchdog directory for watchdog event files.
|
|
//
|
|
|
|
Size = MAX_PATH - sizeof (L"YYMMDD_HHMM_NN.wdl");
|
|
|
|
ReturnedSize = ExpandEnvironmentStrings(L"%SystemRoot%\\LogFiles",
|
|
DirName,
|
|
Size);
|
|
|
|
if ((0 == ReturnedSize) || (ReturnedSize > Size))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CreateDirectory(DirName, NULL);
|
|
|
|
ReturnedSize = ExpandEnvironmentStrings(L"%SystemRoot%\\LogFiles\\Watchdog",
|
|
DirName,
|
|
Size);
|
|
|
|
if ((0 == ReturnedSize) || (ReturnedSize > Size))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CreateDirectory(DirName, NULL);
|
|
|
|
//
|
|
// Create watchdog event file as YYMMDD_HHMM_NN.wdl.
|
|
//
|
|
|
|
GetLocalTime(&Time);
|
|
|
|
for (Retry = 1; Retry < ER_WD_MAX_RETRY; Retry++)
|
|
{
|
|
swprintf(FileName,
|
|
L"%s\\%2.2d%2.2d%2.2d_%2.2d%2.2d_%2.2d.wdl",
|
|
DirName,
|
|
Time.wYear % 100,
|
|
Time.wMonth,
|
|
Time.wDay,
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Retry);
|
|
|
|
if ((GetFileAttributes(FileName) == (DWORD)-1) &&
|
|
(GetLastError() == ERROR_FILE_NOT_FOUND))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we failed to create a suitable file name just fail.
|
|
//
|
|
|
|
if (Retry == ER_WD_MAX_RETRY)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
USHORT
|
|
GenerateSignature(
|
|
OUT PER_WD_PCI_ID PciId,
|
|
OUT PER_WD_DRIVER_INFO DriverInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine generates unique failure signature for given PCI ID and driver data.
|
|
|
|
Arguments:
|
|
|
|
PciId - Points to PCI ID info.
|
|
|
|
DriverInfo - Points to driver version info.
|
|
|
|
Return Value:
|
|
|
|
Failure signature.
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT Signature;
|
|
PUSHORT DataPtr;
|
|
LONG Count;
|
|
LONG MaxCount;
|
|
|
|
Signature = 0;
|
|
|
|
if ((NULL == PciId) || (NULL == DriverInfo))
|
|
{
|
|
return Signature;
|
|
}
|
|
|
|
//
|
|
// Build word CRC checksum for PciId.
|
|
//
|
|
|
|
MaxCount = sizeof (ER_WD_PCI_ID) / sizeof (USHORT);
|
|
DataPtr = (PUSHORT)PciId;
|
|
|
|
for (Count = 0; Count < MaxCount; Count++)
|
|
{
|
|
Signature ^= *DataPtr;
|
|
DataPtr++;
|
|
}
|
|
|
|
//
|
|
// Check for odd byte.
|
|
//
|
|
|
|
if (sizeof (ER_WD_PCI_ID) % sizeof (USHORT))
|
|
{
|
|
Signature ^= *(PUCHAR)DataPtr;
|
|
}
|
|
|
|
//
|
|
// Now append DriverInfo.
|
|
//
|
|
|
|
MaxCount = sizeof (ER_WD_DRIVER_INFO) / sizeof (USHORT);
|
|
DataPtr = (PUSHORT)DriverInfo;
|
|
|
|
for (Count = 0; Count < MaxCount; Count++)
|
|
{
|
|
Signature ^= *DataPtr;
|
|
DataPtr++;
|
|
}
|
|
|
|
//
|
|
// Check for odd byte.
|
|
//
|
|
|
|
if (sizeof (ER_WD_DRIVER_INFO) % sizeof (USHORT))
|
|
{
|
|
Signature ^= *(PUCHAR)DataPtr;
|
|
}
|
|
|
|
return Signature;
|
|
}
|
|
|
|
VOID
|
|
GetDriverInfo(
|
|
IN HKEY Key,
|
|
IN OPTIONAL PWCHAR Extension,
|
|
OUT PER_WD_DRIVER_INFO DriverInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine collects driver's version info.
|
|
|
|
Arguments:
|
|
|
|
Key - Watchdog open key (device specific).
|
|
|
|
Extension - Driver file name extension if one should be appended.
|
|
|
|
DriverInfo - Storage for driver version info.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID VersionBuffer;
|
|
PVOID VersionValue;
|
|
LONG WinStatus;
|
|
DWORD Type;
|
|
DWORD Handle;
|
|
DWORD RegLength;
|
|
ULONG Index;
|
|
USHORT CodePage;
|
|
UINT Length;
|
|
|
|
if (NULL == DriverInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(DriverInfo, sizeof (ER_WD_DRIVER_INFO));
|
|
|
|
//
|
|
// Get driver file name from registry.
|
|
//
|
|
|
|
RegLength = MAX_PATH;
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"DriverName",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)DriverInfo->DriverName,
|
|
&RegLength);
|
|
|
|
if (ERROR_SUCCESS != WinStatus)
|
|
{
|
|
swprintf(DriverInfo->DriverName, L"%s", L"Unknown");
|
|
return;
|
|
}
|
|
|
|
if (Extension)
|
|
{
|
|
if ((wcslen(DriverInfo->DriverName) <= wcslen(Extension)) ||
|
|
wcscmp(DriverInfo->DriverName + wcslen(DriverInfo->DriverName) - wcslen(Extension), Extension))
|
|
{
|
|
wcscat(DriverInfo->DriverName, Extension);
|
|
}
|
|
}
|
|
|
|
Length = GetFileVersionInfoSize(DriverInfo->DriverName, &Handle);
|
|
|
|
if (Length)
|
|
{
|
|
VersionBuffer = malloc(Length);
|
|
|
|
if (NULL != VersionBuffer)
|
|
{
|
|
if (GetFileVersionInfo(DriverInfo->DriverName, Handle, Length, VersionBuffer))
|
|
{
|
|
//
|
|
// Get fixed file info.
|
|
//
|
|
|
|
if (VerQueryValue(VersionBuffer,
|
|
L"\\",
|
|
&VersionValue,
|
|
&Length))
|
|
{
|
|
CopyMemory(&(DriverInfo->FixedFileInfo),
|
|
VersionValue,
|
|
min(Length, sizeof (VS_FIXEDFILEINFO)));
|
|
}
|
|
|
|
//
|
|
// Try to locate English code page.
|
|
//
|
|
|
|
CodePage = 0;
|
|
|
|
if (VerQueryValue(VersionBuffer,
|
|
L"\\VarFileInfo\\Translation",
|
|
&VersionValue,
|
|
&Length))
|
|
{
|
|
for (Index = 0; Index < Length / sizeof (ER_WD_LANG_AND_CODE_PAGE); Index++)
|
|
{
|
|
if (((PER_WD_LANG_AND_CODE_PAGE)VersionValue + Index)->Language == ER_WD_LANG_ENGLISH)
|
|
{
|
|
CodePage = ((PER_WD_LANG_AND_CODE_PAGE)VersionValue + Index)->CodePage;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CodePage)
|
|
{
|
|
WCHAR ValueName[ER_WD_MAX_NAME_LENGTH + 1];
|
|
PWCHAR Destination[] =
|
|
{
|
|
DriverInfo->Comments,
|
|
DriverInfo->CompanyName,
|
|
DriverInfo->FileDescription,
|
|
DriverInfo->FileVersion,
|
|
DriverInfo->InternalName,
|
|
DriverInfo->LegalCopyright,
|
|
DriverInfo->LegalTrademarks,
|
|
DriverInfo->OriginalFilename,
|
|
DriverInfo->PrivateBuild,
|
|
DriverInfo->ProductName,
|
|
DriverInfo->ProductVersion,
|
|
DriverInfo->SpecialBuild,
|
|
NULL
|
|
};
|
|
PWCHAR Source[] =
|
|
{
|
|
L"Comments",
|
|
L"CompanyName",
|
|
L"FileDescription",
|
|
L"FileVersion",
|
|
L"InternalName",
|
|
L"LegalCopyright",
|
|
L"LegalTrademarks",
|
|
L"OriginalFilename",
|
|
L"PrivateBuild",
|
|
L"ProductName",
|
|
L"ProductVersion",
|
|
L"SpecialBuild",
|
|
NULL
|
|
};
|
|
|
|
//
|
|
// Read version properties.
|
|
//
|
|
|
|
for (Index = 0; Source[Index] && Destination[Index]; Index++)
|
|
{
|
|
swprintf(ValueName, L"\\StringFileInfo\\%04X%04X\\%s", ER_WD_LANG_ENGLISH, CodePage, Source[Index]);
|
|
|
|
if (VerQueryValue(VersionBuffer,
|
|
ValueName,
|
|
&VersionValue,
|
|
&Length))
|
|
{
|
|
CopyMemory(Destination[Index],
|
|
VersionValue,
|
|
min(Length * sizeof (WCHAR), ER_WD_MAX_FILE_INFO_LENGTH * sizeof (WCHAR)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(VersionBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
UCHAR
|
|
GetFlags(
|
|
IN HKEY Key
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns error signature flags based on registry
|
|
values stored by watchdog.
|
|
|
|
Arguments:
|
|
|
|
Key - Watchdog open key (device specific).
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR Flags;
|
|
LONG WinStatus;
|
|
DWORD Type;
|
|
DWORD Value;
|
|
DWORD Length;
|
|
|
|
Flags = 0;
|
|
|
|
//
|
|
// Get watchdog values from registry.
|
|
//
|
|
|
|
Length = sizeof (DWORD);
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"DisableBugcheck",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)&Value,
|
|
&Length);
|
|
|
|
if ((ERROR_SUCCESS == WinStatus) && Value)
|
|
{
|
|
Flags |= ER_WD_DISABLE_BUGCHECK_FLAG;
|
|
}
|
|
|
|
Length = sizeof (DWORD);
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"DebuggerNotPresent",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)&Value,
|
|
&Length);
|
|
|
|
if ((ERROR_SUCCESS == WinStatus) && Value)
|
|
{
|
|
Flags |= ER_WD_DEBUGGER_NOT_PRESENT_FLAG;
|
|
}
|
|
|
|
Length = sizeof (DWORD);
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"BugcheckTriggered",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)&Value,
|
|
&Length);
|
|
|
|
if ((ERROR_SUCCESS == WinStatus) && Value)
|
|
{
|
|
Flags |= ER_WD_BUGCHECK_TRIGGERED_FLAG;
|
|
}
|
|
|
|
return Flags;
|
|
}
|
|
|
|
VOID
|
|
GetPciId(
|
|
IN HKEY Key,
|
|
OUT PER_WD_PCI_ID PciId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine collects PCI ID info.
|
|
|
|
Arguments:
|
|
|
|
Key - Watchdog open key (device specific).
|
|
|
|
PciId - Storage for PCI ID info.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR HardwareId;
|
|
PWCHAR Token;
|
|
PWCHAR EndPointer;
|
|
LONG WinStatus;
|
|
DWORD Type;
|
|
DWORD Length;
|
|
|
|
if (NULL == PciId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(PciId, sizeof (ER_WD_PCI_ID));
|
|
|
|
//
|
|
// HardwareId is a MULTI_SZ - we need a bug buffer.
|
|
//
|
|
|
|
HardwareId = (PWCHAR)malloc(ER_WD_MAX_DATA_SIZE);
|
|
|
|
if (NULL == HardwareId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get HardwareId string from registry.
|
|
//
|
|
|
|
Length = ER_WD_MAX_DATA_SIZE;
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"HardwareId",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)HardwareId,
|
|
&Length);
|
|
|
|
if (ERROR_SUCCESS != WinStatus)
|
|
{
|
|
free(HardwareId);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure HardwareId is in uppercase.
|
|
//
|
|
|
|
_wcsupr(HardwareId);
|
|
|
|
//
|
|
// Get VendorId.
|
|
//
|
|
|
|
Token = wcsstr(HardwareId, L"VEN_");
|
|
|
|
if (NULL != Token)
|
|
{
|
|
PciId->VendorId = (USHORT)wcstoul(Token + 4, &EndPointer, 16);
|
|
}
|
|
|
|
//
|
|
// Get DeviceId.
|
|
//
|
|
|
|
Token = wcsstr(HardwareId, L"DEV_");
|
|
|
|
if (NULL != Token)
|
|
{
|
|
PciId->DeviceId = (USHORT)wcstoul(Token + 4, &EndPointer, 16);
|
|
}
|
|
|
|
//
|
|
// Get Revision.
|
|
//
|
|
|
|
Token = wcsstr(HardwareId, L"REV_");
|
|
|
|
if (NULL != Token)
|
|
{
|
|
PciId->Revision = (UCHAR)wcstoul(Token + 4, &EndPointer, 16);
|
|
}
|
|
|
|
//
|
|
// Get SubsystemId.
|
|
//
|
|
|
|
Token = wcsstr(HardwareId, L"SUBSYS_");
|
|
|
|
if (NULL != Token)
|
|
{
|
|
PciId->SubsystemId = wcstoul(Token + 7, &EndPointer, 16);
|
|
}
|
|
|
|
free(HardwareId);
|
|
}
|
|
|
|
BOOL
|
|
SaveWatchdogEventData(
|
|
IN HANDLE FileHandle,
|
|
IN HKEY Key,
|
|
IN PER_WD_DRIVER_INFO DriverInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine transfers watchdog event data from registry to
|
|
the watchdog event report file.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Handle of open watchdog event report file.
|
|
|
|
Key - Watchdog open key (device specific).
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG WinStatus;
|
|
DWORD Index;
|
|
DWORD NameLength;
|
|
DWORD DataSize;
|
|
DWORD ReturnedSize;
|
|
DWORD Type;
|
|
WCHAR Name[ER_WD_MAX_NAME_LENGTH + 1];
|
|
WCHAR DwordBuffer[20];
|
|
PBYTE Data;
|
|
BOOL Status = TRUE;
|
|
|
|
ASSERT(INVALID_HANDLE_VALUE != FileHandle);
|
|
|
|
Data = (PBYTE)malloc(ER_WD_MAX_DATA_SIZE);
|
|
if (NULL == Data)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Pull watchdog data from registry and write it to report.
|
|
//
|
|
|
|
for (Index = 0;; Index++)
|
|
{
|
|
//
|
|
// Read watchdog registry value.
|
|
//
|
|
|
|
NameLength = ER_WD_MAX_NAME_LENGTH;
|
|
DataSize = ER_WD_MAX_DATA_SIZE;
|
|
|
|
WinStatus = RegEnumValue(Key,
|
|
Index,
|
|
Name,
|
|
&NameLength,
|
|
NULL,
|
|
&Type,
|
|
Data,
|
|
&DataSize);
|
|
|
|
if (ERROR_NO_MORE_ITEMS == WinStatus)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != WinStatus)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Pick up strings and dwords only.
|
|
//
|
|
|
|
if ((REG_EXPAND_SZ == Type) || (REG_SZ == Type) || (REG_MULTI_SZ == Type) || (REG_DWORD == Type))
|
|
{
|
|
//
|
|
// Write registry entry to watchdog event file.
|
|
//
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, Name);
|
|
if (TRUE != Status)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, L": ");
|
|
if (TRUE != Status)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (REG_DWORD == Type)
|
|
{
|
|
swprintf(DwordBuffer, L"%u", *(PULONG)Data);
|
|
Status = WriteWatchdogEventFile(FileHandle, DwordBuffer);
|
|
}
|
|
else
|
|
{
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWSTR)Data);
|
|
}
|
|
|
|
if (TRUE != Status)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, L"\r\n");
|
|
if (TRUE != Status)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write driver info to report.
|
|
//
|
|
|
|
if (NULL != DriverInfo)
|
|
{
|
|
if (TRUE == Status)
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverFixedFileInfo: %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X\r\n",
|
|
DriverInfo->FixedFileInfo.dwSignature,
|
|
DriverInfo->FixedFileInfo.dwStrucVersion,
|
|
DriverInfo->FixedFileInfo.dwFileVersionMS,
|
|
DriverInfo->FixedFileInfo.dwFileVersionLS,
|
|
DriverInfo->FixedFileInfo.dwProductVersionMS,
|
|
DriverInfo->FixedFileInfo.dwProductVersionLS,
|
|
DriverInfo->FixedFileInfo.dwFileFlagsMask,
|
|
DriverInfo->FixedFileInfo.dwFileFlags,
|
|
DriverInfo->FixedFileInfo.dwFileOS,
|
|
DriverInfo->FixedFileInfo.dwFileType,
|
|
DriverInfo->FixedFileInfo.dwFileSubtype,
|
|
DriverInfo->FixedFileInfo.dwFileDateMS,
|
|
DriverInfo->FixedFileInfo.dwFileDateLS);
|
|
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->Comments[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverComments: %s\r\n", DriverInfo->Comments);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->CompanyName[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverCompanyName: %s\r\n", DriverInfo->CompanyName);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->FileDescription[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverFileDescription: %s\r\n", DriverInfo->FileDescription);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->FileVersion[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverFileVersion: %s\r\n", DriverInfo->FileVersion);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->InternalName[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverInternalName: %s\r\n", DriverInfo->InternalName);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->LegalCopyright[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverLegalCopyright: %s\r\n", DriverInfo->LegalCopyright);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->LegalTrademarks[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverLegalTrademarks: %s\r\n", DriverInfo->LegalTrademarks);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->OriginalFilename[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverOriginalFilename: %s\r\n", DriverInfo->OriginalFilename);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->PrivateBuild[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverPrivateBuild: %s\r\n", DriverInfo->PrivateBuild);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->ProductName[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverProductName: %s\r\n", DriverInfo->ProductName);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->ProductVersion[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverProductVersion: %s\r\n", DriverInfo->ProductVersion);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
|
|
if ((TRUE == Status) && DriverInfo->SpecialBuild[0])
|
|
{
|
|
swprintf((PWCHAR)Data, L"DriverSpecialBuild: %s\r\n", DriverInfo->SpecialBuild);
|
|
Status = WriteWatchdogEventFile(FileHandle, (PWCHAR)Data);
|
|
}
|
|
}
|
|
|
|
if (NULL != Data)
|
|
{
|
|
free(Data);
|
|
Data = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOL
|
|
WatchdogEventHandler(
|
|
IN BOOL NotifyPcHealth
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the boot time routine to handle pending watchdog events.
|
|
|
|
Arguments:
|
|
|
|
NotifyPcHealth - TRUE if we should report event to PC Health, FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
TRUE if watchdog event(s) found and reported to PC Health, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY Key;
|
|
UCHAR Flags;
|
|
ULONG WinStatus;
|
|
ULONG Type;
|
|
ULONG Length;
|
|
ULONG Shutdown;
|
|
ULONG EventFlag;
|
|
ULONG Index;
|
|
ULONG FileVersionMS;
|
|
ULONG FileVersionLS;
|
|
USHORT Signature;
|
|
SEventInfoW EventInfo;
|
|
HANDLE FileHandle;
|
|
WCHAR WatchdogReport[MAX_PATH];
|
|
WCHAR Stage1Url[ER_WD_MAX_URL_LENGTH + 1];
|
|
WCHAR Stage2Url[ER_WD_MAX_URL_LENGTH + 1];
|
|
WCHAR CorpPath[MAX_PATH];
|
|
PWCHAR MessageBuffer;
|
|
PWCHAR DescriptionBuffer;
|
|
PWCHAR DeviceDescription;
|
|
PWCHAR FinalReport;
|
|
PWCHAR DriverName;
|
|
PWCHAR String000;
|
|
PWCHAR String001;
|
|
PWCHAR String002;
|
|
PWCHAR String003;
|
|
PWCHAR String004;
|
|
PWCHAR String005;
|
|
BOOL LogStatus;
|
|
BOOL ReturnStatus;
|
|
HINSTANCE Instance;
|
|
PER_WD_DRIVER_INFO DriverInfo;
|
|
ER_WD_PCI_ID PciId;
|
|
|
|
MessageBuffer = NULL;
|
|
DescriptionBuffer = NULL;
|
|
DeviceDescription = NULL;
|
|
FinalReport = NULL;
|
|
String000 = NULL;
|
|
String001 = NULL;
|
|
String002 = NULL;
|
|
String003 = NULL;
|
|
String004 = NULL;
|
|
String005 = NULL;
|
|
ReturnStatus = FALSE;
|
|
Instance = (HINSTANCE)GetModuleHandle(NULL);
|
|
DriverInfo = NULL;
|
|
DriverName = NULL;
|
|
|
|
//
|
|
// Check if Watchdog\Display key present.
|
|
// Note: Key not present = dirty shutdown but no watchdog event = we don't care.
|
|
//
|
|
|
|
WinStatus = RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
SUBKEY_WATCHDOG_DISPLAY,
|
|
&Key);
|
|
|
|
if (ERROR_SUCCESS == WinStatus)
|
|
{
|
|
EventFlag = 0;
|
|
|
|
//
|
|
// Check for clean shutdown indicator.
|
|
//
|
|
// TODO: Use NtQueryLastShutdownType() once implemented.
|
|
//
|
|
|
|
Length = sizeof (Shutdown);
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"Shutdown",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)&Shutdown,
|
|
&Length);
|
|
|
|
if (ERROR_SUCCESS != WinStatus)
|
|
{
|
|
//
|
|
// Value not there - assume dirty shutdown.
|
|
//
|
|
|
|
Shutdown = 0;
|
|
}
|
|
|
|
//
|
|
// If dirty shutdown check if watchdog display event captured.
|
|
//
|
|
|
|
if (!Shutdown)
|
|
{
|
|
Length = sizeof (EventFlag);
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"EventFlag",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)&EventFlag,
|
|
&Length);
|
|
|
|
if (ERROR_SUCCESS != WinStatus)
|
|
{
|
|
//
|
|
// Value not there - watchdog display event not captured.
|
|
//
|
|
|
|
EventFlag = 0;
|
|
}
|
|
}
|
|
|
|
if (EventFlag)
|
|
{
|
|
//
|
|
// Report watchdog event to PC Health if requested.
|
|
//
|
|
|
|
if (NotifyPcHealth)
|
|
{
|
|
//
|
|
// Allocate storage for localized strings.
|
|
//
|
|
|
|
String000 = (PWCHAR)malloc(ER_WD_MAX_STRING);
|
|
String001 = (PWCHAR)malloc(ER_WD_MAX_STRING);
|
|
String002 = (PWCHAR)malloc(ER_WD_MAX_STRING);
|
|
String003 = (PWCHAR)malloc(ER_WD_MAX_STRING);
|
|
String004 = (PWCHAR)malloc(ER_WD_MAX_STRING);
|
|
String005 = (PWCHAR)malloc(ER_WD_MAX_STRING);
|
|
|
|
//
|
|
// Load localized strings from resources.
|
|
//
|
|
// Note: It's OK to pass NULL so we don't have to validate buffers.
|
|
//
|
|
|
|
LoadString(Instance, IDS_000, String000, ER_WD_MAX_STRING);
|
|
LoadString(Instance, IDS_001, String001, ER_WD_MAX_STRING);
|
|
LoadString(Instance, IDS_002, String002, ER_WD_MAX_STRING);
|
|
LoadString(Instance, IDS_003, String003, ER_WD_MAX_STRING);
|
|
LoadString(Instance, IDS_004, String004, ER_WD_MAX_STRING);
|
|
LoadString(Instance, IDS_005, String005, ER_WD_MAX_STRING);
|
|
|
|
//
|
|
// Allocate and get DriverInfo data.
|
|
//
|
|
|
|
DriverInfo = (PER_WD_DRIVER_INFO)malloc(sizeof (ER_WD_DRIVER_INFO));
|
|
|
|
if (NULL != DriverInfo)
|
|
{
|
|
GetDriverInfo(Key, L".dll", DriverInfo);
|
|
}
|
|
|
|
//
|
|
// Get PCI ID info.
|
|
//
|
|
|
|
GetPciId(Key, &PciId);
|
|
|
|
//
|
|
// Get watchdog event flags.
|
|
//
|
|
|
|
Flags = GetFlags(Key);
|
|
|
|
//
|
|
// Generate event signature.
|
|
//
|
|
|
|
Signature = GenerateSignature(&PciId, DriverInfo);
|
|
|
|
//
|
|
// Create watchdog report file.
|
|
//
|
|
|
|
LogStatus = CreateWatchdogEventFileName(WatchdogReport);
|
|
|
|
if (TRUE == LogStatus)
|
|
{
|
|
FileHandle = CreateWatchdogEventFile(WatchdogReport);
|
|
|
|
if (INVALID_HANDLE_VALUE == FileHandle)
|
|
{
|
|
LogStatus = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (TRUE == LogStatus)
|
|
{
|
|
LogStatus = WriteWatchdogEventFile(
|
|
FileHandle,
|
|
L"\r\n//\r\n"
|
|
L"// The driver for the display device got stuck in an infinite loop. This\r\n"
|
|
L"// usually indicates a problem with the device itself or with the device\r\n"
|
|
L"// driver programming the hardware incorrectly. Please check with your\r\n"
|
|
L"// display device vendor for any driver updates.\r\n"
|
|
L"//\r\n\r\n");
|
|
}
|
|
|
|
if (TRUE == LogStatus)
|
|
{
|
|
LogStatus = SaveWatchdogEventData(FileHandle, Key, DriverInfo);
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != FileHandle)
|
|
{
|
|
CloseHandle(FileHandle);
|
|
}
|
|
|
|
FinalReport = (TRUE == LogStatus) ? WatchdogReport : NULL;
|
|
|
|
//
|
|
// Get device description.
|
|
//
|
|
|
|
DescriptionBuffer = NULL;
|
|
DeviceDescription = NULL;
|
|
Length = 0;
|
|
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"DeviceDescription",
|
|
NULL,
|
|
&Type,
|
|
NULL,
|
|
&Length);
|
|
|
|
if (ERROR_SUCCESS == WinStatus)
|
|
{
|
|
DescriptionBuffer = (PWCHAR)malloc(Length);
|
|
|
|
if (NULL != DescriptionBuffer)
|
|
{
|
|
WinStatus = RegQueryValueEx(Key,
|
|
L"DeviceDescription",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)DescriptionBuffer,
|
|
&Length);
|
|
}
|
|
else
|
|
{
|
|
Length = 0;
|
|
}
|
|
}
|
|
|
|
if ((ERROR_SUCCESS == WinStatus) && (0 != Length))
|
|
{
|
|
DeviceDescription = DescriptionBuffer;
|
|
}
|
|
else
|
|
{
|
|
DeviceDescription = String004;
|
|
Length = (ER_WD_MAX_STRING + 1) * sizeof (WCHAR);
|
|
}
|
|
|
|
Length += 2 * ER_WD_MAX_STRING * sizeof (WCHAR);
|
|
|
|
|
|
MessageBuffer = (PWCHAR)malloc(Length);
|
|
|
|
if (NULL != MessageBuffer)
|
|
{
|
|
swprintf(MessageBuffer,
|
|
L"%s%s%s",
|
|
String003,
|
|
DeviceDescription,
|
|
String005);
|
|
}
|
|
|
|
//
|
|
// Create URLs and corporate path.
|
|
//
|
|
|
|
if (DriverInfo)
|
|
{
|
|
DriverName = DriverInfo->DriverName;
|
|
FileVersionMS = DriverInfo->FixedFileInfo.dwFileVersionMS;
|
|
FileVersionLS = DriverInfo->FixedFileInfo.dwFileVersionLS;
|
|
}
|
|
else
|
|
{
|
|
DriverName = L"Unknown";
|
|
FileVersionMS = 0;
|
|
FileVersionLS = 0;
|
|
}
|
|
|
|
swprintf(Stage1Url,
|
|
L"\r\nStage1URL=/StageOne/Drivers_Display/%04X%04X%02X%08X/%s/%u_%u_%u_%u/%04X%02X%02X",
|
|
PciId.VendorId,
|
|
PciId.DeviceId,
|
|
PciId.Revision,
|
|
PciId.SubsystemId,
|
|
DriverName,
|
|
(USHORT)(FileVersionMS >> 16),
|
|
(USHORT)(FileVersionMS & 0xffff),
|
|
(USHORT)(FileVersionLS >> 16),
|
|
(USHORT)(FileVersionLS & 0xffff),
|
|
Signature,
|
|
Flags,
|
|
0xea);
|
|
|
|
swprintf(Stage2Url,
|
|
L"\r\nStage2URL=/dw/StageTwo.asp?szAppName=Drivers.Display&szAppVer=%04X%04X%02X%08X&"
|
|
L"szModName=%s&szModVer=%u.%u.%u.%u&Offset=%04X%02X%02X",
|
|
PciId.VendorId,
|
|
PciId.DeviceId,
|
|
PciId.Revision,
|
|
PciId.SubsystemId,
|
|
DriverName,
|
|
(USHORT)(FileVersionMS >> 16),
|
|
(USHORT)(FileVersionMS & 0xffff),
|
|
(USHORT)(FileVersionLS >> 16),
|
|
(USHORT)(FileVersionLS & 0xffff),
|
|
Signature,
|
|
Flags,
|
|
0xea);
|
|
|
|
swprintf(CorpPath,
|
|
L"\\Drivers.Display\\%04X%04X%02X%08X\\%s\\%u.%u.%u.%u\\%04X%02X%02X",
|
|
PciId.VendorId,
|
|
PciId.DeviceId,
|
|
PciId.Revision,
|
|
PciId.SubsystemId,
|
|
DriverName,
|
|
(USHORT)(FileVersionMS >> 16),
|
|
(USHORT)(FileVersionMS & 0xffff),
|
|
(USHORT)(FileVersionLS >> 16),
|
|
(USHORT)(FileVersionLS & 0xffff),
|
|
Signature,
|
|
Flags,
|
|
0xea);
|
|
|
|
//
|
|
// Dots are not allowed in stage1 URL, replace with _ then append .htm.
|
|
//
|
|
|
|
for (Index = 0; Stage1Url[Index] != UNICODE_NULL; Index++)
|
|
{
|
|
if (Stage1Url[Index] == L'.')
|
|
{
|
|
Stage1Url[Index] = L'_';
|
|
}
|
|
}
|
|
|
|
wcscat(Stage1Url, L".htm");
|
|
|
|
//
|
|
// Fill in event record.
|
|
//
|
|
// TODO: Add minidump to watchdog event reports, but only when it contains stack trace
|
|
// from the spinning thread. This is for SKUs < Server, since we're not bugchecking
|
|
// for Server and above. We should also look into snapshot report, this can be good
|
|
// piece of data to send along.
|
|
//
|
|
|
|
ZeroMemory(&EventInfo, sizeof (EventInfo));
|
|
|
|
EventInfo.cbSEI = sizeof (EventInfo);
|
|
EventInfo.wszEventName = L"Thread Stuck in Device Driver";
|
|
EventInfo.wszErrMsg = String002;
|
|
EventInfo.wszHdr = String001;
|
|
EventInfo.wszTitle = String000;
|
|
EventInfo.wszStage1 = Stage1Url;
|
|
EventInfo.wszStage2 = Stage2Url;
|
|
EventInfo.wszFileList = FinalReport;
|
|
EventInfo.wszEventSrc = NULL;
|
|
EventInfo.wszCorpPath = CorpPath;
|
|
EventInfo.wszPlea = MessageBuffer;
|
|
EventInfo.wszSendBtn = NULL;
|
|
EventInfo.wszNoSendBtn = NULL;
|
|
EventInfo.fUseLitePlea = FALSE;
|
|
EventInfo.fUseIEForURLs = TRUE;
|
|
EventInfo.fNoBucketLogs = FALSE;
|
|
EventInfo.fNoDefCabLimit = FALSE;
|
|
|
|
//
|
|
// Notify PC Health.
|
|
//
|
|
|
|
PCHPFNotifyFault(eetUseEventInfo, NULL, &EventInfo);
|
|
|
|
//
|
|
// Clean up buffers.
|
|
//
|
|
|
|
if (NULL != DescriptionBuffer)
|
|
{
|
|
free(DescriptionBuffer);
|
|
DescriptionBuffer = NULL;
|
|
}
|
|
|
|
if (NULL != MessageBuffer)
|
|
{
|
|
free(MessageBuffer);
|
|
MessageBuffer = NULL;
|
|
}
|
|
|
|
if (NULL != String000)
|
|
{
|
|
free(String000);
|
|
String000 = NULL;
|
|
}
|
|
|
|
if (NULL != String001)
|
|
{
|
|
free(String001);
|
|
String001 = NULL;
|
|
}
|
|
|
|
if (NULL != String002)
|
|
{
|
|
free(String002);
|
|
String002 = NULL;
|
|
}
|
|
|
|
if (NULL != String003)
|
|
{
|
|
free(String003);
|
|
String003 = NULL;
|
|
}
|
|
|
|
if (NULL != String004)
|
|
{
|
|
free(String004);
|
|
String004 = NULL;
|
|
}
|
|
|
|
if (NULL != String005)
|
|
{
|
|
free(String005);
|
|
String005 = NULL;
|
|
}
|
|
|
|
if (NULL != DriverInfo)
|
|
{
|
|
free(DriverInfo);
|
|
DriverInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// We trapped watchdog event and notified PC Health, set ReturnStatus to reflect this.
|
|
//
|
|
|
|
ReturnStatus = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Knock down watchdog's EventFlag. We do this after registering our
|
|
// event with PC Health.
|
|
//
|
|
|
|
RegDeleteValue(Key, L"EventFlag");
|
|
RegCloseKey(Key);
|
|
}
|
|
|
|
//
|
|
// TODO: Handle additional device classes here when supported.
|
|
//
|
|
|
|
return ReturnStatus;
|
|
}
|
|
|
|
BOOL
|
|
WriteWatchdogEventFile(
|
|
IN HANDLE FileHandle,
|
|
IN PWSTR String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes a string to watchdog event report file.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Handle of open watchdog event report file.
|
|
|
|
String - Points to the string to write.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Size;
|
|
DWORD ReturnedSize;
|
|
PCHAR MultiByte;
|
|
BOOL Status;
|
|
|
|
ASSERT(INVALID_HANDLE_VALUE != FileHandle);
|
|
ASSERT(NULL != String);
|
|
|
|
//
|
|
// Get buffer size for translated string.
|
|
//
|
|
|
|
Size = WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
String,
|
|
-1,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (Size <= 1)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
MultiByte = (PCHAR)malloc(Size);
|
|
|
|
if (NULL == MultiByte)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Size = WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
String,
|
|
-1,
|
|
MultiByte,
|
|
Size,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (Size > 0)
|
|
{
|
|
Status = WriteFile(FileHandle,
|
|
MultiByte,
|
|
Size - 1,
|
|
&ReturnedSize,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(FALSE);
|
|
Status = FALSE;
|
|
}
|
|
|
|
free(MultiByte);
|
|
return Status;
|
|
}
|