/*++ Copyright (c) 1992-1999 Microsoft Corporation Module Name: kdexts.c Abstract: This function contains some example KD debugger extensions Author: John Vert (jvert) 6-Aug-1992 Revision History: --*/ #include "precomp.h" typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; // Forward function definitions PCHAR FindPreamble( CHAR * BufferPointer, BOOLEAN FirstTime ); VOID ParseBuffer( CHAR * DataStart, ULONG SkipCnt ); PCHAR ParseOidRecord( CHAR * DataStart, ULONG SkipCnt ); PCHAR ParseTimeRecord( CHAR * DataStart, ULONG SkipCnt ); PCHAR ParseStringRecord( CHAR * DataStart, ULONG SkipCnt ); PCHAR ParseSchedRecord( CHAR * DataStart, ULONG SkipCnt ); PCHAR ParseRecvRecord( CHAR * DataStart, ULONG SkipCnt ); PCHAR ParseSendRecord( CHAR * DataStart, ULONG SkipCnt ); static PCHAR SchedModules[] = { "NOP", "TB CONFORMER", "SHAPER", "DRR SEQ", "CBQ"}; static PCHAR SchedActions[] = { "NOP", "ENQUEUE", "DEQUEUE", "CONFORMANCE", "DISCARD"}; static PCHAR SendRecvActions[] = { "", "ENTER", "NO_RESOURCES", "LOW_RESOURCES", "INDICATING", "RETURNED", "NOT_OURS", "OURS", "RETURNING", "TRANSFERRING", "NOT READY"}; static PCHAR RecvEvents[] = { "", "CL_RECV_PACKET", "MP_RETURN_PACKET", "CL_RECV_INDICATION", "CL_RECV_COMPLETE", "MP_TRANSFER_DATA", "CL_TRANSFER_COMPLETE"}; static PCHAR SendEvents[] = { "", "MP_SEND", "MP_CO_SEND", "DUP_PACKET", "DROP_PACKET", "CL_SEND_COMPLETE" }; // // globals // CHAR Preamble[] = {(CHAR)0xef,(CHAR)0xbe,(CHAR)0xad,(CHAR)0xde}; ULONG ByteCheck = 0; ULONG SafetyLimit; BOOL SafeRead( DWORD offset, LPVOID lpBuffer, DWORD cb, LPDWORD lpcbBytesRead ) { if(ByteCheck <= SafetyLimit){ ReadMemory(offset, lpBuffer, cb, lpcbBytesRead); ByteCheck += cb; return TRUE; } else{ dprintf("SafetyLimit of %d, exceeded, quitting!\n", SafetyLimit); return FALSE; } } DWORD MyOpenFile ( IN PCHAR Name, IN PWINDBG_OUTPUT_ROUTINE out, OUT HANDLE *File ) { HANDLE hFile; hFile = CreateFile(Name, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { out ("MyOpenFile: CreateFile Failed.\n"); } *File = hFile; return(INVALID_HANDLE_VALUE == hFile ? (!(ERROR_SUCCESS)) : ERROR_SUCCESS); } VOID MyWriteFile ( PCHAR buffer, HANDLE file, IN PWINDBG_OUTPUT_ROUTINE out ) { LONG len; BOOL bRv; DWORD dwWritten; if (!buffer) { out("MyWriteFile: Buffer is null\n"); } len = strlen(buffer); bRv = WriteFile( file, buffer, len, &dwWritten, NULL ); if (!bRv) { out("WriteFile: Puked\n"); } } CHAR * BufferBase; // Start address of trace buffer, in host memory CHAR * BufferEnd; // End address of trace buffer, in host memory CHAR * BufferPointer; // Current location in host memory buffer LONG BufferSize; // Total size of host memory buffer, in bytes. LONG BufferCount; // Total count of traced bytes. LONG TotalValidBytesRead = 0; #define OUTPUT_ERROR 0 #define OUTPUT_FILE 1 #define OUTPUT_CONSOLE 2 CHAR FileIt; HANDLE FileHandle; ULONG LineNumber = 0; DECLARE_API( tt ) { DWORD hostAddress; CHAR DeviceName[] = "c:\\tmp\\"; CHAR buffer[255]; DWORD status; LONG pBufferBase; LONG pBufferCount; LONG pBufferSize; BOOL success; DWORD bytesread; CHAR *s, *dataStart; ULONG skip=0; char SchedBufBase[] = {"&psched!SchedTraceBuffer"}; char SchedCount[] = {"&psched!SchedTraced"}; char BuffSize[] = {"&psched!SchedBufferSize"}; TotalValidBytesRead = 0; LineNumber = 0; FileIt = OUTPUT_ERROR; if ( *args != '\0' ) { // First argument is the file name. s = strtok((LPSTR)args, " "); strcpy( buffer, DeviceName ); strcat( buffer, s ); // next arg is the optional args. while((s = strtok(NULL, " ")) != NULL) { if(s[0] == '-') { switch(s[1]) { case 's': case 'S': skip = atoi(&s[2]); break; default: dprintf("Usage: !tt [filename] [-S<#>] \n"); dprintf(" S : No of records (from head) to skip \n"); goto cleanup; } } } status = MyOpenFile( buffer, dprintf, &FileHandle ); if ( status == ERROR_SUCCESS ) { FileIt = OUTPUT_FILE; } else { goto cleanup; } dprintf( "handle =%x status=%x FileIt=%d\n", FileHandle, status, FileIt ); } else { FileIt = OUTPUT_CONSOLE; } hostAddress = GetExpression(SchedBufBase); pBufferBase = (LONG)hostAddress; if (!pBufferBase){ dprintf("bad string conversion (%s) \n", SchedBufBase); goto cleanup; } hostAddress = GetExpression(SchedCount); pBufferCount = hostAddress; if (!pBufferCount) { dprintf( " bad string conversion (%s) \n", SchedCount ); goto cleanup; } hostAddress = GetExpression(BuffSize); pBufferSize = hostAddress; if (!pBufferSize) { dprintf( " bad string conversion (%s) \n", SchedCount ); goto cleanup; } success = ReadMemory((ULONG)pBufferCount, &BufferCount, sizeof(LONG), &bytesread ); if (!success){ dprintf("problems reading memory at %x for %x bytes\n", pBufferCount, sizeof(LONG)); goto cleanup; } success = ReadMemory((ULONG)pBufferSize, &BufferSize, sizeof(LONG), &bytesread ); if (!success){ dprintf("problems reading memory at %x for %x bytes\n", pBufferSize, sizeof(LONG)); goto cleanup; } success = ReadMemory((ULONG)pBufferBase, &BufferBase, sizeof(LONG), &bytesread ); if (!success){ dprintf("problems reading memory at %x for %x bytes\n", pBufferBase, sizeof(LONG)); goto cleanup; } SafetyLimit = (ULONG)-1; if(!BufferCount){ dprintf("No data in buffer.\n"); goto cleanup; } dprintf("struct@%x, total bytes traced: %d, buffer size: %d\n", BufferBase, BufferCount, BufferSize); // Find the starting point. If we haven't wrapped the buffer, it's at BufferBase. // If we have wrapped the buffer, we start right after our current pointer. // In either case, BufferPointer is the host address at which we should // start looking for data. if(BufferCount <= BufferSize){ BufferPointer = BufferBase; } else{ BufferPointer = BufferBase + (BufferCount % BufferSize); } BufferEnd = BufferBase + BufferSize; // dataStart will point to the first location after the // first preamble (in host memory), or - will be zero, // indicating that no preamble was found. dataStart = FindPreamble(BufferPointer, TRUE); if(dataStart){ ParseBuffer(dataStart, skip); } else{ dprintf("Error reading token trace buffer. Could not find preamble.\n"); } cleanup: if(FileIt == OUTPUT_FILE){ CloseHandle( FileHandle ); } } PCHAR FindPreamble( CHAR * BufferPointer, BOOLEAN FirstTime ) { LONG count; LONG i; CHAR * readFrom; CHAR hold; BOOL success; DWORD bytesread; count = 0; i = 0; readFrom = BufferPointer; while(TRUE){ success = SafeRead((ULONG)readFrom, &hold, 1, &bytesread ); if (!success){ dprintf("problems reading memory at %x for %x bytes\n", readFrom, 1); break; } if(hold == Preamble[i]){ i++; } else{ i=0; } if(i == sizeof(TRACE_PREAMBLE)){ if(FirstTime){ dprintf("Found start of first record at %d.\n", readFrom+1 - BufferBase); } TotalValidBytesRead += sizeof(TRACE_PREAMBLE); readFrom++; break; } readFrom++; if(readFrom > BufferEnd){ readFrom = BufferBase; } count++; if(count == TRACE_BUFFER_SIZE){ readFrom = 0; break; } } return(readFrom); } VOID ParseBuffer( CHAR * DataStart, ULONG SkipCnt ) { CHAR * dataStart; CHAR * recordEnd; LONG records; BOOL success; CHAR hold; DWORD bytesread; records = 0; dataStart = DataStart; while(TRUE){ success = SafeRead((ULONG)dataStart, &hold, 1, &bytesread ); if (!success){ dprintf("problems reading memory at %x for %x bytes\n", dataStart, 1); break; } switch(hold){ case RECORD_TSTRING: recordEnd = ParseStringRecord(dataStart, SkipCnt); break; case RECORD_OID: recordEnd = ParseOidRecord(dataStart, SkipCnt); break; case RECORD_SCHED: recordEnd = ParseSchedRecord(dataStart, SkipCnt); break; case RECORD_RECV: recordEnd = ParseRecvRecord(dataStart, SkipCnt); break; case RECORD_SEND: recordEnd = ParseSendRecord(dataStart, SkipCnt); break; default: dprintf("Unrecognized record type!\n"); } records++; if(TotalValidBytesRead >= BufferCount){ dprintf("\nCompleted parsing trace buffer. %d records found.\n", records); break; } dataStart = FindPreamble(recordEnd, FALSE); if(dataStart == DataStart){ dprintf("\nCompleted parsing trace buffer. %d records found.\n", records); break; } } } VOID GetBytesToRead( CHAR * DataStart, LONG RecordSize, LONG * BytesToReadFirst, LONG * BytesToReadNext ) { if((DataStart + RecordSize - sizeof(TRACE_PREAMBLE)) > BufferEnd){ *BytesToReadFirst = BufferEnd - DataStart; *BytesToReadNext = RecordSize - sizeof(TRACE_PREAMBLE) - *BytesToReadFirst; } else{ *BytesToReadFirst = RecordSize - sizeof(TRACE_PREAMBLE); *BytesToReadNext = 0; } } PCHAR ParseAnyRecord( CHAR * DataStart, CHAR * Record, LONG RecordSize ) { LONG bytesToReadFirst; LONG bytesToReadNext; BOOL success; CHAR * nextDataStart; DWORD bytesread; CHAR * pRecordData; CHAR buffer[255]; GetBytesToRead(DataStart, RecordSize, &bytesToReadFirst, &bytesToReadNext); pRecordData = Record + sizeof(TRACE_PREAMBLE); success = SafeRead((ULONG)DataStart, pRecordData, bytesToReadFirst, &bytesread ); TotalValidBytesRead += bytesToReadFirst; if (!success){ dprintf("problems reading memory at %x for %x bytes\n", DataStart, 1); } nextDataStart = DataStart + bytesToReadFirst; if(bytesToReadNext){ success = SafeRead((ULONG)BufferBase, pRecordData + bytesToReadFirst, bytesToReadNext, &bytesread); TotalValidBytesRead += bytesToReadNext; if (!success){ dprintf("problems reading memory at %x for %x bytes\n", BufferBase, 1); } nextDataStart = BufferBase + bytesToReadNext; } return(nextDataStart); } PCHAR ParseOidRecord( CHAR * DataStart, ULONG SkipCnt ) { TRACE_RECORD_OID record; CHAR buffer[255]; CHAR * nextDataStart; static PCHAR OIDActions[] = { "", "MpSetInformation", "MpQueryInformation", "SetRequestComplete", "QueryRequestComplete" }; nextDataStart = ParseAnyRecord(DataStart, (CHAR *)&record, sizeof(TRACE_RECORD_OID)); if(++LineNumber < SkipCnt) return nextDataStart; if(record.Now.QuadPart){ wsprintf(buffer, "[%4d]:[%u.%u]: OID: %5s:%9s:%08X:%08X:%08X \n", LineNumber, record.Now.HighPart, record.Now.LowPart, OIDActions[record.Action], record.Local == TRUE?"Local":"Non Local", record.Adapter, record.Oid, record.Status); } else { wsprintf(buffer, "[%4d]: OID: %5s:%9s:%08X:%08X:%08X \n", LineNumber, OIDActions[record.Action], record.Local == TRUE?"Local":"Non Local", record.Adapter, record.Oid, record.Status); } switch(FileIt) { case OUTPUT_FILE: MyWriteFile(buffer, FileHandle, dprintf); break; case OUTPUT_CONSOLE: dprintf(buffer); break; default: dprintf("Failed to create file! Check for tmp directory.\n"); break; } return(nextDataStart); } PCHAR ParseStringRecord( CHAR * DataStart, ULONG SkipCnt ) { TRACE_RECORD_STRING record; LONG bytesToReadFirst; LONG bytesToReadNext; BOOL success; CHAR * nextDataStart; DWORD bytesread; CHAR * pRecordData; CHAR buffer[255]; nextDataStart = ParseAnyRecord(DataStart, (CHAR *)&record, sizeof(TRACE_RECORD_STRING)); if(++LineNumber < SkipCnt) return nextDataStart; if(record.Now.QuadPart){ wsprintf(buffer, "[%4d]:[%u.%u]:%s", LineNumber, record.Now.HighPart, record.Now.LowPart, record.StringStart); } else{ wsprintf(buffer, "[%4d]:%s", LineNumber, record.StringStart); } switch(FileIt) { case OUTPUT_FILE: MyWriteFile(buffer, FileHandle, dprintf); break; case OUTPUT_CONSOLE: dprintf(buffer); break; default: dprintf("Failed to create file! Check for tmp directory.\n"); break; } return(nextDataStart); } PCHAR ParseSchedRecord( CHAR * DataStart, ULONG SkipCnt ) { TRACE_RECORD_SCHED record; LONG bytesToReadFirst; LONG bytesToReadNext; BOOL success; CHAR * nextDataStart; DWORD bytesread; CHAR * pRecordData; ULONG now; CHAR buffer[255]; LARGE_INTEGER ConformanceTime; nextDataStart = ParseAnyRecord(DataStart, (CHAR *)&record, sizeof(TRACE_RECORD_SCHED)); if(++LineNumber < SkipCnt) return nextDataStart; ConformanceTime.QuadPart = record.ConformanceTime; wsprintf(buffer, "[%4d]:[%u.%u]:%s:VC %x:%x:%u:%s:%d:%d:%u:%u\n", LineNumber, record.Now.HighPart, record.Now.LowPart, SchedModules[record.SchedulerComponent], record.VC, record.Packet, record.PacketLength, SchedActions[record.Action], record.Priority, ConformanceTime.HighPart, ConformanceTime.LowPart, record.PacketsInComponent); switch(FileIt) { case OUTPUT_FILE: MyWriteFile(buffer, FileHandle, dprintf); break; case OUTPUT_CONSOLE: dprintf(buffer); break; default: dprintf("Failed to create file! Check for tmp directory.\n"); break; } return(nextDataStart); } PCHAR ParseRecvRecord( CHAR * DataStart, ULONG SkipCnt ) { TRACE_RECORD_RECV record; LONG bytesToReadFirst; LONG bytesToReadNext; BOOL success; CHAR * nextDataStart; DWORD bytesread; CHAR * pRecordData; CHAR buffer[255]; nextDataStart = ParseAnyRecord(DataStart, (CHAR *)&record, sizeof(TRACE_RECORD_RECV)); if(++LineNumber < SkipCnt) return nextDataStart; wsprintf(buffer, "[%4d]:[%u.%u]:Adapter %08X:%s:%s:%x:%x \n", LineNumber, record.Now.HighPart, record.Now.LowPart, record.Adapter, RecvEvents[record.Event], SendRecvActions[record.Action], record.Packet1, record.Packet2); switch(FileIt) { case OUTPUT_FILE: MyWriteFile(buffer, FileHandle, dprintf); break; case OUTPUT_CONSOLE: dprintf(buffer); break; default: dprintf("Failed to create file! Check for tmp directory.\n"); break; } return(nextDataStart); } PCHAR ParseSendRecord( CHAR * DataStart, ULONG SkipCnt ) { TRACE_RECORD_SEND record; LONG bytesToReadFirst; LONG bytesToReadNext; BOOL success; CHAR * nextDataStart; DWORD bytesread; CHAR * pRecordData; CHAR buffer[255]; nextDataStart = ParseAnyRecord(DataStart, (CHAR *)&record, sizeof(TRACE_RECORD_SEND)); if(++LineNumber < SkipCnt) return nextDataStart; wsprintf(buffer, "[%4d]:[%u.%u]:Adapter %08X:%s:%s:%x:%x:%x\n", LineNumber, record.Now.HighPart, record.Now.LowPart, record.Adapter, SendEvents[record.Event], SendRecvActions[record.Action], record.Vc, record.Packet1, record.Packet2); switch(FileIt) { case OUTPUT_FILE: MyWriteFile(buffer, FileHandle, dprintf); break; case OUTPUT_CONSOLE: dprintf(buffer); break; default: dprintf("Failed to create file! Check for tmp directory.\n"); break; } return(nextDataStart); }