process hacker 获取GPU源码阅读

961 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

作者从processhacker2中抽取gpu监测的代码,剥离出来的命令行工具: windowsGpuMonitor

流程

process hacker 获取Gpu 数据的具体逻辑主要在 gpumon.c, counter.c 中, 因此着重对这两个文件中的一些逻辑进行分析。

main 调用 EtGpuMonitorInitialization 进行初始化


VOID EtGpuMonitorInitialization(
    VOID
    )
{
    //  此处有三个布尔变量控制着是否可以采集,以及采集哪些数据的流程
    //  1. EtGpuSupported: 控制着gpu温度数据采集等逻辑
    //  2. EtD3DEnabled: 控制着是否通过d3d 的方式获取数据等逻辑
    //  3. EtGpuEnabled: 控制着整个采集是否可以正常获取数据
    if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR))
    {
        EtGpuSupported = PhWindowsVersion >= WINDOWS_10_RS4;
        EtD3DEnabled = EtGpuSupported && !!PhGetIntegerSetting(SETTING_NAME_ENABLE_GPUPERFCOUNTERS);
        EtpGpuAdapterList = PhCreateList(4);
        //  判断是否可以顺利初始化使用 D3DKMTQueryStatistics 系统API来获取gpu原始数据
        if (EtpInitializeD3DStatistics())
            EtGpuEnabled = TRUE;
    }
    if (EtD3DEnabled)
    {
        //  该函数中分别创建了用于存储一些counter的哈希表
        EtPerfCounterInitialization();
    }
    if (EtGpuEnabled)
    {
        ...
        //  注册了回调函数 EtGpuProcessesUpdatedCallback, 该函数中是获取以及更新目前gpu数据的逻辑
        PhRegisterCallback(
            PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent),
            EtGpuProcessesUpdatedCallback,
            NULL,
            &ProcessesUpdatedCallbackRegistration
            );
    }
}

EtGpuMonitorInitialization 调用 EtpInitializeD3DStatistics 初始化


