/*++ Copyright (c) 1998 Microsoft Corporation Module Name: DBG.C Abstract: This module contains debug only code for DBCLASS driver Author: jdunn Environment: kernel mode only Notes: Revision History: 11-5-96 : created --*/ #include #include "stdarg.h" #include "stdio.h" #include "dbci.h" #include "dbclass.h" #include "dbfilter.h" #ifdef MAX_DEBUG #define DEBUG_HEAP #endif typedef struct _HEAP_TAG_BUFFER { ULONG Sig; ULONG Length; } HEAP_TAG_BUFFER, *PHEAP_TAG_BUFFER; #if DBG extern ULONG DBCLASS_Debug_Trace_Level; #ifdef NTKERN_TRACE ULONG DBCLASS_W98_Debug_Trace = 1; #else ULONG DBCLASS_W98_Debug_Trace = 0; #endif NTSTATUS DBCLASS_GetClassGlobalDebugRegistryParameters( ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS ntStatus; RTL_QUERY_REGISTRY_TABLE QueryTable[4]; PWCHAR usb = L"class\\dbc"; PAGED_CODE(); // // Set up QueryTable to do the following: // // spew level QueryTable[0].QueryRoutine = DBCLASS_GetConfigValue; QueryTable[0].Flags = 0; QueryTable[0].Name = DEBUG_LEVEL_KEY; QueryTable[0].EntryContext = &DBCLASS_Debug_Trace_Level; QueryTable[0].DefaultType = REG_DWORD; QueryTable[0].DefaultData = &DBCLASS_Debug_Trace_Level; QueryTable[0].DefaultLength = sizeof(DBCLASS_Debug_Trace_Level); // ntkern trace buffer QueryTable[1].QueryRoutine = DBCLASS_GetConfigValue; QueryTable[1].Flags = 0; QueryTable[1].Name = DEBUG_WIN9X_KEY; QueryTable[1].EntryContext = &DBCLASS_W98_Debug_Trace; QueryTable[1].DefaultType = REG_DWORD; QueryTable[1].DefaultData = &DBCLASS_W98_Debug_Trace; QueryTable[1].DefaultLength = sizeof(DBCLASS_W98_Debug_Trace); // break on start QueryTable[2].QueryRoutine = DBCLASS_GetConfigValue; QueryTable[2].Flags = 0; QueryTable[2].Name = DEBUG_BREAK_ON; QueryTable[2].EntryContext = &DBCLASS_BreakOn; QueryTable[2].DefaultType = REG_DWORD; QueryTable[2].DefaultData = &DBCLASS_BreakOn; QueryTable[2].DefaultLength = sizeof(DBCLASS_BreakOn); // // Stop // QueryTable[3].QueryRoutine = NULL; QueryTable[3].Flags = 0; QueryTable[3].Name = NULL; ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES, usb, QueryTable, // QueryTable NULL, // Context NULL); // Environment if (NT_SUCCESS(ntStatus)) { DBCLASS_KdPrint((1, "'Debug Trace Level Set: (%d)\n", DBCLASS_Debug_Trace_Level)); if (DBCLASS_W98_Debug_Trace) { DBCLASS_KdPrint((1, "'NTKERN Trace is ON\n")); } else { DBCLASS_KdPrint((1, "'NTKERN Trace is OFF\n")); } if (DBCLASS_BreakOn) { DBCLASS_KdPrint((1, "'DEBUG BREAK is ON\n")); } if (DBCLASS_Debug_Trace_Level > 0) { ULONG DBCLASS_Debug_Asserts = 1; } } if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) { ntStatus = STATUS_SUCCESS; } return ntStatus; } VOID DBCLASS_AssertBaysEmpty( PDBC_CONTEXT DbcContext ) { USHORT bay; for (bay=1; bay <=NUMBER_OF_BAYS(DbcContext); bay++) { DBCLASS_ASSERT( DbcContext->BayInformation[bay].DeviceFilterObject == NULL); } } VOID DBCLASS_Warning( PVOID Context, PUCHAR Message, BOOLEAN DebugBreak ) { DbgPrint("DBCLASS: Warning *************************************************************\n"); DbgPrint("%s", Message); DbgPrint("******************************************************************************\n"); if (DebugBreak) { TRAP(); } } VOID DBCLASS_Assert( IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber, IN PCHAR Message ) /*++ Routine Description: Debug Assert function. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { // this makes the compiler generate a ret ULONG stop = 1; assert_loop: // just call the NT assert function and stop // in the debugger. RtlAssert( FailedAssertion, FileName, LineNumber, Message ); // loop here to prevent users from going past // are assert before we can look at it TRAP(); if (stop) { goto assert_loop; } return; } ULONG _cdecl DBCLASS_KdPrintX( ULONG l, PCH Format, ... ) { va_list list; int i; int arg[6]; if (DBCLASS_Debug_Trace_Level >= l) { if (l <= 1) { // dump line to debugger if (DBCLASS_W98_Debug_Trace) { DbgPrint("DBCLASS.SYS: "); *Format = ' '; } else { DbgPrint("'DBCLASS.SYS: "); } } else { // dump line to NTKERN buffer DbgPrint("'DBCLASS.SYS: "); if (DBCLASS_W98_Debug_Trace) { *Format = 0x27; } } va_start(list, Format); for (i=0; i<6; i++) arg[i] = va_arg(list, int); DbgPrint(Format, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]); } return 0; } PVOID DBCLASS_GetHeap( IN POOL_TYPE PoolType, IN ULONG NumberOfBytes, IN ULONG Signature, IN PLONG TotalAllocatedHeapSpace ) /*++ Routine Description: Debug routine, used to debug heap problems. We are using this since most NT debug functions are not supported by NTKERN. Arguments: PoolType - pool type passed to ExAllocatePool NumberOfBytes - number of bytes for item Signature - four byte signature supplied by caller TotalAllocatedHeapSpace - pointer to variable where client stores the total accumulated heap space allocated. Return Value: pointer to allocated memory --*/ { PUCHAR p; #ifdef DEBUG_HEAP PHEAP_TAG_BUFFER tagBuffer; // we call ExAllocatePoolWithTag but no tag will be added // when running under NTKERN p = (PUCHAR) ExAllocatePoolWithTag(PoolType, NumberOfBytes + sizeof(HEAP_TAG_BUFFER)*2, Signature); if (p) { tagBuffer = (PHEAP_TAG_BUFFER) p; tagBuffer->Sig = Signature; tagBuffer->Length = NumberOfBytes; p += sizeof(HEAP_TAG_BUFFER); *TotalAllocatedHeapSpace += NumberOfBytes; tagBuffer = (PHEAP_TAG_BUFFER) (p + NumberOfBytes); tagBuffer->Sig = Signature; tagBuffer->Length = NumberOfBytes; } // LOGENTRY(LOG_MISC, (PUCHAR) &Signature, 0, 0, 0); // LOGENTRY(LOG_MISC, 'GetH', p, NumberOfBytes, stk[1] & 0x00FFFFFF); #else p = (PUCHAR) ExAllocatePoolWithTag(PoolType, NumberOfBytes, Signature); #endif /* DEBUG_HEAP */ return p; } VOID DBCLASS_RetHeap( IN PVOID P, IN ULONG Signature, IN PLONG TotalAllocatedHeapSpace ) /*++ Routine Description: Debug routine, used to debug heap problems. We are using this since most NT debug functions are not supported by NTKERN. Arguments: P - pointer to free Return Value: none. --*/ { #ifdef DEBUG_HEAP PHEAP_TAG_BUFFER endTagBuffer; PHEAP_TAG_BUFFER beginTagBuffer; DBCLASS_ASSERT(P != 0); beginTagBuffer = (PHEAP_TAG_BUFFER) ((PUCHAR)P - sizeof(HEAP_TAG_BUFFER)); endTagBuffer = (PHEAP_TAG_BUFFER) ((PUCHAR)P + beginTagBuffer->Length); *TotalAllocatedHeapSpace -= beginTagBuffer->Length; // LOGENTRY(LOG_MISC, (PUCHAR) &Signature, 0, 0, 0); // LOGENTRY(LOG_MISC, 'RetH', P, tagBuffer->Length, stk[1] & 0x00FFFFFF); DBCLASS_ASSERT(*TotalAllocatedHeapSpace >= 0); DBCLASS_ASSERT(beginTagBuffer->Sig == Signature); DBCLASS_ASSERT(endTagBuffer->Sig == Signature); DBCLASS_ASSERT(endTagBuffer->Length == beginTagBuffer->Length); // fill the buffer with bad data RtlFillMemory(P, beginTagBuffer->Length, 0xff); beginTagBuffer->Sig = DBCLASS_FREE_TAG; // free the original block ExFreePool(beginTagBuffer); #else ExFreePool(P); #endif /* DEBUG_HEAP */ } #endif /* DBG */ #ifdef DEBUG_LOG KSPIN_LOCK LogSpinLock; struct LOG_ENTRY { ULONG le_sig; // Identifying string ULONG le_info1; // entry specific info ULONG le_info2; // entry specific info ULONG le_info3; // entry specific info }; /* USBD_LOG_ENTRY */ struct LOG_ENTRY *DbclassLStart = 0; // No log yet struct LOG_ENTRY *DbclassLPtr; struct LOG_ENTRY *DbclassLEnd; #ifdef PROFILE ULONG LogMask = LOG_PROFILE; #else ULONG LogMask = 0xFFFFFFFF; #endif VOID DBCLASS_Debug_LogEntry( IN ULONG Mask, IN ULONG Sig, IN ULONG Info1, IN ULONG Info2, IN ULONG Info3 ) /*++ Routine Description: Adds an Entry to USBH log. Arguments: Return Value: None. --*/ { KIRQL irql; typedef union _SIG { struct { UCHAR Byte0; UCHAR Byte1; UCHAR Byte2; UCHAR Byte3; } b; ULONG l; } SIG, *PSIG; SIG sig, rsig; if (DbclassLStart == 0) { return; } if ((Mask & LogMask) == 0) { return; } irql = KeGetCurrentIrql(); if (irql < DISPATCH_LEVEL) { KeAcquireSpinLock(&LogSpinLock, &irql); } else { KeAcquireSpinLockAtDpcLevel(&LogSpinLock); } if (DbclassLPtr > DbclassLStart) { DbclassLPtr -= 1; // Decrement to next entry } else { DbclassLPtr = DbclassLEnd; } if (irql < DISPATCH_LEVEL) { KeReleaseSpinLock(&LogSpinLock, irql); } else { KeReleaseSpinLockFromDpcLevel(&LogSpinLock); } DBCLASS_ASSERT(DbclassLPtr >= DbclassLStart); sig.l = Sig; rsig.b.Byte0 = sig.b.Byte3; rsig.b.Byte1 = sig.b.Byte2; rsig.b.Byte2 = sig.b.Byte1; rsig.b.Byte3 = sig.b.Byte0; DbclassLPtr->le_sig = rsig.l; DbclassLPtr->le_info1 = Info1; DbclassLPtr->le_info2 = Info2; DbclassLPtr->le_info3 = Info3; return; } VOID DBCLASS_LogInit( ) /*++ Routine Description: Init the debug log - remember interesting information in a circular buffer Arguments: Return Value: None. --*/ { #ifdef MAX_DEBUG ULONG logSize = 4096*6; #else ULONG logSize = 4096*3; #endif KeInitializeSpinLock(&LogSpinLock); DbclassLStart = ExAllocatePoolWithTag(NonPagedPool, logSize, DBC_TAG); if (DbclassLStart) { DbclassLPtr = DbclassLStart; // Point the end (and first entry) 1 entry from the end of the segment DbclassLEnd = DbclassLStart + (logSize / sizeof(struct LOG_ENTRY)) - 1; } else { TRAP(); //no mem for log! } return; } VOID DBCLASS_LogFree( ) /*++ Routine Description: Init the debug log - remember interesting information in a circular buffer Arguments: Return Value: None. --*/ { if (DbclassLStart) { ExFreePool(DbclassLStart); } } #endif /* DEBUG_LOG */