1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <android-base/logging.h>
17 #include <audio_utils/clock.h>
18 #include <psh_utils/PowerStats.h>
19
20 namespace android::media::psh_utils {
21
22 // Determine the best start time from a and b, which is
23 // min(a, b) if both exist, otherwise the one that exists.
24 template <typename T>
choose_best_start_time(const T & a,const T & b)25 const T& choose_best_start_time(const T& a, const T& b) {
26 if (a) {
27 return b ? std::min(a, b) : a;
28 } else {
29 return b;
30 }
31 }
32
33 // subtract two time differences.
34 template <typename T, typename U>
sub_time_diff(const T & diff_a,const T & diff_b,const U & abs_c,const U & abs_d)35 const T sub_time_diff(const T& diff_a, const T& diff_b, const U& abs_c, const U& abs_d) {
36 if (diff_a) {
37 return diff_b ? (diff_a - diff_b) : diff_a;
38 } else if (diff_b) {
39 return diff_b;
40 } else { // no difference exists, use absolute time.
41 return abs_c - abs_d;
42 }
43 }
44
toString() const45 std::string PowerStats::Metadata::toString() const {
46 return std::string("start_time_since_boot_ms: ").append(
47 std::to_string(start_time_since_boot_ms))
48 .append(" start_time_monotonic_ms: ").append(std::to_string(start_time_monotonic_ms))
49 .append(audio_utils_time_string_from_ns(start_time_epoch_ms * 1'000'000).time)
50 .append(" duration_ms: ").append(std::to_string(duration_ms))
51 .append(" duration_monotonic_ms: ").append(std::to_string(duration_monotonic_ms));
52 }
53
operator +=(const Metadata & other)54 PowerStats::Metadata PowerStats::Metadata::operator+=(const Metadata& other) {
55 start_time_since_boot_ms = choose_best_start_time(
56 start_time_since_boot_ms, other.start_time_since_boot_ms);
57 start_time_epoch_ms = choose_best_start_time(
58 start_time_epoch_ms, other.start_time_epoch_ms);
59 start_time_monotonic_ms = choose_best_start_time(
60 start_time_monotonic_ms, other.start_time_monotonic_ms);
61 duration_ms += other.duration_ms;
62 duration_monotonic_ms += other.duration_monotonic_ms;
63 return *this;
64 }
65
operator -=(const Metadata & other)66 PowerStats::Metadata PowerStats::Metadata::operator-=(const Metadata& other) {
67 // here we calculate duration, if it makes sense.
68 duration_ms = sub_time_diff(duration_ms, other.duration_ms,
69 start_time_since_boot_ms, other.start_time_since_boot_ms);
70 duration_monotonic_ms = sub_time_diff(
71 duration_monotonic_ms, other.duration_monotonic_ms,
72 start_time_monotonic_ms, other.start_time_monotonic_ms);
73 start_time_since_boot_ms = choose_best_start_time(
74 start_time_since_boot_ms, other.start_time_since_boot_ms);
75 start_time_epoch_ms = choose_best_start_time(
76 start_time_epoch_ms, other.start_time_epoch_ms);
77 start_time_monotonic_ms = choose_best_start_time(
78 start_time_monotonic_ms, other.start_time_monotonic_ms);
79 return *this;
80 }
81
operator +(const Metadata & other) const82 PowerStats::Metadata PowerStats::Metadata::operator+(const Metadata& other) const {
83 Metadata result = *this;
84 result += other;
85 return result;
86 }
87
operator -(const Metadata & other) const88 PowerStats::Metadata PowerStats::Metadata::operator-(const Metadata& other) const {
89 Metadata result = *this;
90 result -= other;
91 return result;
92 }
93
toString() const94 std::string PowerStats::StateResidency::toString() const {
95 return std::string(entity_name).append(state_name)
96 .append(" ").append(std::to_string(time_ms))
97 .append(" ").append(std::to_string(entry_count));
98 }
99
operator +=(const StateResidency & other)100 PowerStats::StateResidency PowerStats::StateResidency::operator+=(const StateResidency& other) {
101 if (entity_name.empty()) entity_name = other.entity_name;
102 if (state_name.empty()) state_name = other.state_name;
103 time_ms += other.time_ms;
104 entry_count += other.entry_count;
105 return *this;
106 }
107
operator -=(const StateResidency & other)108 PowerStats::StateResidency PowerStats::StateResidency::operator-=(const StateResidency& other) {
109 if (entity_name.empty()) entity_name = other.entity_name;
110 if (state_name.empty()) state_name = other.state_name;
111 time_ms -= other.time_ms;
112 entry_count -= other.entry_count;
113 return *this;
114 }
115
operator +(const StateResidency & other) const116 PowerStats::StateResidency PowerStats::StateResidency::operator+(
117 const StateResidency& other) const {
118 StateResidency result = *this;
119 result += other;
120 return result;
121 }
122
operator -(const StateResidency & other) const123 PowerStats::StateResidency PowerStats::StateResidency::operator-(
124 const StateResidency& other) const {
125 StateResidency result = *this;
126 result -= other;
127 return result;
128 }
129
toString() const130 std::string PowerStats::RailEnergy::toString() const {
131 return std::string(subsystem_name)
132 .append(rail_name)
133 .append(" ")
134 .append(std::to_string(energy_uws));
135 }
136
operator +=(const RailEnergy & other)137 PowerStats::RailEnergy PowerStats::RailEnergy::operator+=(const RailEnergy& other) {
138 if (subsystem_name.empty()) subsystem_name = other.subsystem_name;
139 if (rail_name.empty()) rail_name = other.rail_name;
140 energy_uws += other.energy_uws;
141 return *this;
142 }
143
operator -=(const RailEnergy & other)144 PowerStats::RailEnergy PowerStats::RailEnergy::operator-=(const RailEnergy& other) {
145 if (subsystem_name.empty()) subsystem_name = other.subsystem_name;
146 if (rail_name.empty()) rail_name = other.rail_name;
147 energy_uws -= other.energy_uws;
148 return *this;
149 }
150
operator +(const RailEnergy & other) const151 PowerStats::RailEnergy PowerStats::RailEnergy::operator+(const RailEnergy& other) const {
152 RailEnergy result = *this;
153 result += other;
154 return result;
155 }
156
operator -(const RailEnergy & other) const157 PowerStats::RailEnergy PowerStats::RailEnergy::operator-(const RailEnergy& other) const {
158 RailEnergy result = *this;
159 result -= other;
160 return result;
161 }
162
toString(const std::string & prefix) const163 std::string PowerStats::toString(const std::string& prefix) const {
164 std::string result;
165 result.append(prefix).append(metadata.toString()).append("\n");
166 result.append(prefix).append(health_stats.toString()).append("\n");
167 for (const auto &residency: power_entity_state_residency) {
168 result.append(prefix).append(residency.toString()).append("\n");
169 }
170 for (const auto &energy: rail_energy) {
171 result.append(prefix).append(energy.toString()).append("\n");
172 }
173 return result;
174 }
175
normalizedEnergy(const std::string & prefix) const176 std::string PowerStats::normalizedEnergy(const std::string& prefix) const {
177 if (metadata.duration_ms == 0) return {};
178
179 std::string result(prefix);
180 result.append(audio_utils_time_string_from_ns(
181 metadata.start_time_epoch_ms * 1'000'000).time);
182 result.append(" duration_boottime: ")
183 .append(std::to_string(metadata.duration_ms * 1e-3f))
184 .append(" duration_monotonic: ")
185 .append(std::to_string(metadata.duration_monotonic_ms * 1e-3f))
186 .append("\n");
187 if (health_stats.isValid()) {
188 result.append(prefix)
189 .append(health_stats.normalizedEnergy(metadata.duration_ms * 1e-3f)).append("\n");
190 }
191
192 // energy_uws is converted to ave W using recip time in us.
193 const float recipTime = 1e-3 / metadata.duration_ms;
194 int64_t total_energy = 0;
195 for (const auto& energy: rail_energy) {
196 total_energy += energy.energy_uws;
197 result.append(prefix).append(energy.subsystem_name)
198 .append(energy.rail_name)
199 .append(" ")
200 .append(std::to_string(energy.energy_uws * 1e-6))
201 .append(" ")
202 .append(std::to_string(energy.energy_uws * recipTime))
203 .append("\n");
204 }
205 if (total_energy != 0) {
206 result.append(prefix).append("total J and ave W: ")
207 .append(std::to_string(total_energy * 1e-6))
208 .append(" ")
209 .append(std::to_string(total_energy * recipTime))
210 .append("\n");
211 }
212 return result;
213 }
214
215 // seconds, joules, watts
energyFrom(const std::string & railMatcher) const216 std::tuple<float, float, float> PowerStats::energyFrom(const std::string& railMatcher) const {
217 if (metadata.duration_ms == 0) return {};
218
219 // energy_uws is converted to ave W using recip time in us.
220 const float recipTime = 1e-3 / metadata.duration_ms;
221 int64_t total_energy = 0;
222 for (const auto& energy: rail_energy) {
223 if (energy.subsystem_name.find(railMatcher) != std::string::npos
224 || energy.rail_name.find(railMatcher) != std::string::npos) {
225 total_energy += energy.energy_uws;
226 }
227 }
228 return {metadata.duration_ms * 1e-3, total_energy * 1e-6, total_energy * recipTime};
229 }
230
operator +=(const PowerStats & other)231 PowerStats PowerStats::operator+=(const PowerStats& other) {
232 metadata += other.metadata;
233 health_stats += other.health_stats;
234 if (power_entity_state_residency.empty()) {
235 power_entity_state_residency = other.power_entity_state_residency;
236 } else if (power_entity_state_residency.size() == other.power_entity_state_residency.size()) {
237 for (size_t i = 0; i < power_entity_state_residency.size(); ++i) {
238 power_entity_state_residency[i] += other.power_entity_state_residency[i];
239 }
240 }
241 if (rail_energy.empty()) {
242 rail_energy = other.rail_energy;
243 } else if (rail_energy.size() == other.rail_energy.size()) {
244 for (size_t i = 0; i < rail_energy.size(); ++i) {
245 rail_energy[i] += other.rail_energy[i];
246 }
247 }
248 return *this;
249 }
250
operator -=(const PowerStats & other)251 PowerStats PowerStats::operator-=(const PowerStats& other) {
252 metadata -= other.metadata;
253 health_stats -= other.health_stats;
254 if (power_entity_state_residency.empty()) {
255 power_entity_state_residency = other.power_entity_state_residency;
256 } else if (power_entity_state_residency.size() == other.power_entity_state_residency.size()) {
257 for (size_t i = 0; i < power_entity_state_residency.size(); ++i) {
258 power_entity_state_residency[i] -= other.power_entity_state_residency[i];
259 }
260 }
261 if (rail_energy.empty()) {
262 rail_energy = other.rail_energy;
263 } else if (rail_energy.size() == other.rail_energy.size()) {
264 for (size_t i = 0; i < rail_energy.size(); ++i) {
265 rail_energy[i] -= other.rail_energy[i];
266 }
267 }
268 return *this;
269 }
270
operator +(const PowerStats & other) const271 PowerStats PowerStats::operator+(const PowerStats& other) const {
272 PowerStats result = *this;
273 result += other;
274 return result;
275 }
276
operator -(const PowerStats & other) const277 PowerStats PowerStats::operator-(const PowerStats& other) const {
278 PowerStats result = *this;
279 result -= other;
280 return result;
281 }
282
283 } // namespace android::media::psh_utils
284