mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1709 lines
29 KiB
1709 lines
29 KiB
/*++
|
|
|
|
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:<unimplemented>\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, "<root>");
|
|
}
|
|
|
|
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;
|
|
}
|