Backed out 15 changesets (bug 1852806) for causing mda failures on test_video_low_pow...
[gecko.git] / tools / profiler / core / PowerCounters-win.cpp
blobe76e414261961bb07038fe8ca20c102d4353e2da
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 "PowerCounters.h"
6 #include "nsXULAppAPI.h" // for XRE_IsParentProcess
7 #include "nsString.h"
9 #include <windows.h>
10 #include <devioctl.h>
11 #include <setupapi.h> // for SetupDi*
12 // LogSeverity, defined by setupapi.h to DWORD, messes with other code.
13 #undef LogSeverity
15 #undef NTDDI_VERSION
16 #define NTDDI_VERSION NTDDI_WINBLUE
17 #include <emi.h>
19 using namespace mozilla;
21 // This is a counter to collect power utilization during profiling.
22 // It cannot be a raw `ProfilerCounter` because we need to manually add/remove
23 // it while the profiler lock is already held.
24 class PowerMeterChannel final : public BaseProfilerCount {
25 public:
26 explicit PowerMeterChannel(const WCHAR* aChannelName, ULONGLONG aInitialValue,
27 ULONGLONG aInitialTime)
28 : BaseProfilerCount(nullptr, nullptr, nullptr, "power",
29 "Power utilization"),
30 mChannelName(NS_ConvertUTF16toUTF8(aChannelName)),
31 mPreviousValue(aInitialValue),
32 mPreviousTime(aInitialTime),
33 mIsSampleNew(true) {
34 if (mChannelName.Equals("RAPL_Package0_PKG")) {
35 mLabel = "Power: CPU package";
36 mDescription = mChannelName.get();
37 } else if (mChannelName.Equals("RAPL_Package0_PP0")) {
38 mLabel = "Power: CPU cores";
39 mDescription = mChannelName.get();
40 } else if (mChannelName.Equals("RAPL_Package0_PP1")) {
41 mLabel = "Power: iGPU";
42 mDescription = mChannelName.get();
43 } else if (mChannelName.Equals("RAPL_Package0_DRAM")) {
44 mLabel = "Power: DRAM";
45 mDescription = mChannelName.get();
46 } else {
47 unsigned int coreId;
48 if (sscanf(mChannelName.get(), "RAPL_Package0_Core%u_CORE", &coreId) ==
49 1) {
50 mLabelString = "Power: CPU core ";
51 mLabelString.AppendInt(coreId);
52 mLabel = mLabelString.get();
53 mDescription = mChannelName.get();
54 } else {
55 mLabel = mChannelName.get();
60 CountSample Sample() override {
61 CountSample result;
62 result.count = mCounter;
63 result.number = 0;
64 result.isSampleNew = mIsSampleNew;
65 mIsSampleNew = false;
66 return result;
69 void AddSample(ULONGLONG aAbsoluteEnergy, ULONGLONG aAbsoluteTime) {
70 // aAbsoluteTime is the time since the system start in 100ns increments.
71 if (aAbsoluteTime == mPreviousTime) {
72 return;
75 if (aAbsoluteEnergy > mPreviousValue) {
76 int64_t increment = aAbsoluteEnergy - mPreviousValue;
77 mCounter += increment;
78 mPreviousValue += increment;
79 mPreviousTime = aAbsoluteTime;
82 mIsSampleNew = true;
85 private:
86 int64_t mCounter;
87 nsCString mChannelName;
89 // Used as a storage when the label can not be a literal string.
90 nsCString mLabelString;
92 ULONGLONG mPreviousValue;
93 ULONGLONG mPreviousTime;
94 bool mIsSampleNew;
97 class PowerMeterDevice {
98 public:
99 explicit PowerMeterDevice(LPCTSTR aDevicePath) {
100 mHandle = ::CreateFile(aDevicePath, GENERIC_READ,
101 FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
102 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
103 if (mHandle == INVALID_HANDLE_VALUE) {
104 return;
107 EMI_VERSION version = {0};
108 DWORD dwOut;
110 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_VERSION, nullptr, 0, &version,
111 sizeof(version), &dwOut, nullptr) ||
112 (version.EmiVersion != EMI_VERSION_V1 &&
113 version.EmiVersion != EMI_VERSION_V2)) {
114 return;
117 EMI_METADATA_SIZE size = {0};
118 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_METADATA_SIZE, nullptr, 0,
119 &size, sizeof(size), &dwOut, nullptr) ||
120 !size.MetadataSize) {
121 return;
124 UniquePtr<uint8_t[]> metadata(new (std::nothrow)
125 uint8_t[size.MetadataSize]);
126 if (!metadata) {
127 return;
130 if (version.EmiVersion == EMI_VERSION_V2) {
131 EMI_METADATA_V2* metadata2 =
132 reinterpret_cast<EMI_METADATA_V2*>(metadata.get());
133 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_METADATA, nullptr, 0,
134 metadata2, size.MetadataSize, &dwOut, nullptr)) {
135 return;
138 if (!mChannels.reserve(metadata2->ChannelCount)) {
139 return;
142 mDataBuffer =
143 MakeUnique<EMI_CHANNEL_MEASUREMENT_DATA[]>(metadata2->ChannelCount);
144 if (!mDataBuffer) {
145 return;
148 if (!::DeviceIoControl(
149 mHandle, IOCTL_EMI_GET_MEASUREMENT, nullptr, 0, mDataBuffer.get(),
150 sizeof(EMI_CHANNEL_MEASUREMENT_DATA[metadata2->ChannelCount]),
151 &dwOut, nullptr)) {
152 return;
155 EMI_CHANNEL_V2* channel = &metadata2->Channels[0];
156 for (int i = 0; i < metadata2->ChannelCount; ++i) {
157 EMI_CHANNEL_MEASUREMENT_DATA* channel_data = &mDataBuffer[i];
158 mChannels.infallibleAppend(new PowerMeterChannel(
159 channel->ChannelName, channel_data->AbsoluteEnergy,
160 channel_data->AbsoluteTime));
161 channel = EMI_CHANNEL_V2_NEXT_CHANNEL(channel);
163 } else if (version.EmiVersion == EMI_VERSION_V1) {
164 EMI_METADATA_V1* metadata1 =
165 reinterpret_cast<EMI_METADATA_V1*>(metadata.get());
166 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_METADATA, nullptr, 0,
167 metadata1, size.MetadataSize, &dwOut, nullptr)) {
168 return;
171 mDataBuffer = MakeUnique<EMI_CHANNEL_MEASUREMENT_DATA[]>(1);
172 if (!mDataBuffer) {
173 return;
176 if (!::DeviceIoControl(
177 mHandle, IOCTL_EMI_GET_MEASUREMENT, nullptr, 0, mDataBuffer.get(),
178 sizeof(EMI_CHANNEL_MEASUREMENT_DATA), &dwOut, nullptr)) {
179 return;
182 (void)mChannels.append(new PowerMeterChannel(
183 metadata1->MeteredHardwareName, mDataBuffer[0].AbsoluteEnergy,
184 mDataBuffer[0].AbsoluteTime));
188 ~PowerMeterDevice() {
189 if (mHandle != INVALID_HANDLE_VALUE) {
190 ::CloseHandle(mHandle);
194 void Sample() {
195 MOZ_ASSERT(HasChannels());
196 MOZ_ASSERT(mDataBuffer);
198 DWORD dwOut;
199 if (!::DeviceIoControl(
200 mHandle, IOCTL_EMI_GET_MEASUREMENT, nullptr, 0, mDataBuffer.get(),
201 sizeof(EMI_CHANNEL_MEASUREMENT_DATA[mChannels.length()]), &dwOut,
202 nullptr)) {
203 return;
206 for (size_t i = 0; i < mChannels.length(); ++i) {
207 EMI_CHANNEL_MEASUREMENT_DATA* channel_data = &mDataBuffer[i];
208 mChannels[i]->AddSample(channel_data->AbsoluteEnergy,
209 channel_data->AbsoluteTime);
213 bool HasChannels() { return mChannels.length() != 0; }
214 void AppendCountersTo(PowerCounters::CountVector& aCounters) {
215 if (aCounters.reserve(aCounters.length() + mChannels.length())) {
216 for (auto& channel : mChannels) {
217 aCounters.infallibleAppend(channel.get());
222 private:
223 Vector<UniquePtr<PowerMeterChannel>, 4> mChannels;
224 HANDLE mHandle = INVALID_HANDLE_VALUE;
225 UniquePtr<EMI_CHANNEL_MEASUREMENT_DATA[]> mDataBuffer;
228 PowerCounters::PowerCounters() {
229 class MOZ_STACK_CLASS HDevInfoHolder final {
230 public:
231 explicit HDevInfoHolder(HDEVINFO aHandle) : mHandle(aHandle) {}
233 ~HDevInfoHolder() { ::SetupDiDestroyDeviceInfoList(mHandle); }
235 private:
236 HDEVINFO mHandle;
239 if (!XRE_IsParentProcess()) {
240 // Energy meters are global, so only sample them on the parent.
241 return;
244 // Energy Metering Device Interface
245 // {45BD8344-7ED6-49cf-A440-C276C933B053}
247 // Using GUID_DEVICE_ENERGY_METER does not compile as the symbol does not
248 // exist before Windows 10.
249 GUID my_GUID_DEVICE_ENERGY_METER = {
250 0x45bd8344,
251 0x7ed6,
252 0x49cf,
253 {0xa4, 0x40, 0xc2, 0x76, 0xc9, 0x33, 0xb0, 0x53}};
255 HDEVINFO hdev =
256 ::SetupDiGetClassDevs(&my_GUID_DEVICE_ENERGY_METER, nullptr, nullptr,
257 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
258 if (hdev == INVALID_HANDLE_VALUE) {
259 return;
262 HDevInfoHolder hdevHolder(hdev);
264 DWORD i = 0;
265 SP_DEVICE_INTERFACE_DATA did = {0};
266 did.cbSize = sizeof(did);
268 while (::SetupDiEnumDeviceInterfaces(
269 hdev, nullptr, &my_GUID_DEVICE_ENERGY_METER, i++, &did)) {
270 DWORD bufferSize = 0;
271 ::SetupDiGetDeviceInterfaceDetail(hdev, &did, nullptr, 0, &bufferSize,
272 nullptr);
273 if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
274 continue;
277 UniquePtr<uint8_t[]> buffer(new (std::nothrow) uint8_t[bufferSize]);
278 if (!buffer) {
279 continue;
282 PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd =
283 reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buffer.get());
284 MOZ_ASSERT(uintptr_t(buffer.get()) %
285 alignof(PSP_DEVICE_INTERFACE_DETAIL_DATA) ==
287 pdidd->cbSize = sizeof(*pdidd);
288 if (!::SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, bufferSize,
289 &bufferSize, nullptr)) {
290 continue;
293 UniquePtr<PowerMeterDevice> pmd =
294 MakeUnique<PowerMeterDevice>(pdidd->DevicePath);
295 if (!pmd->HasChannels() ||
296 !mPowerMeterDevices.emplaceBack(std::move(pmd))) {
297 NS_WARNING("PowerMeterDevice without measurement channel (or OOM)");
301 for (auto& device : mPowerMeterDevices) {
302 device->AppendCountersTo(mCounters);
306 PowerCounters::~PowerCounters() { mCounters.clear(); }
308 void PowerCounters::Sample() {
309 for (auto& device : mPowerMeterDevices) {
310 device->Sample();