/*++ Copyright (c) 1992 Microsoft Corporation Module Name: USBLOG.c Abstract: WinDbg Extension Api Author: Chris Robinson (crobins) February 1999 Environment: User Mode. Revision History: --*/ #include "precomp.h" #ifndef MAKE_SYMBOL #define MAKE_SYMBOL(m, s) #m "!" #s #endif #define DUMP_STRING(s) dprintf((s)) #define DUMP_DWORD(d) dprintf("0x%08x", (d)) #define DUMP_ADDRESS(a) dprintf("0x%08p", (a)) #define END_LINE() dprintf("\n") #define TRACE_SPEW #define DECLARE_LOG(logname, name, start, end, ptr, lines, desc) \ static struct _USB_LOG logname = { name, start, end, ptr, \ 0, 0, 0, 0, \ 0, 0, lines, desc }; #define DEFAULT_LINES_TO_DUMP 16 #define TAG_CHAR_LENGTH 4 #define TAG_STRING_LENGTH TAG_CHAR_LENGTH+1 #define LOG_SEARCH_DELIMS "," #define LogHasRolledOver(log) (IsValidEntry((log) -> LogStart) && ((log) -> LogStart != (log) -> LogPtr)) #define CalcNumberLines(pe1, pe2) (((pe2) - (pe1)) + 1) #define TAG(str) dprintf("%-6s", str) #define PARAM(str) dprintf("%-12s", str) #define DESC(str) dprintf("%-12s", str) #define USBHUB_LOG_NAME USBHUBLog #define USBD_LOG_NAME USBDLog #define OHCI_LOG_NAME OHCILog #define UHCD_LOG_NAME UHCDLog #define USBHUB_LNAME "USBHUB" #define USBHUB_START MAKE_SYMBOL(usbhub, hublstart) #define USBHUB_END MAKE_SYMBOL(usbhub, hublend) #define USBHUB_PTR MAKE_SYMBOL(usbhub, hublptr) #define USBD_LNAME "USBD" #define USBD_START MAKE_SYMBOL(usbd, lstart) #define USBD_END MAKE_SYMBOL(usbd, lend) #define USBD_PTR MAKE_SYMBOL(usbd, lptr) #define OHCI_LNAME "OpenHCI" #define OHCI_START MAKE_SYMBOL(openhci, ohcilstart) #define OHCI_END MAKE_SYMBOL(openhci, ohcilend) #define OHCI_PTR MAKE_SYMBOL(openhci, ohcilptr) #define UHCD_LNAME "UHCD" #define UHCD_START MAKE_SYMBOL(uhcd, HCDLStart) #define UHCD_END MAKE_SYMBOL(uhcd, HCDLEnd) #define UHCD_PTR MAKE_SYMBOL(uhcd, HCDLPtr) // // USBLOG typedefs // typedef union { ULONG TagValue; CHAR TagChars[TAG_CHAR_LENGTH]; } USBLOG_TAG_READ, *PUSBLOG_TAG_READ; typedef struct _usb_log_entry { USBLOG_TAG_READ Tag; DWORD Param1; DWORD Param2; DWORD Param3; } USB_LOG_ENTRY, *PUSB_LOG_ENTRY; typedef PCHAR (DESC_ROUTINE) ( ULONG64 Entry ); typedef DESC_ROUTINE *PDESC_ROUTINE; typedef struct _USB_LOG { PCHAR LogName; PCHAR LogStartName; PCHAR LogEndName; PCHAR LogPtrName; ULONG64 LogStart; ULONG64 LogEnd; ULONG64 LogPtr; ULONG64 LastSearchResult; ULONG64 LogCurrViewTop; ULONG64 LogCurrViewBottom; LONG LinesToDump; PDESC_ROUTINE DescRoutine; } USB_LOG, *PUSB_LOG; typedef struct _USBLOG_ARGS { ULONG64 Address; LONG NumberLines; PCHAR SearchString; BOOLEAN ResetLog; BOOLEAN SearchLog; } USBLOG_ARGS, *PUSBLOG_ARGS; // // Add logging function declarations // VOID USBLOG_DoLog(PUSB_LOG LogToDump, PCSTR Args); VOID USBLOG_Usage(void); VOID USBLOG_GetParams(PCSTR Args, PUSBLOG_ARGS ParsedArgs); ULONG64 USBLOG_SearchLog(PUSB_LOG Log, ULONG64 SearchBegin, PCHAR SearchString); VOID DumpDefaultLogHeader(VOID); BOOLEAN DumpLog(PUSB_LOG, BOOLEAN, BOOLEAN); BOOLEAN ResetLog(PUSB_LOG); #define GetMostRecentEntry(l) ((l) -> LogPtr) ULONG64 GetEntryBefore(PUSB_LOG, ULONG64); ULONG64 GetEntryAfter(PUSB_LOG, ULONG64); #define IsMostRecentEntry(l, e) ((e) == (l) -> LogPtr) #define IsLeastRecentEntry(l, e) (((LogHasRolledOver((l))) \ ? ( (e) == ((l) -> LogPtr - 1)) \ : ( (e) == ((l) -> LogEnd))) ) VOID GetLogEntryTag(ULONG64, PUSBLOG_TAG_READ); VOID GetLogEntryParams(ULONG64, ULONG64 *, ULONG64 *, ULONG64 *); ULONG64 SearchLogForTag(PUSB_LOG, ULONG64, ULONG, USBLOG_TAG_READ[]); #define GetLastSearchResult(l) ((l) -> LastSearchResult) #define SetLastSearchResult(l, a) ((l) -> LastSearchResult = (a)) #define SetLinesToDump(l, n) ((l) -> LinesToDump = (n)) #define GetLinesToDump(l) ((l) -> LinesToDump) VOID ConvertStringToTag(PCHAR, PUSBLOG_TAG_READ); VOID ConvertTagToString(PUSBLOG_TAG_READ, PCHAR, ULONG); BOOLEAN IsValidEntry(ULONG64); VOID GetCurrentView(PUSB_LOG, ULONG64 *, ULONG64 *); VOID SetCurrentView(PUSB_LOG, ULONG64, ULONG64); VOID LogViewScrollUp(PUSB_LOG); VOID LogViewScrollDown(PUSB_LOG); VOID DisplayCurrentView(PUSB_LOG); VOID DisplayHeader(); // // Global log structure declarations // DECLARE_LOG(USBHUB_LOG_NAME, USBHUB_LNAME, USBHUB_START, USBHUB_END, USBHUB_PTR, DEFAULT_LINES_TO_DUMP, NULL); DECLARE_LOG(USBD_LOG_NAME, USBD_LNAME, USBD_START, USBD_END, USBD_PTR, DEFAULT_LINES_TO_DUMP, NULL); DECLARE_LOG(OHCI_LOG_NAME, OHCI_LNAME, OHCI_START, OHCI_END, OHCI_PTR, DEFAULT_LINES_TO_DUMP, NULL); DECLARE_LOG(UHCD_LOG_NAME, UHCD_LNAME, UHCD_START, UHCD_END, UHCD_PTR, DEFAULT_LINES_TO_DUMP, NULL); // // Define each of these, which is relatively simple // DECLARE_API( usblog ) /*++ Routine Description: Dumps a HID Preparsed Data blob Arguments: args - Address flags Return Value: None --*/ { ULONG index; UCHAR logName[32]; UCHAR buffer[256]; logName[0] = '\0'; memset(buffer, '\0', sizeof(buffer)); if (!*args) { USBLOG_Usage(); } else { if (!sscanf(args, "%s %256c", logName, buffer)) { USBLOG_Usage(); } } index = 0; while ('\0' != logName[index]) { logName[index] = (UCHAR) toupper(logName[index]); index++; } if (!strcmp(logName, "USBHUB")) { USBLOG_DoLog(&(USBHUB_LOG_NAME), buffer); } else if (!strcmp(logName, "USBD")) { USBLOG_DoLog(&(USBD_LOG_NAME), buffer); } else if (!strcmp(logName, "OPENHCI")) { USBLOG_DoLog(&(OHCI_LOG_NAME), buffer); } else if (!strcmp(logName, "UHCD")) { USBLOG_DoLog(&(UHCD_LOG_NAME), buffer); } else { dprintf("Unknown USB log type!\n"); USBLOG_Usage(); } return S_OK; } VOID USBLOG_DoLog( PUSB_LOG LogToDump, PCSTR Args ) { BOOLEAN atEnd; BOOLEAN dumpSuccess; USBLOG_ARGS logArgs; BOOLEAN doDump; BOOLEAN doScroll; ULONG64 searchAddress; TRACE_SPEW("Entering USBLOG_DoLog with args %s\n", Args); doDump = TRUE; doScroll = TRUE; // // Parse the arguments to the logging function // USBLOG_GetParams(Args, &logArgs); // // Analyze the params and modify the log structure if need be // if (0 != logArgs.NumberLines) { SetLinesToDump(LogToDump, logArgs.NumberLines); } if (0 != logArgs.Address) { if (!logArgs.SearchLog) { if (GetLinesToDump(LogToDump) > 0) { SetCurrentView(LogToDump, logArgs.Address, 0); } else { SetCurrentView(LogToDump, 0, logArgs.Address); } doScroll = FALSE; } } if (logArgs.ResetLog) { ResetLog(LogToDump); doScroll = FALSE; } if (logArgs.SearchLog) { if (0 == logArgs.Address) { searchAddress = GetLastSearchResult(LogToDump); if (0 == searchAddress) { searchAddress = GetMostRecentEntry(LogToDump); } } else { searchAddress = logArgs.Address; } searchAddress = USBLOG_SearchLog(LogToDump, searchAddress, logArgs.SearchString); if (0 != searchAddress) { SetLastSearchResult(LogToDump, searchAddress); SetCurrentView(LogToDump, searchAddress, 0); doScroll = FALSE; } else { dprintf("Couldn't find any such tag(s)\n"); doDump = FALSE; } } if (doDump) { dumpSuccess = DumpLog(LogToDump, FALSE, doScroll); if (!dumpSuccess) { dprintf("Error dumping log\n"); } } return; } VOID USBLOG_GetParams( IN PCSTR Args, OUT PUSBLOG_ARGS ParsedArgs ) { PCHAR arg; PCHAR args; CHAR argDelims[] = " \t\n"; // // Initialize the arguments structure first // memset(ParsedArgs, 0x00, sizeof(USBLOG_ARGS)); // // Setup the argument string so that it's not a const anymore which // eliminates compiler errors. // args = (PCHAR) Args; // // The command line for !log is the following: // !log [address] [-r] [-s searchstring] [-l n] // // The argument parsing will assume these can be entered in any order, // so we'll simply examine each argument until there are no more // arguments. The main loop will look for either an address or an // option. If it finds either, it processes as necessary. Otherwise, // the argument is simply ignored. // arg = strtok(args, argDelims); while (NULL != arg) { TRACE_SPEW("Analyzing usblog arg: %s\n", arg); // // Check to see if this is an option or not // if ('-' != *arg) { // // No, then it must be an address, call GetExpression // ParsedArgs -> Address = GetExpression(arg); // // Assume user competence and store the result... // Add the value to the the ParsedArgs structure. // Note that if > 1 address is given, this function // simply uses the last one specified // } else { // // OK, it's an option...Process appropriately // switch (*(arg+1)) { // // Reset Log // case 'r': ParsedArgs -> ResetLog = TRUE; break; // // Set lines to display // case 'l': arg = strtok(NULL, argDelims); if (NULL != arg) { // // Assume user competence and get the decimal string // if (!sscanf(arg, "%d", &(ParsedArgs -> NumberLines))) { ParsedArgs -> NumberLines = 0; } TRACE_SPEW("Parsed -l command with %d lines\n", ParsedArgs -> NumberLines); } break; // // Search the log // case 's': ParsedArgs -> SearchLog = TRUE; ParsedArgs -> SearchString = strtok(NULL, argDelims); break; default: dprintf("Unknown option %c\n", *(arg+1)); break; } } arg = strtok(NULL, argDelims); } return; } ULONG64 USBLOG_SearchLog( PUSB_LOG Log, ULONG64 SearchBegin, PCHAR SearchString ) { ULONG64 firstFoundEntry; USBLOG_TAG_READ tagArray[32]; ULONG index; PCHAR searchToken; TRACE_SPEW("Entering USBLOG_SearchLog looking for %s\n", SearchString); index = 0; firstFoundEntry = 0; searchToken = strtok(SearchString, LOG_SEARCH_DELIMS); while (index < 32 && NULL != searchToken) { TRACE_SPEW("Adding %s to tag array\n", searchToken); ConvertStringToTag(searchToken, &(tagArray[index++])); searchToken = strtok(NULL, LOG_SEARCH_DELIMS); } if (index > 0) { firstFoundEntry = SearchLogForTag(Log, SearchBegin, index, tagArray); } return (firstFoundEntry); } // // Local logging function definitions. // BOOLEAN DumpLog( IN PUSB_LOG Log, IN BOOLEAN StartFromTop, IN BOOLEAN Scroll ) { ULONG lineCount; BOOLEAN resetStatus; ULONG64 currViewTop; ULONG64 currViewBottom; // // Check if the log has been opened/reset yet // GetCurrentView(Log, &currViewTop, &currViewBottom); if (0 == currViewTop || StartFromTop) { // // Reset the log and return FALSE if the reset failed // resetStatus = ResetLog(Log); if (!resetStatus) { return (FALSE); } Scroll = FALSE; } // // Call the log's dump routine based on the direction // if (Scroll) { TRACE_SPEW("Checking lines to dump: %d\n", Log -> LinesToDump); if (Log -> LinesToDump < 0) { LogViewScrollUp(Log); } else { LogViewScrollDown(Log); } } DisplayCurrentView(Log); return (TRUE); } BOOLEAN ResetLog( IN PUSB_LOG Log ) { ULONG bytesRead; ULONG64 symbolAddress; ULONG readStatus; // // Get the address of the start symbol, the end symbol, and the // current pointer symbol // symbolAddress = GetExpression(Log -> LogStartName); if (0 != symbolAddress) { if (!ReadPointer(symbolAddress, &(Log -> LogStart))) { dprintf("Unable to read %p\n", symbolAddress); Log -> LogStart = 0; } } symbolAddress = GetExpression(Log -> LogEndName); if (0 != symbolAddress) { if (!ReadPointer(symbolAddress, &(Log -> LogEnd))) { dprintf("Unable to read %p\n", symbolAddress); Log -> LogEnd = 0; } } symbolAddress = GetExpression(Log -> LogPtrName); if (0 != symbolAddress) { if (!ReadPointer(symbolAddress, &(Log -> LogPtr))) { dprintf("Unable to read %p\n", symbolAddress); Log -> LogPtr= 0; } } if ( (0 == Log -> LogStart) || (0 == Log -> LogEnd) || (0 == Log -> LogPtr) ) { dprintf("Unable to reset log\n"); return (FALSE); } SetCurrentView(Log, Log -> LogPtr, 0); return (TRUE); } VOID GetCurrentView( IN PUSB_LOG Log, OUT ULONG64 *CurrTop, OUT ULONG64 *CurrBottom ) { *CurrTop = Log -> LogCurrViewTop; *CurrBottom = Log -> LogCurrViewBottom; return; } VOID SetCurrentView( IN PUSB_LOG Log, IN ULONG64 NewTop, IN ULONG64 NewBottom ) { LONG lineCount; if (0 == NewTop && 0 == NewBottom) { return; } lineCount = abs(Log -> LinesToDump); if (0 == NewBottom) { // // Calculate the new bottom based on NewTop and the number of lines to // be displayed in the log. // NewBottom = NewTop + lineCount; if (NewTop >= Log -> LogPtr) { lineCount -= (ULONG) CalcNumberLines(NewTop, Log -> LogEnd); if (lineCount > 0) { if (LogHasRolledOver(Log)) { NewBottom = Log -> LogStart + lineCount - 1; if (NewBottom >= Log -> LogPtr) { NewBottom = Log -> LogPtr - 1; } } else { NewBottom = Log -> LogEnd; } } } else { if (lineCount > CalcNumberLines(NewTop, Log -> LogPtr - 1)) { NewBottom = Log -> LogPtr - 1; } } } else if (0 == NewTop) { // // NULL == NewTop -- Need to calculate the NewTop of the view // NewTop = NewBottom - lineCount; if (NewBottom <= Log -> LogPtr - 1) { lineCount -= (ULONG) CalcNumberLines(Log -> LogStart, NewBottom); if (lineCount > 0) { NewTop = Log -> LogEnd - lineCount + 1; if (NewTop < Log -> LogPtr) { NewTop = Log -> LogPtr; } } } else { if (NewTop < Log -> LogPtr) { NewTop = Log -> LogPtr; } } } TRACE_SPEW("Set CurrentView NewTop (0x%08x) NewBottom(0x%08x)\n", NewTop, NewBottom); Log -> LogCurrViewTop = NewTop; Log -> LogCurrViewBottom = NewBottom; return; } ULONG64 GetEntryBefore( IN PUSB_LOG Log, IN ULONG64 Entry ) { if (Entry == Log -> LogEnd) { if (LogHasRolledOver(Log)) { return (Log -> LogStart); } return (0); } // // Check to see if we hit the most recent entry in the log. If so we've // hit the end of the log and will loop again. return NULL. // return ( ((Entry + 1) == Log -> LogPtr) ? 0 : Entry+1); } ULONG64 GetEntryAfter( IN PUSB_LOG Log, IN ULONG64 Entry ) { if (Entry == Log -> LogPtr) { return (0); } if (Entry == Log -> LogStart) { return (Log -> LogEnd); } return (Entry-1); } VOID GetLogEntryTag( IN ULONG64 Entry, OUT PUSBLOG_TAG_READ Tag ) { ULONG bytesRead; InitTypeRead(Entry, uhcd!USB_LOG_ENTRY); // ReadMemory( Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0) -> Tag), Tag, sizeof(*Tag), &bytesRead); Tag->TagValue = (ULONG) ReadField(Tag.TagValue); return; } VOID GetLogEntryParams( IN ULONG64 Entry, OUT ULONG64 *Param1, OUT ULONG64 *Param2, OUT ULONG64 *Param3 ) { ULONG bytesRead; InitTypeRead(Entry, uhcd!USB_LOG_ENTRY); *Param1 = ReadField(Param1); *Param2 = ReadField(Param2); *Param3 = ReadField(Param3); // ReadMemory(Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0)-> Param1), Param1, sizeof(*Param1), &bytesRead); // ReadMemory(Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0)-> Param3), Param2, sizeof(*Param2), &bytesRead); // ReadMemory(Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0)-> Param2), Param3, sizeof(*Param3), &bytesRead); return; } ULONG64 SearchLogForTag( IN PUSB_LOG Log, IN ULONG64 SearchBegin, IN ULONG TagCount, IN USBLOG_TAG_READ TagArray[] ) { ULONG tagIndex; ULONG64 currEntry; USBLOG_TAG_READ currTag; // // Start the search at the most recent log entry // currEntry = SearchBegin; while (currEntry != 0) { GetLogEntryTag(currEntry, &currTag); for (tagIndex = 0; tagIndex < TagCount; tagIndex++) { if (TagArray[tagIndex].TagValue == currTag.TagValue) { return (currEntry); } } currEntry = GetEntryBefore(Log, currEntry); } return (0); } VOID ConvertStringToTag( IN PCHAR TagString, OUT PUSBLOG_TAG_READ Tag ) { USBLOG_TAG_READ tag; ULONG shiftAmount; // // Since a Tag is four characters long, this routine will convert only // the first four characters even though the string might be longer // tag.TagValue = 0; shiftAmount = 0; while (tag.TagValue < 0x01000000 && *TagString) { tag.TagValue += (*TagString) << shiftAmount; TagString++; shiftAmount += 8; } *Tag = tag; return; } VOID ConvertTagToString( IN PUSBLOG_TAG_READ Tag, IN PCHAR String, IN ULONG StringLength ) { ULONG tagIndex; for (tagIndex = 0; tagIndex < 4 && tagIndex < StringLength-1; tagIndex++) { *String = Tag -> TagChars[tagIndex]; *String++; } *String = '\0'; return; } BOOLEAN IsValidEntry( ULONG64 Entry ) { USBLOG_TAG_READ tag; GetLogEntryTag(Entry, &tag); return (0 != tag.TagValue); } VOID LogViewScrollUp( IN PUSB_LOG Log ) { ULONG64 newBottom; TRACE_SPEW("In ScrollUp routine\n"); newBottom = GetEntryAfter(Log, Log -> LogCurrViewTop); if (newBottom) { SetCurrentView(Log, 0, newBottom); } return; } VOID LogViewScrollDown( IN PUSB_LOG Log ) { ULONG64 newTop; TRACE_SPEW("In ScrollDown routine\n"); newTop = GetEntryBefore(Log, Log -> LogCurrViewBottom); if (newTop) { SetCurrentView(Log, newTop, 0); } return; } VOID DisplayCurrentView( IN PUSB_LOG Log ) { ULONG64 viewTop; ULONG64 viewBottom; ULONG64 currEntry; USBLOG_TAG_READ currTag; CHAR TagString[TAG_STRING_LENGTH]; ULONG lineCount; ULONG64 param1; ULONG64 param2; ULONG64 param3; PCHAR desc; // // Display the header // DisplayHeader(); // // Determine which line of the log to begin displaying // GetCurrentView(Log, &viewTop, &viewBottom); // // Start displaying lines and stop when we hit the end of the log // or we have displayed the requested number of lines // // // Check first to see if the top of the view is the most recent entry // if (IsMostRecentEntry(Log, viewTop)) { dprintf("Top of log...\n"); } currEntry = viewTop; while (1) { // // Display a line of the log // GetLogEntryTag(currEntry, &currTag); ConvertTagToString(&currTag, TagString, TAG_STRING_LENGTH); GetLogEntryParams(currEntry, ¶m1, ¶m2, ¶m3); DUMP_ADDRESS(currEntry); DUMP_STRING(" "); DUMP_STRING(TagString); DUMP_STRING(" "); DUMP_DWORD(param1); DUMP_STRING(" "); DUMP_DWORD(param2); DUMP_STRING(" "); DUMP_DWORD(param3); DUMP_STRING(" "); if (0 != Log -> DescRoutine) { desc = Log -> DescRoutine(currEntry); if (0 != desc) { DUMP_STRING(desc); } } END_LINE(); if (currEntry == viewBottom) { break; } currEntry = GetEntryBefore(Log, currEntry); } if (IsLeastRecentEntry(Log, currEntry)) { dprintf("Bottom of log...\n"); } return; } // // Local Function Definitions // VOID DisplayHeader( VOID ) { TRACE_SPEW("Entering dump default log header\n"); END_LINE(); PARAM("Entry"); TAG("Tag"); PARAM("Param1"); PARAM("Param2"); PARAM("Param3"); DESC("Description"); END_LINE(); DUMP_STRING("------------------------------------------------------------------"); END_LINE(); return; } VOID USBLOG_Usage( VOID ) { dprintf("!usblog [addr] [-r] [-s str] [-l n]\n" " - {USBHUB | USBD | UHCD | OpenHCI}\n" " [addr] - address to begin dumping from in \n" " [-r] - reset the log to dump from most recent entry\n" " [-s str] - search for first instance of a particular tag\n" " from the current position; str should be a list\n" " of tags delimited by comma's with no whitespace\n" " [-l n] - set the number of lines to display at a time to n\n"); dprintf("\n"); return; }