/*++ Module Name: ktrace.c Abstract: This module implements a tracing facility for use by all kernel mode modules in Windows NT. Author: Roy D'Souza (rdsouza@gomez) 22-May-1996 Environment: User or Kernel mode. Revision History: --*/ #include "ki.h" #if DBG && IA64_KTRACE #include "ktrace.h" #include "ktracep.h" /*********************************************************************** Format of KTrace record (144 bytes) ***********************************************************************/ typedef struct _KTRACE_RECORD_ { ULONG ModuleID; USHORT MessageType; USHORT MessageIndex; LARGE_INTEGER SystemTime; ULONGLONG Arg1; ULONGLONG Arg2; ULONGLONG Arg3; ULONGLONG Arg4; ULONGLONG Arg5; LARGE_INTEGER HiResTimeStamp; } KTRACE_RECORD, *PKTRACE_RECORD; /* IF YOU MAKE ANY CHANGE TO THE ABOVE TYPEDEF YOU ALSO NEED TO UPDATE THE CONSTANT RECORD_SIZE_IN_BYTES IN FILE KTRACEP.H, SO THAT THE SIMDB MACROS WILL CONTINUE TO WORK CORRECTLY */ /*********************************************************************** KTrace Private Data ***********************************************************************/ KTRACE_RECORD KTrace[MAXIMUM_PROCESSORS][KTRACE_LOG_SIZE]; // // Current queue head // static ULONG KTraceQueueHead = 0; // // By default the mask is on for all: // static ULONG ModuleIDMask = 0xFFFFFFFF; /*********************************************************************** KeEnableKTrace - selectively enable/disable client's rights to trace ***********************************************************************/ VOID NTAPI KeEnableKTrace ( ULONG IDMask ) /*++ Routine Description: Selectively enable or disable individual module's abilities to write to the KTrace. By default all modules are permitted to write traces. Any kernel mode modules can call this routine to toggle write write permissions. Arguments: IDMask A bit pattern which specifies the modules that are to be enabled. Return Value: None. --*/ { ModuleIDMask = IDMask; return; } // KeEnableKTrace /*********************************************************************** CurrentKTraceEntry -- return address of current entry and update trace index ***********************************************************************/ PKTRACE_RECORD CurrentEntry = 0; PKTRACE_RECORD NTAPI KiCurrentKTraceEntry ( ) /*++ Routine Description: Provide pointer to next trace entry for use by assembly code that updates trace table. Arguments: None. Return Value: Pointer to next entry. --*/ { ULONG CurrentProcessor; CurrentProcessor = KeGetCurrentProcessorNumber(); if (CurrentProcessor > MAXIMUM_PROCESSORS) { DbgPrint("KTrace:CurrentKTraceEntry:KeGetCurrentProcessorNumber invalid\n"); return NULL; } CurrentEntry = &KTrace[CurrentProcessor][KTraceQueueHead]; KTraceQueueHead = (KTraceQueueHead + 1) % KTRACE_LOG_SIZE; return CurrentEntry; } // CurrentKTraceEntry /*********************************************************************** AddTrace - add an entry into the trace. ***********************************************************************/ NTSTATUS NTAPI KeAddKTrace ( ULONG ModuleID, USHORT MessageType, USHORT MessageIndex, ULONGLONG Arg1, ULONGLONG Arg2, ULONGLONG Arg3, ULONGLONG Arg4 ) /*++ Routine Description: Add a record to the KTrace log. Can be called by anybody in the kernel. (Need to figure out a way to export this through ntoskrnl.lib so that drivers can access it.) Arguments: ModuleID identifies the client making this call. See ktrace.h for definitions. MessageType specifies the type/severity of the message (i.e. warning, error, checkpoint...) See ktrace.h Arg1-4 for unrestricted use by the client. Return Value: Status STATUS_SUCCESS if successful. STATUS_UNSUCCESSFUL if failure. --*/ { ULONG CurrentProcessor; LARGE_INTEGER PerfCtr; NTSTATUS Status = STATUS_SUCCESS; LARGE_INTEGER SystemTime; if (!(ModuleID & ModuleIDMask)) return STATUS_UNSUCCESSFUL; CurrentProcessor = KeGetCurrentProcessorNumber(); if (CurrentProcessor > MAXIMUM_PROCESSORS) { DbgPrint("KTrace:AddTrace:KeGetCurrentProcessorNumber invalid\n"); return STATUS_UNSUCCESSFUL; } KTrace[CurrentProcessor][KTraceQueueHead].ModuleID = ModuleID; KTrace[CurrentProcessor][KTraceQueueHead].MessageType = MessageType; KTrace[CurrentProcessor][KTraceQueueHead].MessageIndex = MessageIndex; KTrace[CurrentProcessor][KTraceQueueHead].Arg1 = Arg1; KTrace[CurrentProcessor][KTraceQueueHead].Arg2 = Arg2; KTrace[CurrentProcessor][KTraceQueueHead].Arg3 = Arg3; KTrace[CurrentProcessor][KTraceQueueHead].Arg4 = Arg4; KeQuerySystemTime(&SystemTime); KTrace[CurrentProcessor][KTraceQueueHead].SystemTime = SystemTime; #if 0 Status = NtQueryPerformanceCounter(&PerfCtr, NULL); if (!NT_SUCCESS(Status)) { DbgPrint("NtQueryPerformanceCounter failed with %x\n", Status); return Status; } KTrace[CurrentProcessor][KTraceQueueHead].HiResTimeStamp = PerfCtr; #endif KTraceQueueHead = (KTraceQueueHead + 1) % KTRACE_LOG_SIZE; return Status; } // AddTrace /*********************************************************************** QueryDumpKTraceBuffer - API query: selectively dump trace ***********************************************************************/ LONG NTAPI KeQueryDumpKTrace ( ULONG Processor, ULONG StartEntry, ULONG NumberOfEntries, ULONG ModuleFilter, ULONG MessageFilter, BOOLEAN Sort) /*++ Routine Description: Arguments: ProcessorMask Specify the particular processor of interest. StartEntry The offset from the start of the queue to start dumping records. If this entry is 0 then the dumping starts from the head of the queue. If this argument is specified to be larger than the size of the KTrace buffer, then the dump wraps around appropriately. NumberOfEntries The number of log entries to dump starting from StartEntry. ModuleFilter A bit pattern specifying the modules of interest. See ktrace.h for a definition of modules. Only the logs of these modules are included in the dump. Bits representing undefined modules are ignored. MessageFilter A bit pattern specifying the subset of message types to dump. See ktrace.h for definitions of all message types. Sort Sort the output before dumping. Base the sort on the global NT timestamp. Most recent entries are dumped first. (NOT IMPLEMENTED YET). Return Value: The number of records that were dumped. Returns -1 if error. --*/ { ULONG Index = StartEntry; ULONG RecordCount = 0; // // Verify that we have a valid processor number: // #if !defined(NT_UP) if (Processor > KeRegisteredProcessors) { DbgPrint("KTrace error: attempt to access invalid processor" "%d on a system with %d processors\n", Processor, KeRegisteredProcessors); return 0L; } #else if (Processor > 0) { DbgPrint("KTrace error: attempted to access invalid processor" "%d on a uni-processor system\n", Processor); } #endif // // Loop through the entire KTrace // while (NumberOfEntries > 0) { // // See if the current record matches the query criteria // if ((ModuleFilter & KTrace[Processor][Index].ModuleID) && (MessageFilter & KTrace[Processor][Index].MessageType)) { DumpRecord(Processor, Index); RecordCount++; } NumberOfEntries = NumberOfEntries - 1; Index = Index > 0 ? Index - 1 : KTRACE_LOG_SIZE - 1; } return RecordCount; } // QueryDumpKTraceBuffer /*********************************************************************** KePurgeKTrace - delete all records from the trace. ***********************************************************************/ VOID NTAPI KePurgeKTrace ( ) { ULONG Index1, Index2; for (Index1 = 0; Index1 < KTRACE_LOG_SIZE; Index1++) { for (Index2 = 0; Index2 < MAXIMUM_PROCESSORS; Index2++) { KTrace[Index2][Index1].ModuleID = 0; KTrace[Index2][Index1].MessageType = 0; KTrace[Index2][Index1].MessageIndex = 0; KTrace[Index2][Index1].SystemTime.HighPart = 0; KTrace[Index2][Index1].SystemTime.LowPart = 0; KTrace[Index2][Index1].Arg1 = 0; KTrace[Index2][Index1].Arg2 = 0; KTrace[Index2][Index1].Arg3 = 0; KTrace[Index2][Index1].Arg4 = 0; KTrace[Index2][Index1].HiResTimeStamp.HighPart = 0; KTrace[Index2][Index1].HiResTimeStamp.LowPart = 0; } } } /*********************************************************************** DumpRecord - dump a single record specified by processor number & index. ***********************************************************************/ VOID NTAPI DumpRecord (IN ULONG ProcessorNumber, IN ULONG Index) /*++ Routine Description: Dumps out the specified record in the trace associated with the specified processor out to the remote debugger console. Arguments: ProcessorNumber The processor whose associated trace log is to be accessed. Index The offset of the record in the trace to be dumped. Return Value: None. --*/ { #if !defined(NT_UP) if (ProcessorNumber > KeRegisteredProcessors) { DbgPrint("KTrace:DumpRecord:" "illegal processor number %x in a %x-processor system\n", ProcessorNumber, KeRegisteredProcessors); return; } #else if (ProcessorNumber > 0) { DbgPrint("KTrace:DumpRecord:" "illegal processor %x in a uni-processor system\n", ProcessorNumber); } #endif DbgPrint("Dumping Record Index [%ld], Processor = [%ld]\n", Index); DbgPrint("\tModuleID = [%lx]\n", KTrace[ProcessorNumber][Index].ModuleID); DbgPrint("\tMessageType = [%lx]\n", KTrace[ProcessorNumber][Index].MessageType); DbgPrint("\tMessageIndex = [%lx]\n", KTrace[ProcessorNumber][Index].MessageIndex); DbgPrint("\tArg1= [%lx%LX]\n", KTrace[ProcessorNumber][Index].Arg1, KTrace[ProcessorNumber][Index].Arg1); DbgPrint("\tArg2= [%lx%LX]\n", KTrace[ProcessorNumber][Index].Arg2, KTrace[ProcessorNumber][Index].Arg2); DbgPrint("\tArg3= [%lx%LX]\n", KTrace[ProcessorNumber][Index].Arg3, KTrace[ProcessorNumber][Index].Arg3); DbgPrint("\tArg4= [%lx%LX]\n", KTrace[ProcessorNumber][Index].Arg4, KTrace[ProcessorNumber][Index].Arg4); } // DumpRecord #endif // DBG // end ktrace.c