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