/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ProfilerCPUFreq.h" #include "nsString.h" #include "nsThreadUtils.h" #ifdef DEBUG # include "nsPrintfCString.h" #endif #include #include #include #pragma comment(lib, "advapi32.lib") using namespace mozilla; ProfilerCPUFreq::ProfilerCPUFreq() { // Query the size of the text data so you can allocate the buffer. DWORD dwBufferSize = 0; LONG status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, L"Counter 9", NULL, NULL, NULL, &dwBufferSize); if (ERROR_SUCCESS != status) { NS_WARNING(nsPrintfCString("RegQueryValueEx failed getting required buffer " "size. Error is 0x%lx.\n", status) .get()); return; } // Allocate the text buffer and query the text. LPWSTR pBuffer = (LPWSTR)malloc(dwBufferSize); if (!pBuffer) { NS_WARNING("failed to allocate buffer"); return; } status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, L"Counter 9", NULL, NULL, (LPBYTE)pBuffer, &dwBufferSize); if (ERROR_SUCCESS != status) { NS_WARNING( nsPrintfCString("RegQueryValueEx failed with 0x%lx.\n", status).get()); free(pBuffer); return; } LPWSTR pwszCounterText = pBuffer; // Used to cycle through the Counter text // Ignore first pair. pwszCounterText += (wcslen(pwszCounterText) + 1); pwszCounterText += (wcslen(pwszCounterText) + 1); for (; *pwszCounterText; pwszCounterText += (wcslen(pwszCounterText) + 1)) { // Keep a pointer to the counter index, to read the index later if the name // is the one we are looking for. LPWSTR counterIndex = pwszCounterText; pwszCounterText += (wcslen(pwszCounterText) + 1); // Skip past index value if (!wcscmp(L"Processor Information", pwszCounterText)) { mBlockIndex = _wcsdup(counterIndex); } else if (!wcscmp(L"% Processor Performance", pwszCounterText)) { mCounterNameIndex = _wtoi(counterIndex); if (mBlockIndex) { // We have found all the indexes we were looking for. break; } } } free(pBuffer); if (!mBlockIndex) { NS_WARNING("index of the performance counter block not found"); return; } mBuffer = (LPBYTE)malloc(mBufferSize); if (!mBuffer) { NS_WARNING("failed to allocate initial buffer"); return; } dwBufferSize = mBufferSize; // Typically RegQueryValueEx will set the size variable to the required size. // But this does not work when querying object index values, and the buffer // size has to be increased in a loop until RegQueryValueEx no longer returns // ERROR_MORE_DATA. while (ERROR_MORE_DATA == (status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, mBlockIndex, NULL, NULL, mBuffer, &dwBufferSize))) { mBufferSize *= 2; auto* oldBuffer = mBuffer; mBuffer = (LPBYTE)realloc(mBuffer, mBufferSize); if (!mBuffer) { NS_WARNING("failed to reallocate buffer"); free(oldBuffer); return; } dwBufferSize = mBufferSize; } if (ERROR_SUCCESS != status) { NS_WARNING(nsPrintfCString("RegQueryValueEx failed getting required buffer " "size. Error is 0x%lx.\n", status) .get()); free(mBuffer); mBuffer = nullptr; return; } PERF_DATA_BLOCK* dataBlock = (PERF_DATA_BLOCK*)mBuffer; LPBYTE pObject = mBuffer + dataBlock->HeaderLength; PERF_OBJECT_TYPE* object = (PERF_OBJECT_TYPE*)pObject; PERF_COUNTER_DEFINITION* counter = nullptr; { PERF_COUNTER_DEFINITION* pCounter = (PERF_COUNTER_DEFINITION*)(pObject + object->HeaderLength); for (DWORD i = 0; i < object->NumCounters; i++) { if (mCounterNameIndex == pCounter->CounterNameTitleIndex) { counter = pCounter; break; } pCounter++; } } if (!counter || !mCPUCounters.resize(GetNumberOfProcessors())) { NS_WARNING("failing to find counter or resize the mCPUCounters vector"); free(mBuffer); mBuffer = nullptr; return; } MOZ_ASSERT(counter->CounterType == PERF_AVERAGE_BULK); PERF_COUNTER_DEFINITION* baseCounter = counter + 1; MOZ_ASSERT((baseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE); PERF_INSTANCE_DEFINITION* instanceDef = (PERF_INSTANCE_DEFINITION*)(pObject + object->DefinitionLength); for (LONG i = 0; i < object->NumInstances; i++) { PERF_COUNTER_BLOCK* counterBlock = (PERF_COUNTER_BLOCK*)((LPBYTE)instanceDef + instanceDef->ByteLength); LPWSTR name = (LPWSTR)(((LPBYTE)instanceDef) + instanceDef->NameOffset); unsigned int cpuId, coreId; if (swscanf(name, L"%u,%u", &cpuId, &coreId) == 2 && cpuId == 0 && coreId < mCPUCounters.length()) { auto& CPUCounter = mCPUCounters[coreId]; CPUCounter.data = *(UNALIGNED ULONGLONG*)((LPBYTE)counterBlock + counter->CounterOffset); CPUCounter.base = *(DWORD*)((LPBYTE)counterBlock + baseCounter->CounterOffset); // Now get the nominal core frequency. HKEY key; nsAutoString keyName( L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\"); keyName.AppendInt(coreId); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.get(), 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { DWORD data, len; len = sizeof(data); if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast(&data), &len) == ERROR_SUCCESS) { CPUCounter.nominalFrequency = data; } } } instanceDef = (PERF_INSTANCE_DEFINITION*)((LPBYTE)counterBlock + counterBlock->ByteLength); } } ProfilerCPUFreq::~ProfilerCPUFreq() { RegCloseKey(HKEY_PERFORMANCE_DATA); free(mBlockIndex); mBlockIndex = nullptr; free(mBuffer); mBuffer = nullptr; } void ProfilerCPUFreq::Sample() { DWORD dwBufferSize = mBufferSize; if (!mBuffer || (ERROR_SUCCESS != RegQueryValueEx(HKEY_PERFORMANCE_DATA, mBlockIndex, NULL, NULL, mBuffer, &dwBufferSize))) { NS_WARNING("failed to query performance data"); return; } PERF_DATA_BLOCK* dataBlock = (PERF_DATA_BLOCK*)mBuffer; LPBYTE pObject = mBuffer + dataBlock->HeaderLength; PERF_OBJECT_TYPE* object = (PERF_OBJECT_TYPE*)pObject; PERF_COUNTER_DEFINITION* counter = nullptr; { PERF_COUNTER_DEFINITION* pCounter = (PERF_COUNTER_DEFINITION*)(pObject + object->HeaderLength); for (DWORD i = 0; i < object->NumCounters; i++) { if (mCounterNameIndex == pCounter->CounterNameTitleIndex) { counter = pCounter; break; } pCounter++; } } if (!counter) { NS_WARNING("failed to find counter"); return; } MOZ_ASSERT(counter->CounterType == PERF_AVERAGE_BULK); PERF_COUNTER_DEFINITION* baseCounter = counter + 1; MOZ_ASSERT((baseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE); PERF_INSTANCE_DEFINITION* instanceDef = (PERF_INSTANCE_DEFINITION*)(pObject + object->DefinitionLength); for (LONG i = 0; i < object->NumInstances; i++) { PERF_COUNTER_BLOCK* counterBlock = (PERF_COUNTER_BLOCK*)((LPBYTE)instanceDef + instanceDef->ByteLength); LPWSTR name = (LPWSTR)(((LPBYTE)instanceDef) + instanceDef->NameOffset); unsigned int cpuId, coreId; if (swscanf(name, L"%u,%u", &cpuId, &coreId) == 2 && cpuId == 0 && coreId < mCPUCounters.length()) { auto& CPUCounter = mCPUCounters[coreId]; ULONGLONG prevData = CPUCounter.data; DWORD prevBase = CPUCounter.base; CPUCounter.data = *(UNALIGNED ULONGLONG*)((LPBYTE)counterBlock + counter->CounterOffset); CPUCounter.base = *(DWORD*)((LPBYTE)counterBlock + baseCounter->CounterOffset); if (prevBase && prevBase != CPUCounter.base) { CPUCounter.freq = CPUCounter.nominalFrequency * (CPUCounter.data - prevData) / (CPUCounter.base - prevBase) / 1000 * 10; } } instanceDef = (PERF_INSTANCE_DEFINITION*)((LPBYTE)counterBlock + counterBlock->ByteLength); } }