BOOLEAN EtpInitializeD3DStatistics(
    VOID
    )
{
    PPH_LIST deviceAdapterList;
    PWSTR deviceInterfaceList;
    ULONG deviceInterfaceListLength = 0;
    PWSTR deviceInterface;
    D3DKMT_OPENADAPTERFROMDEVICENAME openAdapterFromDeviceName;
    D3DKMT_QUERYSTATISTICS queryStatistics;
    D3DKMT_ADAPTER_PERFDATACAPS perfCaps;
    //  封装调用了系统 API CM_Get_Device_Interface_List_SizeW, 该API 的作用是提前获取device_list 所需占用的内存大小,存储在deviceInterfaceListLength中,
    //  以便提前new 一块内存供存储device_list使用
    if (CM_Get_Device_Interface_List_Size(
        &deviceInterfaceListLength,
        (PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL,
        NULL,
        CM_GET_DEVICE_INTERFACE_LIST_PRESENT
        ) != CR_SUCCESS)
    {
        return FALSE;
    }
    deviceInterfaceList = PhAllocate(deviceInterfaceListLength * sizeof(WCHAR));
    memset(deviceInterfaceList, 0, deviceInterfaceListLength * sizeof(WCHAR));
    //  封装了系统 API CM_Get_Device_Interface_ListW, 该API 的作用是获取到多个以 NULL 结尾的字符串, 存储在deviceInterfaceList中,
    //  每一个字符串都代表着 the symbolic link name of an interface instance。
    if (CM_Get_Device_Interface_List(
        (PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL,
        NULL,
        deviceInterfaceList,
        deviceInterfaceListLength,
        CM_GET_DEVICE_INTERFACE_LIST_PRESENT
        ) != CR_SUCCESS)
    {
        PhFree(deviceInterfaceList);
        return FALSE;
    }
    deviceAdapterList = PhCreateList(10);
    //  将deviceInterfaceList 中的多个以NULL结尾的字符串保存在 deviceAdapterList 中
    for (deviceInterface = deviceInterfaceList; *deviceInterface; deviceInterface += PhCountStringZ(deviceInterface) + 1)
    {
        PhAddItemList(deviceAdapterList, deviceInterface);
    }
    //  通过遍历->打开->判断 每一个deviceAdapterList 中的deviceName, 来判断是否可以通过 D3DKMTQueryStatistics 的方式获取GPU数据
    for (ULONG i = 0; i < deviceAdapterList->Count; i++)
    {
        memset(&openAdapterFromDeviceName, 0, sizeof(D3DKMT_OPENADAPTERFROMDEVICENAME));
        openAdapterFromDeviceName.pDeviceName = deviceAdapterList->Items[i];
        //  调用系统API D3DKMTOpenAdapterFromDeviceName, 该函数通过 deviceName, 将一个graphics adapter handle 与该deviceName "绑定"
        if (!NT_SUCCESS(D3DKMTOpenAdapterFromDeviceName(&openAdapterFromDeviceName)))
            continue;
        if (EtGpuSupported && deviceAdapterList->Count > 1) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3
        {
            //  是 software device 就直接关闭该device handle
            if (EtpIsGpuSoftwareDevice(openAdapterFromDeviceName.hAdapter))
            {
                EtCloseAdapterHandle(openAdapterFromDeviceName.hAdapter);
                continue;
            }
        }
        //  win10 rs4 之后
        if (EtGpuSupported) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3
        {
            D3DKMT_SEGMENTSIZEINFO segmentInfo;
            memset(&segmentInfo, 0, sizeof(D3DKMT_SEGMENTSIZEINFO));
            //  封装了系统API D3DKMTQueryAdapterInfo, 该函数根据要获取的adapter的数据的类型, 来返回相应的数据
            if (NT_SUCCESS(EtQueryAdapterInformation(
                openAdapterFromDeviceName.hAdapter,
                KMTQAITYPE_GETSEGMENTSIZE,
                &segmentInfo,
                sizeof(D3DKMT_SEGMENTSIZEINFO)
                )))
            {
                EtGpuDedicatedLimit += segmentInfo.DedicatedVideoMemorySize;
                EtGpuSharedLimit += segmentInfo.SharedSystemMemorySize;
            }
            memset(&perfCaps, 0, sizeof(D3DKMT_ADAPTER_PERFDATACAPS));
            if (NT_SUCCESS(EtQueryAdapterInformation(
                openAdapterFromDeviceName.hAdapter,
                KMTQAITYPE_ADAPTERPERFDATA_CAPS,
                &perfCaps,
                sizeof(D3DKMT_ADAPTER_PERFDATACAPS)
                )))
            {
                //
                // This will be averaged below.
                //
                EtGpuTemperatureLimit += perfCaps.TemperatureMax;
                EtGpuFanRpmLimit += perfCaps.MaxFanRPM;
            }
        }
        //  not only w10rs4 , 通过 D3DKMTQueryStatistics 的方式来判断是否可以拿到GPU数据
        //  D3DKMT_QUERYSTATISTICS_ADAPTER 类型的 D3DKMTQueryStatistics请求, 拿到该adapter 的信息
        memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
        queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER;
        queryStatistics.AdapterLuid = openAdapterFromDeviceName.AdapterLuid;
        if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
        {
            PETP_GPU_ADAPTER gpuAdapter;
            //  提取出一个 PETP_GPU_ADAPTER 的类, 用来保存 GPU 的相关信息
            gpuAdapter = EtpAddDisplayAdapter(
                openAdapterFromDeviceName.pDeviceName,
                openAdapterFromDeviceName.hAdapter,
                openAdapterFromDeviceName.AdapterLuid,
                queryStatistics.QueryResult.AdapterInformation.NbSegments,
                queryStatistics.QueryResult.AdapterInformation.NodeCount
                );
            //  gpu 总的Node数, segemnt数
            gpuAdapter->FirstNodeIndex = EtGpuNextNodeIndex;
            EtGpuTotalNodeCount += gpuAdapter->NodeCount;
            EtGpuTotalSegmentCount += gpuAdapter->SegmentCount;
            EtGpuNextNodeIndex += gpuAdapter->NodeCount;
            for (ULONG ii = 0; ii < gpuAdapter->SegmentCount; ii++)
            {
                // D3DKMT_QUERYSTATISTICS 类型的 D3DKMTQueryStatistics请求, 拿到每一个segment 中的数据
                memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
                queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT;
                queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid;
                //  此处的segmentId 是用户自定义的数据
                queryStatistics.QuerySegment.SegmentId = ii;
                if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
                {
                    ULONG64 commitLimit;
                    //  此处的 aperture 其实是一个布尔类型, 其作用是为了判断当前的段是不是 aperture段
                    ULONG aperture;
                    if (PhWindowsVersion >= WINDOWS_8)
                    {
                        commitLimit = queryStatistics.QueryResult.SegmentInformation.CommitLimit;
                        aperture = queryStatistics.QueryResult.SegmentInformation.Aperture;
                    }
                    else
                    {
                        //  PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 是 系统 _D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION 结构体的封装
                        PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 segmentInfo;
                        segmentInfo = (PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1)&queryStatistics.QueryResult;
                        commitLimit = segmentInfo->CommitLimit;
                        aperture = segmentInfo->Aperture;
                    }
                    if (!EtGpuSupported || !EtD3DEnabled) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3
                    {
                        if (aperture)
                            EtGpuSharedLimit += commitLimit;
                        else
                            EtGpuDedicatedLimit += commitLimit;
                    }
                    if (aperture)
                        RtlSetBits(&gpuAdapter->ApertureBitMap, ii, 1);
                }
            }
        }
        //  关闭这个 adapter handle
        EtCloseAdapterHandle(openAdapterFromDeviceName.hAdapter);
    }
    ...
    PhDereferenceObject(deviceAdapterList);
    PhFree(deviceInterfaceList);
    //  能有GPU Node 则证明可以通过 D3DKMTQueryStatistics 的方式拿到GPU的数据,反正则不能
    if (EtGpuTotalNodeCount == 0)
        return FALSE;
    return TRUE;
}

EtGpuMonitorInitialization 注册回调 EtGpuProcessesUpdatedCallback 获取及更新数据


VOID NTAPI EtGpuProcessesUpdatedCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    static ULONG runCount = 0; // MUST keep in sync with runCount in process provider
    DOUBLE elapsedTime = 0; // total GPU node elapsed time in micro-seconds
    FLOAT tempGpuUsage = 0;
    ULONG i;
    PLIST_ENTRY listEntry;
    FLOAT maxNodeValue = 0;
    PET_PROCESS_BLOCK maxNodeBlock = NULL;
    if (EtD3DEnabled)
    {
        FLOAT gpuTotal;
        ULONG64 dedicatedTotal;
        ULONG64 sharedTotal;
        //  获取到原始数据并将原始数据存储到相应的 hashtable 和 circularBuffer 中
        EtUpdatePerfCounterData();
        //  从相应的 hashtable 中拿出原始并计算
        gpuTotal = EtLookupTotalGpuUtilization();
        dedicatedTotal = EtLookupTotalGpuDedicated();
        sharedTotal = EtLookupTotalGpuShared();
        if (gpuTotal > 1)
            gpuTotal = 1;
        if (gpuTotal > tempGpuUsage)
            tempGpuUsage = gpuTotal;
        EtGpuNodeUsage = tempGpuUsage;
        EtGpuDedicatedUsage = dedicatedTotal;
        EtGpuSharedUsage = sharedTotal;
    }
    else
    {
        //  从segment中拿出内存的数据, 从node中拿出node的运行时长,并根据该数据计算出GPU使用率
        EtpUpdateSystemSegmentInformation();
        EtpUpdateSystemNodeInformation();
        //  node的使用率 = node的runtime / elapsedTime
        //  使用率 = max(node的使用率)
        elapsedTime = (DOUBLE)EtClockTotalRunningTimeDelta.Delta * 10000000 / EtClockTotalRunningTimeFrequency.QuadPart;
        if (elapsedTime != 0)
        {
            for (i = 0; i < EtGpuTotalNodeCount; i++)
            {
                FLOAT usage = (FLOAT)(EtGpuNodesTotalRunningTimeDelta[i].Delta / elapsedTime);
                //  大于1按1算
                if (usage > 1)
                    usage = 1;
                //  max(node_usage)
                if (usage > tempGpuUsage)
                    tempGpuUsage = usage;
            }
        }
        EtGpuNodeUsage = tempGpuUsage;
    }
    if (EtGpuSupported && EtpGpuAdapterList->Count) {
        ...
    }
    // Update per-process statistics.
    // Note: no lock is needed because we only ever modify the list on this same thread.
    listEntry = EtProcessBlockListHead.Flink;
    //  更新每一个进程所使用GPU的信息
    while (listEntry != &EtProcessBlockListHead)
    {
        PET_PROCESS_BLOCK block;
        block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry);
        if (block->ProcessItem->State & PH_PROCESS_ITEM_REMOVED)
        {
            listEntry = listEntry->Flink;
            continue;
        }
        if (EtD3DEnabled)
        {
            //  依据processId 在相应的HashTable 中找到所需的数据
            ULONG64 sharedUsage;
            ULONG64 dedicatedUsage;
            ULONG64 commitUsage;
            block->GpuNodeUtilization = EtLookupProcessGpuUtilization(block->ProcessItem->ProcessId);
            if (EtLookupProcessGpuMemoryCounters(
                block->ProcessItem->ProcessId,
                &sharedUsage,
                &dedicatedUsage,
                &commitUsage
                ))
            {
                block->GpuSharedUsage = sharedUsage;
                block->GpuDedicatedUsage = dedicatedUsage;
                block->GpuCommitUsage = commitUsage;
            }
            else
            {
                block->GpuSharedUsage = 0;
                block->GpuDedicatedUsage = 0;
                block->GpuCommitUsage = 0;
            }
            ...
        }
        else
        {
            //  EtpUpdateProcessSegmentInformation 中设置了该进程所占GPU内存的数据
            EtpUpdateProcessSegmentInformation(block);
            EtpUpdateProcessNodeInformation(block);
            if (elapsedTime != 0)
            {
                //  对于每一个进程而言, 其node的使用率为:
                //  node_usage = process_runningtime / elapsedTime
                block->GpuNodeUtilization = (FLOAT)(block->GpuRunningTimeDelta.Delta / elapsedTime);
                // HACK
                //  单个进程的使用率不能超过总的使用率
                if (block->GpuNodeUtilization > EtGpuNodeUsage)
                    block->GpuNodeUtilization = EtGpuNodeUsage;
                //for (i = 0; i < EtGpuTotalNodeCount; i++)
                //{
                //    FLOAT usage = (FLOAT)(block->GpuTotalRunningTimeDelta[i].Delta / elapsedTime);
                //
                //    if (usage > block->GpuNodeUtilization)
                //    {
                //        block->GpuNodeUtilization = usage;
                //    }
                //}
                //  使用率最大为1
                if (block->GpuNodeUtilization > 1)
                    block->GpuNodeUtilization = 1;
                if (runCount != 0)
                {
                    block->CurrentGpuUsage = block->GpuNodeUtilization;
                    block->CurrentMemUsage = (ULONG)(block->GpuDedicatedUsage / PAGE_SIZE);
                    block->CurrentMemSharedUsage = (ULONG)(block->GpuSharedUsage / PAGE_SIZE);
                    block->CurrentCommitUsage = (ULONG)(block->GpuCommitUsage / PAGE_SIZE);
                    ...
                }
            }
        }
        if (maxNodeValue < block->GpuNodeUtilization)
        {
            maxNodeValue = block->GpuNodeUtilization;
            maxNodeBlock = block;
        }
        listEntry = listEntry->Flink;
    }
    // Update history buffers.
    if (runCount != 0)
    {
        ...
    }
    runCount++;
}

