/*++ Copyright (c) 1996 Microsoft Corporation Module Name: debug.c Abstract Debug/performance routines Author: Ervin P. Environment: Kernel mode only Revision History: --*/ #include "pch.h" #if DBG // can poke this in the debugger to trap for warnings BOOLEAN dbgTrapOnWarn = FALSE; BOOLEAN dbgTrapOnSS = FALSE; BOOLEAN dbgVerbose = FALSE; BOOLEAN dbgInfo = TRUE; BOOLEAN dbgSkipSecurity = FALSE; BOOLEAN dbgTrapOnHiccup = FALSE; ULONG dbgLastEntry = 0; ULONG dbgInHidclass = 0; VOID DbgCommonEntryExit(BOOLEAN isEntering) { if (isEntering){ dbgInHidclass++; #ifdef _X86_ _asm nop _asm mov eax, [ebp+4] /* <- set breakpt here */ _asm mov dbgLastEntry, eax #endif } else { dbgInHidclass--; } } VOID InitFdoExtDebugInfo(PHIDCLASS_DEVICE_EXTENSION hidclassExt) { FDO_EXTENSION *fdoExt = &hidclassExt->fdoExt; NTSTATUS status; ULONG actualLen; status = IoGetDeviceProperty( hidclassExt->hidExt.PhysicalDeviceObject, DevicePropertyDriverKeyName, sizeof(fdoExt->dbgDriverKeyName), fdoExt->dbgDriverKeyName, &actualLen); if (!NT_SUCCESS(status)) { // // We couldn't get the driver key name. This will happen during // textmode setup on NT, for example, when we're loaded as part of // bootstrapping the system (long before the device installer/class // installer have run). // // Simply initialize the driver key name field to an empty string. // *(fdoExt->dbgDriverKeyName) = L'\0'; } } ULONG dbgMinInterruptDelta = 0x0fffffff; ULONG dbgMaxInterruptsPerSecond = 0; ULONG dbgShortestInt = 0x0fffffff; ULONG dbgLongestInt = 0; LARGE_INTEGER dbgLastIntStart = {0}; ULONG dbgAveIntTime = 0; VOID DbgLogIntStart() { static ULONG dbgInterruptsThisSecond = 0; static ULONG dbgThisSecondStartTime = 0; LARGE_INTEGER timeNow; ULONG lastTimeMilliSec, timeNowMilliSec; KeQuerySystemTime(&timeNow); // convert from usec to millisec timeNowMilliSec = timeNow.LowPart/10000; lastTimeMilliSec = dbgLastIntStart.LowPart/10000; if (timeNow.HighPart == dbgLastIntStart.HighPart){ ULONG delta = timeNowMilliSec - lastTimeMilliSec; if (delta < dbgMinInterruptDelta){ dbgMinInterruptDelta = delta; } if (timeNowMilliSec - dbgThisSecondStartTime < 1000){ dbgInterruptsThisSecond++; if (dbgInterruptsThisSecond > dbgMaxInterruptsPerSecond){ dbgMaxInterruptsPerSecond = dbgInterruptsThisSecond; } } else { dbgThisSecondStartTime = timeNowMilliSec; dbgInterruptsThisSecond = 0; } } else { // this case is harder so skip it dbgThisSecondStartTime = timeNowMilliSec; dbgInterruptsThisSecond = 0; } dbgLastIntStart = timeNow; } VOID DbgLogIntEnd() { LARGE_INTEGER timeNow; KeQuerySystemTime(&timeNow); if (timeNow.HighPart == dbgLastIntStart.HighPart){ ULONG timeNowMilliSec = timeNow.LowPart/10000; ULONG intStartTimeMilliSec = dbgLastIntStart.LowPart/10000; ULONG delta = timeNowMilliSec - intStartTimeMilliSec; if (delta < dbgShortestInt){ dbgShortestInt = delta; } else if (delta > dbgLongestInt){ dbgLongestInt = delta; } { static ULONG dbgIntCount = 0; static ULONG dbgTimeLast1000Ints = 0; if (dbgIntCount < 1000){ dbgIntCount++; dbgTimeLast1000Ints += delta; } else { dbgAveIntTime = dbgTimeLast1000Ints/1000; dbgTimeLast1000Ints = 0; dbgIntCount = 0; } } } else { // This is harder so we just skip it } } NTSTATUS DbgTestGetDeviceStringCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { ASSERT(NT_SUCCESS(Irp->IoStatus.Status)); ExFreePool(Irp->UserBuffer); IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } VOID DbgTestGetDeviceString(PFDO_EXTENSION fdoExt) { PIRP Irp; const ULONG inputLen = 200; Irp = IoAllocateIrp(fdoExt->fdo->StackSize, FALSE); if (Irp){ Irp->UserBuffer = ALLOCATEPOOL(NonPagedPool, inputLen); if (Irp->UserBuffer){ ULONG stringId = HID_STRING_ID_IMANUFACTURER; ULONG languageId = 0x0409; // English PIO_STACK_LOCATION currentIrpSp = IoGetCurrentIrpStackLocation(Irp); Irp->MdlAddress->MappedSystemVa = Irp->UserBuffer; Irp->MdlAddress->MdlFlags |= MDL_SOURCE_IS_NONPAGED_POOL; Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; currentIrpSp->Parameters.DeviceIoControl.OutputBufferLength = inputLen; IoSetCompletionRoutine( Irp, DbgTestGetDeviceStringCompletion, (PVOID)NULL, TRUE, TRUE, TRUE); HidpGetDeviceString(fdoExt, Irp, stringId, languageId); } } } VOID DbgTestGetIndexedString(PFDO_EXTENSION fdoExt) { PIRP Irp; const ULONG inputLen = 200; Irp = IoAllocateIrp(fdoExt->fdo->StackSize, FALSE); if (Irp){ Irp->UserBuffer = ALLOCATEPOOL(NonPagedPool, inputLen); if (Irp->UserBuffer){ ULONG stringIndex = 1; // ??? ULONG languageId = 0x0409; // English PIO_STACK_LOCATION currentIrpSp = IoGetCurrentIrpStackLocation(Irp); Irp->MdlAddress->MappedSystemVa = Irp->UserBuffer; Irp->MdlAddress->MdlFlags |= MDL_SOURCE_IS_NONPAGED_POOL; currentIrpSp->Parameters.DeviceIoControl.InputBufferLength = inputLen; IoSetCompletionRoutine( Irp, DbgTestGetDeviceStringCompletion, (PVOID)NULL, TRUE, TRUE, TRUE); HidpGetIndexedString(fdoExt, Irp, stringIndex, languageId); } } } #define DBG_MAX_DEVOBJ_RECORDS 100 dbgDevObjRecord dbgDevObjs[DBG_MAX_DEVOBJ_RECORDS] = {0}; VOID DbgRecordDevObj(PDEVICE_OBJECT devObj, PCHAR str) { ULONG i; for (i = 0; i < DBG_MAX_DEVOBJ_RECORDS; i++){ if (!ISPTR(dbgDevObjs[i].devObj)){ break; } else if (dbgDevObjs[i].devObj == devObj){ // already there break; } } if ((i < DBG_MAX_DEVOBJ_RECORDS) && !dbgDevObjs[i].devObj){ ULONG j; dbgDevObjs[i].devObj = devObj; for (j = 0; str[j] && (j < dbgDevObjRecord_STRINGSIZE); j++){ dbgDevObjs[i].str[j] = str[j]; } } } #define DBG_MAX_FEATURE_RECORDS 0x1000 dbgFeatureRecord dbgFeatures[DBG_MAX_FEATURE_RECORDS] = {0}; ULONG dbgFeatureFirstFreeIndex = 0; VOID DbgRecordReport(ULONG reportId, ULONG controlCode, BOOLEAN isComplete) { ULONG typeId; switch (controlCode){ case IOCTL_HID_GET_FEATURE: typeId = (ULONG)'fteG'; break; case IOCTL_HID_SET_FEATURE: typeId = (ULONG)'fteS'; break; case IOCTL_HID_GET_INPUT_REPORT: typeId = (ULONG)'iteG'; break; case IOCTL_HID_SET_OUTPUT_REPORT: typeId = (ULONG)'oteS'; break; default: typeId = (ULONG)'xxxx'; TRAP; break; } if (isComplete){ LONG i; // step back to find the report that got completed // assumes no overlapped calls to same feature ASSERT(dbgFeatureFirstFreeIndex > 0); i = dbgFeatureFirstFreeIndex-1; while ((i >= 0) && ((dbgFeatures[i].reportId != reportId) || (dbgFeatures[i].type != typeId) || dbgFeatures[i].completed)){ i--; } ASSERT(i >= 0); if (i >= 0){ dbgFeatures[i].completed = 1; } } else { if (dbgFeatureFirstFreeIndex >= DBG_MAX_FEATURE_RECORDS){ RtlZeroMemory(dbgFeatures, sizeof(dbgFeatures)); dbgFeatureFirstFreeIndex = 0; } dbgFeatures[dbgFeatureFirstFreeIndex].marker = (ULONG)'taeF'; dbgFeatures[dbgFeatureFirstFreeIndex].reportId = reportId; dbgFeatures[dbgFeatureFirstFreeIndex].type = typeId; dbgFeatures[dbgFeatureFirstFreeIndex].completed = 0; dbgFeatureFirstFreeIndex++; } } #define DBG_MAX_READ_RECORDS 0x1000 dbgReadRecord dbgReads[DBG_MAX_READ_RECORDS] = {0}; VOID DbgRecordRead(PIRP irp, ULONG length, ULONG reportId, ULONG completed) { LONG i; for (i = 0; (i < DBG_MAX_READ_RECORDS) && dbgReads[i].irpPtr && ((dbgReads[i].irpPtr != (ULONG_PTR)irp) || dbgReads[i].completed); i++){ } if (i < DBG_MAX_READ_RECORDS){ if (dbgReads[i].irpPtr){ ASSERT(dbgReads[i].irpPtr == (ULONG_PTR)irp); ASSERT(!dbgReads[i].completed); ASSERT(completed); dbgReads[i].length = length; dbgReads[i].reportId = reportId; dbgReads[i].completed = completed; } else { dbgReads[i].irpPtr = (ULONG_PTR)irp; dbgReads[i].length = length; dbgReads[i].reportId = reportId; dbgReads[i].completed = completed; } } } VOID DbgLogIrpMajor(ULONG_PTR irpPtr, ULONG majorFunc, ULONG isForCollectionPdo, ULONG isComplete, ULONG status) { if (dbgVerbose){ char *funcName; switch (majorFunc){ #undef MAKE_CASE #define MAKE_CASE(fnc) case fnc: funcName = #fnc; break; MAKE_CASE(IRP_MJ_CREATE) MAKE_CASE(IRP_MJ_CREATE_NAMED_PIPE) MAKE_CASE(IRP_MJ_CLOSE) MAKE_CASE(IRP_MJ_READ) MAKE_CASE(IRP_MJ_WRITE) MAKE_CASE(IRP_MJ_QUERY_INFORMATION) MAKE_CASE(IRP_MJ_SET_INFORMATION) MAKE_CASE(IRP_MJ_QUERY_EA) MAKE_CASE(IRP_MJ_SET_EA) MAKE_CASE(IRP_MJ_FLUSH_BUFFERS) MAKE_CASE(IRP_MJ_QUERY_VOLUME_INFORMATION) MAKE_CASE(IRP_MJ_SET_VOLUME_INFORMATION) MAKE_CASE(IRP_MJ_DIRECTORY_CONTROL) MAKE_CASE(IRP_MJ_FILE_SYSTEM_CONTROL) MAKE_CASE(IRP_MJ_DEVICE_CONTROL) MAKE_CASE(IRP_MJ_INTERNAL_DEVICE_CONTROL) MAKE_CASE(IRP_MJ_SHUTDOWN) MAKE_CASE(IRP_MJ_LOCK_CONTROL) MAKE_CASE(IRP_MJ_CLEANUP) MAKE_CASE(IRP_MJ_CREATE_MAILSLOT) MAKE_CASE(IRP_MJ_QUERY_SECURITY) MAKE_CASE(IRP_MJ_SET_SECURITY) MAKE_CASE(IRP_MJ_POWER) MAKE_CASE(IRP_MJ_SYSTEM_CONTROL) MAKE_CASE(IRP_MJ_DEVICE_CHANGE) MAKE_CASE(IRP_MJ_QUERY_QUOTA) MAKE_CASE(IRP_MJ_SET_QUOTA) MAKE_CASE(IRP_MJ_PNP) default: funcName = NULL; break; } if (isComplete){ if (funcName){ DBGOUT(("< %s for %s status=%xh (irp=%ph)", funcName, isForCollectionPdo ? "collection" : "device", status, irpPtr)); } else { DBGOUT(("< ???? for %s status=%xh (irp=%ph)", majorFunc, isForCollectionPdo ? "collection" : "device", status, irpPtr)); } } else { if (funcName){ DBGOUT(("> %s (irp=%xh)", funcName, irpPtr)); } else { DBGOUT(("> ???? (irp=%xh)", majorFunc, irpPtr)); } } } } #define DBG_MAX_PNP_IRP_RECORDS 0x1000 dbgPnPIrpRecord dbgPnPIrps[DBG_MAX_PNP_IRP_RECORDS] = {0}; VOID DbgLogPnpIrp(ULONG_PTR irpPtr, ULONG minorFunc, ULONG isForCollectionPdo, ULONG isComplete, ULONG status) { char *funcName; ULONG funcShortName; int i; switch (minorFunc){ #undef MAKE_CASE #define MAKE_CASE(fnc) case fnc: funcName = #fnc; funcShortName = *(ULONG *)(funcName+7); break; MAKE_CASE(IRP_MN_START_DEVICE) MAKE_CASE(IRP_MN_QUERY_REMOVE_DEVICE) MAKE_CASE(IRP_MN_REMOVE_DEVICE) MAKE_CASE(IRP_MN_CANCEL_REMOVE_DEVICE) MAKE_CASE(IRP_MN_STOP_DEVICE) MAKE_CASE(IRP_MN_QUERY_STOP_DEVICE) MAKE_CASE(IRP_MN_CANCEL_STOP_DEVICE) MAKE_CASE(IRP_MN_QUERY_DEVICE_RELATIONS) MAKE_CASE(IRP_MN_QUERY_INTERFACE) MAKE_CASE(IRP_MN_QUERY_CAPABILITIES) MAKE_CASE(IRP_MN_QUERY_RESOURCES) MAKE_CASE(IRP_MN_QUERY_RESOURCE_REQUIREMENTS) MAKE_CASE(IRP_MN_QUERY_DEVICE_TEXT) MAKE_CASE(IRP_MN_READ_CONFIG) MAKE_CASE(IRP_MN_WRITE_CONFIG) MAKE_CASE(IRP_MN_EJECT) MAKE_CASE(IRP_MN_SET_LOCK) MAKE_CASE(IRP_MN_QUERY_ID) MAKE_CASE(IRP_MN_QUERY_PNP_DEVICE_STATE) MAKE_CASE(IRP_MN_QUERY_BUS_INFORMATION) MAKE_CASE(IRP_MN_DEVICE_USAGE_NOTIFICATION) MAKE_CASE(IRP_MN_SURPRISE_REMOVAL) #ifndef IRP_MN_QUERY_LEGACY_BUS_INFORMATION #define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18 #endif // IRP_MN_QUERY_LEGACY_BUS_INFORMATION MAKE_CASE(IRP_MN_QUERY_LEGACY_BUS_INFORMATION) default: funcName = NULL; funcShortName = (ULONG)'\?\?\?\?'; break; } if (dbgVerbose){ if (isComplete){ if (funcName){ DBGOUT((" < %s for %s status=%xh (irp=%ph)", funcName, isForCollectionPdo ? "collection" : "device", status, irpPtr)); } else { DBGOUT((" < ?? for %s status=%xh (irp=%ph)", minorFunc, isForCollectionPdo ? "collection" : "device", status, irpPtr)); } } else { if (funcName){ DBGOUT((" > %s for %s (irp=%xh)", funcName, isForCollectionPdo ? "collection" : "device", irpPtr)); } else { DBGOUT((" > ?? for %s (irp=%xh)", minorFunc, isForCollectionPdo ? "collection" : "device", irpPtr)); } } } if (isComplete){ for (i = 0; (i < DBG_MAX_PNP_IRP_RECORDS) && dbgPnPIrps[i].irpPtr; i++){ if ((dbgPnPIrps[i].irpPtr == irpPtr) && ((dbgPnPIrps[i].status == 0xFFFFFFFF) || (dbgPnPIrps[i].status == STATUS_PENDING))){ dbgPnPIrps[i].status = status; break; } } } else { for (i = 0; i < DBG_MAX_PNP_IRP_RECORDS; i++){ if (!dbgPnPIrps[i].irpPtr){ dbgPnPIrps[i].irpPtr = irpPtr; dbgPnPIrps[i].func = funcShortName; dbgPnPIrps[i].isForCollectionPdo = isForCollectionPdo; dbgPnPIrps[i].status = 0xFFFFFFFF; break; } } } } VOID DbgLogPowerIrp(PVOID devExt, UCHAR minorFunc, ULONG isClientPdo, ULONG isComplete, PCHAR type, ULONG powerState, ULONG status) { char *funcName; switch (minorFunc){ #undef MAKE_CASE #define MAKE_CASE(fnc) case fnc: funcName = #fnc; break; MAKE_CASE(IRP_MN_WAIT_WAKE) MAKE_CASE(IRP_MN_POWER_SEQUENCE) MAKE_CASE(IRP_MN_SET_POWER) MAKE_CASE(IRP_MN_QUERY_POWER) default: funcName = "????"; break; } if (dbgVerbose){ if (isComplete){ DBGOUT((" < %s for %s(ext=%ph) status=%xh ", funcName, isClientPdo ? "collection" : "device", devExt, status)); } else if (minorFunc == IRP_MN_SET_POWER){ DBGOUT((" > %s for %s(ext=%ph) type=%s, powerState=%ph", funcName, isClientPdo ? "collection" : "device", devExt, type, powerState)); } else { DBGOUT((" > %s for %s(ext=%ph) ", funcName, isClientPdo ? "collection" : "device", devExt)); } } } #define DBG_MAX_REPORT_RECORDS 0x100 dbgReportRecord dbgReportRecords[DBG_MAX_REPORT_RECORDS] = { 0 }; ULONG dbgCurrentReportRecord = 0; VOID DbgLogReport(ULONG collectionNumber, ULONG numRecipients, ULONG numPending, ULONG numFailed, PUCHAR report, ULONG reportLength) { ASSERT(dbgCurrentReportRecord <= DBG_MAX_REPORT_RECORDS); if (dbgCurrentReportRecord == DBG_MAX_REPORT_RECORDS){ RtlZeroMemory(dbgReportRecords, DBG_MAX_REPORT_RECORDS*sizeof(dbgReportRecord)); dbgCurrentReportRecord = 0; } dbgReportRecords[dbgCurrentReportRecord].collectionNumber = (UCHAR)collectionNumber; dbgReportRecords[dbgCurrentReportRecord].numRecipients = (UCHAR)numRecipients; if (reportLength > sizeof(dbgReportRecords[dbgCurrentReportRecord].reportBytes)){ reportLength = sizeof(dbgReportRecords[dbgCurrentReportRecord].reportBytes); } RtlCopyMemory((PUCHAR)dbgReportRecords[dbgCurrentReportRecord].reportBytes, report, reportLength); dbgCurrentReportRecord++; if (dbgVerbose){ ULONG i; DBGOUT(("Report (cltn #%d, %d recipients; %d pending, %d failed):", collectionNumber, numRecipients, numPending, numFailed)); DbgPrint("'\t report bytes: \t"); for (i = 0; i < reportLength; i++){ DbgPrint("%02x ", report[i]); } DbgPrint("\n"); } } VOID DbgLogIoctl(ULONG_PTR fdo, ULONG ioControlCode, ULONG status) { if (dbgVerbose){ PCHAR ioctlStr; switch (ioControlCode){ #undef MAKE_CASE #define MAKE_CASE(ioctl) case ioctl: ioctlStr = #ioctl; break; MAKE_CASE(IOCTL_HID_GET_DRIVER_CONFIG) MAKE_CASE(IOCTL_HID_SET_DRIVER_CONFIG) MAKE_CASE(IOCTL_HID_GET_POLL_FREQUENCY_MSEC) MAKE_CASE(IOCTL_HID_SET_POLL_FREQUENCY_MSEC) MAKE_CASE(IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS) MAKE_CASE(IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS) MAKE_CASE(IOCTL_HID_GET_COLLECTION_INFORMATION) MAKE_CASE(IOCTL_HID_GET_COLLECTION_DESCRIPTOR) MAKE_CASE(IOCTL_HID_FLUSH_QUEUE) MAKE_CASE(IOCTL_HID_SET_FEATURE) MAKE_CASE(IOCTL_HID_GET_FEATURE) MAKE_CASE(IOCTL_GET_PHYSICAL_DESCRIPTOR) MAKE_CASE(IOCTL_HID_GET_HARDWARE_ID) MAKE_CASE(IOCTL_HID_GET_MANUFACTURER_STRING) MAKE_CASE(IOCTL_HID_GET_PRODUCT_STRING) MAKE_CASE(IOCTL_HID_GET_SERIALNUMBER_STRING) MAKE_CASE(IOCTL_HID_GET_INDEXED_STRING) MAKE_CASE(IOCTL_INTERNAL_HID_SET_BLUESCREEN) default: ioctlStr = "???"; break; } DBGOUT(("IOCTL %s (%xh) status=%xh (fdo=%ph)", ioctlStr, ioControlCode, status, fdo)); } } #endif