• 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     const auto duration = curr_sample.duration - last_sample.duration;
47     const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter;
48     if (!last_sample.duration) {
49         LOG(VERBOSE) << "Power rail " << power_rail.data()
50                      << ": all power samples have not been collected yet";
51     } else if (duration <= 0 || deltaEnergy < 0) {
52         LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration
53                    << ", deltaEnergy = " << deltaEnergy;
54         return false;
55     } else {
56         *avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration);
57         LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << *avg_power
58                      << ", duration = " << duration << ", deltaEnergy = " << deltaEnergy;
59     }
60     return true;
61 }
62 }  // namespace
63 
registerPowerRailsToWatch(const Json::Value & config)64 bool PowerFiles::registerPowerRailsToWatch(const Json::Value &config) {
65     if (!ParsePowerRailInfo(config, &power_rail_info_map_)) {
66         LOG(ERROR) << "Failed to parse power rail info config";
67         return false;
68     }
69 
70     if (!power_rail_info_map_.size()) {
71         LOG(INFO) << " No power rail info config found";
72         return true;
73     }
74 
75     if (!findEnergySourceToWatch()) {
76         LOG(ERROR) << "Cannot find energy source";
77         return false;
78     }
79 
80     if (!energy_info_map_.size() && !updateEnergyValues()) {
81         LOG(ERROR) << "Faield to update energy info";
82         return false;
83     }
84 
85     for (const auto &power_rail_info_pair : power_rail_info_map_) {
86         std::vector<std::queue<PowerSample>> power_history;
87         if (!power_rail_info_pair.second.power_sample_count ||
88             power_rail_info_pair.second.power_sample_delay == std::chrono::milliseconds::max()) {
89             continue;
90         }
91 
92         PowerSample power_sample = {
93                 .energy_counter = 0,
94                 .duration = 0,
95         };
96 
97         if (power_rail_info_pair.second.virtual_power_rail_info != nullptr &&
98             power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size()) {
99             for (size_t i = 0;
100                  i < power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size();
101                  ++i) {
102                 if (!energy_info_map_.count(power_rail_info_pair.second.virtual_power_rail_info
103                                                     ->linked_power_rails[i])) {
104                     LOG(ERROR) << " Could not find energy source "
105                                << power_rail_info_pair.second.virtual_power_rail_info
106                                           ->linked_power_rails[i];
107                     return false;
108                 }
109                 power_history.emplace_back(std::queue<PowerSample>());
110                 for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
111                     power_history[i].emplace(power_sample);
112                 }
113             }
114         } else {
115             if (energy_info_map_.count(power_rail_info_pair.first)) {
116                 power_history.emplace_back(std::queue<PowerSample>());
117                 for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
118                     power_history[0].emplace(power_sample);
119                 }
120             } else {
121                 LOG(ERROR) << "Could not find energy source " << power_rail_info_pair.first;
122                 return false;
123             }
124         }
125 
126         if (power_history.size()) {
127             power_status_map_[power_rail_info_pair.first] = {
128                     .power_history = power_history,
129                     .last_update_time = boot_clock::time_point::min(),
130                     .last_updated_avg_power = NAN,
131             };
132         } else {
133             LOG(ERROR) << "power history size is zero";
134             return false;
135         }
136         LOG(INFO) << "Successfully to register power rail " << power_rail_info_pair.first;
137     }
138 
139     power_status_log_ = {.prev_log_time = boot_clock::now(),
140                          .prev_energy_info_map = energy_info_map_};
141     return true;
142 }
143 
findEnergySourceToWatch(void)144 bool PowerFiles::findEnergySourceToWatch(void) {
145     std::string devicePath;
146 
147     if (energy_path_set_.size()) {
148         return true;
149     }
150 
151     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kIioRootDir.data()), closedir);
152     if (!dir) {
153         PLOG(ERROR) << "Error opening directory" << kIioRootDir;
154         return false;
155     }
156 
157     // Find any iio:devices that support energy_value
158     while (struct dirent *ent = readdir(dir.get())) {
159         std::string devTypeDir = ent->d_name;
160         if (devTypeDir.find(kDeviceType) != std::string::npos) {
161             devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data());
162             std::string deviceEnergyContent;
163 
164             if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()),
165                                   &deviceEnergyContent)) {
166             } else if (deviceEnergyContent.size()) {
167                 energy_path_set_.emplace(
168                         StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()));
169             }
170         }
171     }
172 
173     if (!energy_path_set_.size()) {
174         return false;
175     }
176 
177     return true;
178 }
179 
updateEnergyValues(void)180 bool PowerFiles::updateEnergyValues(void) {
181     std::string deviceEnergyContent;
182     std::string deviceEnergyContents;
183     std::string line;
184 
185     ATRACE_CALL();
186     for (const auto &path : energy_path_set_) {
187         if (!::android::base::ReadFileToString(path, &deviceEnergyContent)) {
188             LOG(ERROR) << "Failed to read energy content from " << path;
189             return false;
190         } else {
191             deviceEnergyContents.append(deviceEnergyContent);
192         }
193     }
194 
195     std::istringstream energyData(deviceEnergyContents);
196 
197     while (std::getline(energyData, line)) {
198         /* Read rail energy */
199         uint64_t energy_counter = 0;
200         uint64_t duration = 0;
201 
202         /* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */
203         auto start_pos = line.find("T=");
204         auto end_pos = line.find(')');
205         if (start_pos != std::string::npos) {
206             duration =
207                     strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10);
208         } else {
209             continue;
210         }
211 
212         start_pos = line.find(")[");
213         end_pos = line.find(']');
214         std::string railName;
215         if (start_pos != std::string::npos) {
216             railName = line.substr(start_pos + 2, end_pos - start_pos - 2);
217         } else {
218             continue;
219         }
220 
221         start_pos = line.find("],");
222         if (start_pos != std::string::npos) {
223             energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10);
224         } else {
225             continue;
226         }
227 
228         energy_info_map_[railName] = {
229                 .energy_counter = energy_counter,
230                 .duration = duration,
231         };
232     }
233 
234     return true;
235 }
236 
updateAveragePower(std::string_view power_rail,std::queue<PowerSample> * power_history)237 float PowerFiles::updateAveragePower(std::string_view power_rail,
238                                      std::queue<PowerSample> *power_history) {
239     float avg_power = NAN;
240     if (!energy_info_map_.count(power_rail.data())) {
241         LOG(ERROR) << " Could not find power rail " << power_rail.data();
242         return avg_power;
243     }
244     const auto last_sample = power_history->front();
245     const auto curr_sample = energy_info_map_.at(power_rail.data());
246     if (calculateAvgPower(power_rail, last_sample, curr_sample, &avg_power)) {
247         power_history->pop();
248         power_history->push(curr_sample);
249     }
250     return avg_power;
251 }
252 
updatePowerRail(std::string_view power_rail)253 float PowerFiles::updatePowerRail(std::string_view power_rail) {
254     float avg_power = NAN;
255 
256     if (!power_rail_info_map_.count(power_rail.data())) {
257         return avg_power;
258     }
259 
260     if (!power_status_map_.count(power_rail.data())) {
261         return avg_power;
262     }
263 
264     const auto &power_rail_info = power_rail_info_map_.at(power_rail.data());
265     auto &power_status = power_status_map_.at(power_rail.data());
266 
267     boot_clock::time_point now = boot_clock::now();
268     auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
269             now - power_status.last_update_time);
270 
271     if (power_status.last_update_time != boot_clock::time_point::min() &&
272         time_elapsed_ms < power_rail_info.power_sample_delay) {
273         return power_status.last_updated_avg_power;
274     }
275 
276     if (!energy_info_map_.size() && !updateEnergyValues()) {
277         LOG(ERROR) << "Failed to update energy values";
278         return avg_power;
279     }
280 
281     if (power_rail_info.virtual_power_rail_info == nullptr) {
282         avg_power = updateAveragePower(power_rail, &power_status.power_history[0]);
283     } else {
284         const auto offset = power_rail_info.virtual_power_rail_info->offset;
285         float avg_power_val = 0.0;
286         for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
287              i++) {
288             float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i];
289             float avg_power_number = updateAveragePower(
290                     power_rail_info.virtual_power_rail_info->linked_power_rails[i],
291                     &power_status.power_history[i]);
292 
293             switch (power_rail_info.virtual_power_rail_info->formula) {
294                 case FormulaOption::COUNT_THRESHOLD:
295                     if ((coefficient < 0 && avg_power_number < -coefficient) ||
296                         (coefficient >= 0 && avg_power_number >= coefficient))
297                         avg_power_val += 1;
298                     break;
299                 case FormulaOption::WEIGHTED_AVG:
300                     avg_power_val += avg_power_number * coefficient;
301                     break;
302                 case FormulaOption::MAXIMUM:
303                     if (i == 0)
304                         avg_power_val = std::numeric_limits<float>::lowest();
305                     if (avg_power_number * coefficient > avg_power_val)
306                         avg_power_val = avg_power_number * coefficient;
307                     break;
308                 case FormulaOption::MINIMUM:
309                     if (i == 0)
310                         avg_power_val = std::numeric_limits<float>::max();
311                     if (avg_power_number * coefficient < avg_power_val)
312                         avg_power_val = avg_power_number * coefficient;
313                     break;
314                 default:
315                     break;
316             }
317         }
318         if (avg_power_val >= 0) {
319             avg_power_val = avg_power_val + offset;
320         }
321 
322         avg_power = avg_power_val;
323     }
324 
325     if (avg_power < 0) {
326         avg_power = NAN;
327     }
328 
329     power_status.last_updated_avg_power = avg_power;
330     power_status.last_update_time = now;
331     return avg_power;
332 }
333 
refreshPowerStatus(void)334 bool PowerFiles::refreshPowerStatus(void) {
335     if (!updateEnergyValues()) {
336         LOG(ERROR) << "Failed to update energy values";
337         return false;
338     }
339 
340     for (const auto &power_status_pair : power_status_map_) {
341         updatePowerRail(power_status_pair.first);
342     }
343     return true;
344 }
345 
logPowerStatus(const boot_clock::time_point & now)346 void PowerFiles::logPowerStatus(const boot_clock::time_point &now) {
347     // calculate energy and print
348     uint8_t power_rail_log_cnt = 0;
349     uint64_t max_duration = 0;
350     float tot_power = 0.0;
351     std::string out;
352     for (const auto &energy_info_pair : energy_info_map_) {
353         const auto &rail = energy_info_pair.first;
354         if (!power_status_log_.prev_energy_info_map.count(rail)) {
355             continue;
356         }
357         const auto &last_sample = power_status_log_.prev_energy_info_map.at(rail);
358         const auto &curr_sample = energy_info_pair.second;
359         float avg_power = NAN;
360         if (calculateAvgPower(rail, last_sample, curr_sample, &avg_power) && avg_power != NAN) {
361             // start of new line
362             if (power_rail_log_cnt % kMaxPowerLogPerLine == 0) {
363                 if (power_rail_log_cnt != 0) {
364                     out.append("\n");
365                 }
366                 out.append("Power rails ");
367             }
368             out.append(StringPrintf("[%s: %0.2f mW] ", rail.c_str(), avg_power));
369             power_rail_log_cnt++;
370             tot_power += avg_power;
371             max_duration = std::max(max_duration, curr_sample.duration - last_sample.duration);
372         }
373     }
374 
375     if (power_rail_log_cnt) {
376         LOG(INFO) << StringPrintf("Power rails total power: %0.2f mW for %" PRId64 " ms", tot_power,
377                                   max_duration);
378         LOG(INFO) << out;
379     }
380     power_status_log_ = {.prev_log_time = now, .prev_energy_info_map = energy_info_map_};
381 }
382 
383 }  // namespace implementation
384 }  // namespace thermal
385 }  // namespace hardware
386 }  // namespace android
387 }  // namespace aidl
388