Backed out changeset 4191b252db9b (bug 1886734) for causing build bustages @netwerk...
[gecko.git] / tools / profiler / core / PowerCounters-win.cpp
blob6e8f492d6dae9c8065398c17489bc4bd8862719f
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 #include <emi.h>
17 using namespace mozilla;
19 // This is a counter to collect power utilization during profiling.
20 // It cannot be a raw `ProfilerCounter` because we need to manually add/remove
21 // it while the profiler lock is already held.
22 class PowerMeterChannel final : public BaseProfilerCount {
23 public:
24 explicit PowerMeterChannel(const WCHAR* aChannelName, ULONGLONG aInitialValue,
25 ULONGLONG aInitialTime)
26 : BaseProfilerCount(nullptr, nullptr, nullptr, "power",
27 "Power utilization"),
28 mChannelName(NS_ConvertUTF16toUTF8(aChannelName)),
29 mPreviousValue(aInitialValue),
30 mPreviousTime(aInitialTime),
31 mIsSampleNew(true) {
32 if (mChannelName.Equals("RAPL_Package0_PKG")) {
33 mLabel = "Power: CPU package";
34 mDescription = mChannelName.get();
35 } else if (mChannelName.Equals("RAPL_Package0_PP0")) {
36 mLabel = "Power: CPU cores";
37 mDescription = mChannelName.get();
38 } else if (mChannelName.Equals("RAPL_Package0_PP1")) {
39 mLabel = "Power: iGPU";
40 mDescription = mChannelName.get();
41 } else if (mChannelName.Equals("RAPL_Package0_DRAM")) {
42 mLabel = "Power: DRAM";
43 mDescription = mChannelName.get();
44 } else {
45 unsigned int coreId;
46 if (sscanf(mChannelName.get(), "RAPL_Package0_Core%u_CORE", &coreId) ==
47 1) {
48 mLabelString = "Power: CPU core ";
49 mLabelString.AppendInt(coreId);
50 mLabel = mLabelString.get();
51 mDescription = mChannelName.get();
52 } else {
53 mLabel = mChannelName.get();
58 CountSample Sample() override {
59 CountSample result;
60 result.count = mCounter;
61 result.number = 0;
62 result.isSampleNew = mIsSampleNew;
63 mIsSampleNew = false;
64 return result;
67 void AddSample(ULONGLONG aAbsoluteEnergy, ULONGLONG aAbsoluteTime) {
68 // aAbsoluteTime is the time since the system start in 100ns increments.
69 if (aAbsoluteTime == mPreviousTime) {
70 return;
73 if (aAbsoluteEnergy > mPreviousValue) {
74 int64_t increment = aAbsoluteEnergy - mPreviousValue;
75 mCounter += increment;
76 mPreviousValue += increment;
77 mPreviousTime = aAbsoluteTime;
80 mIsSampleNew = true;
83 private:
84 int64_t mCounter;
85 nsCString mChannelName;
87 // Used as a storage when the label can not be a literal string.
88 nsCString mLabelString;
90 ULONGLONG mPreviousValue;
91 ULONGLONG mPreviousTime;
92 bool mIsSampleNew;
95 class PowerMeterDevice {
96 public:
97 explicit PowerMeterDevice(LPCTSTR aDevicePath) {
98 mHandle = ::CreateFile(aDevicePath, GENERIC_READ,
99 FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
100 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
101 if (mHandle == INVALID_HANDLE_VALUE) {
102 return;
105 EMI_VERSION version = {0};
106 DWORD dwOut;
108 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_VERSION, nullptr, 0, &version,
109 sizeof(version), &dwOut, nullptr) ||
110 (version.EmiVersion != EMI_VERSION_V1 &&
111 version.EmiVersion != EMI_VERSION_V2)) {
112 return;
115 EMI_METADATA_SIZE size = {0};
116 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_METADATA_SIZE, nullptr, 0,
117 &size, sizeof(size), &dwOut, nullptr) ||
118 !size.MetadataSize) {
119 return;
122 UniquePtr<uint8_t[]> metadata(new (std::nothrow)
123 uint8_t[size.MetadataSize]);
124 if (!metadata) {
125 return;
128 if (version.EmiVersion == EMI_VERSION_V2) {
129 EMI_METADATA_V2* metadata2 =
130 reinterpret_cast<EMI_METADATA_V2*>(metadata.get());
131 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_METADATA, nullptr, 0,
132 metadata2, size.MetadataSize, &dwOut, nullptr)) {
133 return;
136 if (!mChannels.reserve(metadata2->ChannelCount)) {
137 return;
140 mDataBuffer =
141 MakeUnique<EMI_CHANNEL_MEASUREMENT_DATA[]>(metadata2->ChannelCount);
142 if (!mDataBuffer) {
143 return;
146 if (!::DeviceIoControl(
147 mHandle, IOCTL_EMI_GET_MEASUREMENT, nullptr, 0, mDataBuffer.get(),
148 sizeof(EMI_CHANNEL_MEASUREMENT_DATA[metadata2->ChannelCount]),
149 &dwOut, nullptr)) {
150 return;
153 EMI_CHANNEL_V2* channel = &metadata2->Channels[0];
154 for (int i = 0; i < metadata2->ChannelCount; ++i) {
155 EMI_CHANNEL_MEASUREMENT_DATA* channel_data = &mDataBuffer[i];
156 mChannels.infallibleAppend(new PowerMeterChannel(
157 channel->ChannelName, channel_data->AbsoluteEnergy,
158 channel_data->AbsoluteTime));
159 channel = EMI_CHANNEL_V2_NEXT_CHANNEL(channel);
161 } else if (version.EmiVersion == EMI_VERSION_V1) {
162 EMI_METADATA_V1* metadata1 =
163 reinterpret_cast<EMI_METADATA_V1*>(metadata.get());
164 if (!::DeviceIoControl(mHandle, IOCTL_EMI_GET_METADATA, nullptr, 0,
165 metadata1, size.MetadataSize, &dwOut, nullptr)) {
166 return;
169 mDataBuffer = MakeUnique<EMI_CHANNEL_MEASUREMENT_DATA[]>(1);
170 if (!mDataBuffer) {
171 return;
174 if (!::DeviceIoControl(
175 mHandle, IOCTL_EMI_GET_MEASUREMENT, nullptr, 0, mDataBuffer.get(),
176 sizeof(EMI_CHANNEL_MEASUREMENT_DATA), &dwOut, nullptr)) {
177 return;
180 (void)mChannels.append(new PowerMeterChannel(
181 metadata1->MeteredHardwareName, mDataBuffer[0].AbsoluteEnergy,
182 mDataBuffer[0].AbsoluteTime));
186 ~PowerMeterDevice() {
187 if (mHandle != INVALID_HANDLE_VALUE) {
188 ::CloseHandle(mHandle);
192 void Sample() {
193 MOZ_ASSERT(HasChannels());
194 MOZ_ASSERT(mDataBuffer);
196 DWORD dwOut;
197 if (!::DeviceIoControl(
198 mHandle, IOCTL_EMI_GET_MEASUREMENT, nullptr, 0, mDataBuffer.get(),
199 sizeof(EMI_CHANNEL_MEASUREMENT_DATA[mChannels.length()]), &dwOut,
200 nullptr)) {
201 return;
204 for (size_t i = 0; i < mChannels.length(); ++i) {
205 EMI_CHANNEL_MEASUREMENT_DATA* channel_data = &mDataBuffer[i];
206 mChannels[i]->AddSample(channel_data->AbsoluteEnergy,
207 channel_data->AbsoluteTime);
211 bool HasChannels() { return mChannels.length() != 0; }
212 void AppendCountersTo(PowerCounters::CountVector& aCounters) {
213 if (aCounters.reserve(aCounters.length() + mChannels.length())) {
214 for (auto& channel : mChannels) {
215 aCounters.infallibleAppend(channel.get());
220 private:
221 Vector<UniquePtr<PowerMeterChannel>, 4> mChannels;
222 HANDLE mHandle = INVALID_HANDLE_VALUE;
223 UniquePtr<EMI_CHANNEL_MEASUREMENT_DATA[]> mDataBuffer;
226 PowerCounters::PowerCounters() {
227 class MOZ_STACK_CLASS HDevInfoHolder final {
228 public:
229 explicit HDevInfoHolder(HDEVINFO aHandle) : mHandle(aHandle) {}
231 ~HDevInfoHolder() { ::SetupDiDestroyDeviceInfoList(mHandle); }
233 private:
234 HDEVINFO mHandle;
237 if (!XRE_IsParentProcess()) {
238 // Energy meters are global, so only sample them on the parent.
239 return;
242 // Energy Metering Device Interface
243 // {45BD8344-7ED6-49cf-A440-C276C933B053}
245 // Using GUID_DEVICE_ENERGY_METER does not compile as the symbol does not
246 // exist before Windows 10.
247 GUID my_GUID_DEVICE_ENERGY_METER = {
248 0x45bd8344,
249 0x7ed6,
250 0x49cf,
251 {0xa4, 0x40, 0xc2, 0x76, 0xc9, 0x33, 0xb0, 0x53}};
253 HDEVINFO hdev =
254 ::SetupDiGetClassDevs(&my_GUID_DEVICE_ENERGY_METER, nullptr, nullptr,
255 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
256 if (hdev == INVALID_HANDLE_VALUE) {
257 return;
260 HDevInfoHolder hdevHolder(hdev);
262 DWORD i = 0;
263 SP_DEVICE_INTERFACE_DATA did = {0};
264 did.cbSize = sizeof(did);
266 while (::SetupDiEnumDeviceInterfaces(
267 hdev, nullptr, &my_GUID_DEVICE_ENERGY_METER, i++, &did)) {
268 DWORD bufferSize = 0;
269 ::SetupDiGetDeviceInterfaceDetail(hdev, &did, nullptr, 0, &bufferSize,
270 nullptr);
271 if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
272 continue;
275 UniquePtr<uint8_t[]> buffer(new (std::nothrow) uint8_t[bufferSize]);
276 if (!buffer) {
277 continue;
280 PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd =
281 reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buffer.get());
282 MOZ_ASSERT(uintptr_t(buffer.get()) %
283 alignof(PSP_DEVICE_INTERFACE_DETAIL_DATA) ==
285 pdidd->cbSize = sizeof(*pdidd);
286 if (!::SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, bufferSize,
287 &bufferSize, nullptr)) {
288 continue;
291 UniquePtr<PowerMeterDevice> pmd =
292 MakeUnique<PowerMeterDevice>(pdidd->DevicePath);
293 if (!pmd->HasChannels() ||
294 !mPowerMeterDevices.emplaceBack(std::move(pmd))) {
295 NS_WARNING("PowerMeterDevice without measurement channel (or OOM)");
299 for (auto& device : mPowerMeterDevices) {
300 device->AppendCountersTo(mCounters);
304 PowerCounters::~PowerCounters() { mCounters.clear(); }
306 void PowerCounters::Sample() {
307 for (auto& device : mPowerMeterDevices) {
308 device->Sample();