可以看到在 EtGpuProcessesUpdatedCallback 中存在两种获取数据的逻辑,展开如下。

EtD3DEnable == true

graph TD
PerfQueryCounterData[PerfQueryCounterData] -->|buffer| EtPerfCounterGetCounterData(EtPerfCounterGetCounterData)

EtPerfCounterGetCounterData[EtPerfCounterGetCounterData] -->|perfQueryBuffer|EtUpdatePerfCounterData(EtUpdatePerfCounterData)

EtUpdatePerfCounterData[EtUpdatePerfCounterData] -->|counter| EtPerfCounterProcessGpuEngineUtilizationCounter(EtPerfCounterProcessGpuEngineUtilizationCounter)

EtUpdatePerfCounterData[EtUpdatePerfCounterData] -->|counter|EtPerfCounterGpuProcessUtilizationCounter(EtPerfCounterGpuProcessUtilizationCounter)

EtUpdatePerfCounterData[EtUpdatePerfCounterData] -->|counter|EtPerfCounterGpuAdapterDedicatedCounter(EtPerfCounterGpuAdapterDedicatedCounter)

EtPerfCounterProcessGpuEngineUtilizationCounter[EtPerfCounterProcessGpuEngineUtilizationCounter]-.->
EtGpuRunningTimeHashTable(EtGpuRunningTimeHashTable)

