• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright (C) 2022 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
19 
20 #include "power_files.h"
21 
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/stringprintf.h>
25 #include <android-base/strings.h>
26 #include <dirent.h>
27 #include <utils/Trace.h>
28 
29 namespace aidl {
30 namespace android {
31 namespace hardware {
32 namespace thermal {
33 namespace implementation {
34 
35 constexpr std::string_view kDeviceType("iio:device");
36 constexpr std::string_view kIioRootDir("/sys/bus/iio/devices");
37 constexpr std::string_view kEnergyValueNode("energy_value");
38 
39 using ::android::base::ReadFileToString;
40 using ::android::base::StringPrintf;
41 
42 namespace {
calculateAvgPower(std::string_view power_rail,const PowerSample & last_sample,const PowerSample & curr_sample,float * avg_power)43 bool calculateAvgPower(std::string_view power_rail, const PowerSample &last_sample,
44                        const PowerSample &curr_sample, float *avg_power) {
45     *avg_power = NAN;
46 
47     if (last_sample.duration == 0) {
48         LOG(VERBOSE) << "Power rail " << power_rail.data() << ": samples are under collecting";
49         return true;
50     } else if (curr_sample.duration == last_sample.duration) {
51         LOG(VERBOSE) << "Power rail " << power_rail.data()
52                      << ": has not collected min 2 samples yet";
53         return true;
54     } else if (curr_sample.duration < last_sample.duration ||
55                curr_sample.energy_counter < last_sample.energy_counter) {
56         LOG(ERROR) << "Power rail " << power_rail.data()
57                    << " is invalid: last_sample=" << last_sample.energy_counter
58                    << "(T=" << last_sample.duration << ")"
59                    << ", curr_sample=" << curr_sample.energy_counter
60                    << "(T=" << curr_sample.duration << ")";
61         return false;
62     }
63     const auto duration = curr_sample.duration - last_sample.duration;
64     const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter;
65     *avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration);
66     LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << *avg_power
67                  << ", duration = " << duration << ", deltaEnergy = " << deltaEnergy;
68     return true;
69 }
70 }  // namespace
71 
registerPowerRailsToWatch(const Json::Value & config,std::unordered_map<std::string,std::vector<std::string>> * power_rail_switch_map)72 bool PowerFiles::registerPowerRailsToWatch(
73         const Json::Value &config,
74         std::unordered_map<std::string, std::vector<std::string>> *power_rail_switch_map) {
75     if (!ParsePowerRailInfo(config, &power_rail_info_map_, power_rail_switch_map)) {
76         LOG(ERROR) << "Failed to parse power rail info config";
77         return false;
78     }
79 
80     if (!power_rail_info_map_.size()) {
81         LOG(INFO) << " No power rail info config found";
82         return true;
83     }
84 
85     if (!findEnergySourceToWatch()) {
86         LOG(ERROR) << "Cannot find energy source";
87         return false;
88     }
89 
90     if (!energy_info_map_.size() && !updateEnergyValues()) {
91         LOG(ERROR) << "Faield to update energy info";
92         return false;
93     }
94 
95     for (const auto &power_rail_info_pair : power_rail_info_map_) {
96         std::vector<std::queue<PowerSample>> power_history;
97         if (!power_rail_info_pair.second.power_sample_count ||
98             power_rail_info_pair.second.power_sample_delay == std::chrono::milliseconds::max()) {
99             continue;
100         }
101 
102         if (power_rail_info_pair.second.virtual_power_rail_info != nullptr &&
103             power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size()) {
104             for (size_t i = 0;
105                  i < power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size();
106                  ++i) {
107                 std::string power_rail =
108                         power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails[i];
109                 if (!energy_info_map_.count(power_rail)) {
110                     LOG(ERROR) << " Could not find energy source " << power_rail;
111                     return false;
112                 }
113 
114                 const auto curr_sample = energy_info_map_.at(power_rail);
115                 power_history.emplace_back(std::queue<PowerSample>());
116                 for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
117                     power_history[i].emplace(curr_sample);
118                 }
119             }
120         } else {
121             if (energy_info_map_.count(power_rail_info_pair.first)) {
122                 const auto curr_sample = energy_info_map_.at(power_rail_info_pair.first);
123                 power_history.emplace_back(std::queue<PowerSample>());
124                 for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
125                     power_history[0].emplace(curr_sample);
126                 }
127             } else {
128                 LOG(ERROR) << "Could not find energy source " << power_rail_info_pair.first;
129                 return false;
130             }
131         }
132 
133         if (power_history.size()) {
134             power_status_map_[power_rail_info_pair.first] = {
135                     .last_update_time = boot_clock::time_point::min(),
136                     .power_history = power_history,
137                     .last_updated_avg_power = NAN,
138                     .enabled = true,
139             };
140         } else {
141             LOG(ERROR) << "power history size is zero";
142             return false;
143         }
144         LOG(INFO) << "Successfully to register power rail " << power_rail_info_pair.first;
145     }
146 
147     power_status_log_ = {.prev_log_time = boot_clock::now(),
148                          .prev_energy_info_map = energy_info_map_};
149     return true;
150 }
151 
findEnergySourceToWatch(void)152 bool PowerFiles::findEnergySourceToWatch(void) {
153     std::string devicePath;
154 
155     if (energy_path_set_.size()) {
156         return true;
157     }
158 
159     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kIioRootDir.data()), closedir);
160     if (!dir) {
161         PLOG(ERROR) << "Error opening directory" << kIioRootDir;
162         return false;
163     }
164 
165     // Find any iio:devices that support energy_value
166     while (struct dirent *ent = readdir(dir.get())) {
167         std::string devTypeDir = ent->d_name;
168         if (devTypeDir.find(kDeviceType) != std::string::npos) {
169             devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data());
170             std::string deviceEnergyContent;
171 
172             if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()),
173                                   &deviceEnergyContent)) {
174             } else if (deviceEnergyContent.size()) {
175                 energy_path_set_.emplace(
176                         StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()));
177             }
178         }
179     }
180 
181     if (!energy_path_set_.size()) {
182         return false;
183     }
184 
185     return true;
186 }
187 
updateEnergyValues(void)188 bool PowerFiles::updateEnergyValues(void) {
189     std::string deviceEnergyContent;
190     std::string deviceEnergyContents;
191     std::string line;
192 
193     ATRACE_CALL();
194     for (const auto &path : energy_path_set_) {
195         if (!::android::base::ReadFileToString(path, &deviceEnergyContent)) {
196             LOG(ERROR) << "Failed to read energy content from " << path;
197             return false;
198         } else {
199             deviceEnergyContents.append(deviceEnergyContent);
200         }
201     }
202 
203     std::istringstream energyData(deviceEnergyContents);
204 
205     while (std::getline(energyData, line)) {
206         /* Read rail energy */
207         uint64_t energy_counter = 0;
208         uint64_t duration = 0;
209 
210         /* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */
211         auto start_pos = line.find("T=");
212         auto end_pos = line.find(')');
213         if (start_pos != std::string::npos) {
214             duration =
215                     strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10);
216         } else {
217             continue;
218         }
219 
220         start_pos = line.find(")[");
221         end_pos = line.find(']');
222         std::string railName;
223         if (start_pos != std::string::npos) {
224             railName = line.substr(start_pos + 2, end_pos - start_pos - 2);
225         } else {
226             continue;
227         }
228 
229         start_pos = line.find("],");
230         if (start_pos != std::string::npos) {
231             energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10);
232         } else {
233             continue;
234         }
235 
236         energy_info_map_[railName] = {
237                 .energy_counter = energy_counter,
238                 .duration = duration,
239         };
240     }
241 
242     return true;
243 }
244 
updateAveragePower(std::string_view power_rail,std::queue<PowerSample> * power_history)245 float PowerFiles::updateAveragePower(std::string_view power_rail,
246                                      std::queue<PowerSample> *power_history) {
247     float avg_power = NAN;
248     if (!energy_info_map_.count(power_rail.data())) {
249         LOG(ERROR) << " Could not find power rail " << power_rail.data();
250         return avg_power;
251     }
252     const auto last_sample = power_history->front();
253     const auto curr_sample = energy_info_map_.at(power_rail.data());
254     if (calculateAvgPower(power_rail, last_sample, curr_sample, &avg_power)) {
255         power_history->pop();
256         power_history->push(curr_sample);
257     }
258     return avg_power;
259 }
260 
updatePowerRail(std::string_view power_rail)261 float PowerFiles::updatePowerRail(std::string_view power_rail) {
262     float avg_power = NAN;
263 
264     if (!power_rail_info_map_.count(power_rail.data())) {
265         return avg_power;
266     }
267 
268     if (!power_status_map_.count(power_rail.data())) {
269         return avg_power;
270     }
271 
272     const auto &power_rail_info = power_rail_info_map_.at(power_rail.data());
273     auto &power_status = power_status_map_.at(power_rail.data());
274 
275     boot_clock::time_point now = boot_clock::now();
276     auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
277             now - power_status.last_update_time);
278 
279     if (power_status.last_update_time != boot_clock::time_point::min() &&
280         time_elapsed_ms < power_rail_info.power_sample_delay) {
281         return power_status.last_updated_avg_power;
282     }
283 
284     if (!energy_info_map_.size() && !updateEnergyValues()) {
285         LOG(ERROR) << "Failed to update energy values";
286         return avg_power;
287     }
288 
289     if (power_rail_info.virtual_power_rail_info == nullptr) {
290         avg_power = updateAveragePower(power_rail, &power_status.power_history[0]);
291     } else {
292         const auto offset = power_rail_info.virtual_power_rail_info->offset;
293         float avg_power_val = 0.0;
294         for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
295              i++) {
296             float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i];
297             float avg_power_number = updateAveragePower(
298                     power_rail_info.virtual_power_rail_info->linked_power_rails[i],
299                     &power_status.power_history[i]);
300 
301             switch (power_rail_info.virtual_power_rail_info->formula) {
302                 case FormulaOption::COUNT_THRESHOLD:
303                     if ((coefficient < 0 && avg_power_number < -coefficient) ||
304                         (coefficient >= 0 && avg_power_number >= coefficient))
305                         avg_power_val += 1;
306                     break;
307                 case FormulaOption::WEIGHTED_AVG:
308                     avg_power_val += avg_power_number * coefficient;
309                     break;
310                 case FormulaOption::MAXIMUM:
311                     if (i == 0)
312                         avg_power_val = std::numeric_limits<float>::lowest();
313                     if (avg_power_number * coefficient > avg_power_val)
314                         avg_power_val = avg_power_number * coefficient;
315                     break;
316                 case FormulaOption::MINIMUM:
317                     if (i == 0)
318                         avg_power_val = std::numeric_limits<float>::max();
319                     if (avg_power_number * coefficient < avg_power_val)
320                         avg_power_val = avg_power_number * coefficient;
321                     break;
322                 default:
323                     break;
324             }
325         }
326         if (avg_power_val >= 0) {
327             avg_power_val = avg_power_val + offset;
328         }
329 
330         avg_power = avg_power_val;
331     }
332 
333     if (avg_power < 0) {
334         avg_power = NAN;
335     }
336 
337     power_status.last_updated_avg_power = avg_power;
338     power_status.last_update_time = now;
339     return avg_power;
340 }
341 
refreshPowerStatus(void)342 bool PowerFiles::refreshPowerStatus(void) {
343     if (!updateEnergyValues()) {
344         LOG(ERROR) << "Failed to update energy values";
345         return false;
346     }
347 
348     for (const auto &[power_rail, power_status] : power_status_map_) {
349         if (power_status.enabled) {
350             updatePowerRail(power_rail);
351         }
352     }
353     return true;
354 }
355 
powerSamplingSwitch(std::string_view power_rail,const bool enabled)356 void PowerFiles::powerSamplingSwitch(std::string_view power_rail, const bool enabled) {
357     if (!power_rail_info_map_.contains(power_rail.data())) {
358         LOG(ERROR) << "Unable to clear status for invalid power rail: " << power_rail.data();
359         return;
360     }
361     auto &power_status = power_status_map_.at(power_rail.data());
362     power_status.enabled = enabled;
363 
364     if (!enabled) {
365         PowerSample power_sample = {.energy_counter = 0, .duration = 0};
366 
367         for (size_t i = 0; i < power_status.power_history.size(); i++) {
368             for (size_t j = 0; j < power_status.power_history[i].size(); j++) {
369                 power_status.power_history[i].pop();
370                 power_status.power_history[i].push(power_sample);
371             }
372         }
373         power_status.last_updated_avg_power = NAN;
374     }
375 }
376 
logPowerStatus(const boot_clock::time_point & now)377 void PowerFiles::logPowerStatus(const boot_clock::time_point &now) {
378     // calculate energy and print
379     uint8_t power_rail_log_cnt = 0;
380     uint64_t max_duration = 0;
381     float tot_power = 0.0;
382     std::string out;
383     for (const auto &energy_info_pair : energy_info_map_) {
384         const auto &rail = energy_info_pair.first;
385         if (!power_status_log_.prev_energy_info_map.count(rail)) {
386             continue;
387         }
388         const auto &last_sample = power_status_log_.prev_energy_info_map.at(rail);
389         const auto &curr_sample = energy_info_pair.second;
390         float avg_power = NAN;
391         if (calculateAvgPower(rail, last_sample, curr_sample, &avg_power) &&
392             !std::isnan(avg_power)) {
393             // start of new line
394             if (power_rail_log_cnt % kMaxPowerLogPerLine == 0) {
395                 if (power_rail_log_cnt != 0) {
396                     out.append("\n");
397                 }
398                 out.append("Power rails ");
399             }
400             out.append(StringPrintf("[%s: %0.2f mW] ", rail.c_str(), avg_power));
401             power_rail_log_cnt++;
402             tot_power += avg_power;
403             max_duration = std::max(max_duration, curr_sample.duration - last_sample.duration);
404         }
405     }
406 
407     if (power_rail_log_cnt) {
408         LOG(INFO) << StringPrintf("Power rails total power: %0.2f mW for %" PRId64 " ms", tot_power,
409                                   max_duration);
410         LOG(INFO) << out;
411     }
412     power_status_log_ = {.prev_log_time = now, .prev_energy_info_map = energy_info_map_};
413 }
414 
415 }  // namespace implementation
416 }  // namespace thermal
417 }  // namespace hardware
418 }  // namespace android
419 }  // namespace aidl
420