summaryrefslogtreecommitdiffstats
path: root/tools/profiler/core/ProfilerCPUFreq-win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/profiler/core/ProfilerCPUFreq-win.cpp')
-rw-r--r--tools/profiler/core/ProfilerCPUFreq-win.cpp244
1 files changed, 244 insertions, 0 deletions
diff --git a/tools/profiler/core/ProfilerCPUFreq-win.cpp b/tools/profiler/core/ProfilerCPUFreq-win.cpp
new file mode 100644
index 0000000000..e66f2757ea
--- /dev/null
+++ b/tools/profiler/core/ProfilerCPUFreq-win.cpp
@@ -0,0 +1,244 @@
+/* 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 <stdio.h>
+#include <strsafe.h>
+#include <winperf.h>
+
+#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<LPBYTE>(&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);
+ }
+}