EtPerfCounterGpuProcessUtilizationCounter[EtPerfCounterGpuProcessUtilizationCounter]-.->
EtGpuAdapterDedicatedHashTable(EtGpuAdapterDedicatedHashTable)

EtPerfCounterGpuAdapterDedicatedCounter[EtPerfCounterGpuAdapterDedicatedCounter]-.->
EtGpuProcessCounterHashTable(EtGpuProcessCounterHashTable)

EtGpuRunningTimeHashTable(EtGpuRunningTimeHashTable) --> EtLookupTotalGpuUtilization(EtLookupTotalGpuUtilization)

EtGpuAdapterDedicatedHashTable(EtGpuAdapterDedicatedHashTable) --> 
EtLookupTotalGpuDedicated(EtLookupTotalGpuDedicated)

EtGpuProcessCounterHashTable(EtGpuProcessCounterHashTable) --> 
EtLookupTotalGpuShared(EtLookupTotalGpuShared)


EtLookupTotalGpuUtilization(EtLookupTotalGpuUtilization) --> 
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)


EtLookupTotalGpuDedicated(EtLookupTotalGpuDedicated) --> 
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)

EtLookupTotalGpuShared(EtLookupTotalGpuShared) --> 
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)

从原始数据到最后被monitor 拿到的数据:

  1. EtPerfCounterGetCounterData 调用系统API PerfQueryCounterData 拿到原始数据,并将原始数据存入 buffer中:
