[NTOS:PNP] Add set functions for debugging PNP and IO managers. (#2457)

Useful functions for debugging IO and PNP managers:

PipDumpDeviceNodes() - displays information about a node(s) in the device tree;
PipDumpResourceRequirementsList() - displays information about a Io List;
PipDumpCmResourceList() - displays information about a Cm List

The tree list of devices (DEVICE_NODE structures) is perhaps the main one in the PnP manager. They also store information about the hardware resources required and assigned to devices.
These functions can help with debugging. For example, you can call PipDumpDeviceNodes() before and after device enumeration and compare the resulting information.

For PipDumpDeviceNodes() it is possible to optionally output:
- allocated resources and boot configuration resources
- resources required
- translated resources
It is possible to displays both a single node and the entire tree.
Optionally, you can display child nodes.

The information output format for resource lists is maximally compressed, since often the only debugging port is a monitor.

The DebugLevel parameter allows dumping in two modes:
0 - unconditional;
1 - if NDEBUG is not defined in "debug.c".
This commit is contained in:
Vadim Galyant 2020-04-04 15:30:40 +03:00 committed by GitHub
parent ac843d7b82
commit 7db342f8a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 590 additions and 0 deletions

535
ntoskrnl/io/debug.c Normal file
View file

@ -0,0 +1,535 @@
/*
* PROJECT: ReactOS Kernel
* COPYRIGHT: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* FILE: ntoskrnl/io/debug.c
* PURPOSE: Useful functions for debugging IO and PNP managers
* PROGRAMMERS: Copyright 2020 Vadim Galyant <vgal@rambler.ru>
*/
#include <ntoskrnl.h>
#include "pnpio.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
extern PDEVICE_NODE IopRootDeviceNode;
/* FUNCTIONS ******************************************************************/
/* CmResource */
/* PipDumpCmResourceDescriptor() displays information about a Cm Descriptor
DebugLevel: 0 - always dump
1 - dump if not defined NDEBUG
*/
VOID
NTAPI
PipDumpCmResourceDescriptor(
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
_In_ ULONG DebugLevel)
{
PAGED_CODE();
if (DebugLevel != 0)
{
#ifdef NDEBUG
return;
#endif
}
if (Descriptor == NULL)
{
DPRINT1("Dump CmDescriptor: Descriptor == NULL\n");
return;
}
switch (Descriptor->Type)
{
case CmResourceTypePort:
DPRINT1("[%p:%X:%X] IO: Start %X:%X, Len %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Port.Start.HighPart, Descriptor->u.Port.Start.LowPart, Descriptor->u.Port.Length);
break;
case CmResourceTypeInterrupt:
DPRINT1("[%p:%X:%X] INT: Lev %X Vec %X Aff %IX\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Interrupt.Level, Descriptor->u.Interrupt.Vector, Descriptor->u.Interrupt.Affinity);
break;
case CmResourceTypeMemory:
DPRINT1("[%p:%X:%X] MEM: %X:%X Len %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Memory.Start.HighPart, Descriptor->u.Memory.Start.LowPart, Descriptor->u.Memory.Length);
break;
case CmResourceTypeDma:
DPRINT1("[%p:%X:%X] DMA: Channel %X Port %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Dma.Channel, Descriptor->u.Dma.Port);
break;
case CmResourceTypeDeviceSpecific:
DPRINT1("[%p:%X:%X] DAT: DataSize %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.DeviceSpecificData.DataSize);
break;
case CmResourceTypeBusNumber:
DPRINT1("[%p:%X:%X] BUS: Start %X Len %X Reserv %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.BusNumber.Start, Descriptor->u.BusNumber.Length, Descriptor->u.BusNumber.Reserved);
break;
case CmResourceTypeDevicePrivate:
DPRINT1("[%p:%X:%X] PVT: D[0] %X D[1] %X D[2] %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.DevicePrivate.Data[0], Descriptor->u.DevicePrivate.Data[1], Descriptor->u.DevicePrivate.Data[2]);
break;
default:
DPRINT1("[%p] Unknown type %X\n", Descriptor, Descriptor->Type);
break;
}
}
/* PipGetNextCmPartialDescriptor() return poiner to next a Cm Descriptor */
PCM_PARTIAL_RESOURCE_DESCRIPTOR
NTAPI
PipGetNextCmPartialDescriptor(
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR NextDescriptor;
/* Assume the descriptors are the fixed size ones */
NextDescriptor = CmDescriptor + 1;
/* But check if this is actually a variable-sized descriptor */
if (CmDescriptor->Type == CmResourceTypeDeviceSpecific)
{
/* Add the size of the variable section as well */
NextDescriptor = (PVOID)((ULONG_PTR)NextDescriptor +
CmDescriptor->u.DeviceSpecificData.DataSize);
}
/* Now the correct pointer has been computed, return it */
return NextDescriptor;
}
/* PipDumpCmResourceList() displays information about a Cm List
DebugLevel: 0 - always dump
1 - dump if not defined NDEBUG
*/
VOID
NTAPI
PipDumpCmResourceList(
_In_ PCM_RESOURCE_LIST CmResource,
_In_ ULONG DebugLevel)
{
PCM_FULL_RESOURCE_DESCRIPTOR FullList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
ULONG ix;
ULONG jx;
PAGED_CODE();
if (DebugLevel != 0)
{
#ifdef NDEBUG
return;
#endif
}
DPRINT1("Dump CmList: CmResource %p\n", CmResource);
if (CmResource == NULL)
{
DPRINT1("PipDumpCmResourceList: CmResource == NULL\n");
return;
}
if (CmResource->Count == 0)
{
DPRINT1("PipDumpCmResourceList: CmResource->Count == 0\n");
return;
}
DPRINT1("FullList Count %x\n", CmResource->Count);
FullList = &CmResource->List[0];
for (ix = 0; ix < CmResource->Count; ix++)
{
DPRINT1("List #%X Iface %X Bus #%X Ver.%X Rev.%X Count %X\n",
ix,
FullList->InterfaceType,
FullList->BusNumber,
FullList->PartialResourceList.Version,
FullList->PartialResourceList.Revision,
FullList->PartialResourceList.Count);
Descriptor = FullList->PartialResourceList.PartialDescriptors;
for (jx = 0; jx < FullList->PartialResourceList.Count; jx++)
{
PipDumpCmResourceDescriptor(Descriptor, DebugLevel);
Descriptor = PipGetNextCmPartialDescriptor(Descriptor);
}
FullList = (PCM_FULL_RESOURCE_DESCRIPTOR)Descriptor;
}
}
/* IoResource */
/* PipDumpIoResourceDescriptor() displays information about a Io Descriptor
DebugLevel: 0 - always dump
1 - dump if not defined NDEBUG
*/
VOID
NTAPI
PipDumpIoResourceDescriptor(
_In_ PIO_RESOURCE_DESCRIPTOR Descriptor,
_In_ ULONG DebugLevel)
{
PAGED_CODE();
if (DebugLevel != 0)
{
#ifdef NDEBUG
return;
#endif
}
if (Descriptor == NULL)
{
DPRINT1("DumpResourceDescriptor: Descriptor == 0\n");
return;
}
switch (Descriptor->Type)
{
case CmResourceTypeNull:
DPRINT1("[%p:%X:%X] O: Len %X Align %X Min %I64X, Max %I64X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Generic.Length, Descriptor->u.Generic.Alignment, Descriptor->u.Generic.MinimumAddress.QuadPart, Descriptor->u.Generic.MaximumAddress.QuadPart);
break;
case CmResourceTypePort:
DPRINT1("[%p:%X:%X] IO: Min %X:%X, Max %X:%X, Align %X Len %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Port.MinimumAddress.HighPart, Descriptor->u.Port.MinimumAddress.LowPart, Descriptor->u.Port.MaximumAddress.HighPart, Descriptor->u.Port.MaximumAddress.LowPart, Descriptor->u.Port.Alignment, Descriptor->u.Port.Length);
break;
case CmResourceTypeInterrupt:
DPRINT1("[%p:%X:%X] INT: Min %X Max %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Interrupt.MinimumVector, Descriptor->u.Interrupt.MaximumVector);
break;
case CmResourceTypeMemory:
DPRINT1("[%p:%X:%X] MEM: Min %X:%X, Max %X:%X, Align %X Len %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Memory.MinimumAddress.HighPart, Descriptor->u.Memory.MinimumAddress.LowPart, Descriptor->u.Memory.MaximumAddress.HighPart, Descriptor->u.Memory.MaximumAddress.LowPart, Descriptor->u.Memory.Alignment, Descriptor->u.Memory.Length);
break;
case CmResourceTypeDma:
DPRINT1("[%p:%X:%X] DMA: Min %X Max %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Dma.MinimumChannel, Descriptor->u.Dma.MaximumChannel);
break;
case CmResourceTypeBusNumber:
DPRINT1("[%p:%X:%X] BUS: Min %X Max %X Len %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.BusNumber.MinBusNumber, Descriptor->u.BusNumber.MaxBusNumber, Descriptor->u.BusNumber.Length);
break;
case CmResourceTypeConfigData:
DPRINT1("[%p:%X:%X] CFG: Priority %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.ConfigData.Priority);
break;
case CmResourceTypeDevicePrivate:
DPRINT1("[%p:%X:%X] DAT: %X %X %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.DevicePrivate.Data[0], Descriptor->u.DevicePrivate.Data[1], Descriptor->u.DevicePrivate.Data[2]);
break;
default:
DPRINT1("[%p:%X:%X]. Unknown type %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->Type);
break;
}
}
/* PipDumpResourceRequirementsList() displays information about a Io List
DebugLevel: 0 - always dump
1 - dump if not defined NDEBUG
*/
VOID
NTAPI
PipDumpResourceRequirementsList(
_In_ PIO_RESOURCE_REQUIREMENTS_LIST IoResource,
_In_ ULONG DebugLevel)
{
PIO_RESOURCE_LIST AltList;
PIO_RESOURCE_DESCRIPTOR Descriptor;
ULONG ix;
ULONG jx;
PAGED_CODE();
if (DebugLevel != 0)
{
#ifdef NDEBUG
return;
#endif
}
if (IoResource == NULL)
{
DPRINT1("PipDumpResourceRequirementsList: IoResource == 0\n");
return;
}
DPRINT1("Dump RequirementsList: IoResource %p\n", IoResource);
DPRINT1("Interface %X Bus %X Slot %X AlternativeLists %X\n",
IoResource->InterfaceType,
IoResource->BusNumber,
IoResource->SlotNumber,
IoResource->AlternativeLists);
AltList = &IoResource->List[0];
if (IoResource->AlternativeLists < 1)
{
DPRINT1("PipDumpResourceRequirementsList: AlternativeLists < 1\n");
return;
}
for (ix = 0; ix < IoResource->AlternativeLists; ix++)
{
DPRINT1("AltList %p, AltList->Count %X\n", AltList, AltList->Count);
for (jx = 0; jx < AltList->Count; jx++)
{
Descriptor = &AltList->Descriptors[jx];
PipDumpIoResourceDescriptor(Descriptor, DebugLevel);
}
AltList = (PIO_RESOURCE_LIST)(AltList->Descriptors + AltList->Count);
DPRINT1("End Descriptors %p\n", AltList);
}
}
/* Gets the name of the device node state. */
PWSTR
NTAPI
PipGetDeviceNodeStateName(
_In_ PNP_DEVNODE_STATE State)
{
switch (State)
{
case DeviceNodeUnspecified:
return L"DeviceNodeUnspecified";
case DeviceNodeUninitialized:
return L"DeviceNodeUninitialized";
case DeviceNodeInitialized:
return L"DeviceNodeInitialized";
case DeviceNodeDriversAdded:
return L"DeviceNodeDriversAdded";
case DeviceNodeResourcesAssigned:
return L"DeviceNodeResourcesAssigned";
case DeviceNodeStartPending:
return L"DeviceNodeStartPending";
case DeviceNodeStartCompletion:
return L"DeviceNodeStartCompletion";
case DeviceNodeStartPostWork:
return L"DeviceNodeStartPostWork";
case DeviceNodeStarted:
return L"DeviceNodeStarted";
case DeviceNodeQueryStopped:
return L"DeviceNodeQueryStopped";
case DeviceNodeStopped:
return L"DeviceNodeStopped";
case DeviceNodeRestartCompletion:
return L"DeviceNodeRestartCompletion";
case DeviceNodeEnumeratePending:
return L"DeviceNodeEnumeratePending";
case DeviceNodeEnumerateCompletion:
return L"DeviceNodeEnumerateCompletion";
case DeviceNodeAwaitingQueuedDeletion:
return L"DeviceNodeAwaitingQueuedDeletion";
case DeviceNodeAwaitingQueuedRemoval:
return L"DeviceNodeAwaitingQueuedRemoval";
case DeviceNodeQueryRemoved:
return L"DeviceNodeQueryRemoved";
case DeviceNodeRemovePendingCloses:
return L"DeviceNodeRemovePendingCloses";
case DeviceNodeRemoved:
return L"DeviceNodeRemoved";
case DeviceNodeDeletePendingCloses:
return L"DeviceNodeDeletePendingCloses";
case DeviceNodeDeleted:
return L"DeviceNodeDeleted";
default:
break;
}
if (State != MaxDeviceNodeState)
{
DPRINT1("PipGetDeviceNodeStateName: Unknown State %X\n", State);
}
return L"";
}
/* Dump list arbiters for the device node */
VOID
NTAPI
PipDumpArbiters(
_In_ PDEVICE_NODE DeviceNode,
_In_ ULONG NodeLevel)
{
DPRINT("Level %X DevNode %p for PDO %p\n", NodeLevel, DeviceNode, DeviceNode->PhysicalDeviceObject);
/* Definitions needed for arbiter */
UNIMPLEMENTED;
}
/* PipDumpDeviceNode() displays information about a device node
Flags is bitmask parametr:
1 - traversal of all children nodes
2 - dump allocated resources and boot configuration resources
4 - dump resources required
8 - dump translated resources
DebugLevel: 0 - always dump
1 - dump if not defined NDEBUG
*/
VOID
NTAPI
PipDumpDeviceNode(
_In_ PDEVICE_NODE DeviceNode,
_In_ ULONG NodeLevel,
_In_ ULONG Flags,
_In_ ULONG DebugLevel)
{
PDEVICE_NODE ChildDeviceNode;
if (DebugLevel != 0)
{
#ifdef NDEBUG
return;
#endif
}
DPRINT1("* Level %X DevNode %p for PDO %p\n", NodeLevel, DeviceNode, DeviceNode->PhysicalDeviceObject);
DPRINT1("Instance %wZ\n", &DeviceNode->InstancePath);
if (DeviceNode->ServiceName.Length)
DPRINT1("Service %wZ\n", &DeviceNode->ServiceName);
#if 0
/* It is not used yet */
DPRINT1("State %X %S\n", DeviceNode->State, PipGetDeviceNodeStateName(DeviceNode->State));
DPRINT1("Prev State %X %S\n", DeviceNode->PreviousState, PipGetDeviceNodeStateName(DeviceNode->PreviousState));
#endif
if (DeviceNode->Problem)
DPRINT1("Problem %X\n", DeviceNode->Problem);
#if 0
/* It is not implemeted yet */
PipDumpArbiters(DeviceNode, NodeLevel);
#endif
/* Allocated resources and Boot configuration (reported by IRP_MN_QUERY_RESOURCES)*/
if (Flags & PIP_DUMP_FL_RES_ALLOCATED)
{
if (DeviceNode->ResourceList)
{
DPRINT1("---------- ResourceList ----------\n");
PipDumpCmResourceList(DeviceNode->ResourceList, DebugLevel);
}
if (DeviceNode->BootResources)
{
DPRINT1("---------- BootResources ----------\n");
PipDumpCmResourceList(DeviceNode->BootResources, DebugLevel);
}
}
/* Resources required (reported by IRP_MN_FILTER_RESOURCE_REQUIREMENTS) */
if (Flags & PIP_DUMP_FL_RES_REQUIREMENTS)
{
if (DeviceNode->ResourceRequirements)
{
DPRINT1("---------- ResourceRequirements ----------\n");
PipDumpResourceRequirementsList(DeviceNode->ResourceRequirements, DebugLevel);
}
}
/* Translated resources (AllocatedResourcesTranslated) */
if (Flags & PIP_DUMP_FL_RES_TRANSLATED)
{
if (DeviceNode->ResourceListTranslated)
{
DPRINT1("---------- ResourceListTranslated ----------\n");
PipDumpCmResourceList(DeviceNode->ResourceListTranslated, DebugLevel);
}
}
/* Traversal of all children nodes */
if (Flags & PIP_DUMP_FL_ALL_NODES)
{
for (ChildDeviceNode = DeviceNode->Child;
ChildDeviceNode != NULL;
ChildDeviceNode = ChildDeviceNode->Sibling)
{
/* Recursive call */
PipDumpDeviceNode(ChildDeviceNode, (NodeLevel + 1), Flags, DebugLevel);
}
}
}
/* PipDumpDeviceNodes() displays information about a node(s) in the device tree
If DeviceNode is NULL, then the dump starts at the beginning of the device tree.
Flags is bitmask parametr:
1 - traversal of all children nodes
2 - dump allocated resources and boot configuration resources
4 - dump resources required
8 - dump translated resources
DebugLevel: 0 - always dump
1 - dump if not defined NDEBUG
See also: Windows DDK -> General Driver Information ->
Kernel-Mode Driver Architecture -> Design Guide -> Plug and Play ->
Introduction to Plug and Play -> Hardware Resources
*/
VOID
NTAPI
PipDumpDeviceNodes(
_In_ PDEVICE_NODE DeviceNode,
_In_ ULONG Flags,
_In_ ULONG DebugLevel)
{
if (DebugLevel != 0)
{
#ifdef NDEBUG
return;
#endif
}
DPRINT1("PipDumpDeviceNodes: DeviceNode %X, Flags %X Level %X\n", DeviceNode, Flags, DebugLevel);
if (DeviceNode == NULL)
{
DeviceNode = IopRootDeviceNode;
}
PipDumpDeviceNode(DeviceNode, 0, Flags, DebugLevel);
}
/* EOF */

54
ntoskrnl/io/pnpio.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef _PNPIO_H
#define _PNPIO_H
/* Dump resources flags */
#define PIP_DUMP_FL_ALL_NODES 1
#define PIP_DUMP_FL_RES_ALLOCATED 2
#define PIP_DUMP_FL_RES_REQUIREMENTS 4
#define PIP_DUMP_FL_RES_TRANSLATED 8
/* debug.c */
VOID
NTAPI
PipDumpCmResourceList(
_In_ PCM_RESOURCE_LIST CmResource,
_In_ ULONG DebugLevel
);
PCM_PARTIAL_RESOURCE_DESCRIPTOR
NTAPI
PipGetNextCmPartialDescriptor(
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor
);
VOID
NTAPI
PipDumpCmResourceDescriptor(
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
_In_ ULONG DebugLevel
);
VOID
NTAPI
PipDumpResourceRequirementsList(
_In_ PIO_RESOURCE_REQUIREMENTS_LIST IoResource,
_In_ ULONG DebugLevel
);
VOID
NTAPI
PipDumpIoResourceDescriptor(
_In_ PIO_RESOURCE_DESCRIPTOR Descriptor,
_In_ ULONG DebugLevel
);
VOID
NTAPI
PipDumpDeviceNodes(
_In_ PDEVICE_NODE DeviceNode,
_In_ ULONG Flags,
_In_ ULONG DebugLevel
);
#endif /* _PNPIO_H */

View file

@ -160,6 +160,7 @@ list(APPEND SOURCE
${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnpres.c
${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnproot.c
${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnputil.c
${REACTOS_SOURCE_DIR}/ntoskrnl/io/debug.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ke/apc.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ke/balmgr.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ke/bug.c