/* * Process Hacker Extended Tools - * GPU monitoring * * Copyright (C) 2011-2015 wj32 * Copyright (C) 2016 dmex * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ #define INITGUID #include "exttools.h" #include #include #include "d3dkmt.h" #include "gpumon.h" static GUID GUID_DISPLAY_DEVICE_ARRIVAL_I = { 0x1ca05180, 0xa699, 0x450a, { 0x9a, 0x0c, 0xde, 0x4f, 0xbe, 0x3d, 0xdd, 0x89 } }; static PFND3DKMT_OPENADAPTERFROMDEVICENAME D3DKMTOpenAdapterFromDeviceName_I; static PFND3DKMT_CLOSEADAPTER D3DKMTCloseAdapter_I; static PFND3DKMT_QUERYSTATISTICS D3DKMTQueryStatistics_I; BOOLEAN EtGpuEnabled; static PPH_LIST EtpGpuAdapterList; static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; ULONG EtGpuTotalNodeCount; ULONG EtGpuTotalSegmentCount; ULONG64 EtGpuDedicatedLimit; ULONG64 EtGpuSharedLimit; ULONG EtGpuNextNodeIndex = 0; RTL_BITMAP EtGpuNodeBitMap; PULONG EtGpuNodeBitMapBuffer; ULONG EtGpuNodeBitMapBitsSet; PULONG EtGpuNewNodeBitMapBuffer; PH_UINT64_DELTA EtClockTotalRunningTimeDelta; LARGE_INTEGER EtClockTotalRunningTimeFrequency; PH_UINT64_DELTA EtGpuTotalRunningTimeDelta; PH_UINT64_DELTA EtGpuSystemRunningTimeDelta; FLOAT EtGpuNodeUsage; PH_CIRCULAR_BUFFER_FLOAT EtGpuNodeHistory; PH_CIRCULAR_BUFFER_ULONG EtMaxGpuNodeHistory; // ID of max. GPU usage process PH_CIRCULAR_BUFFER_FLOAT EtMaxGpuNodeUsageHistory; PPH_UINT64_DELTA EtGpuNodesTotalRunningTimeDelta; PPH_CIRCULAR_BUFFER_FLOAT EtGpuNodesHistory; ULONG64 EtGpuDedicatedUsage; ULONG64 EtGpuSharedUsage; PH_CIRCULAR_BUFFER_ULONG EtGpuDedicatedHistory; PH_CIRCULAR_BUFFER_ULONG EtGpuSharedHistory; VOID EtGpuMonitorInitialization( VOID ) { if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR) && WindowsVersion >= WINDOWS_7) { PVOID gdi32Handle; if (gdi32Handle = PhGetDllHandle(L"gdi32.dll")) { D3DKMTOpenAdapterFromDeviceName_I = PhGetProcedureAddress(gdi32Handle, "D3DKMTOpenAdapterFromDeviceName", 0); D3DKMTCloseAdapter_I = PhGetProcedureAddress(gdi32Handle, "D3DKMTCloseAdapter", 0); D3DKMTQueryStatistics_I = PhGetProcedureAddress(gdi32Handle, "D3DKMTQueryStatistics", 0); } if ( D3DKMTOpenAdapterFromDeviceName_I && D3DKMTCloseAdapter_I && D3DKMTQueryStatistics_I ) { EtpGpuAdapterList = PhCreateList(4); if (EtpInitializeD3DStatistics() && EtpGpuAdapterList->Count != 0) EtGpuEnabled = TRUE; } } if (EtGpuEnabled) { ULONG sampleCount; ULONG i; ULONG j; PPH_STRING bitmapString; D3DKMT_QUERYSTATISTICS queryStatistics; sampleCount = PhGetIntegerSetting(L"SampleCount"); PhInitializeCircularBuffer_FLOAT(&EtGpuNodeHistory, sampleCount); PhInitializeCircularBuffer_ULONG(&EtMaxGpuNodeHistory, sampleCount); PhInitializeCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, sampleCount); PhInitializeCircularBuffer_ULONG(&EtGpuDedicatedHistory, sampleCount); PhInitializeCircularBuffer_ULONG(&EtGpuSharedHistory, sampleCount); EtGpuNodesTotalRunningTimeDelta = PhAllocate(sizeof(PH_UINT64_DELTA) * EtGpuTotalNodeCount); memset(EtGpuNodesTotalRunningTimeDelta, 0, sizeof(PH_UINT64_DELTA) * EtGpuTotalNodeCount); EtGpuNodesHistory = PhAllocate(sizeof(PH_CIRCULAR_BUFFER_FLOAT) * EtGpuTotalNodeCount); for (i = 0; i < EtGpuTotalNodeCount; i++) { PhInitializeCircularBuffer_FLOAT(&EtGpuNodesHistory[i], sampleCount); } PhRegisterCallback( &PhProcessesUpdatedEvent, EtGpuProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration ); // Load the node bitmap. bitmapString = PhGetStringSetting(SETTING_NAME_GPU_NODE_BITMAP); if (!(bitmapString->Length & 3) && bitmapString->Length / 4 <= BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount)) { PhHexStringToBuffer(&bitmapString->sr, (PUCHAR)EtGpuNodeBitMapBuffer); EtGpuNodeBitMapBitsSet = RtlNumberOfSetBits(&EtGpuNodeBitMap); } PhDereferenceObject(bitmapString); // Fix up the node bitmap if the current node count differs from what we've seen. if (EtGpuTotalNodeCount != PhGetIntegerSetting(SETTING_NAME_GPU_LAST_NODE_COUNT)) { ULONG maxContextSwitch = 0; ULONG maxContextSwitchNodeIndex = 0; RtlClearAllBits(&EtGpuNodeBitMap); EtGpuNodeBitMapBitsSet = 0; for (i = 0; i < EtpGpuAdapterList->Count; i++) { PETP_GPU_ADAPTER gpuAdapter = EtpGpuAdapterList->Items[i]; for (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; if (NT_SUCCESS(D3DKMTQueryStatistics_I(&queryStatistics))) { // The numbers below are quite arbitrary. if (queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart != 0 && queryStatistics.QueryResult.NodeInformation.GlobalInformation.ContextSwitch > 10000) { RtlSetBits(&EtGpuNodeBitMap, gpuAdapter->FirstNodeIndex + j, 1); EtGpuNodeBitMapBitsSet++; } if (maxContextSwitch < queryStatistics.QueryResult.NodeInformation.GlobalInformation.ContextSwitch) { maxContextSwitch = queryStatistics.QueryResult.NodeInformation.GlobalInformation.ContextSwitch; maxContextSwitchNodeIndex = gpuAdapter->FirstNodeIndex + j; } } } } // Just in case if (EtGpuNodeBitMapBitsSet == 0) { RtlSetBits(&EtGpuNodeBitMap, maxContextSwitchNodeIndex, 1); EtGpuNodeBitMapBitsSet = 1; } PhSetIntegerSetting(SETTING_NAME_GPU_LAST_NODE_COUNT, EtGpuTotalNodeCount); } } } BOOLEAN EtpInitializeD3DStatistics( VOID ) { PWSTR deviceInterfaceList = NULL; ULONG deviceInterfaceListLength = 0; PWSTR deviceInterface; D3DKMT_OPENADAPTERFROMDEVICENAME openAdapterFromDeviceName; D3DKMT_QUERYSTATISTICS queryStatistics; D3DKMT_CLOSEADAPTER closeAdapter; if (CM_Get_Device_Interface_List_Size( &deviceInterfaceListLength, &GUID_DISPLAY_DEVICE_ARRIVAL_I, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ) != CR_SUCCESS) { return FALSE; } deviceInterfaceList = PhAllocate(deviceInterfaceListLength * sizeof(WCHAR)); memset(deviceInterfaceList, 0, deviceInterfaceListLength * sizeof(WCHAR)); if (CM_Get_Device_Interface_List( &GUID_DISPLAY_DEVICE_ARRIVAL_I, NULL, deviceInterfaceList, deviceInterfaceListLength, CM_GET_DEVICE_INTERFACE_LIST_PRESENT ) != CR_SUCCESS) { PhFree(deviceInterfaceList); return FALSE; } for (deviceInterface = deviceInterfaceList; *deviceInterface; deviceInterface += PhCountStringZ(deviceInterface) + 1) { memset(&openAdapterFromDeviceName, 0, sizeof(D3DKMT_OPENADAPTERFROMDEVICENAME)); openAdapterFromDeviceName.pDeviceName = deviceInterface; if (NT_SUCCESS(D3DKMTOpenAdapterFromDeviceName_I(&openAdapterFromDeviceName))) { memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER; queryStatistics.AdapterLuid = openAdapterFromDeviceName.AdapterLuid; if (NT_SUCCESS(D3DKMTQueryStatistics_I(&queryStatistics))) { PETP_GPU_ADAPTER gpuAdapter; ULONG i; gpuAdapter = EtpAllocateGpuAdapter(queryStatistics.QueryResult.AdapterInformation.NbSegments); gpuAdapter->AdapterLuid = openAdapterFromDeviceName.AdapterLuid; gpuAdapter->Description = EtpQueryDeviceDescription(deviceInterface); gpuAdapter->NodeCount = queryStatistics.QueryResult.AdapterInformation.NodeCount; gpuAdapter->SegmentCount = queryStatistics.QueryResult.AdapterInformation.NbSegments; RtlInitializeBitMap(&gpuAdapter->ApertureBitMap, gpuAdapter->ApertureBitMapBuffer, queryStatistics.QueryResult.AdapterInformation.NbSegments); PhAddItemList(EtpGpuAdapterList, gpuAdapter); EtGpuTotalNodeCount += gpuAdapter->NodeCount; EtGpuTotalSegmentCount += gpuAdapter->SegmentCount; gpuAdapter->FirstNodeIndex = EtGpuNextNodeIndex; EtGpuNextNodeIndex += gpuAdapter->NodeCount; for (i = 0; i < gpuAdapter->SegmentCount; i++) { memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT; queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; queryStatistics.QuerySegment.SegmentId = i; if (NT_SUCCESS(D3DKMTQueryStatistics_I(&queryStatistics))) { ULONG64 commitLimit; ULONG aperture; if (WindowsVersion >= WINDOWS_8) { commitLimit = queryStatistics.QueryResult.SegmentInformation.CommitLimit; aperture = queryStatistics.QueryResult.SegmentInformation.Aperture; } else { commitLimit = queryStatistics.QueryResult.SegmentInformationV1.CommitLimit; aperture = queryStatistics.QueryResult.SegmentInformationV1.Aperture; } if (aperture) EtGpuSharedLimit += commitLimit; else EtGpuDedicatedLimit += commitLimit; if (aperture) RtlSetBits(&gpuAdapter->ApertureBitMap, i, 1); } } } memset(&closeAdapter, 0, sizeof(D3DKMT_CLOSEADAPTER)); closeAdapter.hAdapter = openAdapterFromDeviceName.hAdapter; D3DKMTCloseAdapter_I(&closeAdapter); } } PhFree(deviceInterfaceList); EtGpuNodeBitMapBuffer = PhAllocate(BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount)); RtlInitializeBitMap(&EtGpuNodeBitMap, EtGpuNodeBitMapBuffer, EtGpuTotalNodeCount); RtlSetBits(&EtGpuNodeBitMap, 0, 1); EtGpuNodeBitMapBitsSet = 1; return TRUE; } PETP_GPU_ADAPTER EtpAllocateGpuAdapter( _In_ ULONG NumberOfSegments ) { PETP_GPU_ADAPTER adapter; SIZE_T sizeNeeded; sizeNeeded = FIELD_OFFSET(ETP_GPU_ADAPTER, ApertureBitMapBuffer); sizeNeeded += BYTES_NEEDED_FOR_BITS(NumberOfSegments); adapter = PhAllocate(sizeNeeded); memset(adapter, 0, sizeNeeded); return adapter; } PPH_STRING EtpQueryDeviceDescription( _In_ PWSTR DeviceInterface ) { CONFIGRET result; PPH_STRING string; ULONG bufferSize; DEVPROPTYPE devicePropertyType; DEVINST deviceInstanceHandle; ULONG deviceInstanceIdLength = MAX_DEVICE_ID_LEN; WCHAR deviceInstanceId[MAX_DEVICE_ID_LEN]; if (CM_Get_Device_Interface_Property( DeviceInterface, &DEVPKEY_Device_InstanceId, &devicePropertyType, (PBYTE)deviceInstanceId, &deviceInstanceIdLength, 0 ) != CR_SUCCESS) { return NULL; } if (CM_Locate_DevNode( &deviceInstanceHandle, deviceInstanceId, CM_LOCATE_DEVNODE_NORMAL ) != CR_SUCCESS) { return NULL; } bufferSize = 0x40; string = PhCreateStringEx(NULL, bufferSize); if ((result = CM_Get_DevNode_Property( // CM_Get_DevNode_Registry_Property with CM_DRP_DEVICEDESC?? deviceInstanceHandle, &DEVPKEY_Device_DeviceDesc, &devicePropertyType, (PBYTE)string->Buffer, &bufferSize, 0 )) != CR_SUCCESS) { PhDereferenceObject(string); string = PhCreateStringEx(NULL, bufferSize); result = CM_Get_DevNode_Property( deviceInstanceHandle, &DEVPKEY_Device_DeviceDesc, &devicePropertyType, (PBYTE)string->Buffer, &bufferSize, 0 ); } if (result != CR_SUCCESS) { PhDereferenceObject(string); return NULL; } PhTrimToNullTerminatorString(string); return string; } VOID EtpUpdateSegmentInformation( _In_opt_ PET_PROCESS_BLOCK Block ) { ULONG i; ULONG j; PETP_GPU_ADAPTER gpuAdapter; D3DKMT_QUERYSTATISTICS queryStatistics; ULONG64 dedicatedUsage; ULONG64 sharedUsage; if (Block && !Block->ProcessItem->QueryHandle) return; dedicatedUsage = 0; sharedUsage = 0; for (i = 0; i < EtpGpuAdapterList->Count; i++) { gpuAdapter = EtpGpuAdapterList->Items[i]; for (j = 0; j < gpuAdapter->SegmentCount; j++) { memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); if (Block) queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT; else queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT; queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; if (Block) { queryStatistics.hProcess = Block->ProcessItem->QueryHandle; queryStatistics.QueryProcessSegment.SegmentId = j; } else { queryStatistics.QuerySegment.SegmentId = j; } if (NT_SUCCESS(D3DKMTQueryStatistics_I(&queryStatistics))) { if (Block) { ULONG64 bytesCommitted; if (WindowsVersion >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } else { bytesCommitted = (ULONG)queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) sharedUsage += bytesCommitted; else dedicatedUsage += bytesCommitted; } else { ULONG64 bytesCommitted; if (WindowsVersion >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.SegmentInformation.BytesCommitted; } else { bytesCommitted = queryStatistics.QueryResult.SegmentInformationV1.BytesCommitted; } if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) sharedUsage += bytesCommitted; else dedicatedUsage += bytesCommitted; } } } } if (Block) { Block->GpuDedicatedUsage = dedicatedUsage; Block->GpuSharedUsage = sharedUsage; } else { EtGpuDedicatedUsage = dedicatedUsage; EtGpuSharedUsage = sharedUsage; } } VOID EtpUpdateNodeInformation( _In_opt_ PET_PROCESS_BLOCK Block ) { ULONG i; ULONG j; PETP_GPU_ADAPTER gpuAdapter; D3DKMT_QUERYSTATISTICS queryStatistics; ULONG64 totalRunningTime; ULONG64 systemRunningTime; if (Block && !Block->ProcessItem->QueryHandle) return; totalRunningTime = 0; systemRunningTime = 0; for (i = 0; i < EtpGpuAdapterList->Count; i++) { gpuAdapter = EtpGpuAdapterList->Items[i]; for (j = 0; j < gpuAdapter->NodeCount; j++) { if (Block && !RtlCheckBit(&EtGpuNodeBitMap, gpuAdapter->FirstNodeIndex + j)) continue; memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); if (Block) queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_NODE; else queryStatistics.Type = D3DKMT_QUERYSTATISTICS_NODE; queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; if (Block) { queryStatistics.hProcess = Block->ProcessItem->QueryHandle; queryStatistics.QueryProcessNode.NodeId = j; } else { queryStatistics.QueryNode.NodeId = j; } if (NT_SUCCESS(D3DKMTQueryStatistics_I(&queryStatistics))) { if (Block) { totalRunningTime += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime.QuadPart; } else { ULONG nodeIndex; nodeIndex = gpuAdapter->FirstNodeIndex + j; PhUpdateDelta(&EtGpuNodesTotalRunningTimeDelta[nodeIndex], queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart); if (RtlCheckBit(&EtGpuNodeBitMap, gpuAdapter->FirstNodeIndex + j)) { totalRunningTime += queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart; systemRunningTime += queryStatistics.QueryResult.NodeInformation.SystemInformation.RunningTime.QuadPart; } } } } } if (Block) { PhUpdateDelta(&Block->GpuRunningTimeDelta, totalRunningTime); } else { LARGE_INTEGER performanceCounter; NtQueryPerformanceCounter(&performanceCounter, &EtClockTotalRunningTimeFrequency); PhUpdateDelta(&EtClockTotalRunningTimeDelta, performanceCounter.QuadPart); PhUpdateDelta(&EtGpuTotalRunningTimeDelta, totalRunningTime); PhUpdateDelta(&EtGpuSystemRunningTimeDelta, systemRunningTime); } } 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; // total GPU node elapsed time in micro-seconds ULONG i; PLIST_ENTRY listEntry; FLOAT maxNodeValue = 0; PET_PROCESS_BLOCK maxNodeBlock = NULL; // Update global statistics. EtpUpdateSegmentInformation(NULL); EtpUpdateNodeInformation(NULL); elapsedTime = (DOUBLE)EtClockTotalRunningTimeDelta.Delta * 10000000 / EtClockTotalRunningTimeFrequency.QuadPart; if (elapsedTime != 0) EtGpuNodeUsage = (FLOAT)(EtGpuTotalRunningTimeDelta.Delta / (elapsedTime * EtGpuNodeBitMapBitsSet)); else EtGpuNodeUsage = 0; if (EtGpuNodeUsage > 1) EtGpuNodeUsage = 1; // Do the update of the node bitmap if needed. if (EtGpuNewNodeBitMapBuffer) { PULONG newBuffer; newBuffer = _InterlockedExchangePointer(&EtGpuNewNodeBitMapBuffer, NULL); if (newBuffer) { PhFree(EtGpuNodeBitMap.Buffer); EtGpuNodeBitMap.Buffer = newBuffer; EtGpuNodeBitMapBuffer = newBuffer; EtGpuNodeBitMapBitsSet = RtlNumberOfSetBits(&EtGpuNodeBitMap); EtSaveGpuMonitorSettings(); } } // Update per-process statistics. // Note: no lock is needed because we only ever modify the list on this same thread. listEntry = EtProcessBlockListHead.Flink; while (listEntry != &EtProcessBlockListHead) { PET_PROCESS_BLOCK block; block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry); EtpUpdateSegmentInformation(block); EtpUpdateNodeInformation(block); if (elapsedTime != 0) { block->GpuNodeUsage = (FLOAT)(block->GpuRunningTimeDelta.Delta / (elapsedTime * EtGpuNodeBitMapBitsSet)); if (block->GpuNodeUsage > 1) block->GpuNodeUsage = 1; } if (maxNodeValue < block->GpuNodeUsage) { maxNodeValue = block->GpuNodeUsage; maxNodeBlock = block; } listEntry = listEntry->Flink; } // Update history buffers. if (runCount != 0) { PhAddItemCircularBuffer_FLOAT(&EtGpuNodeHistory, EtGpuNodeUsage); PhAddItemCircularBuffer_ULONG(&EtGpuDedicatedHistory, (ULONG)(EtGpuDedicatedUsage / PAGE_SIZE)); PhAddItemCircularBuffer_ULONG(&EtGpuSharedHistory, (ULONG)(EtGpuSharedUsage / PAGE_SIZE)); for (i = 0; i < EtGpuTotalNodeCount; i++) { FLOAT usage; usage = (FLOAT)(EtGpuNodesTotalRunningTimeDelta[i].Delta / elapsedTime); if (usage > 1) usage = 1; PhAddItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], usage); } if (maxNodeBlock) { PhAddItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, HandleToUlong(maxNodeBlock->ProcessItem->ProcessId)); PhAddItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, maxNodeBlock->GpuNodeUsage); PhReferenceProcessRecordForStatistics(maxNodeBlock->ProcessItem->Record); } else { PhAddItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, 0); PhAddItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, 0); } } runCount++; } VOID EtSaveGpuMonitorSettings( VOID ) { PPH_STRING string; string = PhBufferToHexString((PUCHAR)EtGpuNodeBitMapBuffer, BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount)); PhSetStringSetting2(SETTING_NAME_GPU_NODE_BITMAP, &string->sr); PhDereferenceObject(string); } ULONG EtGetGpuAdapterCount( VOID ) { return EtpGpuAdapterList->Count; } ULONG EtGetGpuAdapterIndexFromNodeIndex( _In_ ULONG NodeIndex ) { ULONG i; PETP_GPU_ADAPTER gpuAdapter; for (i = 0; i < EtpGpuAdapterList->Count; i++) { gpuAdapter = EtpGpuAdapterList->Items[i]; if (NodeIndex >= gpuAdapter->FirstNodeIndex && NodeIndex < gpuAdapter->FirstNodeIndex + gpuAdapter->NodeCount) return i; } return -1; } PPH_STRING EtGetGpuAdapterDescription( _In_ ULONG Index ) { PPH_STRING description; if (Index >= EtpGpuAdapterList->Count) return NULL; description = ((PETP_GPU_ADAPTER)EtpGpuAdapterList->Items[Index])->Description; if (description) { PhReferenceObject(description); return description; } else { return NULL; } } VOID EtAllocateGpuNodeBitMap( _Out_ PRTL_BITMAP BitMap ) { SIZE_T numberOfBytes; numberOfBytes = BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount); BitMap->Buffer = PhAllocate(numberOfBytes); BitMap->SizeOfBitMap = EtGpuTotalNodeCount; memset(BitMap->Buffer, 0, numberOfBytes); } VOID EtUpdateGpuNodeBitMap( _In_ PRTL_BITMAP NewBitMap ) { PULONG buffer; buffer = _InterlockedExchangePointer(&EtGpuNewNodeBitMapBuffer, NewBitMap->Buffer); if (buffer) PhFree(buffer); } VOID EtQueryProcessGpuStatistics( _In_ HANDLE ProcessHandle, _Out_ PET_PROCESS_GPU_STATISTICS Statistics ) { NTSTATUS status; ULONG i; ULONG j; PETP_GPU_ADAPTER gpuAdapter; D3DKMT_QUERYSTATISTICS queryStatistics; memset(Statistics, 0, sizeof(ET_PROCESS_GPU_STATISTICS)); for (i = 0; i < EtpGpuAdapterList->Count; i++) { gpuAdapter = EtpGpuAdapterList->Items[i]; Statistics->SegmentCount += gpuAdapter->SegmentCount; Statistics->NodeCount += gpuAdapter->NodeCount; for (j = 0; j < gpuAdapter->SegmentCount; j++) { memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT; queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; queryStatistics.hProcess = ProcessHandle; queryStatistics.QueryProcessSegment.SegmentId = j; if (NT_SUCCESS(status = D3DKMTQueryStatistics_I(&queryStatistics))) { ULONG64 bytesCommitted; if (WindowsVersion >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } else { bytesCommitted = (ULONG)queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) Statistics->SharedCommitted += bytesCommitted; else Statistics->DedicatedCommitted += bytesCommitted; } } for (j = 0; j < gpuAdapter->NodeCount; j++) { memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_NODE; queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; queryStatistics.hProcess = ProcessHandle; queryStatistics.QueryProcessNode.NodeId = j; if (NT_SUCCESS(D3DKMTQueryStatistics_I(&queryStatistics))) { Statistics->RunningTime += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime.QuadPart; Statistics->ContextSwitches += queryStatistics.QueryResult.ProcessNodeInformation.ContextSwitch; } } memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS; queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; queryStatistics.hProcess = ProcessHandle; if (NT_SUCCESS(D3DKMTQueryStatistics_I(&queryStatistics))) { Statistics->BytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesAllocated; Statistics->BytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesReserved; Statistics->WriteCombinedBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.WriteCombinedBytesAllocated; Statistics->WriteCombinedBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.WriteCombinedBytesReserved; Statistics->CachedBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.CachedBytesAllocated; Statistics->CachedBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.CachedBytesReserved; Statistics->SectionBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.SectionBytesAllocated; Statistics->SectionBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.SectionBytesReserved; } } }