_Success_(return)
BOOLEAN EtPerfCounterGetCounterData(
    _In_ HANDLE CounterHandle,
    _Out_ PPERF_DATA_HEADER *CounterBuffer
    )
{
    ...
    buffer = PhAllocate(bufferSize);
    status = PerfQueryCounterData(
        CounterHandle,
        buffer,
        bufferSize,
        &bufferSize
        );
    if (status == ERROR_NOT_ENOUGH_MEMORY)
    {
        if (initialBufferSize < bufferSize)
            initialBufferSize = bufferSize;
        PhFree(buffer);
        buffer = PhAllocate(bufferSize);
        status = PerfQueryCounterData(
            CounterHandle,
            buffer,
            bufferSize,
            &bufferSize
            );
    }
    ...
}
  1. EtUpdatePerfCounterData 通过偏移拿取 PPERF_COUNTER_HEADER, PPERF_DATA_HEADER 等数据,再把这些数据进行分类,将不同类型的数据放入不同的 counter 中。
  2. EtPerfCounterGpuAdapterDedicatedCounter, EtPerfCounterGpuProcessUtilizationCounter, EtPerfCounterProcessGpuEngineUtilizationCounter 函数,将 counter 的数据存储到该类型counter 相对应的 hashtable 中。
  3. EtGpuProcessesUpdatedCallback 获取gpu相关数据时,调用 EtLookupTotalGpuUtilization, EtLookupTotalGpuDedicated, EtLookupTotalGpuShared 方法,从每一个 counter 相对应的 hashtable 中拿到数据。

EtD3DEnable == false

graph TD
D3DKMTQueryStatistics[D3DKMTQueryStatistics] -->|D3DKMT_QUERYSTATISTICS_SEGMENT|EtpUpdateSystemSegmentInformation(EtpUpdateSystemSegmentInformation)

D3DKMTQueryStatistics[D3DKMTQueryStatistics] -->|D3DKMT_QUERYSTATISTICS_NODE|EtpUpdateSystemNodeInformation(EtpUpdateSystemNodeInformation)

EtpUpdateSystemSegmentInformation[EtpUpdateSystemSegmentInformation]-.->EtGpuDedicatedUsage(EtGpuDedicatedUsage)

EtpUpdateSystemSegmentInformation[EtpUpdateSystemSegmentInformation]-.->EtGpuSharedUsage(EtGpuSharedUsage)

