/*++ Copyright (c) 2012 Minoca Corp. All Rights Reserved Module Name: objects.c Abstract: This module implements Object Manager related debugger extensions. Author: Evan Green 11-Sep-2012 Environment: Debug Client --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include #include #include #include // // ---------------------------------------------------------------- Definitions // #define MAX_OBJECT_NAME 512 #define ROOT_OBJECT_NAME "kernel!ObRootObject" #define MALLOC(_x) malloc(_x) #define FREE(_x) free(_x) // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // INT ExtpHandleObjectCommand ( PDEBUGGER_CONTEXT Context, PSTR Command, ULONGLONG Address ); INT ExtpPrintObject ( PDEBUGGER_CONTEXT Context, ULONG IndentationLevel, ULONGLONG ObjectAddress, BOOL OneLiner, BOOL FullPath, BOOL PrintChildren, BOOL FullyRecurse ); VOID ExtpPrintIndentation ( ULONG IndentationLevel ); // // -------------------------------------------------------------------- Globals // // // ------------------------------------------------------------------ Functions // INT ExtObject ( PDEBUGGER_CONTEXT Context, PSTR Command, ULONG ArgumentCount, PSTR *ArgumentValues ) /*++ Routine Description: This routine prints out the contents of an Object: Address - Supplies the address of the Object. Arguments: Context - Supplies a pointer to the debugger applicaton context, which is an argument to most of the API functions. Command - Supplies the subcommand entered. This parameter is unused. ArgumentCount - Supplies the number of arguments in the ArgumentValues array. ArgumentValues - Supplies the values of each argument. This memory will be reused when the function returns, so extensions must not touch this memory after returning from this call. Return Value: 0 if the debugger extension command was successful. Returns an error code if a failure occurred along the way. --*/ { ULONG AddressSize; ULONG ArgumentIndex; ULONG BytesRead; ULONGLONG ObjectAddress; ULONGLONG RootObjectAddress; INT Status; AddressSize = DbgGetTargetPointerSize(Context); // // At least one parameter is required. // if (ArgumentCount < 2) { // // Attempt to find the root object. // Status = DbgEvaluate(Context, ROOT_OBJECT_NAME, &RootObjectAddress); if (Status == 0) { Status = DbgReadMemory(Context, TRUE, RootObjectAddress, AddressSize, &RootObjectAddress, &BytesRead); if ((Status != 0) || (BytesRead != AddressSize)) { DbgOut("Unable to find ObRootObject.\n"); if (Status == 0) { Status = EINVAL; } return Status; } ExtpHandleObjectCommand(Context, Command, RootObjectAddress); } else { DbgOut("Error: Unable to evaluate %s.\n", ROOT_OBJECT_NAME); return Status; } } // // Loop through each argument, evaluate the address, and print the // namespace tree at that object. // for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) { Status = DbgEvaluate(Context, ArgumentValues[ArgumentIndex], &ObjectAddress); if (Status != 0) { DbgOut("Failed to evaluate address at \"%s\".\n", ArgumentValues[ArgumentIndex]); } ExtpHandleObjectCommand(Context, Command, ObjectAddress); if (ArgumentIndex != ArgumentCount - 1) { DbgOut("\n----\n"); } } DbgOut("\n"); return 0; } // // --------------------------------------------------------- Internal Functions // INT ExtpHandleObjectCommand ( PDEBUGGER_CONTEXT Context, PSTR Command, ULONGLONG Address ) /*++ Routine Description: This routine handles an object command. Arguments: Context - Supplies a pointer to the application context. Command - Supplies a pointer to the command to handle. Address - Supplies the address of the object to print. Return Value: 0 on success. Returns an error code on failure. --*/ { INT Success; if (Command == NULL) { Success = ExtpPrintObject(Context, 1, Address, FALSE, TRUE, FALSE, FALSE); } else if (strcmp(Command, "list") == 0) { Success = ExtpPrintObject(Context, 0, Address, TRUE, FALSE, TRUE, FALSE); } else if (strcmp(Command, "tree") == 0) { Success = ExtpPrintObject(Context, 0, Address, TRUE, FALSE, TRUE, TRUE); } else if (strcmp(Command, "help") == 0) { DbgOut("Valid subcommands are:\n " "!object - print an object.\n " "!object.list - print an object and its children.\n " "!object.tree - print the entire tree underneath " "the given object.\n"); Success = 0; } else { DbgOut("Error: Invalid subcommand. Run !object.help for " "detailed usage.\n"); Success = 0; } return Success; } INT ExtpPrintObject ( PDEBUGGER_CONTEXT Context, ULONG IndentationLevel, ULONGLONG ObjectAddress, BOOL OneLiner, BOOL FullPath, BOOL PrintChildren, BOOL FullyRecurse ) /*++ Routine Description: This routine prints out an object. Arguments: Context - Supplies a pointer to the application context. IndentationLevel - Supplies the current indentation level that the object should be printed at. ObjectAddress - Supplies the virtual address (in the target) of the object to print. OneLiner - Supplies a boolean indicating that only one line of text should be printed. FullPath - Supplies a boolean indicating whether the full object path should be printed or not. PrintChildren - Supplies a boolean indicating whether or not the routine should recurse into printing the object's direct children. FullyRecurse - Supplies a boolean indicating whether or not the routine should fully recurse to all descendents of the object. Return Value: 0 on success. Returns an error code on failure. --*/ { ULONG AddressSize; ULONG BytesRead; ULONGLONG ChildAddress; ULONGLONG ChildListHeadAddress; ULONG ChildListOffset; ULONGLONG CurrentListEntryAddress; PSTR CurrentName; ULONG CurrentNameSize; ULONGLONG CurrentObjectName; ULONGLONG CurrentObjectParent; PVOID Data; ULONG DataSize; ULONGLONG FirstChild; BOOL FirstWaiter; PSTR FullName; ULONG FullNameSize; PVOID ListEntryData; ULONG ListEntryDataSize; PTYPE_SYMBOL ListEntryType; ULONGLONG ListHeadAddress; ULONGLONG LockHeld; PSTR NewFullName; ULONGLONG NextObjectAddress; ULONGLONG NextSibling; PVOID ObjectData; ULONG ObjectDataSize; ULONGLONG ObjectParent; PTYPE_SYMBOL ObjectType; ULONGLONG ObjectTypeValue; ULONGLONG OwningThread; ULONGLONG RootObjectAddress; ULONG SiblingEntryOffset; INT Status; ULONGLONG WaitBlockEntryAddress; ULONG WaitBlockEntryListEntryOffset; PTYPE_SYMBOL WaitBlockEntryType; ULONG WaitersOffset; ULONG WaitQueueOffset; PTYPE_SYMBOL WaitQueueType; AddressSize = DbgGetTargetPointerSize(Context); CurrentName = NULL; Data = NULL; FullName = NULL; FullNameSize = 0; ListEntryData = NULL; ObjectData = NULL; RootObjectAddress = 0; ExtpPrintIndentation(IndentationLevel); // // Attempt to read the object header. // Status = DbgReadTypeByName(Context, ObjectAddress, "OBJECT_HEADER", &ObjectType, &ObjectData, &ObjectDataSize); if (Status != 0) { DbgOut("Error: Could not read object.\n"); goto PrintObjectEnd; } Status = DbgReadIntegerMember(Context, ObjectType, "Type", ObjectAddress, ObjectData, ObjectDataSize, &ObjectTypeValue); if (Status != 0) { goto PrintObjectEnd; } Status = DbgReadIntegerMember(Context, ObjectType, "Name", ObjectAddress, ObjectData, ObjectDataSize, &CurrentObjectName); if (Status != 0) { goto PrintObjectEnd; } Status = DbgReadIntegerMember(Context, ObjectType, "Parent", ObjectAddress, ObjectData, ObjectDataSize, &ObjectParent); if (Status != 0) { goto PrintObjectEnd; } CurrentObjectParent = ObjectParent; if ((ObjectTypeValue == ObjectInvalid) || (ObjectTypeValue >= ObjectMaxTypes)) { DbgOut("%08I64x probably not an object, has type %I64x.\n", ObjectAddress, ObjectTypeValue); Status = EINVAL; goto PrintObjectEnd; } // // If the full name should be printed, collect that now. // CurrentName = MALLOC(MAX_OBJECT_NAME); if (CurrentName == NULL) { DbgOut("Error: Memory allocation failure.\n"); Status = ENOMEM; goto PrintObjectEnd; } if (FullPath != FALSE) { // // Attempt to find the root object. // Status = DbgEvaluate(Context, ROOT_OBJECT_NAME, &RootObjectAddress); if (Status == 0) { Status = DbgReadMemory(Context, TRUE, RootObjectAddress, AddressSize, &RootObjectAddress, &BytesRead); if ((Status != 0) || (BytesRead != sizeof(PVOID))) { DbgOut("Unable to find ObRootObject.\n"); RootObjectAddress = 0; if (Status == 0) { Status = EINVAL; } goto PrintObjectEnd; } } else { RootObjectAddress = 0; } // // Iterate up through the tree towards the root, prepending the object // name at each step. // while (TRUE) { // // Read in the current object's name string, or at least as much of // it as this extension cares to read. // if (CurrentObjectName == 0) { strncpy(CurrentName, "", MAX_OBJECT_NAME); } else { Status = DbgReadMemory(Context, TRUE, CurrentObjectName, MAX_OBJECT_NAME, CurrentName, &BytesRead); if (Status != 0) { DbgOut("Error: Unable to read object name at 0x%08I64x.\n", CurrentObjectName); goto PrintObjectEnd; } // // Terminate the string. // if (BytesRead == MAX_OBJECT_NAME) { CurrentName[MAX_OBJECT_NAME - 1] = '\0'; } else { CurrentName[BytesRead] = '\0'; } } // // Create a new full path big enough to hold everything, and copy // it in. // CurrentNameSize = strlen(CurrentName); NewFullName = MALLOC(CurrentNameSize + FullNameSize + 2); if (NewFullName == NULL) { DbgOut("Error: Memory allocation failure for %d bytes.\n", CurrentNameSize + FullNameSize + 2); Status = ENOMEM; goto PrintObjectEnd; } strcpy(NewFullName, "/"); strcat(NewFullName, CurrentName); if (FullName != NULL) { strcat(NewFullName, FullName); FREE(FullName); } FullName = NewFullName; FullNameSize += CurrentNameSize + 1; // // Find the parent, read it in, and loop. // if ((CurrentObjectParent == 0) || (CurrentObjectParent == RootObjectAddress)) { break; } assert(Data == NULL); Status = DbgReadType(Context, CurrentObjectParent, ObjectType, &Data, &DataSize); if (Status != 0) { DbgOut("Error reading object at 0x%08I64x.\n", CurrentObjectParent); goto PrintObjectEnd; } Status = DbgReadIntegerMember(Context, ObjectType, "Name", ObjectAddress, Data, DataSize, &CurrentObjectName); if (Status != 0) { goto PrintObjectEnd; } Status = DbgReadIntegerMember(Context, ObjectType, "Parent", ObjectAddress, Data, DataSize, &CurrentObjectParent); if (Status != 0) { goto PrintObjectEnd; } free(Data); Data = NULL; } } else { // // Just read in this object's name. // if (CurrentObjectName == 0) { CurrentName[0] = '\0'; } else { Status = DbgReadMemory(Context, TRUE, CurrentObjectName, MAX_OBJECT_NAME, CurrentName, &BytesRead); if (Status != 0) { DbgOut("Error: Unable to read object name at 0x%08I64x.\n", CurrentObjectName); goto PrintObjectEnd; } // // Terminate the string. // if (BytesRead == MAX_OBJECT_NAME) { CurrentName[MAX_OBJECT_NAME - 1] = '\0'; } else { CurrentName[BytesRead] = '\0'; } } FullName = CurrentName; CurrentName = NULL; } // // Get some attributes. // Status = DbgReadIntegerMember(Context, ObjectType, "SiblingEntry.Next", ObjectAddress, ObjectData, ObjectDataSize, &NextSibling); if (Status != 0) { goto PrintObjectEnd; } Status = DbgGetMemberOffset(ObjectType, "SiblingEntry", &SiblingEntryOffset, NULL); if (Status != 0) { goto PrintObjectEnd; } Status = DbgGetMemberOffset(ObjectType, "ChildListHead", &ChildListOffset, NULL); if (Status != 0) { goto PrintObjectEnd; } Status = DbgGetMemberOffset(ObjectType, "WaitQueue", &WaitQueueOffset, NULL); if (Status != 0) { goto PrintObjectEnd; } SiblingEntryOffset /= BITS_PER_BYTE; ChildListOffset /= BITS_PER_BYTE; WaitQueueOffset /= BITS_PER_BYTE; Status = DbgReadIntegerMember(Context, ObjectType, "ChildListHead.Next", ObjectAddress, ObjectData, ObjectDataSize, &FirstChild); if (Status != 0) { goto PrintObjectEnd; } Status = DbgGetTypeByName(Context, "LIST_ENTRY", &ListEntryType); if (Status != 0) { goto PrintObjectEnd; } // // Print out the one line version or the detailed version. // if (OneLiner != FALSE) { DbgOut("0x%08I64x ", ObjectAddress); DbgPrintTypeMember(Context, ObjectAddress, ObjectData, ObjectDataSize, ObjectType, "Type", 0, 0); DbgOut(" %s\n", FullName); } else { DbgOut("%20s : 0x%08I64x\n", "Object", ObjectAddress); ExtpPrintIndentation(IndentationLevel); DbgOut("%20s : ", "Type"); DbgPrintTypeMember(Context, ObjectAddress, ObjectData, ObjectDataSize, ObjectType, "Type", 0, 0); DbgOut("\n"); ExtpPrintIndentation(IndentationLevel); DbgOut("%20s : %s\n", "Name", FullName); ExtpPrintIndentation(IndentationLevel); Status = DbgReadIntegerMember(Context, ObjectType, "WaitQueue.Lock.LockHeld", ObjectAddress, ObjectData, ObjectDataSize, &LockHeld); if ((Status == 0) && (LockHeld != FALSE)) { Status = DbgReadIntegerMember(Context, ObjectType, "WaitQueue.Lock.OwningThread", ObjectAddress, ObjectData, ObjectDataSize, &OwningThread); if (Status == 0) { DbgOut("%20s : 0x%08x.\n", "Locked", OwningThread); ExtpPrintIndentation(IndentationLevel); } } // // Print various attributes of the object. // DbgOut("%20s : Parent 0x%08x Sibling ", "Relatives", ObjectParent); NextObjectAddress = NextSibling - SiblingEntryOffset; if (NextSibling == 0) { DbgOut("NULL"); NextObjectAddress = 0; } else if (NextSibling == ObjectAddress + SiblingEntryOffset) { DbgOut("NONE"); NextObjectAddress = 0; } else { DbgOut("0x%08I64x", NextObjectAddress); } DbgOut(" Child "); if (FirstChild == 0) { DbgOut("NULL\n"); } else if (FirstChild == ObjectAddress + ChildListOffset) { DbgOut("NONE\n"); } else { ChildAddress = FirstChild - ChildListOffset; DbgOut("0x%08I64x\n", ChildAddress); } ExtpPrintIndentation(IndentationLevel); DbgOut("%20s : ", "State"); DbgPrintTypeMember(Context, ObjectAddress, ObjectData, ObjectDataSize, ObjectType, "WaitQueue.State", 0, 0); DbgOut("\n"); ExtpPrintIndentation(IndentationLevel); DbgOut("%20s : ", "Ref Count"); DbgPrintTypeMember(Context, ObjectAddress, ObjectData, ObjectDataSize, ObjectType, "ReferenceCount", 0, 0); DbgOut("\n"); ExtpPrintIndentation(IndentationLevel); DbgOut("%20s : ", "Flags"); DbgPrintTypeMember(Context, ObjectAddress, ObjectData, ObjectDataSize, ObjectType, "Flags", 0, 0); DbgOut("\n"); ExtpPrintIndentation(IndentationLevel); // // Print a list of all threads waiting on this object. // DbgOut("%20s : ", "Waiters"); Status = DbgGetTypeByName(Context, "WAIT_QUEUE", &WaitQueueType); if (Status != 0) { goto PrintObjectEnd; } Status = DbgGetMemberOffset(WaitQueueType, "Waiters", &WaitersOffset, NULL); if (Status != 0) { goto PrintObjectEnd; } WaitersOffset /= BITS_PER_BYTE; Status = DbgGetTypeByName(Context, "WAIT_BLOCK_ENTRY", &WaitBlockEntryType); if (Status != 0) { goto PrintObjectEnd; } Status = DbgGetMemberOffset(WaitBlockEntryType, "WaitListEntry", &WaitBlockEntryListEntryOffset, NULL); if (Status != 0) { goto PrintObjectEnd; } WaitBlockEntryListEntryOffset /= BITS_PER_BYTE; FirstWaiter = TRUE; ListHeadAddress = ObjectAddress + WaitQueueOffset + WaitersOffset; Status = DbgReadIntegerMember(Context, ObjectType, "WaitQueue.Waiters.Next", ObjectAddress, ObjectData, ObjectDataSize, &CurrentListEntryAddress); if (Status != 0) { goto PrintObjectEnd; } while (CurrentListEntryAddress != ListHeadAddress) { if (FirstWaiter == FALSE) { DbgOut(" : "); } else { FirstWaiter = FALSE; } WaitBlockEntryAddress = CurrentListEntryAddress - WaitBlockEntryListEntryOffset; DbgOut("0x%08I64x\n", WaitBlockEntryAddress); ExtpPrintIndentation(IndentationLevel); assert(ListEntryData == NULL); Status = DbgReadType(Context, CurrentListEntryAddress, ListEntryType, &ListEntryData, &ListEntryDataSize); if (Status != 0) { goto PrintObjectEnd; } Status = DbgReadIntegerMember(Context, ListEntryType, "Next", 0, ListEntryData, ListEntryDataSize, &CurrentListEntryAddress); if (Status != 0) { goto PrintObjectEnd; } free(ListEntryData); ListEntryData = NULL; } DbgOut("\n"); } // // If children should be printed, go through their list. // if (PrintChildren != FALSE) { // // Get the first child and enumerate until no more siblings of that // child are found. // ChildListHeadAddress = ObjectAddress + ChildListOffset; ObjectAddress = FirstChild; while ((ObjectAddress != (INTN)NULL) && (ObjectAddress != ChildListHeadAddress)) { Status = ExtpPrintObject(Context, IndentationLevel + 1, ObjectAddress - SiblingEntryOffset, TRUE, FALSE, FullyRecurse, FullyRecurse); if (Status != 0) { DbgOut("Failed to print child at 0x%I64x.\n", ObjectAddress); goto PrintObjectEnd; } assert(ListEntryData == NULL); Status = DbgReadType(Context, ObjectAddress, ListEntryType, &ListEntryData, &ListEntryDataSize); if (Status != 0) { DbgOut("Error: Could not read LIST_ENTRY at 0x%I64x.\n", ObjectAddress); goto PrintObjectEnd; } Status = DbgReadIntegerMember(Context, ListEntryType, "Next", 0, ListEntryData, ListEntryDataSize, &ObjectAddress); if (Status != 0) { goto PrintObjectEnd; } free(ListEntryData); ListEntryData = NULL; } } Status = 0; PrintObjectEnd: if (ObjectData != NULL) { free(ObjectData); } if (Data != NULL) { free(Data); } if (ListEntryData != NULL) { free(ListEntryData); } if (CurrentName != NULL) { FREE(CurrentName); } if (FullName != NULL) { FREE(FullName); } return Status; } VOID ExtpPrintIndentation ( ULONG IndentationLevel ) /*++ Routine Description: This routine prints out some indentation spaces. Arguments: IndentationLevel - Supplies the current indentation level. Return Value: None. --*/ { DbgOut("%*s", IndentationLevel, ""); return; }