1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/power_monitor/cpu_frequency_utils.h"
6
7 #include "base/system/sys_info.h"
8 #include "base/time/time.h"
9 #include "base/timer/elapsed_timer.h"
10 #include "build/build_config.h"
11
12 #if BUILDFLAG(IS_WIN)
13 #include <windows.h>
14
15 #include <powerbase.h>
16 #include <winternl.h>
17 #endif
18
19 namespace base {
20 namespace {
21
22 #if BUILDFLAG(IS_WIN)
23 // From
24 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa373184(v=vs.85).aspx.
25 // Note that this structure definition was accidentally omitted from WinNT.h.
26 typedef struct _PROCESSOR_POWER_INFORMATION {
27 ULONG Number;
28 ULONG MaxMhz;
29 ULONG CurrentMhz;
30 ULONG MhzLimit;
31 ULONG MaxIdleState;
32 ULONG CurrentIdleState;
33 } PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
34 #endif
35
36 } // namespace
37
EstimateCpuFrequency()38 double EstimateCpuFrequency() {
39 #if defined(ARCH_CPU_X86_FAMILY)
40 // The heuristic to estimate CPU frequency is based on UIforETW code.
41 // see: https://github.com/google/UIforETW/blob/main/UIforETW/CPUFrequency.cpp
42 // https://github.com/google/UIforETW/blob/main/UIforETW/SpinALot64.asm
43 base::ElapsedTimer timer;
44 const int kAmountOfIterations = 50000;
45 const int kAmountOfInstructions = 10;
46 for (int i = 0; i < kAmountOfIterations; ++i) {
47 __asm__ __volatile__(
48 "addl %%eax, %%eax\n"
49 "addl %%eax, %%eax\n"
50 "addl %%eax, %%eax\n"
51 "addl %%eax, %%eax\n"
52 "addl %%eax, %%eax\n"
53 "addl %%eax, %%eax\n"
54 "addl %%eax, %%eax\n"
55 "addl %%eax, %%eax\n"
56 "addl %%eax, %%eax\n"
57 "addl %%eax, %%eax\n"
58 :
59 :
60 : "eax");
61 }
62
63 const base::TimeDelta elapsed = timer.Elapsed();
64 const double estimated_frequency =
65 (kAmountOfIterations * kAmountOfInstructions) / elapsed.InSecondsF();
66 return estimated_frequency;
67 #else
68 return 0.0;
69 #endif
70 }
71
GetCpuMaxMhz()72 unsigned long GetCpuMaxMhz() {
73 #if BUILDFLAG(IS_WIN)
74 size_t num_cpu = static_cast<size_t>(base::SysInfo::NumberOfProcessors());
75 std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpu);
76 if (!NT_SUCCESS(CallNtPowerInformation(
77 ProcessorInformation, nullptr, 0, &info[0],
78 static_cast<ULONG>(sizeof(PROCESSOR_POWER_INFORMATION) * num_cpu)))) {
79 return 0;
80 }
81
82 unsigned long max_mhz = 0;
83 for (const auto& i : info) {
84 max_mhz = std::max(max_mhz, i.MaxMhz);
85 }
86
87 return max_mhz;
88 #else
89 return 0;
90 #endif
91 }
92
GetCpuMhzLimit()93 unsigned long GetCpuMhzLimit() {
94 #if BUILDFLAG(IS_WIN)
95 size_t num_cpu = static_cast<size_t>(base::SysInfo::NumberOfProcessors());
96 std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpu);
97 if (!NT_SUCCESS(CallNtPowerInformation(
98 ProcessorInformation, nullptr, 0, &info[0],
99 static_cast<ULONG>(sizeof(PROCESSOR_POWER_INFORMATION) * num_cpu)))) {
100 return 0;
101 }
102
103 unsigned long mhz_limit = 0;
104 for (const auto& i : info) {
105 mhz_limit = std::max(mhz_limit, i.MhzLimit);
106 }
107
108 return mhz_limit;
109 #else
110 return 0;
111 #endif
112 }
113
114 } // namespace base
115