EtpUpdateSystemNodeInformation[EtpUpdateSystemNodeInformation]-.->
EtGpuNodesTotalRunningTimeDelta(EtGpuNodesTotalRunningTimeDelta)

EtGpuDedicatedUsage(EtGpuDedicatedUsage) --> 
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)

EtGpuSharedUsage(EtGpuSharedUsage) --> 
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)

EtGpuNodesTotalRunningTimeDelta(EtGpuNodesTotalRunningTimeDelta) --> 
EtGpuProcessesUpdatedCallback(EtGpuProcessesUpdatedCallback)

EtGpuProcessesUpdatedCallback 调用 EtpUpdateSystemSegmentInformation 获取以及更新GPU内存数据

VOID EtpUpdateSystemSegmentInformation(
    VOID
    )
{
    ULONG i;
    ULONG j;
    PETP_GPU_ADAPTER gpuAdapter;
    D3DKMT_QUERYSTATISTICS queryStatistics;
    ULONG64 dedicatedUsage;
    ULONG64 sharedUsage;
    dedicatedUsage = 0;
    sharedUsage = 0;
    //  遍历每一个可用的 gpu adapter
    for (i = 0; i < EtpGpuAdapterList->Count; i++)
    {
        gpuAdapter = EtpGpuAdapterList->Items[i];
        //  遍历每一个 gpu adapter 的每一个segment
        for (j = 0; j < gpuAdapter->SegmentCount; j++)
        {
            memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
            queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT;
            queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid;
            queryStatistics.QuerySegment.SegmentId = j;
            //  发送 D3DKMT_QUERYSTATISTICS_SEGMENT 类型的 D3DKMTQueryStatistics 请求, 拿到该段的内存数据
            if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
            {
                ULONG64 bytesCommitted;
                ULONG aperture;
                if (PhWindowsVersion >= WINDOWS_8)
                {
                    bytesCommitted = queryStatistics.QueryResult.SegmentInformation.BytesResident;
                    aperture = queryStatistics.QueryResult.SegmentInformation.Aperture;
                }
                else
                {
                    PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 segmentInfo;
                    segmentInfo = (PD3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1)&queryStatistics.QueryResult;
                    bytesCommitted = segmentInfo->BytesResident;
                    aperture = segmentInfo->Aperture;
                }
                if (aperture) // RtlCheckBit(&gpuAdapter->ApertureBitMap, j)
                    sharedUsage += bytesCommitted;
                else
                    dedicatedUsage += bytesCommitted;
            }
        }
    }
    EtGpuDedicatedUsage = dedicatedUsage;
    EtGpuSharedUsage = sharedUsage;
}
EtGpuProcessesUpdatedCallback 调用 EtpUpdateSystemNodeInformation 获取以及更新node数据

VOID EtpUpdateSystemNodeInformation(
    VOID
    )
{
    PETP_GPU_ADAPTER gpuAdapter;
    D3DKMT_QUERYSTATISTICS queryStatistics;
    LARGE_INTEGER performanceCounter;
    for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++)
    {
        gpuAdapter = EtpGpuAdapterList->Items[i];
        for (ULONG j = 0; j < gpuAdapter->NodeCount; j++)
        {
            memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
            queryStatistics.Type = D3DKMT_QUERYSTATISTICS_NODE;
            queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid;
            queryStatistics.QueryNode.NodeId = j;
            //  发送类型为 D3DKMT_QUERYSTATISTICS_NODE 的 D3DKMTQueryStatistics 请求, 拿到该node 的runtime 数据
            if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)))
            {
                ULONG64 runningTime;
                //ULONG64 systemRunningTime;
                runningTime = queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart;
                //systemRunningTime = queryStatistics.QueryResult.NodeInformation.SystemInformation.RunningTime.QuadPart;
                //  更新每一个 每一个gpuadapter的每一个node 的runningTime
                PhUpdateDelta(&EtGpuNodesTotalRunningTimeDelta[gpuAdapter->FirstNodeIndex + j], runningTime);
            }
        }
    }
    PhQueryPerformanceCounter(&performanceCounter, &EtClockTotalRunningTimeFrequency);
    PhUpdateDelta(&EtClockTotalRunningTimeDelta, performanceCounter.QuadPart);
}