windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/usbser/debugwdm.c
2020-09-26 16:20:57 +08:00

1442 lines
39 KiB
C

/***************************************************************************
Copyright (c) 1998 Microsoft Corporation
Module Name:
DEBUGWDM.C
Abstract:
Debug and diagnostic routines for WDM driver
Environment:
Kernel mode only
Notes:
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Copyright (c) 1998 Microsoft Corporation. All Rights Reserved.
Revision History:
12/23/97 : created
Author:
Tom Green
****************************************************************************/
#include <wdm.h>
#include <ntddser.h>
#include <stdio.h>
#include <stdlib.h>
#include <usb.h>
#include <usbdrivr.h>
#include <usbdlib.h>
#include <usbcomm.h>
#ifdef WMI_SUPPORT
#include <wmilib.h>
#include <wmidata.h>
#include <wmistr.h>
#endif
#include "usbser.h"
#include "serioctl.h"
#include "utils.h"
#include "debugwdm.h"
// memory allocation stats
LOCAL ULONG MemoryAllocated = 0L;
LOCAL ULONG MemAllocFailCnt = 0L;
LOCAL ULONG MemAllocCnt = 0L;
LOCAL ULONG MemFreeFailCnt = 0L;
LOCAL ULONG MemFreeCnt = 0L;
LOCAL ULONG MaxMemAllocated = 0L;
// signature to write at end of allocated memory block
#define MEM_ALLOC_SIGNATURE (ULONG) 'CLLA'
// signature to write at end of freed memory block
#define MEM_FREE_SIGNATURE (ULONG) 'EERF'
#ifdef PROFILING_ENABLED
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,Debug_OpenWDMDebug)
#pragma alloc_text(PAGE,Debug_CloseWDMDebug)
#pragma alloc_text(PAGE,Debug_SizeIRPHistoryTable)
#pragma alloc_text(PAGE,Debug_SizeDebugPathHist)
#pragma alloc_text(PAGE,Debug_SizeErrorLog)
#pragma alloc_text(PAGE,Debug_ExtractAttachedDevices)
#pragma alloc_text(PAGE,Debug_GetDriverInfo)
#pragma alloc_text(PAGE,Debug_ExtractIRPHist)
#pragma alloc_text(PAGE,Debug_ExtractPathHist)
#pragma alloc_text(PAGE,Debug_ExtractErrorLog)
#pragma alloc_text(PAGE,Debug_DumpDriverLog)
#pragma alloc_text(PAGE,Debug_TranslateStatus)
#pragma alloc_text(PAGE,Debug_TranslateIoctl)
#endif // ALLOC_PRAGMA
// data structures, macros, and data that the outside world doesn't need to know about
// amount of data to save from IRP buffer
#define IRP_DATA_SIZE 0x04
// size for temporary string formatting buffers
#define TMP_STR_BUFF_SIZE 0x100
// initial number of entries in tables and logs
#define DEFAULT_LOG_SIZE 64L
// data structures for debug stuff
// entry for IRP history table for IRPs going in and out
typedef struct IRPHistory
{
LARGE_INTEGER TimeStamp;
PDEVICE_OBJECT DeviceObject;
PIRP Irp;
ULONG MajorFunction;
ULONG IrpByteCount;
UCHAR IrpData[IRP_DATA_SIZE];
UCHAR IrpDataCount;
} IRPHist, *PIRPHist;
// entry for execution tracing
typedef struct PATHHistory
{
LARGE_INTEGER TimeStamp;
PCHAR Path;
} PATHHist, *PPATHHist;
// entry for error log
typedef struct ERRORLog
{
LARGE_INTEGER TimeStamp;
NTSTATUS Status;
} ERRLog, *PERRLog;
// this is for translating a code into an ASCII string
typedef struct Code2Ascii
{
NTSTATUS Code;
PCHAR Str;
} Code2Ascii;
// local data for debug file
// IRP history table components
LOCAL PIRPHist IRPHistoryTable = NULL;
LOCAL ULONG IRPHistoryIndex = 0L;
GLOBAL ULONG IRPHistorySize = 0L;
// Debug path storage
LOCAL PPATHHist DebugPathHist = NULL;
LOCAL ULONG DebugPathIndex = 0L;
GLOBAL ULONG DebugPathSize = 0L;
// Error log components
LOCAL PERRLog ErrorLog = NULL;
LOCAL ULONG ErrorLogIndex = 0L;
GLOBAL ULONG ErrorLogSize = 0L;
// this is for translating NT status codes into ASCII strings
LOCAL Code2Ascii NTErrors[] =
{
STATUS_SUCCESS, "STATUS_SUCCESS",
STATUS_PENDING, "STATUS_PENDING",
STATUS_TIMEOUT, "STATUS_TIMEOUT",
STATUS_DEVICE_BUSY, "STATUS_DEVICE_BUSY",
STATUS_INSUFFICIENT_RESOURCES, "STATUS_INSUFFICIENT_RESOURCES",
STATUS_INVALID_DEVICE_REQUEST, "STATUS_INVALID_DEVICE_REQUEST",
STATUS_DEVICE_NOT_READY, "STATUS_DEVICE_NOT_READY",
STATUS_INVALID_BUFFER_SIZE, "STATUS_INVALID_BUFFER_SIZE",
STATUS_INVALID_PARAMETER, "STATUS_INVALID_PARAMETER",
STATUS_INVALID_HANDLE, "STATUS_INVALID_HANDLE",
STATUS_OBJECT_PATH_NOT_FOUND, "STATUS_OBJECT_PATH_NOT_FOUND",
STATUS_BUFFER_TOO_SMALL, "STATUS_BUFFER_TOO_SMALL",
STATUS_NOT_SUPPORTED, "STATUS_NOT_SUPPORTED",
STATUS_DEVICE_DATA_ERROR, "STATUS_DEVICE_DATA_ERROR",
STATUS_CANCELLED, "STATUS_CANCELLED",
STATUS_OBJECT_NAME_INVALID, "STATUS_OBJECT_NAME_INVALID",
STATUS_OBJECT_NAME_NOT_FOUND, "STATUS_OBJECT_NAME_NOT_FOUND"
};
LOCAL ULONG NumNTErrs = sizeof(NTErrors) / sizeof(Code2Ascii);
LOCAL CHAR UnknownStatus[80];
// this is for translating IOCTL codes into ASCII strings
LOCAL Code2Ascii IoctlCodes[] =
{
IRP_MJ_CREATE, "CREATE",
IRP_MJ_CREATE_NAMED_PIPE, "CNPIPE",
IRP_MJ_CLOSE, "CLOSE ",
IRP_MJ_READ, "READ ",
IRP_MJ_WRITE, "WRITE ",
IRP_MJ_QUERY_INFORMATION, "QRYINF",
IRP_MJ_SET_INFORMATION, "SETINF",
IRP_MJ_QUERY_EA, "QRYEA ",
IRP_MJ_SET_EA, "SETEA ",
IRP_MJ_FLUSH_BUFFERS, "FLSBUF",
IRP_MJ_QUERY_VOLUME_INFORMATION, "QRYVOL",
IRP_MJ_SET_VOLUME_INFORMATION, "SETVOL",
IRP_MJ_DIRECTORY_CONTROL, "DIRCTL",
IRP_MJ_FILE_SYSTEM_CONTROL, "SYSCTL",
IRP_MJ_DEVICE_CONTROL, "DEVCTL",
IRP_MJ_INTERNAL_DEVICE_CONTROL, "INDVCT",
IRP_MJ_SHUTDOWN, "SHTDWN",
IRP_MJ_LOCK_CONTROL, "LOKCTL",
IRP_MJ_CLEANUP, "CLNUP ",
IRP_MJ_CREATE_MAILSLOT, "MAILSL",
IRP_MJ_QUERY_SECURITY, "QRYSEC",
IRP_MJ_SET_SECURITY, "SETSEC",
IRP_MJ_SYSTEM_CONTROL, "SYSCTL",
IRP_MJ_DEVICE_CHANGE, "DEVCHG",
IRP_MJ_QUERY_QUOTA, "QRYQUO",
IRP_MJ_SET_QUOTA, "SETQUO",
IRP_MJ_POWER, "POWER ",
IRP_MJ_PNP, "PNP ",
IRP_MJ_MAXIMUM_FUNCTION, "MAXFNC"
};
LOCAL ULONG NumIoctl = sizeof(IoctlCodes) / sizeof(Code2Ascii);
LOCAL CHAR UnknownIoctl[80];
/************************************************************************/
/* Debug_OpenWDMDebug */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Allocate resources and init history tables and logs. */
/* */
/* Arguments: */
/* */
/* VOID */
/* */
/* Return Value: */
/* */
/* NTSTATUS */
/* */
/************************************************************************/
NTSTATUS
Debug_OpenWDMDebug(VOID)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
// allocate tables and logs
NtStatus = Debug_SizeIRPHistoryTable(DEFAULT_LOG_SIZE);
if(!NT_SUCCESS(NtStatus))
{
Debug_CloseWDMDebug();
return NtStatus;
}
NtStatus = Debug_SizeDebugPathHist(DEFAULT_LOG_SIZE);
if(!NT_SUCCESS(NtStatus))
{
Debug_CloseWDMDebug();
return NtStatus;
}
NtStatus = Debug_SizeErrorLog(DEFAULT_LOG_SIZE);
if(!NT_SUCCESS(NtStatus))
{
Debug_CloseWDMDebug();
return NtStatus;
}
return NtStatus;
} // Debug_OpenWDMDebug
/************************************************************************/
/* Debug_CloseWDMDebug */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Free up resources used for history tables and logs. */
/* */
/* Arguments: */
/* */
/* VOID */
/* */
/* Return Value: */
/* */
/* VOID */
/* */
/************************************************************************/
VOID
Debug_CloseWDMDebug(VOID)
{
PAGED_CODE();
if(DebugPathHist)
{
DEBUG_MEMFREE(DebugPathHist);
DebugPathHist = NULL;
DebugPathSize = 0L;
}
if(IRPHistoryTable)
{
DEBUG_MEMFREE(IRPHistoryTable);
IRPHistoryTable = NULL;
IRPHistorySize = 0L;
}
if(ErrorLog)
{
DEBUG_MEMFREE(ErrorLog);
ErrorLog = NULL;
ErrorLogSize = 0L;
}
Debug_CheckAllocations();
// see if we have a leak
DEBUG_ASSERT("Memory Allocation Leak", MemAllocCnt == MemFreeCnt);
} // Debug_CloseWDMDebug
/************************************************************************/
/* Debug_SizeIRPHistoryTable */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Allocate IRP history table */
/* */
/* Arguments: */
/* */
/* Size - number of entries in table */
/* */
/* Return Value: */
/* */
/* NTSTATUS */
/* */
/************************************************************************/
NTSTATUS
Debug_SizeIRPHistoryTable(IN ULONG Size)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
// see if they are trying to set the same size
if(Size == IRPHistorySize)
return NtStatus;
// get rid of old history table if we got one
if(IRPHistoryTable)
DEBUG_MEMFREE(IRPHistoryTable);
IRPHistoryTable = NULL;
IRPHistoryIndex = 0L;
IRPHistorySize = 0L;
if(Size != 0L)
{
IRPHistoryTable = DEBUG_MEMALLOC(NonPagedPool, sizeof(IRPHist) * Size);
if(IRPHistoryTable == NULL)
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
else
{
RtlZeroMemory(IRPHistoryTable, sizeof(IRPHist) * Size);
IRPHistorySize = Size;
}
}
return NtStatus;
} // Debug_SizeIRPHistoryTable
/************************************************************************/
/* Debug_SizeDebugPathHist */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Allocate path history */
/* */
/* Arguments: */
/* */
/* Size - number of entries in history */
/* */
/* Return Value: */
/* */
/* NTSTATUS */
/* */
/************************************************************************/
NTSTATUS
Debug_SizeDebugPathHist(IN ULONG Size)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
// see if they are trying to set the same size
if(Size == DebugPathSize)
return NtStatus;
// get rid of old path history if we got one
if(DebugPathHist)
DEBUG_MEMFREE(DebugPathHist);
DebugPathHist = NULL;
DebugPathIndex = 0L;
DebugPathSize = 0L;
if(Size != 0L)
{
DebugPathHist = DEBUG_MEMALLOC(NonPagedPool, sizeof(PATHHist) * Size);
if(DebugPathHist == NULL)
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
else
{
RtlZeroMemory(DebugPathHist, sizeof(PATHHist) * Size);
DebugPathSize = Size;
}
}
return NtStatus;
} // Debug_SizeDebugPathHist
/************************************************************************/
/* Debug_SizeErrorLog */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Allocate error log */
/* */
/* Arguments: */
/* */
/* Size - number of entries in error log */
/* */
/* Return Value: */
/* */
/* NTSTATUS */
/* */
/************************************************************************/
NTSTATUS
Debug_SizeErrorLog(IN ULONG Size)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
// see if they are trying to set the same size
if(Size == ErrorLogSize)
return NtStatus;
// get rid of old error log if we got one
if(ErrorLog)
DEBUG_MEMFREE(ErrorLog);
ErrorLog = NULL;
ErrorLogIndex = 0L;
ErrorLogSize = 0L;
if(Size != 0L)
{
ErrorLog = DEBUG_MEMALLOC(NonPagedPool, sizeof(ERRLog) * Size);
// make sure we actually allocated some memory
if(ErrorLog == NULL)
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
else
{
RtlZeroMemory(ErrorLog, sizeof(ERRLog) * Size);
ErrorLogSize = Size;
}
}
return NtStatus;
} // Debug_SizeErrorLog
/************************************************************************/
/* Debug_LogIrpHist */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Logs IRP history. These are timestamped and put in a */
/* circular buffer for extraction later. */
/* */
/* Arguments: */
/* */
/* DeviceObject - pointer to device object. */
/* Irp - pointer to IRP. */
/* MajorFunction - major function of IRP. */
/* IoBuffer - buffer for data passed in and out of driver. */
/* BufferLen - length of data buffer. */
/* */
/* Return Value: */
/* */
/* VOID */
/* */
/************************************************************************/
VOID
Debug_LogIrpHist(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
IN ULONG MajorFunction, IN PVOID IoBuffer, IN ULONG BufferLen)
{
PIRPHist IrpHist;
// get pointer to current entry in IRP history table
IrpHist = &IRPHistoryTable[IRPHistoryIndex++];
// point to the next entry in the IRP history table
IRPHistoryIndex %= IRPHistorySize;
// get time stamp
IrpHist->TimeStamp = KeQueryPerformanceCounter(NULL);
// save IRP, device object, major function and first 8 bytes of data in buffer
IrpHist->DeviceObject = DeviceObject;
IrpHist->Irp = Irp;
IrpHist->MajorFunction = MajorFunction;
// copy any data if we have it
IrpHist->IrpByteCount = BufferLen;
IrpHist->IrpDataCount = (UCHAR) min(IRP_DATA_SIZE, BufferLen);
if(BufferLen)
*(ULONG *) IrpHist->IrpData = *(ULONG *) IoBuffer;
} // Debug_LogIrpHist
/************************************************************************/
/* Debug_LogPath */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Logs execution path through code. These are timestamped and put */
/* in a circular buffer for extraction later. Kernel print routines */
/* are also called. */
/* */
/* DANGER DANGER Will Robinson - the argument to this must be a */
/* const char pointer, */
/* */
/* Arguments: */
/* */
/* Path - Pointer to const char array that contains description of */
/* of path. */
/* */
/* Return Value: */
/* */
/* VOID */
/* */
/************************************************************************/
VOID
Debug_LogPath(IN CHAR *Path)
{
PPATHHist PHist;
// get pointer to current entry in path history
PHist = &DebugPathHist[DebugPathIndex++];
// point to the next entry in path trace
DebugPathIndex %= DebugPathSize;
// get time stamp
PHist->TimeStamp = KeQueryPerformanceCounter(NULL);
// save path string
PHist->Path = Path;
// now call kernel print routines
DEBUG_TRACE2(("%s\n", Path));
} // Debug_LogPath
/************************************************************************/
/* Debug_LogError */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Logs NTSTATUS type errors. These are timestamped and put in a */
/* circular buffer for extraction later. */
/* */
/* Arguments: */
/* */
/* NtStatus - NTSTATUS error to log. */
/* */
/* Return Value: */
/* */
/* VOID */
/* */
/************************************************************************/
VOID
Debug_LogError(IN NTSTATUS NtStatus)
{
PERRLog ErrLog;
// no error, so don't log
if(NtStatus == STATUS_SUCCESS)
return;
// get pointer to current entry in error log
ErrLog = &ErrorLog[ErrorLogIndex++];
// point to the next entry in error log
ErrorLogIndex %= ErrorLogSize;
// get time stamp
ErrLog->TimeStamp = KeQueryPerformanceCounter(NULL);
// save status
ErrLog->Status = NtStatus;
} // Debug_LogError
/************************************************************************/
/* Debug_Trap */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Trap. Causes execution to halt after logging message. */
/* */
/* Arguments: */
/* */
/* TrapCause - pointer to char array that contains description */
/* of cause of trap. */
/* */
/* Return Value: */
/* */
/* VOID */
/* */
/************************************************************************/
VOID
Debug_Trap(IN PCHAR TrapCause)
{
// log the path
DEBUG_LOG_PATH("Debug_Trap: ");
DEBUG_LOG_PATH(TrapCause);
// kernel debugger print
DEBUG_TRACE3(("Debug_Trap: "));
DEBUG_TRACE3(("%s\n",TrapCause));
// halt execution
DEBUG_TRAP();
} // Debug_TRAP
/************************************************************************/
/* Debug_Assert */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Assertion routine. */
/* */
/* Arguments: */
/* */
/* This should not be called directly. Use DEBUG_ASSERT macro. */
/* */
/* Return Value: */
/* */
/* VOID */
/* */
/************************************************************************/
VOID
Debug_Assert(IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber,
IN PCHAR Message)
{
#if DBG
// just call the assert routine
RtlAssert(FailedAssertion, FileName, LineNumber, Message);
#else
DEBUG_TRAP();
#endif
} // Debug_Assert
/************************************************************************/
/* Debug_ExtractAttachedDevices */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Formats and places attached device info into a buffer. */
/* */
/* Arguments: */
/* */
/* DriverObject - pointer to driver object. */
/* */
/* Buffer - pointer to buffer to fill with IRP history. */
/* BuffSize - size of Buffer. */
/* */
/* Return Value: */
/* */
/* ULONG - number of bytes written in buffer. */
/* */
/************************************************************************/
ULONG
Debug_ExtractAttachedDevices(IN PDRIVER_OBJECT DriverObject, OUT PCHAR Buffer, IN ULONG BuffSize)
{
PCHAR StrBuff;
PDEVICE_EXTENSION DeviceExtension;
PDEVICE_OBJECT DeviceObject;
BOOLEAN Dev = FALSE;
PAGED_CODE();
// make sure we have a pointer and a number of bytes
if(Buffer == NULL || BuffSize == 0L)
return 0L;
// allocate buffer for formatting strings
StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
if(StrBuff == NULL)
return 0L;
// title
sprintf(StrBuff, "\n\n\nAttached Devices\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
// columns
sprintf(StrBuff, "Device Device Obj IRPs Complete Byte Count\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
// get the first device object
DeviceObject = DriverObject->DeviceObject;
// march through linked list of devices
while(DeviceObject)
{
// found at least one device
Dev = TRUE;
// Get a pointer to the device extension
DeviceExtension = DeviceObject->DeviceExtension;
sprintf(StrBuff, "%-17s 0x%p 0x%08X 0x%08X%08X\n", &DeviceExtension->LinkName[12],
DeviceObject, DeviceExtension->IRPCount,
DeviceExtension->ByteCount.HighPart,
DeviceExtension->ByteCount.LowPart);
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
DeviceObject = DeviceObject->NextDevice;
}
// if we don't have any devices, say so, but this should never happen (I think)
if(!Dev)
{
sprintf(StrBuff, "No attached devices\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
DEBUG_MEMFREE(StrBuff);
return strlen(Buffer);
} // Debug_ExtractAttachedDevices
/************************************************************************/
/* Debug_GetDriverInfo */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Formats and places driver info into a buffer. */
/* */
/* Arguments: */
/* */
/* Buffer - pointer to buffer to fill with IRP history. */
/* BuffSize - size of Buffer. */
/* */
/* Return Value: */
/* */
/* ULONG - number of bytes written in buffer. */
/* */
/************************************************************************/
ULONG
Debug_GetDriverInfo(OUT PCHAR Buffer, IN ULONG BuffSize)
{
PCHAR StrBuff;
PAGED_CODE();
// make sure we have a pointer and a number of bytes
if(Buffer == NULL || BuffSize == 0L)
return 0L;
// allocate buffer for formatting strings
StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
if(StrBuff == NULL)
return 0L;
// driver name and version
sprintf(StrBuff, "\n\n\nDriver: %s\n\nVersion: %s\n\n", DriverName, DriverVersion);
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
DEBUG_MEMFREE(StrBuff);
return strlen(Buffer);
} // Debug_GetDriverInfo
/************************************************************************/
/* Debug_ExtractIRPHist */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Formats and places IRP history info into a buffer. */
/* */
/* Arguments: */
/* */
/* Buffer - pointer to buffer to fill with IRP history. */
/* BuffSize - size of Buffer. */
/* */
/* Return Value: */
/* */
/* ULONG - number of bytes written in buffer. */
/* */
/************************************************************************/
ULONG
Debug_ExtractIRPHist(OUT PCHAR Buffer, IN ULONG BuffSize)
{
ULONG Index, Size;
PIRPHist IrpHist;
PCHAR StrBuff;
BOOLEAN Hist = FALSE;
PAGED_CODE();
// make sure we have a pointer and a number of bytes
if(Buffer == NULL || BuffSize == 0L)
return 0L;
// allocate buffer for formatting strings
StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
if(StrBuff == NULL)
return 0L;
// title
sprintf(StrBuff, "\n\n\nIRP History\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
// see if error log is on
if(IRPHistorySize == 0L)
{
sprintf(StrBuff, "IRP History is disabled\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
else
{
// columns
sprintf(StrBuff, "Time Stamp Device Obj IRP Func Byte Count Data\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
Index = IRPHistoryIndex;
for(Size = 0; Size < IRPHistorySize; Size++)
{
// get pointer to current entry in IRP history table
IrpHist = &IRPHistoryTable[Index++];
// parse timestamp and IRP history and write to buffer
if(IrpHist->TimeStamp.LowPart)
{
UCHAR DataCount;
CHAR DataBuff[10];
// we have at least one entry
Hist = TRUE;
sprintf(StrBuff, "0x%08X%08X 0x%p 0x%p %s 0x%08X ",
IrpHist->TimeStamp.HighPart, IrpHist->TimeStamp.LowPart,
IrpHist->DeviceObject, IrpHist->Irp,
Debug_TranslateIoctl(IrpHist->MajorFunction),
IrpHist->IrpByteCount);
// add data bytes if we got them
for(DataCount = 0; DataCount < IrpHist->IrpDataCount; DataCount++)
{
sprintf(DataBuff, "%02x ", IrpHist->IrpData[DataCount]);
strcat(StrBuff, DataBuff);
}
sprintf(DataBuff, "\n");
strcat(StrBuff, DataBuff);
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
// point to the next entry in the IRP history table
Index %= IRPHistorySize;
}
// if we don't have history, say so, but this should never happen (I think)
if(!Hist)
{
sprintf(StrBuff, "No IRP history\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
}
DEBUG_MEMFREE(StrBuff);
return strlen(Buffer);
} // Debug_ExtractIRPHist
/************************************************************************/
/* Debug_ExtractPathHist */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Formats and places path history info into buffer. */
/* */
/* Arguments: */
/* */
/* Buffer - pointer to buffer to fill with path history. */
/* BuffSize - size of Buffer. */
/* */
/* Return Value: */
/* */
/* ULONG - number of bytes written in buffer. */
/* */
/************************************************************************/
ULONG
Debug_ExtractPathHist(OUT PCHAR Buffer, IN ULONG BuffSize)
{
ULONG Index, Size;
PPATHHist PHist;
PCHAR StrBuff;
BOOLEAN Hist = FALSE;
PAGED_CODE();
// make sure we have a pointer and a number of bytes
if(Buffer == NULL || BuffSize == 0L)
return 0L;
// allocate buffer for formatting strings
StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
if(StrBuff == NULL)
return 0L;
// title
sprintf(StrBuff, "\n\n\nExecution Path History\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
// see if path history is on
if(DebugPathSize == 0L)
{
sprintf(StrBuff, "Path History is disabled\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
else
{
// columns
sprintf(StrBuff, "Time Stamp Path\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
Index = DebugPathIndex;
for(Size = 0; Size < DebugPathSize; Size++)
{
// get pointer to current entry in path history
PHist = &DebugPathHist[Index++];
// parse timestamp and path and write to buffer. Check for NULL entries
if(PHist->TimeStamp.LowPart)
{
// at least we have one entry
Hist = TRUE;
sprintf(StrBuff, "0x%08X%08X %s\n", PHist->TimeStamp.HighPart,
PHist->TimeStamp.LowPart, PHist->Path);
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
// point to the next entry in path trace
Index %= DebugPathSize;
}
// if we don't have history, say so, but this should never happen (I think)
if(!Hist)
{
sprintf(StrBuff, "No execution path history\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
}
DEBUG_MEMFREE(StrBuff);
return strlen(Buffer);
} // Debug_ExtractPathHist
/************************************************************************/
/* Debug_ExtractErrorLog */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Formats and places error log info into buffer. */
/* */
/* Arguments: */
/* */
/* Buffer - pointer to buffer to fill with IRP history. */
/* BuffSize - size of Buffer. */
/* */
/* Return Value: */
/* */
/* ULONG - number of bytes written in buffer. */
/* */
/************************************************************************/
ULONG
Debug_ExtractErrorLog(OUT PCHAR Buffer, IN ULONG BuffSize)
{
ULONG Index, Size;
PERRLog ErrLog;
PCHAR StrBuff;
BOOLEAN Errors = FALSE;
PAGED_CODE();
// make sure we have a pointer and a number of bytes
if(Buffer == NULL || BuffSize == 0L)
return 0L;
// allocate buffer for formatting strings
StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
if(StrBuff == NULL)
return 0L;
// title
sprintf(StrBuff, "\n\n\nError Log\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
// see if error log is on
if(ErrorLogSize == 0L)
{
sprintf(StrBuff, "Error Log is disabled\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
else
{
// columns
sprintf(StrBuff, "Time Stamp Error\n\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
Index = ErrorLogIndex;
for(Size = 0; Size < ErrorLogSize; Size++)
{
// get pointer to current entry in error log
ErrLog = &ErrorLog[Index++];
// parse timestamp and error and write to buffer
if(ErrLog->TimeStamp.LowPart)
{
// we have at least one error
Errors = TRUE;
sprintf(StrBuff, "0x%08X%08X %s\n", ErrLog->TimeStamp.HighPart,
ErrLog->TimeStamp.LowPart, Debug_TranslateStatus(ErrLog->Status));
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
// point at next entry
Index %= ErrorLogSize;
}
// if we don't have errors, say so
if(!Errors)
{
sprintf(StrBuff, "No errors in log\n");
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
}
}
DEBUG_MEMFREE(StrBuff);
return strlen(Buffer);
} // Debug_ExtractErrorLog
/************************************************************************/
/* Debug_DumpDriverLog */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Dumps all history and logging to buffer. */
/* */
/* Arguments: */
/* */
/* DeviceObject - pointer to device object. */
/* */
/* Buffer - pointer to buffer to fill with IRP history. */
/* BuffSize - size of pBuffer. */
/* */
/* Return Value: */
/* */
/* ULONG - number of bytes written in buffer. */
/* */
/************************************************************************/
ULONG
Debug_DumpDriverLog(IN PDEVICE_OBJECT DeviceObject, OUT PCHAR Buffer, IN ULONG BuffSize)
{
PCHAR StrBuff;
PAGED_CODE();
// make sure we have a pointer and a number of bytes
if(Buffer == NULL || BuffSize == 0L)
return 0L;
// allocate buffer for formatting strings
StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
if(StrBuff == NULL)
return 0L;
// driver name and version, memory allocated
sprintf(StrBuff, "\n\n\nDriver: %s\n\nVersion: %s\n\nMemory Allocated: 0x%08X\nMaximum Memory Allocated: 0x%08X\n",
DriverName, DriverVersion, MemoryAllocated, MaxMemAllocated);
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
// memory allocation stats
sprintf(StrBuff, "MemAlloc Count: 0x%08X\nMemFree Count: 0x%08X\nMemAlloc Fail Count: 0x%08X\nMemFree Fail Count: 0x%08X\n",
MemAllocCnt, MemFreeCnt, MemAllocFailCnt, MemFreeFailCnt);
// make sure it fits in buffer
if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
strcat(Buffer, StrBuff);
// get attached devices
Debug_ExtractAttachedDevices(DeviceObject->DriverObject, Buffer, BuffSize);
// get IRP history
Debug_ExtractIRPHist(Buffer, BuffSize);
// get execution path history
Debug_ExtractPathHist(Buffer, BuffSize);
// get error log
Debug_ExtractErrorLog(Buffer, BuffSize);
DEBUG_MEMFREE(StrBuff);
return strlen(Buffer);
} // Debug_DumpDriverLog
/************************************************************************/
/* Debug_TranslateStatus */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Translates NTSTATUS into ASCII string. */
/* */
/* Arguments: */
/* */
/* NtStatus - NTSTATUS code. */
/* */
/* Return Value: */
/* */
/* PCHAR - pointer to error string. */
/* */
/************************************************************************/
PCHAR
Debug_TranslateStatus(IN NTSTATUS NtStatus)
{
ULONG Err;
PAGED_CODE();
for(Err = 0; Err < NumNTErrs; Err++)
{
if(NtStatus == NTErrors[Err].Code)
return NTErrors[Err].Str;
}
// fell through, not an error we handle
sprintf(UnknownStatus, "Unknown error 0x%08X", NtStatus);
return UnknownStatus;
} // Debug_TranslateStatus
/************************************************************************/
/* Debug_TranslateIoctl */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Translates IOCTL into ASCII string. */
/* */
/* Arguments: */
/* */
/* Ioctl - ioctl code. */
/* */
/* Return Value: */
/* */
/* PCHAR - pointer to error string. */
/* */
/************************************************************************/
PCHAR
Debug_TranslateIoctl(IN LONG Ioctl)
{
ULONG Index;
PAGED_CODE();
// it's kind of repetitive to search at this point, but just in case
// they change the actual IOCTLs we will be covered
for(Index = 0; Index < NumIoctl; Index++)
{
if(Ioctl == IoctlCodes[Index].Code)
return IoctlCodes[Index].Str;
}
// fell through, not an error we handle
sprintf(UnknownIoctl, "0x%04X", Ioctl);
return UnknownIoctl;
} // Debug_TranslateIoctl
#endif // PROFILING_ENABLED
VOID
Debug_CheckAllocations(VOID)
{
DEBUG_TRACE1(("MemoryAllocated = 0x%08X\n", MemoryAllocated));
DEBUG_TRACE1(("MemAllocCnt = 0x%08X MemFreeCnt = 0x%08X\n",
MemAllocCnt, MemFreeCnt));
} // Debug_CheckAllocations
/************************************************************************/
/* Debug_MemAlloc */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Allocates block of memory. Stores length of block and a */
/* signature ULONG for keeping track of amount of memory allocated */
/* and checking for bogus calls to Debug_MemFree. The signature */
/* can also be used to determine if someone has written past the */
/* end of the block. */
/* */
/* Arguments: */
/* */
/* PoolType - Pool to allocate memory from. */
/* NumberOfBytes - Number of bytes to allocate. */
/* */
/* Return Value: */
/* */
/* PVOID - pointer to allocated memory. */
/* */
/************************************************************************/
PVOID
Debug_MemAlloc(IN POOL_TYPE PoolType, IN ULONG NumberOfBytes)
{
#ifdef _WIN64
return ExAllocatePool(PoolType, NumberOfBytes);
#else
PULONG Mem;
// allocate memory plus a little extra for our own use
Mem = ExAllocatePool(PoolType, NumberOfBytes + (2 * sizeof(ULONG)));
// see if we actually allocated any memory
if(Mem)
{
// keep track of how much we allocated
MemoryAllocated += NumberOfBytes;
// see if we have a new maximum
if(MemoryAllocated > MaxMemAllocated)
MaxMemAllocated = MemoryAllocated;
// store number of bytes allocated at start of memory allocated
*Mem++ = NumberOfBytes;
// now we are pointing at the memory allocated for caller
// put signature word at end
// get new pointer that points to end of buffer - ULONG
Mem = (PULONG) (((PUCHAR) Mem) + NumberOfBytes);
// write signature
*Mem = MEM_ALLOC_SIGNATURE;
// get back pointer to return to caller
Mem = (PULONG) (((PUCHAR) Mem) - NumberOfBytes);
// log stats
MemAllocCnt++;
}
else
// failed, log stats
MemAllocFailCnt++;
return (PVOID) Mem;
#endif
} // Debug_MemAlloc
/************************************************************************/
/* Debug_MemFree */
/************************************************************************/
/* */
/* Routine Description: */
/* */
/* Frees memory allocated in call to Debug_MemAlloc. Checks for */
/* signature ULONG at the end of allocated memory to make sure */
/* this is a valid block to free. */
/* */
/* Arguments: */
/* */
/* Mem - pointer to allocated block to free */
/* */
/* Return Value: */
/* */
/* VOID. Traps if it is an invalid block. */
/* */
/************************************************************************/
VOID
Debug_MemFree(IN PVOID Mem)
{
#ifdef _WIN64
ExFreePool(Mem);
#else
PULONG Tmp = (PULONG) Mem;
ULONG BuffSize;
// point at size ULONG at start of buffer, and address to free
Tmp--;
// get the size of memory allocated by caller
BuffSize = *Tmp;
// point at signature and make sure it's O.K.
((PCHAR) Mem) += BuffSize;
if(*((PULONG) Mem) == MEM_ALLOC_SIGNATURE)
{
// let's go ahead and get rid of signature in case we get called
// with this pointer again and memory is still paged in
*((PULONG) Mem) = MEM_FREE_SIGNATURE;
// adjust amount of memory allocated
MemoryAllocated -= BuffSize;
// free real pointer
ExFreePool(Tmp);
// log stats
MemFreeCnt++;
}
else
{
// not a real allocated block, or someone wrote past the end
MemFreeFailCnt++;
DEBUG_TRAP();
}
#endif
} // Debug_MemFree