Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / tools / profiler / core / ProfilerCPUFreq-win.cpp
blobe66f2757eae5900c56b55dbfdc85d6cc7590f165
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "ProfilerCPUFreq.h"
6 #include "nsString.h"
7 #include "nsThreadUtils.h"
8 #ifdef DEBUG
9 # include "nsPrintfCString.h"
10 #endif
12 #include <stdio.h>
13 #include <strsafe.h>
14 #include <winperf.h>
16 #pragma comment(lib, "advapi32.lib")
18 using namespace mozilla;
20 ProfilerCPUFreq::ProfilerCPUFreq() {
21 // Query the size of the text data so you can allocate the buffer.
22 DWORD dwBufferSize = 0;
23 LONG status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, L"Counter 9", NULL, NULL,
24 NULL, &dwBufferSize);
25 if (ERROR_SUCCESS != status) {
26 NS_WARNING(nsPrintfCString("RegQueryValueEx failed getting required buffer "
27 "size. Error is 0x%lx.\n",
28 status)
29 .get());
30 return;
33 // Allocate the text buffer and query the text.
34 LPWSTR pBuffer = (LPWSTR)malloc(dwBufferSize);
35 if (!pBuffer) {
36 NS_WARNING("failed to allocate buffer");
37 return;
39 status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, L"Counter 9", NULL, NULL,
40 (LPBYTE)pBuffer, &dwBufferSize);
41 if (ERROR_SUCCESS != status) {
42 NS_WARNING(
43 nsPrintfCString("RegQueryValueEx failed with 0x%lx.\n", status).get());
44 free(pBuffer);
45 return;
48 LPWSTR pwszCounterText = pBuffer; // Used to cycle through the Counter text
49 // Ignore first pair.
50 pwszCounterText += (wcslen(pwszCounterText) + 1);
51 pwszCounterText += (wcslen(pwszCounterText) + 1);
53 for (; *pwszCounterText; pwszCounterText += (wcslen(pwszCounterText) + 1)) {
54 // Keep a pointer to the counter index, to read the index later if the name
55 // is the one we are looking for.
56 LPWSTR counterIndex = pwszCounterText;
57 pwszCounterText += (wcslen(pwszCounterText) + 1); // Skip past index value
59 if (!wcscmp(L"Processor Information", pwszCounterText)) {
60 mBlockIndex = _wcsdup(counterIndex);
61 } else if (!wcscmp(L"% Processor Performance", pwszCounterText)) {
62 mCounterNameIndex = _wtoi(counterIndex);
63 if (mBlockIndex) {
64 // We have found all the indexes we were looking for.
65 break;
69 free(pBuffer);
71 if (!mBlockIndex) {
72 NS_WARNING("index of the performance counter block not found");
73 return;
76 mBuffer = (LPBYTE)malloc(mBufferSize);
77 if (!mBuffer) {
78 NS_WARNING("failed to allocate initial buffer");
79 return;
81 dwBufferSize = mBufferSize;
83 // Typically RegQueryValueEx will set the size variable to the required size.
84 // But this does not work when querying object index values, and the buffer
85 // size has to be increased in a loop until RegQueryValueEx no longer returns
86 // ERROR_MORE_DATA.
87 while (ERROR_MORE_DATA ==
88 (status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, mBlockIndex, NULL,
89 NULL, mBuffer, &dwBufferSize))) {
90 mBufferSize *= 2;
91 auto* oldBuffer = mBuffer;
92 mBuffer = (LPBYTE)realloc(mBuffer, mBufferSize);
93 if (!mBuffer) {
94 NS_WARNING("failed to reallocate buffer");
95 free(oldBuffer);
96 return;
98 dwBufferSize = mBufferSize;
101 if (ERROR_SUCCESS != status) {
102 NS_WARNING(nsPrintfCString("RegQueryValueEx failed getting required buffer "
103 "size. Error is 0x%lx.\n",
104 status)
105 .get());
106 free(mBuffer);
107 mBuffer = nullptr;
108 return;
111 PERF_DATA_BLOCK* dataBlock = (PERF_DATA_BLOCK*)mBuffer;
112 LPBYTE pObject = mBuffer + dataBlock->HeaderLength;
113 PERF_OBJECT_TYPE* object = (PERF_OBJECT_TYPE*)pObject;
114 PERF_COUNTER_DEFINITION* counter = nullptr;
116 PERF_COUNTER_DEFINITION* pCounter =
117 (PERF_COUNTER_DEFINITION*)(pObject + object->HeaderLength);
118 for (DWORD i = 0; i < object->NumCounters; i++) {
119 if (mCounterNameIndex == pCounter->CounterNameTitleIndex) {
120 counter = pCounter;
121 break;
123 pCounter++;
126 if (!counter || !mCPUCounters.resize(GetNumberOfProcessors())) {
127 NS_WARNING("failing to find counter or resize the mCPUCounters vector");
128 free(mBuffer);
129 mBuffer = nullptr;
130 return;
133 MOZ_ASSERT(counter->CounterType == PERF_AVERAGE_BULK);
134 PERF_COUNTER_DEFINITION* baseCounter = counter + 1;
135 MOZ_ASSERT((baseCounter->CounterType & PERF_COUNTER_BASE) ==
136 PERF_COUNTER_BASE);
138 PERF_INSTANCE_DEFINITION* instanceDef =
139 (PERF_INSTANCE_DEFINITION*)(pObject + object->DefinitionLength);
140 for (LONG i = 0; i < object->NumInstances; i++) {
141 PERF_COUNTER_BLOCK* counterBlock =
142 (PERF_COUNTER_BLOCK*)((LPBYTE)instanceDef + instanceDef->ByteLength);
144 LPWSTR name = (LPWSTR)(((LPBYTE)instanceDef) + instanceDef->NameOffset);
145 unsigned int cpuId, coreId;
146 if (swscanf(name, L"%u,%u", &cpuId, &coreId) == 2 && cpuId == 0 &&
147 coreId < mCPUCounters.length()) {
148 auto& CPUCounter = mCPUCounters[coreId];
149 CPUCounter.data = *(UNALIGNED ULONGLONG*)((LPBYTE)counterBlock +
150 counter->CounterOffset);
151 CPUCounter.base =
152 *(DWORD*)((LPBYTE)counterBlock + baseCounter->CounterOffset);
154 // Now get the nominal core frequency.
155 HKEY key;
156 nsAutoString keyName(
157 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\");
158 keyName.AppendInt(coreId);
160 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.get(), 0, KEY_QUERY_VALUE,
161 &key) == ERROR_SUCCESS) {
162 DWORD data, len;
163 len = sizeof(data);
165 if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data),
166 &len) == ERROR_SUCCESS) {
167 CPUCounter.nominalFrequency = data;
171 instanceDef = (PERF_INSTANCE_DEFINITION*)((LPBYTE)counterBlock +
172 counterBlock->ByteLength);
176 ProfilerCPUFreq::~ProfilerCPUFreq() {
177 RegCloseKey(HKEY_PERFORMANCE_DATA);
178 free(mBlockIndex);
179 mBlockIndex = nullptr;
180 free(mBuffer);
181 mBuffer = nullptr;
184 void ProfilerCPUFreq::Sample() {
185 DWORD dwBufferSize = mBufferSize;
186 if (!mBuffer ||
187 (ERROR_SUCCESS != RegQueryValueEx(HKEY_PERFORMANCE_DATA, mBlockIndex,
188 NULL, NULL, mBuffer, &dwBufferSize))) {
189 NS_WARNING("failed to query performance data");
190 return;
193 PERF_DATA_BLOCK* dataBlock = (PERF_DATA_BLOCK*)mBuffer;
194 LPBYTE pObject = mBuffer + dataBlock->HeaderLength;
195 PERF_OBJECT_TYPE* object = (PERF_OBJECT_TYPE*)pObject;
196 PERF_COUNTER_DEFINITION* counter = nullptr;
198 PERF_COUNTER_DEFINITION* pCounter =
199 (PERF_COUNTER_DEFINITION*)(pObject + object->HeaderLength);
200 for (DWORD i = 0; i < object->NumCounters; i++) {
201 if (mCounterNameIndex == pCounter->CounterNameTitleIndex) {
202 counter = pCounter;
203 break;
205 pCounter++;
208 if (!counter) {
209 NS_WARNING("failed to find counter");
210 return;
213 MOZ_ASSERT(counter->CounterType == PERF_AVERAGE_BULK);
214 PERF_COUNTER_DEFINITION* baseCounter = counter + 1;
215 MOZ_ASSERT((baseCounter->CounterType & PERF_COUNTER_BASE) ==
216 PERF_COUNTER_BASE);
218 PERF_INSTANCE_DEFINITION* instanceDef =
219 (PERF_INSTANCE_DEFINITION*)(pObject + object->DefinitionLength);
220 for (LONG i = 0; i < object->NumInstances; i++) {
221 PERF_COUNTER_BLOCK* counterBlock =
222 (PERF_COUNTER_BLOCK*)((LPBYTE)instanceDef + instanceDef->ByteLength);
224 LPWSTR name = (LPWSTR)(((LPBYTE)instanceDef) + instanceDef->NameOffset);
225 unsigned int cpuId, coreId;
226 if (swscanf(name, L"%u,%u", &cpuId, &coreId) == 2 && cpuId == 0 &&
227 coreId < mCPUCounters.length()) {
228 auto& CPUCounter = mCPUCounters[coreId];
229 ULONGLONG prevData = CPUCounter.data;
230 DWORD prevBase = CPUCounter.base;
231 CPUCounter.data = *(UNALIGNED ULONGLONG*)((LPBYTE)counterBlock +
232 counter->CounterOffset);
233 CPUCounter.base =
234 *(DWORD*)((LPBYTE)counterBlock + baseCounter->CounterOffset);
235 if (prevBase && prevBase != CPUCounter.base) {
236 CPUCounter.freq = CPUCounter.nominalFrequency *
237 (CPUCounter.data - prevData) /
238 (CPUCounter.base - prevBase) / 1000 * 10;
241 instanceDef = (PERF_INSTANCE_DEFINITION*)((LPBYTE)counterBlock +
242 counterBlock->ByteLength);