[M120 Migration][VD] Enable direct rendering for TVPlus
[platform/framework/web/chromium-efl.git] / components / power_metrics / energy_metrics_provider_linux.cc
1 // Copyright 2023 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 "components/power_metrics/energy_metrics_provider_linux.h"
6
7 #include <linux/perf_event.h>
8 #include <sys/syscall.h>
9
10 #include <array>
11
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_file.h"
15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/strings/strcat.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20
21 namespace power_metrics {
22
23 namespace {
24
25 constexpr const char* kPowerEventPath = "/sys/bus/event_source/devices/power";
26
27 // Existing metrics that can be read via perf event.
28 constexpr std::array<const char*, 5> kMetrics{
29     "energy-pkg", "energy-cores", "energy-gpu", "energy-ram", "energy-psys"};
30
31 bool ReadUint64FromFile(base::FilePath path, uint64_t* output) {
32   std::string buf;
33   if (!base::ReadFileToString(path, &buf)) {
34     return false;
35   }
36   return base::StringToUint64(base::TrimString(buf, "\n", base::TRIM_TRAILING),
37                               output);
38 }
39
40 bool ReadHexFromFile(base::FilePath path, uint64_t* output) {
41   std::string buf;
42   if (!base::ReadFileToString(path, &buf)) {
43     return false;
44   }
45   base::ReplaceFirstSubstringAfterOffset(&buf, 0, "event=", "");
46   return base::HexStringToUInt64(
47       base::TrimString(buf, "\n", base::TRIM_TRAILING), output);
48 }
49
50 bool ReadDoubleFromFile(base::FilePath path, double* output) {
51   std::string buf;
52   if (!base::ReadFileToString(path, &buf)) {
53     return false;
54   }
55   return base::StringToDouble(base::TrimString(buf, "\n", base::TRIM_TRAILING),
56                               output);
57 }
58
59 // When pid == -1 and cpu >= 0, perf event measures all processes/threads on the
60 // specified CPU. This requires admin or a /proc/sys/kernel/perf_event_paranoid
61 // value of less than 1. Here, we only consider cpu0. See details in
62 // https://man7.org/linux/man-pages/man2/perf_event_open.2.html.
63 base::ScopedFD OpenPerfEvent(perf_event_attr* perf_attr) {
64   base::ScopedFD perf_fd{syscall(__NR_perf_event_open, perf_attr, /*pid=*/-1,
65                                  /*cpu=*/0, /*group_fd=*/-1,
66                                  PERF_FLAG_FD_CLOEXEC)};
67   return perf_fd;
68 }
69
70 void SetEnergyMetric(const std::string& metric_type,
71                      EnergyMetricsProvider::EnergyMetrics& energy_metrics,
72                      uint64_t absolute_energy) {
73   if (metric_type == "energy-pkg") {
74     energy_metrics.package_nanojoules = absolute_energy;
75   } else if (metric_type == "energy-cores") {
76     energy_metrics.cpu_nanojoules = absolute_energy;
77   } else if (metric_type == "energy-gpu") {
78     energy_metrics.gpu_nanojoules = absolute_energy;
79   } else if (metric_type == "energy-ram") {
80     energy_metrics.dram_nanojoules = absolute_energy;
81   } else if (metric_type == "energy-psys") {
82     energy_metrics.psys_nanojoules = absolute_energy;
83   }
84 }
85
86 }  // namespace
87
88 EnergyMetricsProviderLinux::PowerEvent::PowerEvent(std::string metric_type,
89                                                    double scale,
90                                                    base::ScopedFD fd)
91     : metric_type(metric_type), scale(scale), fd(std::move(fd)) {}
92
93 EnergyMetricsProviderLinux::PowerEvent::~PowerEvent() = default;
94
95 EnergyMetricsProviderLinux::PowerEvent::PowerEvent(PowerEvent&& other) =
96     default;
97 EnergyMetricsProviderLinux::PowerEvent&
98 EnergyMetricsProviderLinux::PowerEvent::operator=(PowerEvent&& other) = default;
99
100 EnergyMetricsProviderLinux::EnergyMetricsProviderLinux() = default;
101 EnergyMetricsProviderLinux::~EnergyMetricsProviderLinux() = default;
102
103 // static
104 std::unique_ptr<EnergyMetricsProviderLinux>
105 EnergyMetricsProviderLinux::Create() {
106   return base::WrapUnique(new EnergyMetricsProviderLinux());
107 }
108
109 absl::optional<EnergyMetricsProvider::EnergyMetrics>
110 EnergyMetricsProviderLinux::CaptureMetrics() {
111   if (!Initialize()) {
112     return absl::nullopt;
113   }
114
115   EnergyMetrics energy_metrics = {0};
116   for (const auto& event : events_) {
117     uint64_t absolute_energy;
118     if (!base::ReadFromFD(event.fd.get(),
119                           reinterpret_cast<char*>(&absolute_energy),
120                           sizeof(absolute_energy))) {
121       LOG(ERROR) << "Failed to read absolute energy of " << event.metric_type;
122       continue;
123     }
124     SetEnergyMetric(event.metric_type, energy_metrics,
125                     static_cast<uint64_t>(event.scale * absolute_energy));
126   }
127   return energy_metrics;
128 }
129
130 bool EnergyMetricsProviderLinux::Initialize() {
131   if (is_initialized_) {
132     if (events_.empty()) {
133       return false;
134     }
135     return true;
136   }
137
138   is_initialized_ = true;
139
140   // Check if there are available power-related events on local platform.
141   if (!base::PathExists(base::FilePath(kPowerEventPath))) {
142     LOG(WARNING) << "No available power event";
143     return false;
144   }
145
146   // Check if perf_event_paranoid is set to 0 as required.
147   uint64_t perf_event_paranoid;
148   if (!ReadUint64FromFile(
149           base::FilePath("/proc/sys/kernel/perf_event_paranoid"),
150           &perf_event_paranoid)) {
151     LOG(WARNING) << "Failed to get perf_event_paranoid";
152     return false;
153   }
154   if (perf_event_paranoid) {
155     LOG(WARNING) << "Permission denied for acquiring energy metrics";
156     return false;
157   }
158
159   // Since the power Processor Monitor Unit (PMU) is dynamic, we have to get the
160   // type for perf_event_attr from /sys/bus/event_source/devices/power/type.
161   uint64_t attr_type;
162   if (!ReadUint64FromFile(
163           base::FilePath(base::StrCat({kPowerEventPath, "/type"})),
164           &attr_type)) {
165     LOG(WARNING) << "Failed to get perf event type";
166     return false;
167   }
168
169   // For each metric, get their file descriptors.
170   for (auto* const metric : kMetrics) {
171     base::FilePath config_path =
172         base::FilePath(base::StrCat({kPowerEventPath, "/events/", metric}));
173     base::FilePath scale_path = base::FilePath(
174         base::StrCat({kPowerEventPath, "/events/", metric, ".scale"}));
175     // Some energy metrics may be unavailable on different platforms, so the
176     // corresponding file path does not exist, which is normal.
177     if (!base::PathExists(config_path) || !base::PathExists(scale_path)) {
178       continue;
179     }
180
181     // Get the specified config for this event.
182     uint64_t attr_config;
183     if (!ReadHexFromFile(config_path, &attr_config)) {
184       LOG(ERROR) << "Failed to get config " << config_path.value();
185       continue;
186     }
187
188     // Each event has its own scale to convert ticks to joules, which is usually
189     // set to 2.3283064365386962890625e-10.
190     double scale;
191     if (!ReadDoubleFromFile(scale_path, &scale)) {
192       LOG(ERROR) << "Failed to get scale of " << metric;
193       continue;
194     }
195     // Convert the unit from joules/tick to nanojoules/tick.
196     scale = scale * 1e9;
197
198     perf_event_attr perf_attr = {0};
199     perf_attr.size = static_cast<uint32_t>(sizeof(perf_attr));
200     perf_attr.type = static_cast<uint32_t>(attr_type);
201     perf_attr.config = attr_config;
202     base::ScopedFD fd = OpenPerfEvent(&perf_attr);
203     if (!fd.is_valid()) {
204       LOG(ERROR) << "Failed to get fd of " << metric;
205       continue;
206     }
207     events_.push_back({metric, scale, std::move(fd)});
208   }
209
210   if (events_.empty()) {
211     LOG(WARNING) << "No available energy metric";
212     return false;
213   }
214   return true;
215 }
216
217 }  // namespace power_metrics