/*++ Copyright (c) 1999 Microsoft Corporation Module Name: dbgrm.c - DbgExtension Structure information specific to RM APIs Abstract: Revision History: Who When What -------- -------- ---------------------------------------------- josephj 03-01-99 Created Notes: --*/ #ifdef TESTPROGRAM #include "c.h" #else #include "precomp.h" #endif #include "util.h" #include "parse.h" #include "dbgrm.h" enum { typeid_NULL, typeid_RM_OBJECT_HEADER, typeid_RM_TASK, typeid_RM_ASSOCIATIONS, typeid_RM_GROUP, typeid_RM_STACK_RECORD, typeid_RM_OBJECT_LOG, typeid_RM_OBJECT_TREE }; // // STRUCTURES CONCERNING TYPE "OBJECT_HEADER" // // Actually handles dumping of object info. // void RmDumpObj( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ); // Actually handles dumping of task info. // void RmDumpTask( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ); // Actually handles dumping of task info. // void RmDumpGroup( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ); // Actually handles dumping of task info. // void RmDumpAssociations( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ); // Actually handles dumping of task info. // void RmDumpStackRecord( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ); // Actually handles dumping of object log info. // void RmDumpObjectLog( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ); // Actually handles dumping of object decendents tree // void RmDumpObjectTree( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ); // Node function for dumping one node of the list of children. // ULONG NodeFunc_DumpObjectTree ( UINT_PTR uNodeAddr, UINT uIndex, void *pvContext ); BITFIELD_INFO rgRM_OBJECT_STATE[] = { { "O_ALLOC", RMOBJSTATE_ALLOCMASK, RMOBJSTATE_ALLOCATED }, { "O_DEALLOC", RMOBJSTATE_ALLOCMASK, RMOBJSTATE_DEALLOCATED }, #if 0 // don't want this -- as it gets displayed for non-task, looking wierd. { "T_IDLE", RMTSKSTATE_MASK, RMTSKSTATE_IDLE }, #endif // 0 { "T_STARTING", RMTSKSTATE_MASK, RMTSKSTATE_STARTING }, { "T_ACTIVE", RMTSKSTATE_MASK, RMTSKSTATE_ACTIVE }, { "T_PENDING", RMTSKSTATE_MASK, RMTSKSTATE_PENDING }, { "T_ENDING", RMTSKSTATE_MASK, RMTSKSTATE_ENDING }, { "T_DELAYED", RMTSKDELSTATE_MASK, RMTSKDELSTATE_DELAYED }, { "T_ABORT_DELAY", RMTSKABORTSTATE_MASK, RMTSKABORTSTATE_ABORT_DELAY }, { NULL } }; TYPE_INFO type_RM_OBJECT_HEADER = { "RM_OBJECT_HEADER", "obj", typeid_RM_OBJECT_HEADER, 0, //fTYPEINFO_ISLIST, // Flags sizeof(RM_OBJECT_HEADER), NULL, // FIELD_INFO 0, // offset to next object. NULL, // rgBitFieldInfo RmDumpObj // pfnSpecializedDump }; TYPE_INFO type_RM_TASK = { "RM_TASK", "tsk", typeid_RM_TASK, 0, //fTYPEINFO_ISLIST, // Flags sizeof(RM_TASK), NULL, // FIELD_INFO 0, // offset to next object. NULL, // rgBitFieldInfo RmDumpTask // pfnSpecializedDump }; TYPE_INFO type_RM_GROUP = { "RM_GROUP", "grp", typeid_RM_GROUP, 0, //fTYPEINFO_ISLIST, // Flags sizeof(RM_GROUP), NULL, // FIELD_INFO 0, // offset to next object. NULL, // rgBitFieldInfo RmDumpGroup // pfnSpecializedDump }; TYPE_INFO type_RM_ASSOCIATIONS = { "RM_ASSOCIATIONS", "asc", typeid_RM_ASSOCIATIONS, 0, //fTYPEINFO_ISLIST, // Flags 1, //This is not really an object, but we must have nonzero size. NULL, // FIELD_INFO 0, // offset to next object. NULL, // rgBitFieldInfo RmDumpAssociations // pfnSpecializedDump }; TYPE_INFO type_RM_STACK_RECORD = { "RM_STACK_RECORD", "sr", typeid_RM_STACK_RECORD, 0, //fTYPEINFO_ISLIST, // Flags sizeof(RM_STACK_RECORD), NULL, // FIELD_INFO 0, // offset to next object. NULL, // rgBitFieldInfo RmDumpStackRecord // pfnSpecializedDump }; TYPE_INFO type_RM_OBJECT_LOG = { "RM_OBJECT_LOG", "log", //typeid_RM_STACK_RECORD, typeid_RM_OBJECT_LOG, 0, //fTYPEINFO_ISLIST, // Flags 1, //This is not really an object, but we must have nonzero size. NULL, // FIELD_INFO 0, // offset to next object. NULL, // rgBitFieldInfo RmDumpObjectLog // pfnSpecializedDump }; TYPE_INFO type_RM_OBJECT_TREE = { "RM_OBJECT_TREE", "tree", typeid_RM_OBJECT_TREE, 0, //fTYPEINFO_ISLIST, // Flags 1, //This is not really an object, but we must have nonzero size. NULL, // FIELD_INFO 0, // offset to next object. NULL, // rgBitFieldInfo RmDumpObjectTree // pfnSpecializedDump }; TYPE_INFO *g_rgRM_Types[] = { &type_RM_OBJECT_HEADER, &type_RM_TASK, &type_RM_GROUP, &type_RM_ASSOCIATIONS, &type_RM_STACK_RECORD, &type_RM_OBJECT_LOG, &type_RM_OBJECT_TREE, NULL }; UINT_PTR RM_ResolveAddress( TYPE_INFO *pType ); NAMESPACE RM_NameSpace = { g_rgRM_Types, NULL, // g_rgRM_Globals, RM_ResolveAddress }; UINT_PTR RM_ResolveAddress( TYPE_INFO *pType ) { return 0; } void do_rm(PCSTR args) { DBGCOMMAND *pCmd = Parse(args, &RM_NameSpace); if (pCmd) { DumpCommand(pCmd); DoCommand(pCmd, NULL); FreeCommand(pCmd); pCmd = NULL; } return; } void do_help(PCSTR args) { return; } void dump_object_fields(UINT_PTR uAddr, PRM_OBJECT_HEADER pObj); // Actually handles dumping of object info. // void RmDumpObj( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ) /*++ !rm obj 0x838c7560 Object 0x838c7560 (LocalIP) Hdr Sig :A13L State:0xc4db69b3 Refs:990 pLock: 0x838c7560 pSIinfo:0xfdd0a965 pDInfo :0xd54d947c pParent: 0x2995941a pRoot:0x060af4a8 pHLink :0xce4294fe HdrSize: 0x123 Assoc:909 --*/ { RM_OBJECT_HEADER Obj; bool fRet; do { char rgDescriptionBuf[256]; // First let's read the pObj structure. // fRet = dbgextReadMemory( uAddr, &Obj, sizeof(Obj), "RM_OBJECT_HEADER" ); if (!fRet) break; // Try to read the object's description. // fRet = dbgextReadSZ( (UINT_PTR) Obj.szDescription, rgDescriptionBuf, sizeof(rgDescriptionBuf), "Obj.szDescription" ); if (!fRet) break; MyDbgPrintf("\nObject 0x%p (%s)\n", uAddr, rgDescriptionBuf); dump_object_fields(uAddr, &Obj); } while(FALSE); } // Actually handles dumping of task info. // void RmDumpTask( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ) { RM_TASK Task; bool fRet; do { char rgDescriptionBuf[256]; // First let's read the pObj structure. // fRet = dbgextReadMemory( uAddr, &Task, sizeof(Task), "RM_OBJECT_HEADER" ); if (!fRet) break; // Try to read the object's description. // fRet = dbgextReadSZ( (UINT_PTR) Task.Hdr.szDescription, rgDescriptionBuf, sizeof(rgDescriptionBuf), "Task.Hdr.szDescription" ); if (!fRet) break; // Dump the object header // { MyDbgPrintf("\nTask 0x%p (%s)\n", uAddr, rgDescriptionBuf); dump_object_fields(uAddr, &Task.Hdr); } // // Now Dump the task-specific fields... // { /* TskHdr pfn: 0x5399424c State:0x812d7211(IDLE) SCtxt:0x050eefc4 pBlkTsk:0x377c74bc lnkFellows:0x2b88126f Pending Tasks 0x84215fa5 0xb51f9e9e 0x9e954e81 0x696095b9 0x0c07aeff */ MyDbgPrintf( " TaskHdr:\n" " pfn:0x%p SCtxt:0x%08lx\n", Task.pfnHandler, Task.SuspendContext ); MyDbgPrintf( " pBlkTsk:0x%p lnkFellows:0x%p\n", Task.pTaskIAmPendingOn, &(((PRM_TASK) uAddr)->linkFellowPendingTasks) ); // Note we can't use IsListEmpty because of the different address space. // if (Task.listTasksPendingOnMe.Flink == Task.listTasksPendingOnMe.Blink) { MyDbgPrintf(" No pending tasks.\n"); } else { MyDbgPrintf(" Pending tasks:\n"); dbgextDumpDLlist( (UINT_PTR) &(((PRM_TASK) uAddr)->listTasksPendingOnMe), FIELD_OFFSET(RM_TASK, linkFellowPendingTasks), "Pending tasks list" ); } } } while(FALSE); } void dbg_walk_rm_hash_table( PRM_HASH_TABLE pTable, UINT uContainingOffset, char *szDescription ); #if RM_EXTRA_CHECKING void dbg_print_rm_associations( PRM_HASH_TABLE pRmAssociationHashTable, UINT MaxToPrint ); void dbg_print_object_log_entries( UINT_PTR uObjectListOffset, UINT MaxToPrint ); #endif // RM_EXTRA_CHECKING // Actually handles dumping of task info. // void RmDumpGroup( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ) /* !rm grp 0x838c7560 Group 0x4d650b98 (LocalIP Group) of object 0x11eafd78 (Interface) Num:11 State:ENABLED pSInfo: 0x944b6d1b pULTsk: 0x8c312bca Members: 0x8db3267c 0xa639f663 0x8f3530a6 0xa4bfe0b9 0x995dd9bf 0x61e1344b 0xd6323f50 0x606339fd 0x2e8ed2a4 0x62e52f27 0xa82b59ab */ { RM_GROUP Group; bool fRet; do { char rgDescriptionBuf[256]; char rgOwningObjectDescriptionBuf[256]; // First let's read the Group structure. // fRet = dbgextReadMemory( uAddr, &Group, sizeof(Group), "RM_GROUP" ); if (!fRet) break; // Try to read the group's description. // fRet = dbgextReadSZ( (UINT_PTR) Group.szDescription, rgDescriptionBuf, sizeof(rgDescriptionBuf), "Obj.szDescription" ); if (!fRet) break; // Try to read the owning object's description. // do { UINT_PTR uAddress; fRet = dbgextReadUINT_PTR( (UINT_PTR) &(Group.pOwningObject->szDescription), &uAddress, "Owning Obj.szDescription ptr" ); if (!fRet) break; fRet = dbgextReadSZ( uAddress, rgOwningObjectDescriptionBuf, sizeof(rgOwningObjectDescriptionBuf), "Owning Obj.szDescription" ); } while (FALSE); if (!fRet) { *rgOwningObjectDescriptionBuf = 0; } MyDbgPrintf( "\nGroup 0x%p (%s) of object 0x%p (%s)\n", uAddr, rgDescriptionBuf, Group.pOwningObject, rgOwningObjectDescriptionBuf ); MyDbgPrintf( " Num:0x%08x State:%s pSInfo:0x%08x\n", Group.HashTable.NumItems, (Group.fEnabled) ? "ENABLED " : "DISABLED", Group.pStaticInfo ); MyDbgPrintf( " pULTsk:0x%08x\n", Group.pUnloadTask ); if (Group.HashTable.NumItems==0) { MyDbgPrintf(" No members.\n"); } else { MyDbgPrintf(" Members:\n"); dbg_walk_rm_hash_table( &Group.HashTable, FIELD_OFFSET(RM_OBJECT_HEADER, HashLink), "Group members" ); } } while(FALSE); } // Actually handles dumping of task info. // void RmDumpAssociations( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ) { /* !rm asc 0x9ba265f8 Associations for object 0x838c7560 (LocalIP): Child of 0x010091A0 (Globals) Parent of 0x00073558 (Task2) Parent of 0x00073920 (Task3a) Parent of 0x000739F8 (Task3b) */ RM_OBJECT_HEADER Obj; bool fRet; UINT uNumAssociations = -1; do { char rgDescriptionBuf[256]; // First let's read the pObj structure. // fRet = dbgextReadMemory( uAddr, &Obj, sizeof(Obj), "RM_OBJECT_HEADER" ); if (!fRet) break; // Try to read the object's description. // fRet = dbgextReadSZ( (UINT_PTR) Obj.szDescription, rgDescriptionBuf, sizeof(rgDescriptionBuf), "Obj.szDescription" ); if (!fRet) break; // Try to get the number of associations field in the diag info struct. // if (Obj.pDiagInfo != NULL) { bool fRet; UINT_PTR uNumItemsOffset = (UINT_PTR) &(Obj.pDiagInfo->AssociationTable.NumItems); fRet = dbgextReadUINT( uNumItemsOffset, &uNumAssociations, "pDiagInfo->AssociationTable.NumItems" ); if (!fRet) { uNumAssociations = (UINT) -1; } } if (uNumAssociations == 0) { MyDbgPrintf( "\nObject 0x%p (%s) has no associations.\n", uAddr, rgDescriptionBuf ); } else if (uNumAssociations == (UINT)-1) { MyDbgPrintf( "\nObject 0x%p (%s) associations are not available.\n", uAddr, rgDescriptionBuf ); } else { #if RM_EXTRA_CHECKING // Get the association hash table table. // RM_HASH_TABLE AssociationTable; MyDbgPrintf( "\nAssociations (50 max) for 0x%p (%s):\n", uAddr, rgDescriptionBuf ); fRet = dbgextReadMemory( (UINT_PTR) &(Obj.pDiagInfo->AssociationTable), &AssociationTable, sizeof(AssociationTable), "Association Table" ); if (!fRet) break; dbg_print_rm_associations( &AssociationTable, 50 ); #endif // RM_EXTRA_CHECKING } } while(FALSE); } // Actually handles dumping of task info. // void RmDumpStackRecord( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ) { /* !rm sr 0x838c7560 Stack Record 0x838c7560 TmpRefs: 2 HeldLocks: 0xe916a45f 0x23d8d2d3 0x5f47a2f2 */ RM_STACK_RECORD sr; bool fRet; do { // First let's read the RM_STACK_RECORD structure. // fRet = dbgextReadMemory( uAddr, &sr, sizeof(sr), "RM_STACK_RECORD" ); if (!fRet) break; MyDbgPrintf( "\nStack Record 0x%p\n", uAddr); MyDbgPrintf( " TmpRefs:0x%08x LockLevel:0x%08lx pFirst:0x%08lx NumHeld=%lu\n", sr.TmpRefs, sr.LockInfo.CurrentLevel, sr.LockInfo.pFirst, sr.LockInfo.pNextFree-sr.LockInfo.pFirst ); // Display held locks. // if (sr.LockInfo.CurrentLevel==0) { MyDbgPrintf(" No held locks.\n"); } else { // MyDbgPrintf(" Held locks:\n"); } } while(FALSE); } // Actually handles dumping of the object log // void RmDumpObjectLog( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ) { /* !rm log 0x9ba265f8 Log for object 0x838c7560 (LocalIP): Added association X Deleted association Y ... */ RM_OBJECT_HEADER Obj; bool fRet; UINT uNumEntries = 0; do { char rgDescriptionBuf[256]; // First let's read the pObj structure. // fRet = dbgextReadMemory( uAddr, &Obj, sizeof(Obj), "RM_OBJECT_HEADER" ); if (!fRet) break; // Try to read the object's description. // fRet = dbgextReadSZ( (UINT_PTR) Obj.szDescription, rgDescriptionBuf, sizeof(rgDescriptionBuf), "Obj.szDescription" ); if (!fRet) break; // Try to get the number of log entries field in the diag info struct. // if (Obj.pDiagInfo != NULL) { bool fRet; UINT_PTR uNumItemsOffset = (UINT_PTR) &(Obj.pDiagInfo->NumObjectLogEntries); fRet = dbgextReadUINT( uNumItemsOffset, &uNumEntries, "pDiagInfo->NumObjectLogEntries" ); if (!fRet) { uNumEntries = (UINT) -1; } } if (uNumEntries == 0) { MyDbgPrintf( "\nObject 0x%p (%s) has no log entries.\n", uAddr, rgDescriptionBuf ); } else if (uNumEntries == (UINT)-1) { MyDbgPrintf( "\nObject 0x%p (%s) log entries are not available.\n", uAddr, rgDescriptionBuf ); } else { #if RM_EXTRA_CHECKING UINT uNumToDump = uNumEntries; if (uNumToDump > 50) { uNumToDump = 50; } MyDbgPrintf( "\nLog entries for 0x%p (%s) (%lu of %lu):\n", uAddr, rgDescriptionBuf, uNumToDump, uNumEntries ); dbg_print_object_log_entries( (UINT_PTR) &(Obj.pDiagInfo->listObjectLog), uNumToDump ); #endif // RM_EXTRA_CHECKING } } while(FALSE); } // Actually handles dumping of the object tree // void RmDumpObjectTree( struct _TYPE_INFO *pType, UINT_PTR uAddr, char *szFieldSpec, UINT uFlags ) { /* !rm tree 0x9ba265f8 Tree for object 0x838c7560 (LocalIP) (Parent 0x82222222) Display sample: 0x2222222(RemoteIp) |---0x22222222(Dest) |---|---0x22222222(Dest) |---|---|---0x22222222(Dest) |---|---0x22222222(pTask) |---0x11111111(RemoteIp) */ RM_OBJECT_HEADER Obj; RM_OBJECT_HEADER ParentObj; bool fRet; UINT uNumEntries = 0; do { char rgDescriptionBuf[256]; char rgParentDescriptionBuf[256]; // First let's read the pObj structure. // fRet = dbgextReadMemory( uAddr, &Obj, sizeof(Obj), "RM_OBJECT_HEADER" ); if (!fRet) break; // Try to read the object's description. // fRet = dbgextReadSZ( (UINT_PTR) Obj.szDescription, rgDescriptionBuf, sizeof(rgDescriptionBuf), "Obj.szDescription" ); if (!fRet) break; // Try to read the parent's object. // if (Obj.pParentObject!=NULL && (UINT_PTR) Obj.pParentObject != uAddr) { fRet = dbgextReadMemory( (UINT_PTR) Obj.pParentObject, &ParentObj, sizeof(ParentObj), "RM_OBJECT_HEADER" ); if (!fRet) break; // Try to get pParent's description. // fRet = dbgextReadSZ( (UINT_PTR) ParentObj.szDescription, rgParentDescriptionBuf, sizeof(rgParentDescriptionBuf), "ParentObj.szDescription" ); if (!fRet) break; } else { strcpy(rgParentDescriptionBuf, ""); } MyDbgPrintf( "\nObject Tree for 0x%p(%s) with parent 0x%p(%s):\n", uAddr, rgDescriptionBuf, Obj.pParentObject, rgParentDescriptionBuf ); NodeFunc_DumpObjectTree( (UINT_PTR) &(((PRM_OBJECT_HEADER)uAddr)->linkSiblings), 0, // Index (unused) (void *)0 // pvContext == level ); } while(FALSE); } void dump_object_fields(UINT_PTR uAddr, PRM_OBJECT_HEADER pObj) { UINT uNumAssociations = (UINT) -1; // Try to get the number of associations field in the diag info struct. // if (pObj->pDiagInfo != NULL) { bool fRet; UINT_PTR uNumItemsOffset = (UINT_PTR) &(pObj->pDiagInfo->AssociationTable.NumItems); fRet = dbgextReadUINT( uNumItemsOffset, &uNumAssociations, "pDiagInfo->AssociationTable.NumItems" ); if (!fRet) { uNumAssociations = (UINT) -1; } } MyDbgPrintf( " Hdr:\n" " Sig:0x%08x State:0x%08x Refs:0x%08x\n", pObj->Sig, pObj->State, pObj->TotRefs ); MyDbgPrintf( " pLock:0x%p pSInfo:0x%p pDInfo:0x%p\n", pObj->pLock, pObj->pStaticInfo, pObj->pDiagInfo ); MyDbgPrintf( " pParent:0x%p pRoot:0x%p pHLink:0x%p\n", pObj->pParentObject, pObj->pRootObject, &(((PRM_OBJECT_HEADER) uAddr)->HashLink) ); MyDbgPrintf( " HdrSize:0x%08lx Assoc:%d\n", sizeof(*pObj), uNumAssociations ); MyDbgPrintf( " RmState: "); DumpBitFields( pObj->RmState, rgRM_OBJECT_STATE ); MyDbgPrintf( "\n"); } void dbg_walk_rm_hash_table( PRM_HASH_TABLE pRmHashTable, UINT uContainingOffset, char *szDescription ) { // For now, we get the whole hash table array in one fell swoop... // PRM_HASH_LINK rgTable[512]; UINT TableLength = pRmHashTable->TableLength; bool fRet; do { // Sanity check. // if (TableLength > sizeof(rgTable)/sizeof(*rgTable)) { MyDbgPrintf( " HashTable length %lu too large\n", TableLength ); break; } // Read the whole hash table. // fRet = dbgextReadMemory( (UINT_PTR) pRmHashTable->pTable, rgTable, TableLength * sizeof(*rgTable), "Hash Table" ); if (!fRet) break; // Now go through the table visiting each list... // { PRM_HASH_LINK *ppLink, *ppLinkEnd; UINT uCount = 0; UINT uMax = 15; ppLink = rgTable; ppLinkEnd = ppLink + TableLength; for ( ; ppLink < ppLinkEnd; ppLink++) { PRM_HASH_LINK pLink = *ppLink; for (;pLink != NULL; uCount++) { char *szPrefix; char *szSuffix; RM_HASH_LINK Link; szPrefix = " "; szSuffix = ""; if (uCount%4) { szPrefix = " "; if ((uCount%4)==3) { szSuffix = "\n"; } } if (uCount >= uMax) break; MyDbgPrintf( "%s0x%p%s", szPrefix, ((char *) pLink) - uContainingOffset, szSuffix ); // Let's try to read this link. // fRet = dbgextReadMemory( (UINT_PTR) pLink, &Link, sizeof(Link), "Hash Link" ); if (!fRet) break; pLink = Link.pNext; } if (!fRet || (uCount >= uMax)) break; } { MyDbgPrintf("\n"); } if (uCount < pRmHashTable->NumItems) { MyDbgPrintf(" ...\n"); } } } while (FALSE); } #if RM_EXTRA_CHECKING void dbg_dump_one_association( RM_PRIVATE_DBG_ASSOCIATION *pAssoc ); void dbg_print_rm_associations( PRM_HASH_TABLE pRmAssociationHashTable, UINT MaxToPrint ) { // For now, we get the whole hash table array in one fell swoop... // PRM_HASH_LINK rgTable[512]; UINT TableLength = pRmAssociationHashTable->TableLength; bool fRet; do { // Sanity check. // if (TableLength > sizeof(rgTable)/sizeof(*rgTable)) { MyDbgPrintf( " HashTable length %lu too large\n", TableLength ); break; } // Read the whole hash table. // fRet = dbgextReadMemory( (UINT_PTR) pRmAssociationHashTable->pTable, rgTable, TableLength * sizeof(*rgTable), "Hash Table" ); if (!fRet) break; // Now go through the table visiting each list... // { PRM_HASH_LINK *ppLink, *ppLinkEnd; UINT uCount = 0; UINT uMax = MaxToPrint; ppLink = rgTable; ppLinkEnd = ppLink + TableLength; for ( ; ppLink < ppLinkEnd; ppLink++) { PRM_HASH_LINK pLink = *ppLink; for (;pLink != NULL; uCount++) { RM_PRIVATE_DBG_ASSOCIATION Assoc; UINT_PTR uAssocOffset = (UINT_PTR) CONTAINING_RECORD( pLink, RM_PRIVATE_DBG_ASSOCIATION, HashLink ); if (uCount >= uMax) break; // Let's try to read this association... // fRet = dbgextReadMemory( uAssocOffset, &Assoc, sizeof(Assoc), "Association" ); if (!fRet) break; dbg_dump_one_association(&Assoc); pLink = Assoc.HashLink.pNext; } if (!fRet || (uCount >= uMax)) break; } if (uCount < pRmAssociationHashTable->NumItems) { MyDbgPrintf(" ...\n"); } } } while (FALSE); } void dbg_dump_one_association( RM_PRIVATE_DBG_ASSOCIATION *pAssoc ) /*++ Dump the information on the specific association. pAssoc is valid memory, however anything it points to is not in our address space. Since the association contains a format string, which may have "%s"s in it, we need scan this format string and read any strings referenced. All this effort is well worth it. Check out the sample output! Associations for 0x01023A40 (Globals): Owns group 0x01023AC4 (O1 Group) Parent of 0x00073240 (O2) Parent of 0x00073488 (O2) Associations for 0x000736D0 (Task3a): Child of 0x00073240 (O2) Pending on 0x000732C8 (TaskO2) --*/ { char rgFormatString[256]; char rgStrings[3][256]; char *szFormatString; ULONG_PTR Args[3]; char *szDefaultFormatString = "\tAssociation (E1=0x%x, E2=0x%x, T=0x%x)\n"; bool fRet = FALSE; do { // Try to read the format string. // { fRet = dbgextReadSZ( (UINT_PTR) pAssoc->szFormatString, rgFormatString, sizeof(rgFormatString), "Association format" ); if (fRet) { szFormatString = rgFormatString; } else { break; } } // Now run through the format string, looking for "%s"s. // and munging as required. // { char *pc = rgFormatString; UINT uCount=0; Args[0] = pAssoc->Entity1; Args[1] = pAssoc->Entity2; Args[2] = pAssoc->AssociationID; while (uCount<3 && pc[0]!=0 && pc[1]!=0) { if (pc[0]=='%') { if (pc[1]=='s') { // pc[1]='p'; fRet = dbgextReadSZ( (UINT_PTR) Args[uCount], rgStrings[uCount], sizeof(rgStrings[uCount]), "Association format" ); if (fRet) { Args[uCount] = (ULONG_PTR) rgStrings[uCount]; } else { break; } } pc++; // we want to end up skipping past both chars. uCount++; } pc++; } } } while (FALSE); if (!fRet) { // Back off to the defaults.. // szFormatString = szDefaultFormatString; Args[0] = pAssoc->Entity1; Args[1] = pAssoc->Entity2; Args[2] = pAssoc->AssociationID; } MyDbgPrintf( szFormatString, Args[0], Args[1], Args[2] ); } ULONG NodeFunc_DumpObjectLogFromObjectLink ( UINT_PTR uNodeAddr, UINT uIndex, void *pvContext ) { RM_DBG_LOG_ENTRY LE; LIST_ENTRY *pLink = (LIST_ENTRY*) uNodeAddr; UINT_PTR uLEOffset = (UINT_PTR) CONTAINING_RECORD( pLink, RM_DBG_LOG_ENTRY, linkObjectLog ); char rgPrefixString[256]; char rgFormatString[256]; char rgStrings[4][256]; char *szPrefixString; char *szFormatString; ULONG_PTR Args[4]; char *szDefaultFormatString = "Log Entry (P1=%p, P2=%p, P3=%p, P4=%p, szFmt=%p)\n"; bool fRet = FALSE; // Read the containing record. // fRet = dbgextReadMemory( uLEOffset, &LE, sizeof(LE), "Log Entry" ); if (!fRet) return 0; // EARLY RETURN; #if 0 if (LE.pfnDumpEntry != NULL) { // // TODO we need to get the corresponding function to dump this // specialized entry. // MyDbgPrintf( "Specialized (pfn=%p szFmt=%p, P1=%p, P2=%p, P3=%p, P4=%p)\n", LE.pfnDumpEntry, LE.szFormatString, LE.Param1, LE.Param2, LE.Param3, LE.Param4 ); return 0; // EARLY RETURN } #else // // Above check is invalid, because in all cases there is a pfnDump function. // #endif // // TODO -- following code is very similar to the dump-association code -- // move common stuff to some utility function. // do { // Try to read the prefix string. // { fRet = FALSE; if (LE.szPrefix != NULL) { fRet = dbgextReadSZ( (UINT_PTR) LE.szPrefix, rgPrefixString, sizeof(rgPrefixString), "Prefix String" ); } if (fRet) { szPrefixString = rgPrefixString; } else { szPrefixString = ""; } } // Try to read the format string. // { fRet = dbgextReadSZ( (UINT_PTR) LE.szFormatString, rgFormatString, sizeof(rgFormatString), "Log entry format" ); if (fRet) { szFormatString = rgFormatString; } else { break; } } // Now run through the format string, looking for "%s"s. // and munging as required. // { char *pc = rgFormatString; UINT uCount=0; Args[0] = LE.Param1; Args[1] = LE.Param2; Args[2] = LE.Param3; Args[3] = LE.Param4; while (uCount<4 && pc[0]!=0 && pc[1]!=0) { if (pc[0]=='%') { if (pc[1]=='s') { // pc[1]='p'; fRet = dbgextReadSZ( (UINT_PTR) Args[uCount], rgStrings[uCount], sizeof(rgStrings[uCount]), "Log entry param" ); if (fRet) { Args[uCount] = (ULONG_PTR) rgStrings[uCount]; } else { break; } } pc++; // we want to end up skipping past both chars. uCount++; } pc++; } } } while (FALSE); if (!fRet) { // Back off to the defaults.. // szPrefixString = ""; szFormatString = szDefaultFormatString; Args[0] = LE.Param1; Args[1] = LE.Param2; Args[2] = LE.Param3; Args[3] = LE.Param4; } MyDbgPrintf(szPrefixString); MyDbgPrintf( szFormatString, Args[0], Args[1], Args[2], Args[3], LE.szFormatString ); return 0; } void dbg_print_object_log_entries( UINT_PTR uObjectListOffset, UINT MaxToPrint ) { WalkDLlist( uObjectListOffset, 0, //uOffsetStartLink NULL, // pvContext NodeFunc_DumpObjectLogFromObjectLink, MaxToPrint, "Object log" ); } #endif // RM_EXTRA_CHECKING char szDumpTreePrefix[] = "|---|---|---|---|---|---|---|---|---|---" "|---|---|---|---|---|---|---|---|---|---"; ULONG NodeFunc_DumpObjectTree ( UINT_PTR uNodeAddr, UINT uIndex, void *pvContext ) { bool fRet = FALSE; do { char rgDescriptionBuf[256]; LIST_ENTRY *pLink = (LIST_ENTRY*) uNodeAddr; UINT_PTR uObjOffset = (UINT_PTR) CONTAINING_RECORD( pLink, RM_OBJECT_HEADER, linkSiblings ); UINT Level = (UINT) (UINT_PTR) pvContext; // we put the level in the context. RM_OBJECT_HEADER Obj; char *szPrefix; // First make szPrefix point to the end (trailing zero) of the prefix string. // szPrefix = szDumpTreePrefix + sizeof(szDumpTreePrefix)-1; // Now back up "Level" times. // if (Level < ((sizeof(szDumpTreePrefix)-1)/4)) { szPrefix -= Level*4; } else { // Level is too large -- don't display anything. // MyDbgPrintf("Dump Tree depth(%d) is too large.\n", Level); break; } // Read the containing record. // fRet = dbgextReadMemory( uObjOffset, &Obj, sizeof(Obj), "Object" ); if (!fRet) break; // Try to read the object's description. // fRet = dbgextReadSZ( (UINT_PTR) Obj.szDescription, rgDescriptionBuf, sizeof(rgDescriptionBuf), "Obj.szDescription" ); if (!fRet) break; // Display the object info. // MyDbgPrintf( "%s%p(%s)\n", szPrefix, uObjOffset, rgDescriptionBuf ); // // Now walk the list of children, displaying each of them. // WalkDLlist( (UINT_PTR) &(((PRM_OBJECT_HEADER)uObjOffset)->listChildren), 0, //uOffsetStartLink (void*) (Level+1), // pvContext NodeFunc_DumpObjectTree, 50, // Max children per node to dump "Object children" ); } while (FALSE); return 0; }