/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include namespace android::media::psh_utils { // Determine the best start time from a and b, which is // min(a, b) if both exist, otherwise the one that exists. template const T& choose_best_start_time(const T& a, const T& b) { if (a) { return b ? std::min(a, b) : a; } else { return b; } } // subtract two time differences. template const T sub_time_diff(const T& diff_a, const T& diff_b, const U& abs_c, const U& abs_d) { if (diff_a) { return diff_b ? (diff_a - diff_b) : diff_a; } else if (diff_b) { return diff_b; } else { // no difference exists, use absolute time. return abs_c - abs_d; } } std::string PowerStats::Metadata::toString() const { return std::string("start_time_since_boot_ms: ").append( std::to_string(start_time_since_boot_ms)) .append(" start_time_monotonic_ms: ").append(std::to_string(start_time_monotonic_ms)) .append(audio_utils_time_string_from_ns(start_time_epoch_ms * 1'000'000).time) .append(" duration_ms: ").append(std::to_string(duration_ms)) .append(" duration_monotonic_ms: ").append(std::to_string(duration_monotonic_ms)); } PowerStats::Metadata PowerStats::Metadata::operator+=(const Metadata& other) { start_time_since_boot_ms = choose_best_start_time( start_time_since_boot_ms, other.start_time_since_boot_ms); start_time_epoch_ms = choose_best_start_time( start_time_epoch_ms, other.start_time_epoch_ms); start_time_monotonic_ms = choose_best_start_time( start_time_monotonic_ms, other.start_time_monotonic_ms); duration_ms += other.duration_ms; duration_monotonic_ms += other.duration_monotonic_ms; return *this; } PowerStats::Metadata PowerStats::Metadata::operator-=(const Metadata& other) { // here we calculate duration, if it makes sense. duration_ms = sub_time_diff(duration_ms, other.duration_ms, start_time_since_boot_ms, other.start_time_since_boot_ms); duration_monotonic_ms = sub_time_diff( duration_monotonic_ms, other.duration_monotonic_ms, start_time_monotonic_ms, other.start_time_monotonic_ms); start_time_since_boot_ms = choose_best_start_time( start_time_since_boot_ms, other.start_time_since_boot_ms); start_time_epoch_ms = choose_best_start_time( start_time_epoch_ms, other.start_time_epoch_ms); start_time_monotonic_ms = choose_best_start_time( start_time_monotonic_ms, other.start_time_monotonic_ms); return *this; } PowerStats::Metadata PowerStats::Metadata::operator+(const Metadata& other) const { Metadata result = *this; result += other; return result; } PowerStats::Metadata PowerStats::Metadata::operator-(const Metadata& other) const { Metadata result = *this; result -= other; return result; } std::string PowerStats::StateResidency::toString() const { return std::string(entity_name).append(state_name) .append(" ").append(std::to_string(time_ms)) .append(" ").append(std::to_string(entry_count)); } PowerStats::StateResidency PowerStats::StateResidency::operator+=(const StateResidency& other) { if (entity_name.empty()) entity_name = other.entity_name; if (state_name.empty()) state_name = other.state_name; time_ms += other.time_ms; entry_count += other.entry_count; return *this; } PowerStats::StateResidency PowerStats::StateResidency::operator-=(const StateResidency& other) { if (entity_name.empty()) entity_name = other.entity_name; if (state_name.empty()) state_name = other.state_name; time_ms -= other.time_ms; entry_count -= other.entry_count; return *this; } PowerStats::StateResidency PowerStats::StateResidency::operator+( const StateResidency& other) const { StateResidency result = *this; result += other; return result; } PowerStats::StateResidency PowerStats::StateResidency::operator-( const StateResidency& other) const { StateResidency result = *this; result -= other; return result; } std::string PowerStats::RailEnergy::toString() const { return std::string(subsystem_name) .append(rail_name) .append(" ") .append(std::to_string(energy_uws)); } PowerStats::RailEnergy PowerStats::RailEnergy::operator+=(const RailEnergy& other) { if (subsystem_name.empty()) subsystem_name = other.subsystem_name; if (rail_name.empty()) rail_name = other.rail_name; energy_uws += other.energy_uws; return *this; } PowerStats::RailEnergy PowerStats::RailEnergy::operator-=(const RailEnergy& other) { if (subsystem_name.empty()) subsystem_name = other.subsystem_name; if (rail_name.empty()) rail_name = other.rail_name; energy_uws -= other.energy_uws; return *this; } PowerStats::RailEnergy PowerStats::RailEnergy::operator+(const RailEnergy& other) const { RailEnergy result = *this; result += other; return result; } PowerStats::RailEnergy PowerStats::RailEnergy::operator-(const RailEnergy& other) const { RailEnergy result = *this; result -= other; return result; } std::string PowerStats::toString(const std::string& prefix) const { std::string result; result.append(prefix).append(metadata.toString()).append("\n"); result.append(prefix).append(health_stats.toString()).append("\n"); for (const auto &residency: power_entity_state_residency) { result.append(prefix).append(residency.toString()).append("\n"); } for (const auto &energy: rail_energy) { result.append(prefix).append(energy.toString()).append("\n"); } return result; } std::string PowerStats::normalizedEnergy(const std::string& prefix) const { if (metadata.duration_ms == 0) return {}; std::string result(prefix); result.append(audio_utils_time_string_from_ns( metadata.start_time_epoch_ms * 1'000'000).time); result.append(" duration_boottime: ") .append(std::to_string(metadata.duration_ms * 1e-3f)) .append(" duration_monotonic: ") .append(std::to_string(metadata.duration_monotonic_ms * 1e-3f)) .append("\n"); if (health_stats.isValid()) { result.append(prefix) .append(health_stats.normalizedEnergy(metadata.duration_ms * 1e-3f)).append("\n"); } // energy_uws is converted to ave W using recip time in us. const float recipTime = 1e-3 / metadata.duration_ms; int64_t total_energy = 0; for (const auto& energy: rail_energy) { total_energy += energy.energy_uws; result.append(prefix).append(energy.subsystem_name) .append(energy.rail_name) .append(" ") .append(std::to_string(energy.energy_uws * 1e-6)) .append(" ") .append(std::to_string(energy.energy_uws * recipTime)) .append("\n"); } if (total_energy != 0) { result.append(prefix).append("total J and ave W: ") .append(std::to_string(total_energy * 1e-6)) .append(" ") .append(std::to_string(total_energy * recipTime)) .append("\n"); } return result; } // seconds, joules, watts std::tuple PowerStats::energyFrom(const std::string& railMatcher) const { if (metadata.duration_ms == 0) return {}; // energy_uws is converted to ave W using recip time in us. const float recipTime = 1e-3 / metadata.duration_ms; int64_t total_energy = 0; for (const auto& energy: rail_energy) { if (energy.subsystem_name.find(railMatcher) != std::string::npos || energy.rail_name.find(railMatcher) != std::string::npos) { total_energy += energy.energy_uws; } } return {metadata.duration_ms * 1e-3, total_energy * 1e-6, total_energy * recipTime}; } PowerStats PowerStats::operator+=(const PowerStats& other) { metadata += other.metadata; health_stats += other.health_stats; if (power_entity_state_residency.empty()) { power_entity_state_residency = other.power_entity_state_residency; } else if (power_entity_state_residency.size() == other.power_entity_state_residency.size()) { for (size_t i = 0; i < power_entity_state_residency.size(); ++i) { power_entity_state_residency[i] += other.power_entity_state_residency[i]; } } if (rail_energy.empty()) { rail_energy = other.rail_energy; } else if (rail_energy.size() == other.rail_energy.size()) { for (size_t i = 0; i < rail_energy.size(); ++i) { rail_energy[i] += other.rail_energy[i]; } } return *this; } PowerStats PowerStats::operator-=(const PowerStats& other) { metadata -= other.metadata; health_stats -= other.health_stats; if (power_entity_state_residency.empty()) { power_entity_state_residency = other.power_entity_state_residency; } else if (power_entity_state_residency.size() == other.power_entity_state_residency.size()) { for (size_t i = 0; i < power_entity_state_residency.size(); ++i) { power_entity_state_residency[i] -= other.power_entity_state_residency[i]; } } if (rail_energy.empty()) { rail_energy = other.rail_energy; } else if (rail_energy.size() == other.rail_energy.size()) { for (size_t i = 0; i < rail_energy.size(); ++i) { rail_energy[i] -= other.rail_energy[i]; } } return *this; } PowerStats PowerStats::operator+(const PowerStats& other) const { PowerStats result = *this; result += other; return result; } PowerStats PowerStats::operator-(const PowerStats& other) const { PowerStats result = *this; result -= other; return result; } } // namespace android::media::psh_utils