diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-01-10 00:21:13 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-01-10 00:21:13 +0000 |
commit | 683806ea2885dd8a318fb5fcfd4d0ee72917eb88 (patch) | |
tree | ad5712b8813ebb2728918c80b504ae5fee5198e8 | |
parent | 4c2d22da9e00d233e06b06173f57e325c7c4cfd9 (diff) | |
parent | 0aaebb34f1bf6deaae986d5063cf28b46b22fc58 (diff) |
Snap for 9470583 from 0aaebb34f1bf6deaae986d5063cf28b46b22fc58 to tm-qpr3-release
Change-Id: I49ed959256152e17a7295ad57c5491bb3efa78d9
55 files changed, 2471 insertions, 519 deletions
diff --git a/atrace/AtraceDevice.cpp b/atrace/AtraceDevice.cpp index 5f8b9cd..bbe7230 100644 --- a/atrace/AtraceDevice.cpp +++ b/atrace/AtraceDevice.cpp @@ -48,7 +48,11 @@ const std::map<std::string, TracingConfig> kTracingMap = { }, { "memory", - {"Memory", {{"fastrpc/fastrpc_dma_stat", false}, {"dmabuf_heap", false}}}, + {"Memory", + {{"fastrpc/fastrpc_dma_stat", false}, + {"dmabuf_heap", false}, + {"cma/cma_alloc_start", false}, + {"cma/cma_alloc_info", false}}}, }, { "ion", diff --git a/atrace/android.hardware.atrace@1.0-service.pixel.rc b/atrace/android.hardware.atrace@1.0-service.pixel.rc index 7d2a50d..c198407 100644 --- a/atrace/android.hardware.atrace@1.0-service.pixel.rc +++ b/atrace/android.hardware.atrace@1.0-service.pixel.rc @@ -36,6 +36,9 @@ on late-init chmod 0666 /sys/kernel/tracing/events/thermal_exynos/enable chmod 0666 /sys/kernel/debug/tracing/events/thermal_exynos_gpu/enable chmod 0666 /sys/kernel/tracing/events/thermal_exynos_gpu/enable + # memory trace points + chmod 0666 /sys/kernel/tracing/events/cma/cma_alloc_start/enable + chmod 0666 /sys/kernel/tracing/events/cma/cma_alloc_info/enable service vendor.atrace-hal-1-0 /vendor/bin/hw/android.hardware.atrace@1.0-service.pixel interface android.hardware.atrace@1.0::IAtraceDevice default diff --git a/battery_mitigation/BatteryMitigation.cpp b/battery_mitigation/BatteryMitigation.cpp index 252b1ee..98419fc 100644 --- a/battery_mitigation/BatteryMitigation.cpp +++ b/battery_mitigation/BatteryMitigation.cpp @@ -16,6 +16,10 @@ #include <battery_mitigation/BatteryMitigation.h> +#include <sstream> + +#define MAX_BROWNOUT_DATA_AGE_SECONDS 300 + namespace android { namespace hardware { namespace google { @@ -26,6 +30,37 @@ BatteryMitigation::BatteryMitigation(const struct MitigationConfig::Config &cfg) mThermalMgr->updateConfig(cfg); } +bool BatteryMitigation::isMitigationLogTimeValid(std::chrono::system_clock::time_point startTime, + const char *const logFilePath, + const char *const timestampFormat, + const std::regex pattern) { + std::string logFile; + if (!android::base::ReadFileToString(logFilePath, &logFile)) { + return false; + } + std::istringstream content(logFile); + std::string line; + int counter = 0; + std::smatch pattern_match; + while (std::getline(content, line)) { + if (std::regex_match(line, pattern_match, pattern)) { + std::tm triggeredTimestamp = {}; + std::istringstream ss(pattern_match.str()); + ss >> std::get_time(&triggeredTimestamp, timestampFormat); + auto logFileTime = std::chrono::system_clock::from_time_t(mktime(&triggeredTimestamp)); + auto delta = std::chrono::duration_cast<std::chrono::seconds>(startTime - logFileTime); + if ((delta.count() < MAX_BROWNOUT_DATA_AGE_SECONDS) && (delta.count() > 0)) { + return true; + } + } + counter += 1; + if (counter > 5) { + break; + } + } + return false; +} + } // namespace pixel } // namespace google } // namespace hardware diff --git a/battery_mitigation/MitigationThermalManager.cpp b/battery_mitigation/MitigationThermalManager.cpp index b1320e7..c304917 100644 --- a/battery_mitigation/MitigationThermalManager.cpp +++ b/battery_mitigation/MitigationThermalManager.cpp @@ -15,14 +15,13 @@ */ #define LOG_TAG "mitigation-logger" -#include <battery_mitigation/MitigationThermalManager.h> - #include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/parseint.h> #include <android-base/properties.h> #include <android-base/strings.h> +#include <battery_mitigation/MitigationThermalManager.h> #include <errno.h> #include <sys/time.h> @@ -84,6 +83,7 @@ void MitigationThermalManager::updateConfig(const struct MitigationConfig::Confi kSystemPath = cfg.SystemPath; kSystemName = cfg.SystemName; kFilteredZones = cfg.FilteredZones; + kTimestampFormat = cfg.TimestampFormat; } bool MitigationThermalManager::connectThermalHal() { @@ -124,6 +124,7 @@ void MitigationThermalManager::thermalCb(const Temperature &temperature) { std::stringstream oss; oss << temperature.name << " triggered at " << temperature.value << std::endl << std::flush; android::base::WriteStringToFd(oss.str(), fd); + fsync(fd); for (int i = 0; i < NUM_OF_SAMPLES; i++) { auto now = std::chrono::system_clock::now(); @@ -132,9 +133,11 @@ void MitigationThermalManager::thermalCb(const Temperature &temperature) { std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()); struct tm now_tm; localtime_r(&time_sec, &now_tm); - oss << std::put_time(&now_tm, "%m-%d %H:%M:%S.") << std::setw(3) << std::setfill('0') - << ms.count() << std::endl << std::flush; + oss << std::put_time(&now_tm, kTimestampFormat.c_str()) << "." << std::setw(3) + << std::setfill('0') << ms.count() << std::endl + << std::flush; android::base::WriteStringToFd(oss.str(), fd); + fsync(fd); oss.str(""); /* log System info */ for (int j = 0; j < kSystemName.size(); j++) { diff --git a/battery_mitigation/include/battery_mitigation/BatteryMitigation.h b/battery_mitigation/include/battery_mitigation/BatteryMitigation.h index 86dda06..d383969 100644 --- a/battery_mitigation/include/battery_mitigation/BatteryMitigation.h +++ b/battery_mitigation/include/battery_mitigation/BatteryMitigation.h @@ -28,6 +28,9 @@ using ::android::sp; class BatteryMitigation : public RefBase { public: BatteryMitigation(const struct MitigationConfig::Config &cfg); + bool isMitigationLogTimeValid(std::chrono::system_clock::time_point startTime, + const char *const logFilePath, const char *const timestampFormat, + const std::regex pattern); private: MitigationThermalManager *mThermalMgr; diff --git a/battery_mitigation/include/battery_mitigation/MitigationConfig.h b/battery_mitigation/include/battery_mitigation/MitigationConfig.h index 36a4f5e..825e30c 100644 --- a/battery_mitigation/include/battery_mitigation/MitigationConfig.h +++ b/battery_mitigation/include/battery_mitigation/MitigationConfig.h @@ -29,6 +29,7 @@ class MitigationConfig { const std::vector<std::string> FilteredZones; const std::vector<std::string> SystemName; const char *const LogFilePath; + const char *const TimestampFormat; }; MitigationConfig(const struct Config &cfg); @@ -38,6 +39,7 @@ class MitigationConfig { const std::vector<std::string> kFilteredZones; const std::vector<std::string> kSystemName; const char *const kLogFilePath; + const char *const kTimestampFormat; }; } // namespace pixel diff --git a/battery_mitigation/include/battery_mitigation/MitigationThermalManager.h b/battery_mitigation/include/battery_mitigation/MitigationThermalManager.h index 5c4ddbe..688c5d8 100644 --- a/battery_mitigation/include/battery_mitigation/MitigationThermalManager.h +++ b/battery_mitigation/include/battery_mitigation/MitigationThermalManager.h @@ -31,10 +31,11 @@ #include <unistd.h> #include <utils/Mutex.h> -#include "MitigationConfig.h" - #include <fstream> #include <iostream> +#include <regex> + +#include "MitigationConfig.h" namespace android { namespace hardware { @@ -116,6 +117,7 @@ class MitigationThermalManager { std::vector<std::string> kFilteredZones; std::vector<std::string> kSystemName; std::string kLogFilePath; + std::string kTimestampFormat; }; } // namespace pixel diff --git a/common/pixel-common-device.mk b/common/pixel-common-device.mk index 4bb4398..dfa5f15 100644 --- a/common/pixel-common-device.mk +++ b/common/pixel-common-device.mk @@ -21,6 +21,14 @@ PRODUCT_PACKAGES += \ BOARD_VENDOR_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/ramdump/common # Pixel Experience + +ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT))) +ifeq (,$(filter aosp_%,$(TARGET_PRODUCT))) +PRODUCT_PACKAGES_DEBUG += wifi_diagnostic +BOARD_VENDOR_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/wifi_diagnostic +endif +endif + PRODUCT_PACKAGES_DEBUG += wifi_sniffer BOARD_VENDOR_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/wifi_sniffer diff --git a/fastboot/Fastboot.cpp b/fastboot/Fastboot.cpp index 922334b..913d82d 100644 --- a/fastboot/Fastboot.cpp +++ b/fastboot/Fastboot.cpp @@ -16,15 +16,16 @@ #include "fastboot/Fastboot.h" -#include <string> -#include <unordered_map> -#include <vector> -#include <map> - #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <dlfcn.h> + +#include <map> +#include <string> +#include <unordered_map> +#include <vector> // FS headers #include <ext4_utils/wipe.h> @@ -169,11 +170,35 @@ enum WipeVolumeStatus wipe_volume(const std::string &volume) { return WIPE_OK; } +// Attempt to reuse a WipeKeys function that might be found in the recovery +// library in order to clear any digital car keys on the secure element. +bool WipeDigitalCarKeys(void) { + static constexpr const char *kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so"; + void *librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW); + if (librecovery_ui_ext == nullptr) { + // Dynamic library not found. Returning true since this likely + // means target does not support DCK. + return true; + } + + bool *(*WipeKeysFunc)(void *const); + reinterpret_cast<void *&>(WipeKeysFunc) = dlsym(librecovery_ui_ext, "WipeKeys"); + if (WipeKeysFunc == nullptr) { + // No WipeKeys implementation found. Returning true since this likely + // means target does not support DCK. + return true; + } + + return (*WipeKeysFunc)(nullptr); +} + Return<void> Fastboot::doOemSpecificErase(V1_1::IFastboot::doOemSpecificErase_cb _hidl_cb) { // Erase metadata partition along with userdata partition. // Keep erasing Titan M even if failing on this case. auto wipe_status = wipe_volume("/metadata"); + bool dck_wipe_success = WipeDigitalCarKeys(); + // Connect to Titan M ::nos::NuggetClient client; client.Open(); @@ -188,19 +213,27 @@ Return<void> Fastboot::doOemSpecificErase(V1_1::IFastboot::doOemSpecificErase_cb memcpy(magic.data(), &magicValue, sizeof(magicValue)); const uint8_t retry_count = 5; uint32_t nugget_status; - for(uint8_t i = 0; i < retry_count; i++) { + for (uint8_t i = 0; i < retry_count; i++) { nugget_status = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr); - if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK) { + if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK && dck_wipe_success) { _hidl_cb({Status::SUCCESS, wipe_vol_ret_msg[wipe_status]}); return Void(); } } // Return exactly what happened - if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) { + if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK && !dck_wipe_success) { + _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata, Titan M user data, and DCK"}); + } else if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) { _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data"}); + } else if (nugget_status != APP_SUCCESS && !dck_wipe_success) { + _hidl_cb({Status::FAILURE_UNKNOWN, "Titan M user data and DCK wipe failed"}); } else if (nugget_status != APP_SUCCESS) { _hidl_cb({Status::FAILURE_UNKNOWN, "Titan M user data wipe failed"}); + } else if (wipe_status != WIPE_OK && !dck_wipe_success) { + _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata and DCK"}); + } else if (!dck_wipe_success) { + _hidl_cb({Status::FAILURE_UNKNOWN, "DCK wipe failed"}); } else { if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end()) _hidl_cb({Status::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status]}); diff --git a/health/BatteryDefender.cpp b/health/BatteryDefender.cpp index ad2dc20..66876a5 100644 --- a/health/BatteryDefender.cpp +++ b/health/BatteryDefender.cpp @@ -296,11 +296,6 @@ void BatteryDefender::stateMachine_runAction(const state_E state, const HealthIn if (health_info.batteryLevel >= triggerLevel) { mHasReachedHighCapacityLevel = true; } - - /* Do the same as above when dock-defend triggers */ - if (mIsDockDefendTrigger) { - mHasReachedHighCapacityLevel = true; - } } break; case STATE_ACTIVE: diff --git a/mm/device_gki.mk b/mm/device_gki.mk index cd4fdf8..adcf227 100644 --- a/mm/device_gki.mk +++ b/mm/device_gki.mk @@ -1,7 +1,13 @@ PRODUCT_COPY_FILES += \ hardware/google/pixel/mm/pixel-mm-gki.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/pixel-mm-gki.rc \ hardware/google/pixel/mm/fstab.zram.2g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.2g \ - hardware/google/pixel/mm/fstab.zram.3g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.3g + hardware/google/pixel/mm/fstab.zram.3g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.3g \ + hardware/google/pixel/mm/fstab.zram.4g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.4g \ + hardware/google/pixel/mm/fstab.zram.5g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.5g \ + hardware/google/pixel/mm/fstab.zram.6g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.6g \ + hardware/google/pixel/mm/fstab.zram.40p:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.40p \ + hardware/google/pixel/mm/fstab.zram.50p:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.50p \ + hardware/google/pixel/mm/fstab.zram.60p:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.60p ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT))) PRODUCT_PACKAGES += \ diff --git a/mm/fstab.zram.40p b/mm/fstab.zram.40p new file mode 100644 index 0000000..c85a59b --- /dev/null +++ b/mm/fstab.zram.40p @@ -0,0 +1 @@ +/dev/block/zram0 none swap defaults zramsize=40%,max_comp_streams=8,zram_backingdev_size=512M diff --git a/mm/fstab.zram.4g b/mm/fstab.zram.4g new file mode 100644 index 0000000..0cfad5a --- /dev/null +++ b/mm/fstab.zram.4g @@ -0,0 +1 @@ +/dev/block/zram0 none swap defaults zramsize=4294967296,max_comp_streams=8,zram_backingdev_size=512M diff --git a/mm/fstab.zram.50p b/mm/fstab.zram.50p new file mode 100644 index 0000000..b0e1ed6 --- /dev/null +++ b/mm/fstab.zram.50p @@ -0,0 +1 @@ +/dev/block/zram0 none swap defaults zramsize=50%,max_comp_streams=8,zram_backingdev_size=512M diff --git a/mm/fstab.zram.5g b/mm/fstab.zram.5g new file mode 100644 index 0000000..8cd4d56 --- /dev/null +++ b/mm/fstab.zram.5g @@ -0,0 +1 @@ +/dev/block/zram0 none swap defaults zramsize=5368709120,max_comp_streams=8,zram_backingdev_size=512M diff --git a/mm/fstab.zram.60p b/mm/fstab.zram.60p new file mode 100644 index 0000000..0e05048 --- /dev/null +++ b/mm/fstab.zram.60p @@ -0,0 +1 @@ +/dev/block/zram0 none swap defaults zramsize=60%,max_comp_streams=8,zram_backingdev_size=512M diff --git a/mm/fstab.zram.6g b/mm/fstab.zram.6g new file mode 100644 index 0000000..04fcd49 --- /dev/null +++ b/mm/fstab.zram.6g @@ -0,0 +1 @@ +/dev/block/zram0 none swap defaults zramsize=6442450944,max_comp_streams=8,zram_backingdev_size=512M diff --git a/mm/pixel-mm-gki.rc b/mm/pixel-mm-gki.rc index e5ab7f7..6119a72 100644 --- a/mm/pixel-mm-gki.rc +++ b/mm/pixel-mm-gki.rc @@ -19,11 +19,49 @@ on init # khugepaged tuning write /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs 60000 -on property:persist.vendor.zram.size=* - setprop vendor.zram.size ${persist.vendor.zram.size} +# Property from experiments - server config +on property:persist.device_config.vendor_system_native_boot.zram_size=* + setprop vendor.zram.size ${persist.device_config.vendor_system_native_boot.zram_size} + +# Property for local test. It can overwrite the server side config +on property:sys.boot_completed=1 && property:persist.vendor.boot.zram.size=* + setprop vendor.zram.size ${persist.vendor.boot.zram.size} on property:sys.boot_completed=1 swapon_all /vendor/etc/fstab.zram.${vendor.zram.size} on property:sys.boot_completed=1 chmod 444 /sys/kernel/debug/page_owner + + # Create mm_event trace point. + # For legacy devices, only mm_event is using this trace instance. + # Debugfs is only used in legacy devices and going to be deprecated. + # If others want to put more,it should get hard review from pixel-perf-team. + mkdir /sys/kernel/tracing/instances/pixel 0755 system system + chown system system /sys/kernel/tracing/instances/pixel/trace + chmod 0660 /sys/kernel/tracing/instances/pixel/trace + chown system system /sys/kernel/tracing/instances/pixel/tracing_on + chmod 0660 /sys/kernel/tracing/instances/pixel/tracing_on + write /sys/kernel/tracing/instances/pixel/buffer_size_kb 64 + write /sys/kernel/tracing/instances/pixel/events/cma/cma_alloc_busy_retry/enable 1 + write /sys/kernel/tracing/instances/pixel/events/cma/cma_alloc_start/enable 1 + write /sys/kernel/tracing/instances/pixel/events/cma/cma_alloc_info/enable 1 + +# turns off tracing right before bugreporting to keep more traces +on property:init.svc.dumpstatez=running + write /sys/kernel/tracing/instances/pixel/tracing_on 0 + +on property:init.svc.dumpstatez=stopped + write /sys/kernel/tracing/instances/pixel/tracing_on 1 + +on property:init.svc.bugreport=running + write /sys/kernel/tracing/instances/pixel/tracing_on 0 + +on property:init.svc.bugreport=stopped + write /sys/kernel/tracing/instances/pixel/tracing_on 1 + +on property:init.svc.bugreportd=running + write /sys/kernel/tracing/instances/pixel/tracing_on 0 + +on property:init.svc.bugreportd=stopped + write /sys/kernel/tracing/instances/pixel/tracing_on 1 diff --git a/pixelstats/Android.bp b/pixelstats/Android.bp index f99e8e8..ae49b53 100644 --- a/pixelstats/Android.bp +++ b/pixelstats/Android.bp @@ -72,6 +72,8 @@ cc_library { "PcaChargeStats.cpp", "StatsHelper.cpp", "SysfsCollector.cpp", + "ThermalStatsReporter.cpp", + "TempResidencyReporter.cpp", "UeventListener.cpp", "WirelessChargeStats.cpp", "WlcReporter.cpp", diff --git a/pixelstats/ChargeStatsReporter.cpp b/pixelstats/ChargeStatsReporter.cpp index 9892668..6bc2b64 100644 --- a/pixelstats/ChargeStatsReporter.cpp +++ b/pixelstats/ChargeStatsReporter.cpp @@ -45,21 +45,23 @@ void ChargeStatsReporter::ReportChargeStats(const std::shared_ptr<IStats> &stats const std::string line, const std::string wline_at, const std::string wline_ac, const std::string pca_line) { - int charge_stats_fields[] = {ChargeStats::kAdapterTypeFieldNumber, - ChargeStats::kAdapterVoltageFieldNumber, - ChargeStats::kAdapterAmperageFieldNumber, - ChargeStats::kSsocInFieldNumber, - ChargeStats::kVoltageInFieldNumber, - ChargeStats::kSsocOutFieldNumber, - ChargeStats::kVoltageOutFieldNumber, - ChargeStats::kAdapterCapabilities0FieldNumber, - ChargeStats::kAdapterCapabilities1FieldNumber, - ChargeStats::kAdapterCapabilities2FieldNumber, - ChargeStats::kAdapterCapabilities3FieldNumber, - ChargeStats::kAdapterCapabilities4FieldNumber, - ChargeStats::kReceiverState0FieldNumber, - ChargeStats::kReceiverState1FieldNumber, - ChargeStats::kChargeCapacityFieldNumber}; + int charge_stats_fields[] = { + ChargeStats::kAdapterTypeFieldNumber, + ChargeStats::kAdapterVoltageFieldNumber, + ChargeStats::kAdapterAmperageFieldNumber, + ChargeStats::kSsocInFieldNumber, + ChargeStats::kVoltageInFieldNumber, + ChargeStats::kSsocOutFieldNumber, + ChargeStats::kVoltageOutFieldNumber, + ChargeStats::kChargeCapacityFieldNumber, + ChargeStats::kAdapterCapabilities0FieldNumber, + ChargeStats::kAdapterCapabilities1FieldNumber, + ChargeStats::kAdapterCapabilities2FieldNumber, + ChargeStats::kAdapterCapabilities3FieldNumber, + ChargeStats::kAdapterCapabilities4FieldNumber, + ChargeStats::kReceiverState0FieldNumber, + ChargeStats::kReceiverState1FieldNumber, + }; const int32_t chg_fields_size = std::size(charge_stats_fields); static_assert(chg_fields_size == 15, "Unexpected charge stats fields size"); const int32_t wlc_fields_size = 7; @@ -70,7 +72,7 @@ void ChargeStatsReporter::ReportChargeStats(const std::shared_ptr<IStats> &stats ALOGD("processing %s", line.c_str()); if (sscanf(line.c_str(), "%d,%d,%d, %d,%d,%d,%d %d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], - &tmp[4], &tmp[5], &tmp[6], &tmp[14]) == 8) { + &tmp[4], &tmp[5], &tmp[6], &tmp[7]) == 8) { /* Age Adjusted Charge Rate (AACR) logs an additional battery capacity in order to determine * the charge curve needed to minimize battery cycle life degradation, while also minimizing * impact to the user. @@ -89,8 +91,8 @@ void ChargeStatsReporter::ReportChargeStats(const std::shared_ptr<IStats> &stats } else { tmp[0] = wireless_charge_stats_.TranslateSysModeToAtomValue(ssoc_tmp); ALOGD("wlc: processing %s", wline_ac.c_str()); - if (sscanf(wline_ac.c_str(), "D:%x,%x,%x,%x,%x, %x,%x", &tmp[7], &tmp[8], &tmp[9], - &tmp[10], &tmp[11], &tmp[12], &tmp[13]) != 7) + if (sscanf(wline_ac.c_str(), "D:%x,%x,%x,%x,%x, %x,%x", &tmp[8], &tmp[9], &tmp[10], + &tmp[11], &tmp[12], &tmp[13], &tmp[14]) != 7) ALOGE("Couldn't process %s", wline_ac.c_str()); else fields_size = chg_fields_size; /* include wlc stats */ @@ -104,14 +106,14 @@ void ChargeStatsReporter::ReportChargeStats(const std::shared_ptr<IStats> &stats ALOGE("Couldn't process %s", pca_line.c_str()); } else { fields_size = chg_fields_size; /* include pca stats */ - tmp[9] = pca_rs[2]; - tmp[10] = pca_rs[3]; - tmp[11] = pca_rs[4]; - tmp[13] = pca_rs[1]; + tmp[10] = pca_rs[2]; + tmp[11] = pca_rs[3]; + tmp[12] = pca_rs[4]; + tmp[14] = pca_rs[1]; if (wline_at.empty()) { - tmp[7] = pca_ac[0]; - tmp[8] = pca_ac[1]; - tmp[12] = pca_rs[0]; + tmp[8] = pca_ac[0]; + tmp[9] = pca_ac[1]; + tmp[13] = pca_rs[0]; } } } diff --git a/pixelstats/MmMetricsReporter.cpp b/pixelstats/MmMetricsReporter.cpp index 558fd2d..874cc5b 100644 --- a/pixelstats/MmMetricsReporter.cpp +++ b/pixelstats/MmMetricsReporter.cpp @@ -18,6 +18,7 @@ #include <aidl/android/frameworks/stats/IStats.h> #include <android-base/file.h> +#include <android-base/parsedouble.h> #include <android-base/parseint.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -53,13 +54,13 @@ const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetric {"nr_anon_pages", PixelMmMetricsPerHour::kAnonPagesFieldNumber, false}, {"nr_file_pages", PixelMmMetricsPerHour::kFilePagesFieldNumber, false}, {"nr_slab_reclaimable", PixelMmMetricsPerHour::kSlabReclaimableFieldNumber, false}, + {"nr_slab_unreclaimable", PixelMmMetricsPerHour::kSlabUnreclaimableFieldNumber, false}, {"nr_zspages", PixelMmMetricsPerHour::kZspagesFieldNumber, false}, {"nr_unevictable", PixelMmMetricsPerHour::kUnevictableFieldNumber, false}, }; const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetricsPerDayInfo = { {"workingset_refault", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true}, - {"workingset_refault_file", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true}, {"pswpin", PixelMmMetricsPerDay::kPswpinFieldNumber, true}, {"pswpout", PixelMmMetricsPerDay::kPswpoutFieldNumber, true}, {"allocstall_dma", PixelMmMetricsPerDay::kAllocstallDmaFieldNumber, true}, @@ -78,6 +79,18 @@ const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetric {"pgalloc_costly_order", PixelMmMetricsPerDay::kPgallocHighFieldNumber, true}, {"pgcache_hit", PixelMmMetricsPerDay::kPgcacheHitFieldNumber, true}, {"pgcache_miss", PixelMmMetricsPerDay::kPgcacheMissFieldNumber, true}, + {"workingset_refault_file", PixelMmMetricsPerDay::kWorkingsetRefaultFileFieldNumber, true}, + {"workingset_refault_anon", PixelMmMetricsPerDay::kWorkingsetRefaultAnonFieldNumber, true}, + {"compact_success", PixelMmMetricsPerDay::kCompactSuccessFieldNumber, true}, + {"compact_fail", PixelMmMetricsPerDay::kCompactFailFieldNumber, true}, + {"kswapd_low_wmark_hit_quickly", PixelMmMetricsPerDay::kKswapdLowWmarkHqFieldNumber, true}, + {"kswapd_high_wmark_hit_quickly", PixelMmMetricsPerDay::kKswapdHighWmarkHqFieldNumber, + true}, + {"thp_file_alloc", PixelMmMetricsPerDay::kThpFileAllocFieldNumber, true}, + {"thp_zero_page_alloc", PixelMmMetricsPerDay::kThpZeroPageAllocFieldNumber, true}, + {"thp_split_page", PixelMmMetricsPerDay::kThpSplitPageFieldNumber, true}, + {"thp_migration_split", PixelMmMetricsPerDay::kThpMigrationSplitFieldNumber, true}, + {"thp_deferred_split_page", PixelMmMetricsPerDay::kThpDeferredSplitPageFieldNumber, true}, }; const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kCmaStatusInfo = { @@ -146,7 +159,11 @@ MmMetricsReporter::MmMetricsReporter() kIonTotalPoolsPath("/sys/kernel/dma_heap/total_pools_kb"), kIonTotalPoolsPathForLegacy("/sys/kernel/ion/total_pools_kb"), kGpuTotalPages("/sys/kernel/pixel_stat/gpu/mem/total_page_count"), - kPixelStatMm("/sys/kernel/pixel_stat/mm") { + kCompactDuration("/sys/kernel/pixel_stat/mm/compaction/mm_compaction_duration"), + kDirectReclaimBasePath("/sys/kernel/pixel_stat/mm/vmscan/direct_reclaim"), + kPixelStatMm("/sys/kernel/pixel_stat/mm"), + prev_compaction_duration_(kNumCompactionDurationPrevMetrics, 0), + prev_direct_reclaim_(kNumDirectReclaimPrevMetrics, 0) { is_user_build_ = checkUserBuild(); ker_mm_metrics_support_ = checkKernelMMMetricSupport(); } @@ -170,6 +187,90 @@ bool MmMetricsReporter::ReadFileToUint(const char *const path, uint64_t *val) { return true; } +/* + * This function reads whole file and parses tokens separated by <delim> into + * long integers. Useful for direct reclaim & compaction duration sysfs nodes. + * Data write is using all or none policy: It will not write partial data unless + * all data values are good. + * + * path: file to open/read + * data: where to store the results + * start_idx: index into data[] where to start saving the results + * delim: delimiters separating different longs + * skip: how many resulting longs to skip before saving + * nonnegtive: set to true to validate positive numbers + * + * Return value: number of longs actually stored on success. negative + * error codes on errors. + */ +static int ReadFileToLongs(const std::string &path, std::vector<long> *data, int start_idx, + const char *delim, int skip, bool nonnegative = false) { + std::vector<long> out; + enum { err_read_file = -1, err_parse = -2 }; + std::string file_contents; + + if (!ReadFileToString(path, &file_contents)) { + // Don't print this log if the file doesn't exist, since logs will be printed repeatedly. + if (errno != ENOENT) { + ALOGI("Unable to read %s - %s", path.c_str(), strerror(errno)); + } + return err_read_file; + } + + file_contents = android::base::Trim(file_contents); + std::vector<std::string> words = android::base::Tokenize(file_contents, delim); + if (words.size() == 0) + return 0; + + for (auto &w : words) { + if (skip) { + skip--; + continue; + } + long tmp; + if (!android::base::ParseInt(w, &tmp) || (nonnegative && tmp < 0)) + return err_parse; + out.push_back(tmp); + } + + int min_size = std::max(static_cast<int>(out.size()) + start_idx, 0); + if (min_size > data->size()) + data->resize(min_size); + std::copy(out.begin(), out.end(), data->begin() + start_idx); + + return out.size(); +} + +/* + * This function calls ReadFileToLongs, and checks the expected number + * of long integers read. Useful for direct reclaim & compaction duration + * sysfs nodes. + * + * path: file to open/read + * data: where to store the results + * start_idx: index into data[] where to start saving the results + * delim: delimiters separating different longs + * skip: how many resulting longs to skip before saving + * expected_num: number of expected longs to be read. + * nonnegtive: set to true to validate positive numbers + * + * Return value: true if successfully get expected number of long values. + * otherwise false. + */ +static inline bool ReadFileToLongsCheck(const std::string &path, std::vector<long> *store, + int start_idx, const char *delim, int skip, + int expected_num, bool nonnegative = false) { + int num = ReadFileToLongs(path, store, start_idx, delim, skip, nonnegative); + + if (num == expected_num) + return true; + + int last_idx = std::min(start_idx + expected_num, static_cast<int>(store->size())); + std::fill(store->begin() + start_idx, store->begin() + last_idx, -1); + + return false; +} + bool MmMetricsReporter::reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id, const std::vector<VendorAtomValue> &values, const std::string &atom_name) { @@ -272,7 +373,7 @@ void MmMetricsReporter::fillAtomValues(const std::vector<MmMetricsInfo> &metrics if (max_idx < entry.atom_key) max_idx = entry.atom_key; } - int size = max_idx - kVendorAtomOffset + 1; + unsigned int size = max_idx - kVendorAtomOffset + 1; if (atom_values->size() < size) atom_values->resize(size, tmp); @@ -301,6 +402,10 @@ void MmMetricsReporter::fillAtomValues(const std::vector<MmMetricsInfo> &metrics (*prev_mm_metrics) = mm_metrics; } +void MmMetricsReporter::aggregatePixelMmMetricsPer5Min() { + aggregatePressureStall(); +} + void MmMetricsReporter::logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client) { if (!MmMetricsSupported()) return; @@ -312,28 +417,23 @@ void MmMetricsReporter::logPixelMmMetricsPerHour(const std::shared_ptr<IStats> & uint64_t ion_total_pools = getIonTotalPools(); uint64_t gpu_memory = getGpuMemory(); - std::vector<VendorAtomValue> values; - bool is_first_atom = (prev_hour_vmstat_.size() == 0) ? true : false; - fillAtomValues(kMmMetricsPerHourInfo, vmstat, &prev_hour_vmstat_, &values); - - // resize values to add the following fields + // allocate enough values[] entries for the metrics. VendorAtomValue tmp; tmp.set<VendorAtomValue::longValue>(0); - int size = PixelMmMetricsPerHour::kGpuMemoryFieldNumber - kVendorAtomOffset + 1; - if (values.size() < size) { - values.resize(size, tmp); - } + int last_value_index = + PixelMmMetricsPerHour::kPsiMemSomeAvg300AvgFieldNumber - kVendorAtomOffset; + std::vector<VendorAtomValue> values(last_value_index + 1, tmp); + + fillAtomValues(kMmMetricsPerHourInfo, vmstat, &prev_hour_vmstat_, &values); tmp.set<VendorAtomValue::longValue>(ion_total_pools); values[PixelMmMetricsPerHour::kIonTotalPoolsFieldNumber - kVendorAtomOffset] = tmp; tmp.set<VendorAtomValue::longValue>(gpu_memory); values[PixelMmMetricsPerHour::kGpuMemoryFieldNumber - kVendorAtomOffset] = tmp; + fillPressureStallAtom(&values); - // Don't report the first atom to avoid big spike in accumulated values. - if (!is_first_atom) { - // Send vendor atom to IStats HAL - reportVendorAtom(stats_client, PixelAtoms::Atom::kPixelMmMetricsPerHour, values, - "PixelMmMetricsPerHour"); - } + // Send vendor atom to IStats HAL + reportVendorAtom(stats_client, PixelAtoms::Atom::kPixelMmMetricsPerHour, values, + "PixelMmMetricsPerHour"); } void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client) { @@ -344,8 +444,21 @@ void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &s if (vmstat.size() == 0) return; - std::vector<VendorAtomValue> values; + std::vector<long> direct_reclaim; + readDirectReclaimStat(&direct_reclaim); + + std::vector<long> compaction_duration; + readCompactionDurationStat(&compaction_duration); + bool is_first_atom = (prev_day_vmstat_.size() == 0) ? true : false; + + // allocate enough values[] entries for the metrics. + VendorAtomValue tmp; + tmp.set<VendorAtomValue::longValue>(0); + int last_value_index = + PixelMmMetricsPerDay::kThpDeferredSplitPageFieldNumber - kVendorAtomOffset; + std::vector<VendorAtomValue> values(last_value_index + 1, tmp); + fillAtomValues(kMmMetricsPerDayInfo, vmstat, &prev_day_vmstat_, &values); std::map<std::string, uint64_t> pixel_vmstat = @@ -355,6 +468,8 @@ void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &s &prev_kswapd_stime_, &values); fillProcessStime(PixelMmMetricsPerDay::kKcompactdStimeClksFieldNumber, "kcompactd0", &kcompactd_pid_, &prev_kcompactd_stime_, &values); + fillDirectReclaimStatAtom(direct_reclaim, &values); + fillCompactionDurationStatAtom(direct_reclaim, &values); // Don't report the first atom to avoid big spike in accumulated values. if (!is_first_atom) { @@ -498,6 +613,544 @@ std::map<std::string, uint64_t> MmMetricsReporter::readCmaStat( } /** + * This function reads compaction duration sysfs node + * (/sys/kernel/pixel_stat/mm/compaction/mm_compaction_duration) + * + * store: vector to save compaction duration info + */ +void MmMetricsReporter::readCompactionDurationStat(std::vector<long> *store) { + static const std::string path(kCompactDuration); + constexpr int num_metrics = 6; + + store->resize(num_metrics); + + int start_idx = 0; + int expected_num = num_metrics; + + if (!ReadFileToLongsCheck(path, store, start_idx, " ", 1, expected_num, true)) { + ALOGI("Unable to read %s for the direct reclaim info.", path.c_str()); + } +} + +/** + * This function fills atom values (values) from acquired compaction duration + * information from vector store + * + * store: the already collected (by readCompactionDurationStat()) compaction + * duration information + * values: the atom value vector to be filled. + */ +void MmMetricsReporter::fillCompactionDurationStatAtom(const std::vector<long> &store, + std::vector<VendorAtomValue> *values) { + // first metric index + constexpr int start_idx = PixelMmMetricsPerDay::kCompactionTotalTimeFieldNumber; + constexpr int num_metrics = 6; + + if (!MmMetricsSupported()) + return; + + int size = start_idx + num_metrics - kVendorAtomOffset; + if (values->size() < size) + values->resize(size); + + for (int i = 0; i < num_metrics; i++) { + VendorAtomValue tmp; + if (store[i] == -1) { + tmp.set<VendorAtomValue::longValue>(0); + } else { + tmp.set<VendorAtomValue::longValue>(store[i] - prev_compaction_duration_[i]); + prev_compaction_duration_[i] = store[i]; + } + (*values)[start_idx + i] = tmp; + } + prev_compaction_duration_ = store; +} + +/** + * This function reads direct reclaim sysfs node (4 files: + * /sys/kernel/pixel_stat/mm/vmscan/direct_reclaim/<level>/latency_stat, + * where <level> = native, top, visible, other.), and save total time and + * 4 latency information per file. Total (1+4) x 4 = 20 metrics will be + * saved. + * + * store: vector to save direct reclaim info + */ +void MmMetricsReporter::readDirectReclaimStat(std::vector<long> *store) { + static const std::string base_path(kDirectReclaimBasePath); + static const std::vector<std::string> dr_levels{"native", "top", "visible", "other"}; + static const std::string sysfs_name = "latency_stat"; + constexpr int num_metrics_per_file = 5; + int num_file = dr_levels.size(); + int num_metrics = num_metrics_per_file * num_file; + + store->resize(num_metrics); + int pass = -1; + for (auto level : dr_levels) { + ++pass; + std::string path = base_path + '/' + level + '/' + sysfs_name; + int start_idx = pass * num_metrics_per_file; + int expected_num = num_metrics_per_file; + if (!ReadFileToLongsCheck(path, store, start_idx, " ", 1, expected_num, true)) { + ALOGI("Unable to read %s for the direct reclaim info.", path.c_str()); + } + } +} + +/** + * This function fills atom values (values) from acquired direct reclaim + * information from vector store + * + * store: the already collected (by readDirectReclaimStat()) direct reclaim + * information + * values: the atom value vector to be filled. + */ +void MmMetricsReporter::fillDirectReclaimStatAtom(const std::vector<long> &store, + std::vector<VendorAtomValue> *values) { + // first metric index + constexpr int start_idx = PixelMmMetricsPerDay::kDirectReclaimNativeLatencyTotalTimeFieldNumber; + constexpr int num_metrics = 20; /* num_metrics_per_file * num_file */ + + if (!MmMetricsSupported()) + return; + + int size = start_idx + num_metrics - kVendorAtomOffset; + if (values->size() < size) + values->resize(size); + + for (int i = 0; i < num_metrics; i++) { + VendorAtomValue tmp; + tmp.set<VendorAtomValue::longValue>(store[i] - prev_direct_reclaim_[i]); + (*values)[start_idx + i] = tmp; + } + prev_direct_reclaim_ = store; +} + +/** + * This function reads pressure (PSI) files (loop thru all 3 files: cpu, io, and + * memory) and calls the parser to parse and store the metric values. + * Note that each file have two lines (except cpu has one line only): one with + * a leading "full", and the other with a leading "some", showing the category + * for that line. + * A category has 4 metrics, avg10, avg60, avg300, and total. + * i.e. the moving average % of PSI in 10s, 60s, 300s time window plus lastly + * the total stalled time, except that 'cpu' has no 'full' category. + * In total, we have 3 x 2 x 4 - 4 = 24 - 4 = 20 metrics, arranged in + * the order of + * + * cpu_some_avg<xyz> + * cpu_some_total + * io_full_avg<xyz> + * io_full_total + * io_some_avg<xyz> + * io_some_total + * mem_full_avg<xyz> + * mem_full_total + * mem_some_avg<xyz> + * mem_some_total + * + * where <xyz>=10, 60, 300 in the order as they appear. + * + * Note that for those avg values (i.e. <abc>_<def>_avg<xyz>), they + * are in percentage with 2-decimal digit accuracy. We will use an + * integer in 2-decimal fixed point format to represent the values. + * i.e. value x 100, or to cope with floating point errors, + * floor(value x 100 + 0.5) + * + * In fact, in newer kernels, "cpu" PSI has no "full" category. Some + * old kernel has them all zeros, to keep backward compatibility. The + * parse function called by this function is able to detect and ignore + * the "cpu, full" category. + * + * sample pressure stall files: + * /proc/pressure # cat cpu + * some avg10=2.93 avg60=3.17 avg300=3.15 total=94628150260 + * /proc/pressure # cat io + * some avg10=1.06 avg60=1.15 avg300=1.18 total=37709873805 + * full avg10=1.06 avg60=1.10 avg300=1.11 total=36592322936 + * /proc/pressure # cat memory + * some avg10=0.00 avg60=0.00 avg300=0.00 total=29705314 + * full avg10=0.00 avg60=0.00 avg300=0.00 total=17234456 + * + * PSI information definitions could be found at + * https://www.kernel.org/doc/html/latest/accounting/psi.html + * + * basePath: the base path to the pressure stall information + * store: pointer to the vector to store the 20 metrics in the mentioned + * order + */ +void MmMetricsReporter::readPressureStall(const char *basePath, std::vector<long> *store) { + constexpr int kTypeIdxCpu = 0; + + // Callers should have already prepared this, but we resize it here for safety + store->resize(kPsiNumAllMetrics); + std::fill(store->begin(), store->end(), -1); + + // To make the process unified, we prepend an imaginary "cpu + full" + // type-category combination. Now, each file (cpu, io, memnry) contains + // two categories, i.e. "full" and "some". + // Each category has <kPsiNumNames> merics and thus need that many entries + // to store them, except that the first category (the imaginary one) do not + // need any storage. So we set the save index for the 1st file ("cpu") to + // -kPsiNumNames. + int file_save_idx = -kPsiNumNames; + + // loop thru all pressure stall files: cpu, io, memory + for (int type_idx = 0; type_idx < kPsiNumFiles; + ++type_idx, file_save_idx += kPsiMetricsPerFile) { + std::string file_contents; + std::string path = std::string("") + basePath + '/' + kPsiTypes[type_idx]; + + if (!ReadFileToString(path, &file_contents)) { + // Don't print this log if the file doesn't exist, since logs will be printed + // repeatedly. + if (errno != ENOENT) + ALOGI("Unable to read %s - %s", path.c_str(), strerror(errno)); + goto err_out; + } + if (!MmMetricsReporter::parsePressureStallFileContent(type_idx == kTypeIdxCpu, + file_contents, store, file_save_idx)) + goto err_out; + } + return; + +err_out: + std::fill(store->begin(), store->end(), -1); +} + +/* + * This function parses a pressure stall file, which contains two + * lines, i.e. the "full", and "some" lines, except that the 'cpu' file + * contains only one line ("some"). Refer to the function comments of + * readPressureStall() for pressure stall file format. + * + * For old kernel, 'cpu' file might contain an extra line for "full", which + * will be ignored. + * + * is_cpu: Is the data from the file 'cpu' + * lines: the file content + * store: the output vector to hold the parsed data. + * file_save_idx: base index to start saving 'store' vector for this file. + * + * Return value: true on success, false otherwise. + */ +bool MmMetricsReporter::parsePressureStallFileContent(bool is_cpu, std::string lines, + std::vector<long> *store, int file_save_idx) { + constexpr int kNumOfWords = 5; // expected number of words separated by spaces. + constexpr int kCategoryFull = 0; + + std::istringstream data(lines); + std::string line; + + while (std::getline(data, line)) { + int category_idx = 0; + + line = android::base::Trim(line); + std::vector<std::string> words = android::base::Tokenize(line, " "); + if (words.size() != kNumOfWords) { + ALOGE("PSI parse fail: num of words = %d != expected %d", + static_cast<int>(words.size()), kNumOfWords); + return false; + } + + // words[0] should be either "full" or "some", the category name. + for (auto &cat : kPsiCategories) { + if (words[0].compare(cat) == 0) + break; + ++category_idx; + } + if (category_idx == kPsiNumCategories) { + ALOGE("PSI parse fail: unknown category %s", words[0].c_str()); + return false; + } + + // skip (cpu, full) combination. + if (is_cpu && category_idx == kCategoryFull) { + ALOGI("kernel: old PSI sysfs node."); + continue; + } + + // Now we have separated words in a vector, e.g. + // ["some", "avg10=2.93", "avg60=3.17", "avg300=3.15", total=94628150260"] + // call parsePressureStallWords to parse them. + int line_save_idx = file_save_idx + category_idx * kPsiNumNames; + if (!parsePressureStallWords(words, store, line_save_idx)) + return false; + } + return true; +} + +// This function parses the already split words, e.g. +// ["some", "avg10=0.00", "avg60=0.00", "avg300=0.00", "total=29705314"], +// from a line (category) in a pressure stall file. +// +// words: the split words in the form of "name=value" +// store: the output vector +// line_save_idx: the base start index to save in vector for this line (category) +// +// Return value: true on success, false otherwise. +bool MmMetricsReporter::parsePressureStallWords(std::vector<std::string> words, + std::vector<long> *store, int line_save_idx) { + // Skip the first word, which is already parsed by the caller. + // All others are value pairs in "name=value" form. + // e.g. ["some", "avg10=0.00", "avg60=0.00", "avg300=0.00", "total=29705314"] + // "some" is skipped. + for (int i = 1; i < words.size(); ++i) { + std::vector<std::string> metric = android::base::Tokenize(words[i], "="); + if (metric.size() != 2) { + ALOGE("%s: parse error (name=value) @ idx %d", __FUNCTION__, i); + return false; + } + if (!MmMetricsReporter::savePressureMetrics(metric[0], metric[1], store, line_save_idx)) + return false; + } + return true; +} + +// This function parses one value pair in "name=value" format, and depending on +// the name, save to its proper location in the store vector. +// name = "avg10" -> save to index base_save_idx. +// name = "avg60" -> save to index base_save_idx + 1. +// name = "avg300" -> save to index base_save_idx + 2. +// name = "total" -> save to index base_save_idx + 3. +// +// name: the metrics name +// value: the metrics value +// store: the output vector +// base_save_idx: the base save index +// +// Return value: true on success, false otherwise. +// +bool MmMetricsReporter::savePressureMetrics(std::string name, std::string value, + std::vector<long> *store, int base_save_idx) { + int name_idx = 0; + constexpr int kNameIdxTotal = 3; + + for (auto &mn : kPsiMetricNames) { + if (name.compare(mn) == 0) + break; + ++name_idx; + } + if (name_idx == kPsiNumNames) { + ALOGE("%s: parse error: unknown metric name.", __FUNCTION__); + return false; + } + + long out; + if (name_idx == kNameIdxTotal) { + // 'total' metrics + unsigned long tmp; + if (!android::base::ParseUint(value, &tmp)) + out = -1; + else + out = tmp; + } else { + // 'avg' metrics + double d = -1.0; + if (android::base::ParseDouble(value, &d)) + out = static_cast<long>(d * 100 + 0.5); + else + out = -1; + } + + if (base_save_idx + name_idx >= store->size()) { + // should never reach here + ALOGE("out of bound access to store[] (src line %d) @ index %d", __LINE__, + base_save_idx + name_idx); + return false; + } else { + (*store)[base_save_idx + name_idx] = out; + } + return true; +} + +/** + * This function reads in the current pressure (PSI) information, and aggregates + * it (except for the "total" information, which will overwrite + * the previous value without aggregation. + * + * data are arranged in the following order, and must comply the order defined + * in the proto: + * + * // note: these 5 'total' metrics are not aggregated. + * cpu_some_total + * io_full_total + * io_some_total + * mem_full_total + * mem_some_total + * + * // 9 aggregated metrics as above avg<xyz>_<aggregate> + * // where <xyz> = 10, 60, 300; <aggregate> = min, max, sum + * cpu_some_avg10_min + * cpu_some_avg10_max + * cpu_some_avg10_sum + * cpu_some_avg60_min + * cpu_some_avg60_max + * cpu_some_avg60_sum + * cpu_some_avg300_min + * cpu_some_avg300_max + * cpu_some_avg300_sum + * + * // similar 9 metrics as above avg<xyz>_<aggregate> + * io_full_avg<xyz>_<aggregate> + * + * // similar 9 metrics as above avg<xyz>_<aggregate> + * io_some_avg<xyz>_<aggregate> + * + * // similar 9 metrics as above avg<xyz>_<aggregate> + * mem_full_avg<xyz>_<aggregate> + * + * // similar 9 metrics as above avg<xyz>_<aggregate> + * mem_some_avg<xyz>_<aggregate> + * + * In addition, it increases psi_data_set_count_ by 1 (in order to calculate + * the average from the "_sum" aggregate.) + */ +void MmMetricsReporter::aggregatePressureStall() { + constexpr int kFirstTotalOffset = kPsiNumAvgs; + + if (!MmMetricsSupported()) + return; + + std::vector<long> psi(kPsiNumAllMetrics, -1); + readPressureStall(kPsiBasePath, &psi); + + // Pre-check for possible later out of bound error, if readPressureStall() + // decreases the vector size. + // It's for safety only. The condition should never be true. + if (psi.size() != kPsiNumAllMetrics) { + ALOGE("Wrong psi[] size %d != expected %d after read.", static_cast<int>(psi.size()), + kPsiNumAllMetrics); + return; + } + + // check raw metrics and preventively handle errors: Although we don't expect read sysfs + // node could fail. Discard all current readings on any error. + for (int i = 0; i < kPsiNumAllMetrics; ++i) { + if (psi[i] == -1) { + ALOGE("Bad data @ psi[%ld] = -1", psi[i]); + goto err_out; + } + } + + // "total" metrics are accumulative: just replace the previous accumulation. + for (int i = 0; i < kPsiNumAllTotals; ++i) { + int psi_idx; + + psi_idx = i * kPsiNumNames + kFirstTotalOffset; + if (psi_idx >= psi.size()) { + // should never reach here + ALOGE("out of bound access to psi[] (src line %d) @ index %d", __LINE__, psi_idx); + goto err_out; + } else { + psi_total_[i] = psi[psi_idx]; + } + } + + // "avg" metrics will be aggregated to min, max and sum + // later on, the sum will be divided by psi_data_set_count_ to get the average. + int aggr_idx; + aggr_idx = 0; + for (int psi_idx = 0; psi_idx < kPsiNumAllMetrics; ++psi_idx) { + if (psi_idx % kPsiNumNames == kFirstTotalOffset) + continue; // skip 'total' metrics, already processed. + + if (aggr_idx + 3 > kPsiNumAllUploadAvgMetrics) { + // should never reach here + ALOGE("out of bound access to psi_aggregated_[] (src line %d) @ index %d ~ %d", + __LINE__, aggr_idx, aggr_idx + 2); + return; // give up avgs, but keep totals (so don't go err_out + } + + long value = psi[psi_idx]; + if (psi_data_set_count_ == 0) { + psi_aggregated_[aggr_idx++] = value; + psi_aggregated_[aggr_idx++] = value; + psi_aggregated_[aggr_idx++] = value; + } else { + psi_aggregated_[aggr_idx++] = std::min(value, psi_aggregated_[aggr_idx]); + psi_aggregated_[aggr_idx++] = std::max(value, psi_aggregated_[aggr_idx]); + psi_aggregated_[aggr_idx++] += value; + } + } + ++psi_data_set_count_; + return; + +err_out: + for (int i = 0; i < kPsiNumAllTotals; ++i) psi_total_[i] = -1; +} + +/** + * This function fills atom values (values) from psi_aggregated_[] + * + * values: the atom value vector to be filled. + */ +void MmMetricsReporter::fillPressureStallAtom(std::vector<VendorAtomValue> *values) { + constexpr int avg_of_avg_offset = 2; + constexpr int total_start_idx = + PixelMmMetricsPerHour::kPsiCpuSomeTotalFieldNumber - kVendorAtomOffset; + constexpr int avg_start_idx = total_start_idx + kPsiNumAllTotals; + + if (!MmMetricsSupported()) + return; + + VendorAtomValue tmp; + + // The caller should have setup the correct total size, + // but we check and extend the size when it's too small for safety. + unsigned int min_value_size = total_start_idx + kPsiNumAllUploadMetrics; + if (values->size() < min_value_size) + values->resize(min_value_size); + + // "total" metric + int metric_idx = total_start_idx; + for (int save = 0; save < kPsiNumAllTotals; ++save, ++metric_idx) { + if (psi_data_set_count_ == 0) + psi_total_[save] = -1; // no data: invalidate the current total + + // A good difference needs a good previous value and a good current value. + if (psi_total_[save] != -1 && prev_psi_total_[save] != -1) + tmp.set<VendorAtomValue::longValue>(psi_total_[save] - prev_psi_total_[save]); + else + tmp.set<VendorAtomValue::longValue>(-1); + + prev_psi_total_[save] = psi_total_[save]; + if (metric_idx >= values->size()) { + // should never reach here + ALOGE("out of bound access to value[] for psi-total @ index %d", metric_idx); + goto cleanup; + } else { + (*values)[metric_idx] = tmp; + } + } + + // "avg" metrics -> aggregate to min, max, and avg of the original avg + metric_idx = avg_start_idx; + for (int save = 0; save < kPsiNumAllUploadAvgMetrics; ++save, ++metric_idx) { + if (psi_data_set_count_) { + if (save % kPsiNumOfAggregatedType == avg_of_avg_offset) { + // avg of avg + tmp.set<VendorAtomValue::intValue>(psi_aggregated_[save] / psi_data_set_count_); + } else { + // min or max of avg + tmp.set<VendorAtomValue::intValue>(psi_aggregated_[save]); + } + } else { + tmp.set<VendorAtomValue::intValue>(-1); + } + if (metric_idx >= values->size()) { + // should never reach here + ALOGE("out of bound access to value[] for psi-avg @ index %d", metric_idx); + goto cleanup; + } else { + (*values)[metric_idx] = tmp; + } + } + +cleanup: + psi_data_set_count_ = 0; +} + +/** * This function is to collect CMA metrics and upload them. * The CMA metrics are collected by readCmaStat(), copied into atom values * by fillAtomValues(), and then uploaded by reportVendorAtom(). The collected diff --git a/pixelstats/SysfsCollector.cpp b/pixelstats/SysfsCollector.cpp index 6c661ff..203ff0f 100644 --- a/pixelstats/SysfsCollector.cpp +++ b/pixelstats/SysfsCollector.cpp @@ -43,20 +43,25 @@ using android::base::ReadFileToString; using android::base::StartsWith; using android::base::WriteStringToFile; using android::hardware::google::pixel::PixelAtoms::BatteryCapacity; -using android::hardware::google::pixel::PixelAtoms::BootStatsInfo; using android::hardware::google::pixel::PixelAtoms::BlockStatsReported; +using android::hardware::google::pixel::PixelAtoms::BootStatsInfo; using android::hardware::google::pixel::PixelAtoms::F2fsCompressionInfo; using android::hardware::google::pixel::PixelAtoms::F2fsGcSegmentInfo; -using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo; using android::hardware::google::pixel::PixelAtoms::F2fsSmartIdleMaintEnabledStateChanged; +using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo; using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth; using android::hardware::google::pixel::PixelAtoms::StorageUfsResetCount; +using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats; +using android::hardware::google::pixel::PixelAtoms::VendorAudioHardwareStatsReported; using android::hardware::google::pixel::PixelAtoms::VendorChargeCycles; using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed; +using android::hardware::google::pixel::PixelAtoms::VendorLongIRQStatsReported; +using android::hardware::google::pixel::PixelAtoms::VendorResumeLatencyStats; using android::hardware::google::pixel::PixelAtoms::VendorSlowIo; using android::hardware::google::pixel::PixelAtoms::VendorSpeakerImpedance; using android::hardware::google::pixel::PixelAtoms::VendorSpeakerStatsReported; using android::hardware::google::pixel::PixelAtoms::VendorSpeechDspStat; +using android::hardware::google::pixel::PixelAtoms::VendorTempResidencyStats; using android::hardware::google::pixel::PixelAtoms::ZramBdStat; using android::hardware::google::pixel::PixelAtoms::ZramMmStat; @@ -84,7 +89,13 @@ SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths) kSpeakerExcursionPath(sysfs_paths.SpeakerExcursionPath), kSpeakerHeartbeatPath(sysfs_paths.SpeakerHeartBeatPath), kUFSErrStatsPath(sysfs_paths.UFSErrStatsPath), - kBlockStatsLength(sysfs_paths.BlockStatsLength) {} + kBlockStatsLength(sysfs_paths.BlockStatsLength), + kAmsRatePath(sysfs_paths.AmsRatePath), + kThermalStatsPaths(sysfs_paths.ThermalStatsPaths), + kCCARatePath(sysfs_paths.CCARatePath), + kTempResidencyPath(sysfs_paths.TempResidencyPath), + kLongIRQMetricsPath(sysfs_paths.LongIRQMetricsPath), + kResumeLatencyMetricsPath(sysfs_paths.ResumeLatencyMetricsPath) {} bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) { return ReadFileToInt(path.c_str(), val); @@ -288,85 +299,82 @@ void SysfsCollector::logSpeakerImpedance(const std::shared_ptr<IStats> &stats_cl * Report the last-detected impedance, temperature and heartbeats of left & right speakers. */ void SysfsCollector::logSpeakerHealthStats(const std::shared_ptr<IStats> &stats_client) { - std::string file_contents; + std::string file_contents_impedance; + std::string file_contents_temperature; + std::string file_contents_excursion; + std::string file_contents_heartbeat; + int count, i; + float impedance_ohm[4]; + float temperature_C[4]; + float excursion_mm[4]; + float heartbeat[4]; if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) { ALOGD("Audio impedance path not specified"); return; + } else if (!ReadFileToString(kImpedancePath, &file_contents_impedance)) { + ALOGD("Unable to read speaker impedance path %s", kImpedancePath); + return; } if (kSpeakerTemperaturePath == nullptr || strlen(kSpeakerTemperaturePath) == 0) { ALOGD("Audio speaker temperature path not specified"); return; - } - - if (kSpeakerHeartbeatPath == nullptr || strlen(kSpeakerHeartbeatPath) == 0) { - ALOGD("Audio speaker heartbeat path not specified"); + } else if (!ReadFileToString(kSpeakerTemperaturePath, &file_contents_temperature)) { + ALOGD("Unable to read speaker temperature path %s", kSpeakerTemperaturePath); return; } if (kSpeakerExcursionPath == nullptr || strlen(kSpeakerExcursionPath) == 0) { ALOGD("Audio speaker excursion path not specified"); return; - } - - float left_impedance_ohm, right_impedance_ohm; - - if (!ReadFileToString(kImpedancePath, &file_contents)) { - ALOGE("Unable to read speaker impedance path %s", kImpedancePath); - return; - } - if (sscanf(file_contents.c_str(), "%g,%g", &left_impedance_ohm, &right_impedance_ohm) != 2) { - ALOGE("Unable to parse speaker impedance %s", file_contents.c_str()); + } else if (!ReadFileToString(kSpeakerExcursionPath, &file_contents_excursion)) { + ALOGD("Unable to read speaker excursion path %s", kSpeakerExcursionPath); return; } - float left_temperature_C, right_temperature_C; - if (!ReadFileToString(kSpeakerTemperaturePath, &file_contents)) { - ALOGE("Unable to read speaker temperature path %s", kSpeakerTemperaturePath); + if (kSpeakerHeartbeatPath == nullptr || strlen(kSpeakerHeartbeatPath) == 0) { + ALOGD("Audio speaker heartbeat path not specified"); return; - } - if (sscanf(file_contents.c_str(), "%g,%g", &left_temperature_C, &right_temperature_C) != 2) { - ALOGE("Unable to parse speaker temperature %s", file_contents.c_str()); + } else if (!ReadFileToString(kSpeakerHeartbeatPath, &file_contents_heartbeat)) { + ALOGD("Unable to read speaker heartbeat path %s", kSpeakerHeartbeatPath); return; } - float left_excursion_mm, right_excursion_mm; - if (!ReadFileToString(kSpeakerExcursionPath, &file_contents)) { - ALOGE("Unable to read speaker excursion path %s", kSpeakerExcursionPath); + count = sscanf(file_contents_impedance.c_str(), "%g,%g,%g,%g", &impedance_ohm[0], + &impedance_ohm[1], &impedance_ohm[2], &impedance_ohm[3]); + if (count <= 0) return; - } - if (sscanf(file_contents.c_str(), "%g,%g", &left_excursion_mm, &right_excursion_mm) != 2) { - ALOGE("Unable to parse speaker excursion %s", file_contents.c_str()); + + count = sscanf(file_contents_temperature.c_str(), "%g,%g,%g,%g", &temperature_C[0], + &temperature_C[1], &temperature_C[2], &temperature_C[3]); + if (count <= 0) return; - } - float left_heartbeat, right_heartbeat; - if (!ReadFileToString(kSpeakerHeartbeatPath, &file_contents)) { - ALOGE("Unable to read speaker heartbeat path %s", kSpeakerHeartbeatPath); + count = sscanf(file_contents_excursion.c_str(), "%g,%g,%g,%g", &excursion_mm[0], + &excursion_mm[1], &excursion_mm[2], &excursion_mm[3]); + if (count <= 0) return; - } - if (sscanf(file_contents.c_str(), "%g,%g", &left_heartbeat, &right_heartbeat) != 2) { - ALOGE("Unable to parse speaker heartbeat %s", file_contents.c_str()); + + count = sscanf(file_contents_heartbeat.c_str(), "%g,%g,%g,%g", &heartbeat[0], &heartbeat[1], + &heartbeat[2], &heartbeat[3]); + if (count <= 0) return; - } - VendorSpeakerStatsReported left_obj; - left_obj.set_speaker_location(0); - left_obj.set_impedance(static_cast<int32_t>(left_impedance_ohm * 1000)); - left_obj.set_max_temperature(static_cast<int32_t>(left_temperature_C * 1000)); - left_obj.set_excursion(static_cast<int32_t>(left_excursion_mm * 1000)); - left_obj.set_heartbeat(static_cast<int32_t>(left_heartbeat)); + VendorSpeakerStatsReported obj[4]; + for (i = 0; i < count && i < 4; i++) { + obj[i].set_speaker_location(i); + obj[i].set_impedance(static_cast<int32_t>(impedance_ohm[i] * 1000)); + obj[i].set_max_temperature(static_cast<int32_t>(temperature_C[i] * 1000)); + obj[i].set_excursion(static_cast<int32_t>(excursion_mm[i] * 1000)); + obj[i].set_heartbeat(static_cast<int32_t>(heartbeat[i])); - VendorSpeakerStatsReported right_obj; - right_obj.set_speaker_location(1); - right_obj.set_impedance(static_cast<int32_t>(right_impedance_ohm * 1000)); - right_obj.set_max_temperature(static_cast<int32_t>(right_temperature_C * 1000)); - right_obj.set_excursion(static_cast<int32_t>(right_excursion_mm * 1000)); - right_obj.set_heartbeat(static_cast<int32_t>(right_heartbeat)); + reportSpeakerHealthStat(stats_client, obj[i]); + } +} - reportSpeakerHealthStat(stats_client, left_obj); - reportSpeakerHealthStat(stats_client, right_obj); +void SysfsCollector::logThermalStats(const std::shared_ptr<IStats> &stats_client) { + thermal_stats_reporter_.logThermalStats(stats_client, kThermalStatsPaths); } /** @@ -718,7 +726,7 @@ void SysfsCollector::logF2fsGcSegmentInfo(const std::shared_ptr<IStats> &stats_c tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_low); values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentLowFieldNumber - kVendorAtomOffset] = tmp; tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_mid); - values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentLowFieldNumber - kVendorAtomOffset] = tmp; + values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentMidFieldNumber - kVendorAtomOffset] = tmp; // Send vendor atom to IStats HAL VendorAtom event = {.reverseDomainName = "", @@ -812,6 +820,10 @@ void SysfsCollector::logBlockStatsReported(const std::shared_ptr<IStats> &stats_ } } +void SysfsCollector::logTempResidencyStats(const std::shared_ptr<IStats> &stats_client) { + temp_residency_reporter_.logTempResidencyStats(stats_client, kTempResidencyPath); +} + void SysfsCollector::reportZramMmStat(const std::shared_ptr<IStats> &stats_client) { std::string file_contents; if (!kZramMmStatPath) { @@ -969,6 +981,301 @@ void SysfsCollector::logBootStats(const std::shared_ptr<IStats> &stats_client) { } } +/** + * Report the AMS & CCA rate. + */ +void SysfsCollector::logVendorAudioHardwareStats(const std::shared_ptr<IStats> &stats_client) { + std::string file_contents; + uint32_t milli_ams_rate, cca_active_rate, cca_enable_rate; + bool isAmsReady = false, isCCAReady = false; + + if (kAmsRatePath == nullptr) { + ALOGD("Audio AMS Rate path not specified"); + } else { + if (!ReadFileToString(kAmsRatePath, &file_contents)) { + ALOGD("Unable to read ams_rate path %s", kAmsRatePath); + } else { + if (sscanf(file_contents.c_str(), "%u", &milli_ams_rate) != 1) { + ALOGD("Unable to parse ams_rate %s", file_contents.c_str()); + } else { + isAmsReady = true; + ALOGD("milli_ams_rate = %u", milli_ams_rate); + } + } + } + + if (kCCARatePath == nullptr) { + ALOGD("Audio CCA Rate path not specified"); + } else { + if (!ReadFileToString(kCCARatePath, &file_contents)) { + ALOGD("Unable to read cca_rate path %s", kCCARatePath); + } else { + if (sscanf(file_contents.c_str(), "%u,%u", &cca_active_rate, &cca_enable_rate) != 2) { + ALOGD("Unable to parse cca rates %s", file_contents.c_str()); + } else { + isCCAReady = true; + ALOGD("cca_active_rate = %u, cca_enable_rate = %u", cca_active_rate, + cca_enable_rate); + } + } + } + + if (!(isAmsReady || isCCAReady)) { + ALOGD("no ams or cca data to report"); + return; + } + + std::vector<VendorAtomValue> values(3); + VendorAtomValue tmp; + + if (isAmsReady) { + tmp.set<VendorAtomValue::intValue>(milli_ams_rate); + values[VendorAudioHardwareStatsReported::kMilliRateOfAmsPerDayFieldNumber - + kVendorAtomOffset] = tmp; + } + + if (isCCAReady) { + tmp.set<VendorAtomValue::intValue>(cca_active_rate); + values[VendorAudioHardwareStatsReported::kRateOfCcaActivePerDayFieldNumber - + kVendorAtomOffset] = tmp; + + tmp.set<VendorAtomValue::intValue>(cca_enable_rate); + values[VendorAudioHardwareStatsReported::kRateOfCcaEnablePerDayFieldNumber - + kVendorAtomOffset] = tmp; + } + + // Send vendor atom to IStats HAL + VendorAtom event = {.reverseDomainName = "", + .atomId = PixelAtoms::Atom::kVendorAudioHardwareStatsReported, + .values = std::move(values)}; + + const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); + if (!ret.isOk()) + ALOGE("Unable to report VendorAudioHardwareStatsReported to Stats service"); +} + +/** + * Logs the Resume Latency stats. + */ +void SysfsCollector::logVendorResumeLatencyStats(const std::shared_ptr<IStats> &stats_client) { + std::string file_contents; + if (!kResumeLatencyMetricsPath) { + ALOGE("ResumeLatencyMetrics path not specified"); + return; + } + if (!ReadFileToString(kResumeLatencyMetricsPath, &file_contents)) { + ALOGE("Unable to ResumeLatencyMetric %s - %s", kResumeLatencyMetricsPath, strerror(errno)); + return; + } + + int offset = 0; + int bytes_read; + const char *data = file_contents.c_str(); + int data_len = file_contents.length(); + + int curr_bucket_cnt; + if (!sscanf(data + offset, "Resume Latency Bucket Count: %d\n%n", &curr_bucket_cnt, + &bytes_read)) + return; + offset += bytes_read; + if (offset >= data_len) + return; + + int64_t max_latency; + if (!sscanf(data + offset, "Max Resume Latency: %ld\n%n", &max_latency, &bytes_read)) + return; + offset += bytes_read; + if (offset >= data_len) + return; + + uint64_t sum_latency; + if (!sscanf(data + offset, "Sum Resume Latency: %lu\n%n", &sum_latency, &bytes_read)) + return; + offset += bytes_read; + if (offset >= data_len) + return; + + if (curr_bucket_cnt > kMaxResumeLatencyBuckets) + return; + if (curr_bucket_cnt != prev_data.bucket_cnt) { + prev_data.resume_latency_buckets.clear(); + } + + int64_t total_latency_cnt = 0; + int64_t count; + int index = 2; + std::vector<VendorAtomValue> values(curr_bucket_cnt + 2); + VendorAtomValue tmp; + // Iterate over resume latency buckets to get latency count within some latency thresholds + while (sscanf(data + offset, "%*ld - %*ldms ====> %ld\n%n", &count, &bytes_read) == 1 || + sscanf(data + offset, "%*ld - infms ====> %ld\n%n", &count, &bytes_read) == 1) { + offset += bytes_read; + if (offset >= data_len && (index + 1 < curr_bucket_cnt + 2)) + return; + if (curr_bucket_cnt == prev_data.bucket_cnt) { + tmp.set<VendorAtomValue::longValue>(count - + prev_data.resume_latency_buckets[index - 2]); + prev_data.resume_latency_buckets[index - 2] = count; + } else { + tmp.set<VendorAtomValue::longValue>(count); + prev_data.resume_latency_buckets.push_back(count); + } + if (index >= curr_bucket_cnt + 2) + return; + values[index] = tmp; + index += 1; + total_latency_cnt += count; + } + tmp.set<VendorAtomValue::longValue>(max_latency); + values[0] = tmp; + if ((sum_latency - prev_data.resume_latency_sum_ms < 0) || + (total_latency_cnt - prev_data.resume_count <= 0)) { + tmp.set<VendorAtomValue::longValue>(-1); + ALOGI("average resume latency get overflow"); + } else { + tmp.set<VendorAtomValue::longValue>( + (int64_t)(sum_latency - prev_data.resume_latency_sum_ms) / + (total_latency_cnt - prev_data.resume_count)); + } + values[1] = tmp; + + prev_data.resume_latency_sum_ms = sum_latency; + prev_data.resume_count = total_latency_cnt; + prev_data.bucket_cnt = curr_bucket_cnt; + // Send vendor atom to IStats HAL + VendorAtom event = {.reverseDomainName = "", + .atomId = PixelAtoms::Atom::kVendorResumeLatencyStats, + .values = std::move(values)}; + const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); + if (!ret.isOk()) + ALOGE("Unable to report VendorResumeLatencyStats to Stats service"); +} + +bool cmp(const std::pair<int, int64_t> &a, const std::pair<int, int64_t> &b) { + return a.second > b.second; +} + +/** + * Sort irq stats by irq latency, and load top 5 irq stats. + */ +void process_irqatom_values(std::vector<std::pair<int, int64_t>> sorted_pair, + std::vector<VendorAtomValue> *values) { + VendorAtomValue tmp; + sort(sorted_pair.begin(), sorted_pair.end(), cmp); + int irq_stats_size = sorted_pair.size(); + for (int i = 0; i < 5; i++) { + if (irq_stats_size < 5 && i >= irq_stats_size) { + tmp.set<VendorAtomValue::longValue>(-1); + values->push_back(tmp); + tmp.set<VendorAtomValue::longValue>(0); + values->push_back(tmp); + } else { + tmp.set<VendorAtomValue::longValue>(sorted_pair[i].first); + values->push_back(tmp); + tmp.set<VendorAtomValue::longValue>(sorted_pair[i].second); + values->push_back(tmp); + } + } +} + +/** + * Logs the Long irq stats. + */ +void SysfsCollector::logVendorLongIRQStatsReported(const std::shared_ptr<IStats> &stats_client) { + std::string file_contents; + if (!kLongIRQMetricsPath) { + ALOGV("LongIRQ path not specified"); + return; + } + if (!ReadFileToString(kLongIRQMetricsPath, &file_contents)) { + ALOGE("Unable to LongIRQ %s - %s", kLongIRQMetricsPath, strerror(errno)); + return; + } + int offset = 0; + int bytes_read; + const char *data = file_contents.c_str(); + int data_len = file_contents.length(); + // Get, process, store softirq stats + std::vector<std::pair<int, int64_t>> sorted_softirq_pair; + int64_t softirq_count; + if (sscanf(data + offset, "long SOFTIRQ count: %ld\n%n", &softirq_count, &bytes_read) != 1) + return; + offset += bytes_read; + if (offset >= data_len) + return; + std::vector<VendorAtomValue> values; + VendorAtomValue tmp; + if (softirq_count - prev_data.softirq_count < 0) { + tmp.set<VendorAtomValue::intValue>(-1); + ALOGI("long softirq count get overflow"); + } else { + tmp.set<VendorAtomValue::longValue>(softirq_count - prev_data.softirq_count); + } + values.push_back(tmp); + + if (sscanf(data + offset, "long SOFTIRQ detail (num, latency):\n%n", &bytes_read) != 0) + return; + offset += bytes_read; + if (offset >= data_len) + return; + + // Iterate over softirq stats and record top 5 long softirq + int64_t softirq_latency; + int softirq_num; + while (sscanf(data + offset, "%d %ld\n%n", &softirq_num, &softirq_latency, &bytes_read) == 2) { + sorted_softirq_pair.push_back(std::make_pair(softirq_num, softirq_latency)); + offset += bytes_read; + if (offset >= data_len) + return; + } + process_irqatom_values(sorted_softirq_pair, &values); + + // Get, process, store irq stats + std::vector<std::pair<int, int64_t>> sorted_irq_pair; + int64_t irq_count; + if (sscanf(data + offset, "long IRQ count: %ld\n%n", &irq_count, &bytes_read) != 1) + return; + offset += bytes_read; + if (offset >= data_len) + return; + if (irq_count - prev_data.irq_count < 0) { + tmp.set<VendorAtomValue::intValue>(-1); + ALOGI("long irq count get overflow"); + } else { + tmp.set<VendorAtomValue::longValue>(irq_count - prev_data.irq_count); + } + values.push_back(tmp); + + if (sscanf(data + offset, "long IRQ detail (num, latency):\n%n", &bytes_read) != 0) + return; + offset += bytes_read; + if (offset >= data_len) + return; + + int64_t irq_latency; + int irq_num; + int index = 0; + // Iterate over softirq stats and record top 5 long irq + while (sscanf(data + offset, "%d %ld\n%n", &irq_num, &irq_latency, &bytes_read) == 2) { + sorted_irq_pair.push_back(std::make_pair(irq_num, irq_latency)); + offset += bytes_read; + if (offset >= data_len && index < 5) + return; + index += 1; + } + process_irqatom_values(sorted_irq_pair, &values); + + prev_data.softirq_count = softirq_count; + prev_data.irq_count = irq_count; + // Send vendor atom to IStats HAL + VendorAtom event = {.reverseDomainName = "", + .atomId = PixelAtoms::Atom::kVendorLongIrqStatsReported, + .values = std::move(values)}; + const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); + if (!ret.isOk()) + ALOGE("Unable to report kVendorLongIRQStatsReported to Stats service"); +} + void SysfsCollector::logPerDay() { const std::shared_ptr<IStats> stats_client = getStatsService(); if (!stats_client) { @@ -998,6 +1305,15 @@ void SysfsCollector::logPerDay() { logSpeakerHealthStats(stats_client); mm_metrics_reporter_.logCmaStatus(stats_client); mm_metrics_reporter_.logPixelMmMetricsPerDay(stats_client); + logVendorAudioHardwareStats(stats_client); + logThermalStats(stats_client); + logTempResidencyStats(stats_client); + logVendorLongIRQStatsReported(stats_client); + logVendorResumeLatencyStats(stats_client); +} + +void SysfsCollector::aggregatePer5Min() { + mm_metrics_reporter_.aggregatePixelMmMetricsPer5Min(); } void SysfsCollector::logPerHour() { @@ -1027,17 +1343,29 @@ void SysfsCollector::collect(void) { // Sleep for 30 seconds on launch to allow codec driver to load. sleep(30); + // sample & aggregate for the first time. + aggregatePer5Min(); + // Collect first set of stats on boot. logPerHour(); logPerDay(); - // Set an one-hour timer. struct itimerspec period; - const int kSecondsPerHour = 60 * 60; - int hours = 0; - period.it_interval.tv_sec = kSecondsPerHour; + + // gcd (greatest common divisor) of all the following timings + constexpr int kSecondsPerWake = 5 * 60; + + constexpr int kWakesPer5Min = 5 * 60 / kSecondsPerWake; + constexpr int kWakesPerHour = 60 * 60 / kSecondsPerWake; + constexpr int kWakesPerDay = 24 * 60 * 60 / kSecondsPerWake; + + int wake_5min = 0; + int wake_hours = 0; + int wake_days = 0; + + period.it_interval.tv_sec = kSecondsPerWake; period.it_interval.tv_nsec = 0; - period.it_value.tv_sec = kSecondsPerHour; + period.it_value.tv_sec = kSecondsPerWake; period.it_value.tv_nsec = 0; if (timerfd_settime(timerfd, 0, &period, NULL)) { @@ -1047,22 +1375,41 @@ void SysfsCollector::collect(void) { while (1) { int readval; - do { + union { char buf[8]; + uint64_t count; + } expire; + + do { errno = 0; - readval = read(timerfd, buf, sizeof(buf)); + readval = read(timerfd, expire.buf, sizeof(expire.buf)); } while (readval < 0 && errno == EINTR); if (readval < 0) { ALOGE("Timerfd error - %s\n", strerror(errno)); return; } - hours++; - logPerHour(); - if (hours == 24) { - // Collect stats every 24hrs after. + wake_5min += expire.count; + wake_hours += expire.count; + wake_days += expire.count; + + if (wake_5min >= kWakesPer5Min) { + wake_5min %= kWakesPer5Min; + aggregatePer5Min(); + } + + if (wake_hours >= kWakesPerHour) { + if (wake_hours >= 2 * kWakesPerHour) + ALOGW("Hourly wake: sleep too much: expire.count=%" PRId64, expire.count); + wake_hours %= kWakesPerHour; + logPerHour(); + } + + if (wake_days >= kWakesPerDay) { + if (wake_hours >= 2 * kWakesPerDay) + ALOGW("Daily wake: sleep too much: expire.count=%" PRId64, expire.count); + wake_days %= kWakesPerDay; logPerDay(); - hours = 0; } } } diff --git a/pixelstats/TempResidencyReporter.cpp b/pixelstats/TempResidencyReporter.cpp new file mode 100644 index 0000000..baa1d4a --- /dev/null +++ b/pixelstats/TempResidencyReporter.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "pixelstats: TempResidencyStats" + +#include <aidl/android/frameworks/stats/IStats.h> +#include <android-base/chrono_utils.h> +#include <android-base/file.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <android/binder_manager.h> +#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> +#include <pixelstats/TempResidencyReporter.h> +#include <utils/Log.h> + +#include <cinttypes> + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +using aidl::android::frameworks::stats::IStats; +using aidl::android::frameworks::stats::VendorAtom; +using aidl::android::frameworks::stats::VendorAtomValue; +using android::base::ReadFileToString; +using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats; + +/** + * Parse file_contents and read residency stats into cur_stats. + */ +bool parse_file_contents(std::string file_contents, + std::map<std::string, std::vector<int64_t>> *cur_stats) { + const char *data = file_contents.c_str(); + int data_len = file_contents.length(); + char sensor_name[32]; + int offset = 0; + int bytes_read; + + while (sscanf(data + offset, "THERMAL ZONE: %31s\n%n", sensor_name, &bytes_read) == 1) { + int64_t temp_res_value; + int num_stats_buckets; + int index = 0; + offset += bytes_read; + if (offset >= data_len) + return false; + + std::string sensor_name_str = sensor_name; + + if (!sscanf(data + offset, "NUM_TEMP_RESIDENCY_BUCKETS: %d\n%n", &num_stats_buckets, + &bytes_read)) + return false; + offset += bytes_read; + if (offset >= data_len) + return false; + while (index < num_stats_buckets) { + if (sscanf(data + offset, "-inf - %*d ====> %ldms\n%n", &temp_res_value, &bytes_read) != + 1 && + sscanf(data + offset, "%*d - %*d ====> %ldms\n%n", &temp_res_value, &bytes_read) != + 1 && + sscanf(data + offset, "%*d - inf ====> %ldms\n\n%n", &temp_res_value, + &bytes_read) != 1) + return false; + + (*cur_stats)[sensor_name_str].push_back(temp_res_value); + index++; + + offset += bytes_read; + if ((offset >= data_len) && (index < num_stats_buckets)) + return false; + } + } + return true; +} + +/** + * Logs the Temperature residency stats for every thermal zone. + */ +void TempResidencyReporter::logTempResidencyStats(const std::shared_ptr<IStats> &stats_client, + const char *const temperature_residency_path) { + if (!temperature_residency_path) { + ALOGV("TempResidencyStatsPath path not specified"); + return; + } + std::string file_contents; + if (!ReadFileToString(temperature_residency_path, &file_contents)) { + ALOGE("Unable to read TempResidencyStatsPath %s - %s", temperature_residency_path, + strerror(errno)); + return; + } + std::map<std::string, std::vector<int64_t>> cur_stats_map; + if (!parse_file_contents(file_contents, &cur_stats_map)) { + ALOGE("Fail to parse TempResidencyStatsPath"); + return; + } + if (!cur_stats_map.size()) + return; + ::android::base::boot_clock::time_point curTime = ::android::base::boot_clock::now(); + int64_t since_last_update_ms = + std::chrono::duration_cast<std::chrono::milliseconds>(curTime - prevTime).count(); + + auto cur_stats_map_iterator = cur_stats_map.begin(); + VendorAtomValue tmp_atom_value; + + // Iterate through cur_stats_map by sensor_name + while (cur_stats_map_iterator != cur_stats_map.end()) { + std::vector<VendorAtomValue> values; + std::string sensor_name_str = cur_stats_map_iterator->first; + std::vector<int64_t> residency_stats = cur_stats_map_iterator->second; + tmp_atom_value.set<VendorAtomValue::stringValue>(sensor_name_str); + values.push_back(tmp_atom_value); + tmp_atom_value.set<VendorAtomValue::longValue>(since_last_update_ms); + values.push_back(tmp_atom_value); + + bool key_in_map = (prev_stats.find(sensor_name_str)) != prev_stats.end(); + bool stat_len_match = (residency_stats.size() == prev_stats[sensor_name_str].size()); + if (key_in_map && !stat_len_match) + prev_stats[sensor_name_str].clear(); + + int64_t sum_residency = 0; + if (residency_stats.size() > kMaxBucketLen) { + cur_stats_map_iterator++; + continue; + } + // Iterate over every temperature residency buckets + for (int index = 0; index < residency_stats.size(); index++) { + // Get diff if stats arr length match previous stats + // Otherwise use raw stats as temperature residency stats per day + if (key_in_map && stat_len_match) { + int64_t diff_residency = + residency_stats[index] - prev_stats[sensor_name_str][index]; + tmp_atom_value.set<VendorAtomValue::longValue>(diff_residency); + sum_residency += diff_residency; + prev_stats[sensor_name_str][index] = residency_stats[index]; + } else { + tmp_atom_value.set<VendorAtomValue::longValue>(residency_stats[index]); + sum_residency += residency_stats[index]; + prev_stats[sensor_name_str].push_back(residency_stats[index]); + } + values.push_back(tmp_atom_value); + } + if (abs(since_last_update_ms - sum_residency) > kMaxResidencyDiffMs) + ALOGI("Thermal zone: %s Temperature residency stats not good!\ndevice sum_residency: " + "%ldms, since_last_update_ms %ldms\n", + sensor_name_str.c_str(), sum_residency, since_last_update_ms); + + // Send vendor atom to IStats HAL + VendorAtom event = {.reverseDomainName = "", + .atomId = PixelAtoms::Atom::kVendorTempResidencyStats, + .values = std::move(values)}; + ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); + if (!ret.isOk()) + ALOGE("Unable to report VendorTempResidencyStats to Stats service"); + + cur_stats_map_iterator++; + } + prevTime = curTime; +} + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/pixelstats/ThermalStatsReporter.cpp b/pixelstats/ThermalStatsReporter.cpp new file mode 100644 index 0000000..30fb30d --- /dev/null +++ b/pixelstats/ThermalStatsReporter.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "pixelstats: ThermalStats" + +#include <aidl/android/frameworks/stats/IStats.h> +#include <android-base/file.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <android/binder_manager.h> +#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> +#include <pixelstats/ThermalStatsReporter.h> +#include <utils/Log.h> + +#include <cinttypes> + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +using aidl::android::frameworks::stats::IStats; +using aidl::android::frameworks::stats::VendorAtom; +using aidl::android::frameworks::stats::VendorAtomValue; +using android::base::ReadFileToString; +using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats; + +ThermalStatsReporter::ThermalStatsReporter() {} + +bool ThermalStatsReporter::readDfsCount(const std::string &path, int64_t *val) { + std::string file_contents; + + if (path.empty()) { + ALOGE("Empty path"); + return false; + } + + if (!ReadFileToString(path.c_str(), &file_contents)) { + ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno)); + return false; + } else { + int64_t trips[8]; + + if (sscanf(file_contents.c_str(), + "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 + " %" SCNd64 " %" SCNd64, + &trips[0], &trips[1], &trips[2], &trips[3], &trips[4], &trips[5], &trips[6], + &trips[7]) < 8) { + ALOGE("Unable to parse trip_counters %s from file %s", file_contents.c_str(), + path.c_str()); + return false; + } + + /* Trip#6 corresponds to DFS count */ + *val = trips[6]; + } + + return true; +} + +bool ThermalStatsReporter::captureThermalDfsStats( + const std::vector<std::string> &thermal_stats_paths, struct ThermalDfsCounts *pcur_data) { + bool report_stats = false; + std::string path; + + if (thermal_stats_paths.size() < kNumOfThermalDfsStats) { + ALOGE("Number of thermal stats paths (%lu) is less than expected (%d)", + thermal_stats_paths.size(), kNumOfThermalDfsStats); + return false; + } + + path = thermal_stats_paths[ThermalDfsStats::kBigDfsCountFieldNumber - kVendorAtomOffset]; + if (!readDfsCount(path, &(pcur_data->big_count))) { + pcur_data->big_count = prev_data.big_count; + } else { + report_stats |= (pcur_data->big_count > prev_data.big_count); + } + + path = thermal_stats_paths[ThermalDfsStats::kMidDfsCountFieldNumber - kVendorAtomOffset]; + if (!readDfsCount(path, &(pcur_data->mid_count))) { + pcur_data->mid_count = prev_data.mid_count; + } else { + report_stats |= (pcur_data->mid_count > prev_data.mid_count); + } + + path = thermal_stats_paths[ThermalDfsStats::kLittleDfsCountFieldNumber - kVendorAtomOffset]; + if (!readDfsCount(path, &(pcur_data->little_count))) { + pcur_data->little_count = prev_data.little_count; + } else { + report_stats |= (pcur_data->little_count > prev_data.little_count); + } + + path = thermal_stats_paths[ThermalDfsStats::kGpuDfsCountFieldNumber - kVendorAtomOffset]; + if (!readDfsCount(path, &(pcur_data->gpu_count))) { + pcur_data->gpu_count = prev_data.gpu_count; + } else { + report_stats |= (pcur_data->gpu_count > prev_data.gpu_count); + } + + path = thermal_stats_paths[ThermalDfsStats::kTpuDfsCountFieldNumber - kVendorAtomOffset]; + if (!readDfsCount(path, &(pcur_data->tpu_count))) { + pcur_data->tpu_count = prev_data.tpu_count; + } else { + report_stats |= (pcur_data->tpu_count > prev_data.tpu_count); + } + + path = thermal_stats_paths[ThermalDfsStats::kAurDfsCountFieldNumber - kVendorAtomOffset]; + if (!readDfsCount(path, &(pcur_data->aur_count))) { + pcur_data->aur_count = prev_data.aur_count; + } else { + report_stats |= (pcur_data->aur_count > prev_data.aur_count); + } + + return report_stats; +} + +void ThermalStatsReporter::logThermalDfsStats(const std::shared_ptr<IStats> &stats_client, + const std::vector<std::string> &thermal_stats_paths) { + struct ThermalDfsCounts cur_data = prev_data; + + if (!captureThermalDfsStats(thermal_stats_paths, &cur_data)) { + prev_data = cur_data; + ALOGI("No update found for thermal stats"); + return; + } + + VendorAtomValue tmp; + int64_t max_dfs_count = static_cast<int64_t>(INT32_MAX); + int dfs_count; + std::vector<VendorAtomValue> values(kNumOfThermalDfsStats); + + dfs_count = std::min<int64_t>(cur_data.big_count - prev_data.big_count, max_dfs_count); + tmp.set<VendorAtomValue::intValue>(dfs_count); + values[ThermalDfsStats::kBigDfsCountFieldNumber - kVendorAtomOffset] = tmp; + + dfs_count = std::min<int64_t>(cur_data.mid_count - prev_data.mid_count, max_dfs_count); + tmp.set<VendorAtomValue::intValue>(dfs_count); + values[ThermalDfsStats::kMidDfsCountFieldNumber - kVendorAtomOffset] = tmp; + + dfs_count = std::min<int64_t>(cur_data.little_count - prev_data.little_count, max_dfs_count); + tmp.set<VendorAtomValue::intValue>(dfs_count); + values[ThermalDfsStats::kLittleDfsCountFieldNumber - kVendorAtomOffset] = tmp; + + dfs_count = std::min<int64_t>(cur_data.gpu_count - prev_data.gpu_count, max_dfs_count); + tmp.set<VendorAtomValue::intValue>(dfs_count); + values[ThermalDfsStats::kGpuDfsCountFieldNumber - kVendorAtomOffset] = tmp; + + dfs_count = std::min<int64_t>(cur_data.tpu_count - prev_data.tpu_count, max_dfs_count); + tmp.set<VendorAtomValue::intValue>(dfs_count); + values[ThermalDfsStats::kTpuDfsCountFieldNumber - kVendorAtomOffset] = tmp; + + dfs_count = std::min<int64_t>(cur_data.aur_count - prev_data.aur_count, max_dfs_count); + tmp.set<VendorAtomValue::intValue>(dfs_count); + values[ThermalDfsStats::kAurDfsCountFieldNumber - kVendorAtomOffset] = tmp; + + prev_data = cur_data; + + ALOGD("Report updated thermal metrics to stats service"); + // Send vendor atom to IStats HAL + VendorAtom event = {.reverseDomainName = "", + .atomId = PixelAtoms::Atom::kThermalDfsStats, + .values = std::move(values)}; + const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); + if (!ret.isOk()) + ALOGE("Unable to report thermal DFS stats to Stats service"); +} + +void ThermalStatsReporter::logThermalStats(const std::shared_ptr<IStats> &stats_client, + const std::vector<std::string> &thermal_stats_paths) { + logThermalDfsStats(stats_client, thermal_stats_paths); +} + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/pixelstats/include/pixelstats/MmMetricsReporter.h b/pixelstats/include/pixelstats/MmMetricsReporter.h index 89f12f5..cfcfac3 100644 --- a/pixelstats/include/pixelstats/MmMetricsReporter.h +++ b/pixelstats/include/pixelstats/MmMetricsReporter.h @@ -37,6 +37,7 @@ using aidl::android::frameworks::stats::VendorAtomValue; class MmMetricsReporter { public: MmMetricsReporter(); + void aggregatePixelMmMetricsPer5Min(); void logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client); void logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client); void logCmaStatus(const std::shared_ptr<IStats> &stats_client); @@ -62,6 +63,43 @@ class MmMetricsReporter { static const std::vector<MmMetricsInfo> kCmaStatusInfo; static const std::vector<MmMetricsInfo> kCmaStatusExtInfo; + // raw PSI + static constexpr const char *kPsiBasePath = "/proc/pressure"; + static constexpr const char *kPsiTypes[3] = {"cpu", "io", "memory"}; + static constexpr const char *kPsiCategories[2] = {"full", "some"}; + static constexpr const char *kPsiMetricNames[4] = {"avg10", "avg60", "avg300", "total"}; + static constexpr int kPsiNumFiles = sizeof(kPsiTypes) / sizeof(kPsiTypes[0]); + static constexpr int kPsiNumCategories = sizeof(kPsiCategories) / sizeof(kPsiCategories[0]); + // number of statistics metric names (one total and several timed averages, per category) + static constexpr int kPsiNumNames = sizeof(kPsiMetricNames) / sizeof(kPsiMetricNames[0]); + + // Though cpu has no 'full' category, here we assume it has + // So, all file will contain 2 lines x 4 metrics per line = 8 metrics total. + static constexpr int kPsiMetricsPerFile = kPsiNumCategories * kPsiNumNames; + + // we have 1 'total' and all others 'averages' per category + // "total" metrics are already accumulative and thus no aggregation is needed. + // raw values are used. + static constexpr int kPsiNumTotals = 1; + static constexpr int kPsiNumAvgs = kPsiNumNames - kPsiNumTotals; + + // -1 since "cpu" type has no "full" category + static constexpr int kPsiNumAllCategories = kPsiNumFiles * kPsiNumCategories - 1; + + // number of raw metrics: total and avgs, and the combined all: added together. + static constexpr int kPsiNumAllTotals = kPsiNumAllCategories * kPsiNumTotals; + static constexpr int kPsiNumAllAvgs = kPsiNumAllCategories * kPsiNumAvgs; + static constexpr int kPsiNumAllMetrics = kPsiNumAllTotals + kPsiNumAllAvgs; + + // aggregated into (1) min, (2) max, (3) average (internally the sum is kept than the average) + static constexpr int kPsiNumOfAggregatedType = 3; + + // # of upload metrics will have a aggregation factor on all 'average' type raw metrics. + static constexpr int kPsiNumAllUploadAvgMetrics = kPsiNumAllAvgs * kPsiNumOfAggregatedType; + static constexpr int kPsiNumAllUploadTotalMetrics = kPsiNumAllTotals; + static constexpr int kPsiNumAllUploadMetrics = + kPsiNumAllUploadTotalMetrics + kPsiNumAllUploadAvgMetrics; + bool checkKernelMMMetricSupport(); bool MmMetricsSupported() { @@ -80,6 +118,21 @@ class MmMetricsReporter { bool ReadFileToUint(const char *const path, uint64_t *val); bool reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id, const std::vector<VendorAtomValue> &values, const std::string &atom_name); + void readCompactionDurationStat(std::vector<long> *store); + void fillCompactionDurationStatAtom(const std::vector<long> &store, + std::vector<VendorAtomValue> *values); + void readDirectReclaimStat(std::vector<long> *store); + void fillDirectReclaimStatAtom(const std::vector<long> &store, + std::vector<VendorAtomValue> *values); + void readPressureStall(const char *basePath, std::vector<long> *store); + bool parsePressureStallFileContent(bool is_cpu, std::string lines, std::vector<long> *store, + int file_save_idx); + bool parsePressureStallWords(std::vector<std::string> words, std::vector<long> *store, + int line_save_idx); + bool savePressureMetrics(std::string name, std::string value, std::vector<long> *store, + int base_save_idx); + void fillPressureStallAtom(std::vector<VendorAtomValue> *values); + void aggregatePressureStall(); std::map<std::string, uint64_t> readVmStat(const char *path); uint64_t getIonTotalPools(); uint64_t getGpuMemory(); @@ -103,12 +156,22 @@ class MmMetricsReporter { const char *const kIonTotalPoolsPath; const char *const kIonTotalPoolsPathForLegacy; const char *const kGpuTotalPages; + const char *const kCompactDuration; + const char *const kDirectReclaimBasePath; const char *const kPixelStatMm; // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so // store everything in the values array at the index of the field number // -2. - const int kVendorAtomOffset = 2; - + static constexpr int kVendorAtomOffset = 2; + static constexpr int kNumCompactionDurationPrevMetrics = 6; + static constexpr int kNumDirectReclaimPrevMetrics = 20; + + std::vector<long> prev_compaction_duration_; + std::vector<long> prev_direct_reclaim_; + long prev_psi_total_[kPsiNumAllTotals]; + long psi_total_[kPsiNumAllTotals]; + long psi_aggregated_[kPsiNumAllUploadAvgMetrics]; // min, max and avg of original avgXXX + int psi_data_set_count_ = 0; std::map<std::string, uint64_t> prev_hour_vmstat_; std::map<std::string, uint64_t> prev_day_vmstat_; std::map<std::string, uint64_t> prev_day_pixel_vmstat_; diff --git a/pixelstats/include/pixelstats/SysfsCollector.h b/pixelstats/include/pixelstats/SysfsCollector.h index 0a772ce..df4a331 100644 --- a/pixelstats/include/pixelstats/SysfsCollector.h +++ b/pixelstats/include/pixelstats/SysfsCollector.h @@ -24,6 +24,8 @@ #include "BatteryHealthReporter.h" #include "MitigationStatsReporter.h" #include "MmMetricsReporter.h" +#include "TempResidencyReporter.h" +#include "ThermalStatsReporter.h" namespace android { namespace hardware { @@ -61,6 +63,12 @@ class SysfsCollector { const char *const SpeakerHeartBeatPath; const std::vector<std::string> UFSErrStatsPath; const int BlockStatsLength; + const char *const AmsRatePath; + const std::vector<std::string> ThermalStatsPaths; + const char *const CCARatePath; + const char *const TempResidencyPath; + const char *const LongIRQMetricsPath; + const char *const ResumeLatencyMetricsPath; }; SysfsCollector(const struct SysfsPaths &paths); @@ -69,6 +77,7 @@ class SysfsCollector { private: bool ReadFileToInt(const std::string &path, int *val); bool ReadFileToInt(const char *path, int *val); + void aggregatePer5Min(); void logPerDay(); void logPerHour(); @@ -91,12 +100,17 @@ class SysfsCollector { void logBatteryEEPROM(const std::shared_ptr<IStats> &stats_client); void logSpeakerHealthStats(const std::shared_ptr<IStats> &stats_client); void logF2fsSmartIdleMaintEnabled(const std::shared_ptr<IStats> &stats_client); + void logThermalStats(const std::shared_ptr<IStats> &stats_client); void reportSlowIoFromFile(const std::shared_ptr<IStats> &stats_client, const char *path, const VendorSlowIo::IoOperation &operation_s); + void logTempResidencyStats(const std::shared_ptr<IStats> &stats_client); void reportZramMmStat(const std::shared_ptr<IStats> &stats_client); void reportZramBdStat(const std::shared_ptr<IStats> &stats_client); int getReclaimedSegments(const std::string &mode); + void logVendorAudioHardwareStats(const std::shared_ptr<IStats> &stats_client); + void logVendorLongIRQStatsReported(const std::shared_ptr<IStats> &stats_client); + void logVendorResumeLatencyStats(const std::shared_ptr<IStats> &stats_client); const char *const kSlowioReadCntPath; const char *const kSlowioWriteCntPath; @@ -122,12 +136,19 @@ class SysfsCollector { const char *const kSpeakerHeartbeatPath; const std::vector<std::string> kUFSErrStatsPath; const int kBlockStatsLength; + const char *const kAmsRatePath; + const std::vector<std::string> kThermalStatsPaths; + const char *const kCCARatePath; + const char *const kTempResidencyPath; + const char *const kLongIRQMetricsPath; + const char *const kResumeLatencyMetricsPath; BatteryEEPROMReporter battery_EEPROM_reporter_; MmMetricsReporter mm_metrics_reporter_; MitigationStatsReporter mitigation_stats_reporter_; + ThermalStatsReporter thermal_stats_reporter_; BatteryHealthReporter battery_health_reporter_; - + TempResidencyReporter temp_residency_reporter_; // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so // store everything in the values array at the index of the field number // -2. @@ -135,6 +156,17 @@ class SysfsCollector { bool log_once_reported = false; int64_t prev_huge_pages_since_boot_ = -1; + + struct perf_metrics_data { + int64_t softirq_count; + int64_t irq_count; + uint64_t resume_latency_sum_ms; + int64_t resume_count; + std::vector<int64_t> resume_latency_buckets; + int bucket_cnt; + }; + struct perf_metrics_data prev_data; + const int kMaxResumeLatencyBuckets = 36; }; } // namespace pixel diff --git a/pixelstats/include/pixelstats/TempResidencyReporter.h b/pixelstats/include/pixelstats/TempResidencyReporter.h new file mode 100644 index 0000000..07e3343 --- /dev/null +++ b/pixelstats/include/pixelstats/TempResidencyReporter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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 <aidl/android/frameworks/stats/IStats.h> +#include <android-base/chrono_utils.h> +#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> + +#include <string> + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +using aidl::android::frameworks::stats::IStats; +using aidl::android::frameworks::stats::VendorAtomValue; + +/** + * A class to upload Pixel TempResidency Stats metrics + */ +class TempResidencyReporter { + public: + void logTempResidencyStats(const std::shared_ptr<IStats> &stats_client, + const char *const temperature_residency_path); + + private: + std::map<std::string, std::vector<int64_t>> prev_stats; + ::android::base::boot_clock::time_point prevTime = + ::android::base::boot_clock::time_point::min(); + const int kMaxBucketLen = 20; + const int kMaxResidencyDiffMs = 3000; +}; + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/pixelstats/include/pixelstats/ThermalStatsReporter.h b/pixelstats/include/pixelstats/ThermalStatsReporter.h new file mode 100644 index 0000000..57f246e --- /dev/null +++ b/pixelstats/include/pixelstats/ThermalStatsReporter.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef HARDWARE_GOOGLE_PIXEL_PIXELSTATS_THERMALSTATSREPORTER_H +#define HARDWARE_GOOGLE_PIXEL_PIXELSTATS_THERMALSTATSREPORTER_H + +#include <aidl/android/frameworks/stats/IStats.h> +#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> + +#include <string> + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +using aidl::android::frameworks::stats::IStats; +using aidl::android::frameworks::stats::VendorAtomValue; + +/** + * A class to upload Pixel Thermal Stats metrics + */ +class ThermalStatsReporter { + public: + ThermalStatsReporter(); + void logThermalStats(const std::shared_ptr<IStats> &stats_client, + const std::vector<std::string> &thermal_stats_paths); + + private: + struct ThermalDfsCounts { + int64_t big_count; + int64_t mid_count; + int64_t little_count; + int64_t gpu_count; + int64_t tpu_count; + int64_t aur_count; + }; + + // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so + // store everything in the values array at the index of the field number + // -2. + const int kVendorAtomOffset = 2; + const int kNumOfThermalDfsStats = 6; + struct ThermalDfsCounts prev_data; + + void logThermalDfsStats(const std::shared_ptr<IStats> &stats_client, + const std::vector<std::string> &thermal_stats_paths); + bool captureThermalDfsStats(const std::vector<std::string> &thermal_stats_paths, + struct ThermalDfsCounts *cur_data); + bool readDfsCount(const std::string &path, int64_t *val); +}; + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android + +#endif // HARDWARE_GOOGLE_PIXEL_PIXELSTATS_THERMALSTATSREPORTER_H diff --git a/pixelstats/pixelatoms.proto b/pixelstats/pixelatoms.proto index be08192..433768a 100644 --- a/pixelstats/pixelatoms.proto +++ b/pixelstats/pixelatoms.proto @@ -81,6 +81,12 @@ message Atom { BatteryHealthUsage battery_health_usage = 105038; F2fsSmartIdleMaintEnabledStateChanged f2fs_smart_idle_maint_enabled_state_changed = 105039; BlockStatsReported block_stats_reported = 105040; + VendorAudioHardwareStatsReported vendor_audio_hardware_stats_reported = 105041; + + ThermalDfsStats thermal_dfs_stats = 105042; + VendorLongIRQStatsReported vendor_long_irq_stats_reported = 105043; + VendorResumeLatencyStats vendor_resume_latency_stats = 105044; + VendorTempResidencyStats vendor_temp_residency_stats = 105045; } // AOSP atom ID range ends at 109999 } @@ -525,12 +531,63 @@ message PixelMmMetricsPerHour { optional int64 unevictable = 7; optional int64 ion_total_pools = 8; optional int64 gpu_memory = 9; + optional int64 slab_unreclaimable = 10; + optional int64 psi_cpu_some_total = 11; + optional int64 psi_io_full_total = 12; + optional int64 psi_io_some_total = 13; + optional int64 psi_mem_full_total = 14; + optional int64 psi_mem_some_total = 15; + optional int32 psi_cpu_some_avg10_min = 16; + optional int32 psi_cpu_some_avg10_max = 17; + optional int32 psi_cpu_some_avg10_avg = 18; + optional int32 psi_cpu_some_avg60_min = 19; + optional int32 psi_cpu_some_avg60_max = 20; + optional int32 psi_cpu_some_avg60_avg = 21; + optional int32 psi_cpu_some_avg300_min = 22; + optional int32 psi_cpu_some_avg300_max = 23; + optional int32 psi_cpu_some_avg300_avg = 24; + optional int32 psi_io_full_avg10_min = 25; + optional int32 psi_io_full_avg10_max = 26; + optional int32 psi_io_full_avg10_avg = 27; + optional int32 psi_io_full_avg60_min = 28; + optional int32 psi_io_full_avg60_max = 29; + optional int32 psi_io_full_avg60_avg = 30; + optional int32 psi_io_full_avg300_min = 31; + optional int32 psi_io_full_avg300_max = 32; + optional int32 psi_io_full_avg300_avg = 33; + optional int32 psi_io_some_avg10_min = 34; + optional int32 psi_io_some_avg10_max = 35; + optional int32 psi_io_some_avg10_avg = 36; + optional int32 psi_io_some_avg60_min = 37; + optional int32 psi_io_some_avg60_max = 38; + optional int32 psi_io_some_avg60_avg = 39; + optional int32 psi_io_some_avg300_min = 40; + optional int32 psi_io_some_avg300_max = 41; + optional int32 psi_io_some_avg300_avg = 42; + optional int32 psi_mem_full_avg10_min = 43; + optional int32 psi_mem_full_avg10_max = 44; + optional int32 psi_mem_full_avg10_avg = 45; + optional int32 psi_mem_full_avg60_min = 46; + optional int32 psi_mem_full_avg60_max = 47; + optional int32 psi_mem_full_avg60_avg = 48; + optional int32 psi_mem_full_avg300_min = 49; + optional int32 psi_mem_full_avg300_max = 50; + optional int32 psi_mem_full_avg300_avg = 51; + optional int32 psi_mem_some_avg10_min = 52; + optional int32 psi_mem_some_avg10_max = 53; + optional int32 psi_mem_some_avg10_avg = 54; + optional int32 psi_mem_some_avg60_min = 55; + optional int32 psi_mem_some_avg60_max = 56; + optional int32 psi_mem_some_avg60_avg = 57; + optional int32 psi_mem_some_avg300_min = 58; + optional int32 psi_mem_some_avg300_max = 59; + optional int32 psi_mem_some_avg300_avg = 60; } /* A message containing Pixel memory metrics collected daily. */ message PixelMmMetricsPerDay { optional string reverse_domain_name = 1; - optional int64 workingset_refault = 2; + optional int64 workingset_refault = 2; /* refault_file */ optional int64 pswpin = 3; optional int64 pswpout = 4; optional int64 allocstall_dma = 5; @@ -551,6 +608,43 @@ message PixelMmMetricsPerDay { optional int64 pgcache_miss = 20; optional int64 kswapd_stime_clks = 21; optional int64 kcompactd_stime_clks = 22; + optional int64 direct_reclaim_native_latency_total_time = 23; + optional int64 direct_reclaim_native_latency0 = 24; + optional int64 direct_reclaim_native_latency1 = 25; + optional int64 direct_reclaim_native_latency2 = 26; + optional int64 direct_reclaim_native_latency3 = 27; + optional int64 direct_reclaim_visible_latency_total_time = 28; + optional int64 direct_reclaim_visible_latency0 = 29; + optional int64 direct_reclaim_visible_latency1 = 30; + optional int64 direct_reclaim_visible_latency2 = 31; + optional int64 direct_reclaim_visible_latency3 = 32; + optional int64 direct_reclaim_top_latency_total_time = 33; + optional int64 direct_reclaim_top_latency0 = 34; + optional int64 direct_reclaim_top_latency1 = 35; + optional int64 direct_reclaim_top_latency2 = 36; + optional int64 direct_reclaim_top_latency3 = 37; + optional int64 direct_reclaim_other_latency_total_time = 38; + optional int64 direct_reclaim_other_latency0 = 39; + optional int64 direct_reclaim_other_latency1 = 40; + optional int64 direct_reclaim_other_latency2 = 41; + optional int64 direct_reclaim_other_latency3 = 42; + optional int64 compaction_total_time = 43; + optional int64 compaction_ev_count0 = 44; + optional int64 compaction_ev_count1 = 45; + optional int64 compaction_ev_count2 = 46; + optional int64 compaction_ev_count3 = 47; + optional int64 compaction_ev_count4 = 48; + optional int64 workingset_refault_anon = 49; + optional int64 workingset_refault_file = 50; + optional int64 compact_success = 51; + optional int64 compact_fail = 52; + optional int64 kswapd_low_wmark_hq = 53; + optional int64 kswapd_high_wmark_hq = 54; + optional int64 thp_file_alloc = 55; + optional int64 thp_zero_page_alloc = 56; + optional int64 thp_split_page = 57; + optional int64 thp_migration_split = 58; + optional int64 thp_deferred_split_page = 59; } /* A message containing CMA metrics collected from dogfooding only. */ @@ -814,6 +908,25 @@ message PowerMitigationStats { } /** + * Log thermal statistics. + */ +message ThermalDfsStats { + optional string reverse_domain_name = 1; + // The last count of BIG cluster dfs triggers + optional int32 big_dfs_count = 2; + // The last count of MID cluster dfs triggers + optional int32 mid_dfs_count = 3; + // The last count of LITTLE cluster dfs triggers + optional int32 little_dfs_count = 4; + // The last count of GPU dfs triggers + optional int32 gpu_dfs_count = 5; + // The last count of TPU dfs triggers + optional int32 tpu_dfs_count = 6; + // The last count of DSP dfs triggers + optional int32 aur_dfs_count = 7; +} + +/** * Log how many segments have been reclaimed in a specific GC mode. */ message F2fsGcSegmentInfo { @@ -1048,11 +1161,11 @@ message BatteryHealthStatus { optional int32 health_algorithm = 2; enum HealthStatus { - UNKNOWN = 0; // The health status is unknown due to a SW limitation or issue - NOMINAL = 1; // The battery is operating as expected - MARGINAL = 2; // The battery may need replacement soon - NEEDS_REPLACEMENT = 3; // The battery needs replacement - FAILED = 4; // The battery has failed and no longer operates as expected + UNKNOWN = -1; // The health status is unknown due to a SW limitation or issue + NOMINAL = 0; // The battery is operating as expected + MARGINAL = 1; // The battery may need replacement soon + NEEDS_REPLACEMENT = 2; // The battery needs replacement + FAILED = 3; // The battery has failed and no longer operates as expected } // HealthStatus calculated using health_index, health_perf_index. @@ -1147,3 +1260,154 @@ message BlockStatsReported { /* total wait time for write requests */ optional int64 write_ticks = 7; } + +/** + * Logs the reported vendor audio hardware stats. + */ +message VendorAudioHardwareStatsReported { + optional string reverse_domain_name = 1; + /* The percentage of calls in a day where auto-mic-switch triggered. + * It represented as a fixed-point integer with three decimal place. + * E.g.:12.345% is repsented by 12345. + */ + optional int32 milli_rate_of_ams_per_day = 2; + + /* The percentage of calls in a day where CCA is active. + * It represented as a fixed-point and rounded integer. + * E.g.:12.345% is represented by 12. + * CCA can only be applied under some radio bands. + */ + + /* cca_active: UI enable & algorithm is active (C1) */ + optional int32 rate_of_cca_active_per_day = 3; + + /* cca_enable: UI enable & algorithm is inactive. (C2) */ + optional int32 rate_of_cca_enable_per_day = 4; +} + +/* + * Logs vendor stats about long IRQs. + * + * IRQ is considered long when it exceeds a threshold (currently 1 ms). + * Stats include top 5 slowest IRQs: their numbers and the worst latency. + * Stats are reset after every report. + */ +message VendorLongIRQStatsReported { + optional string reverse_domain_name = 1; + + // Count of long soft IRQ since last report. + optional int64 long_softirq_count = 2; + + optional int64 top1_softirq_num = 3; + optional int64 top1_softirq_latency_us = 4; + optional int64 top2_softirq_num = 5; + optional int64 top2_softirq_latency_us = 6; + optional int64 top3_softirq_num = 7; + optional int64 top3_softirq_latency_us = 8; + optional int64 top4_softirq_num = 9; + optional int64 top4_softirq_latency_us = 10; + optional int64 top5_softirq_num = 11; + optional int64 top5_softirq_latency_us = 12; + + // Count of long IRQ since last report. + optional int64 long_irq_count = 13; + + optional int64 top1_irq_num = 14; + optional int64 top1_irq_latency_us = 15; + optional int64 top2_irq_num = 16; + optional int64 top2_irq_latency_us = 17; + optional int64 top3_irq_num = 18; + optional int64 top3_irq_latency_us = 19; + optional int64 top4_irq_num = 20; + optional int64 top4_irq_latency_us = 21; + optional int64 top5_irq_num = 22; + optional int64 top5_irq_latency_us = 23; +} + +/** + * Logs the Temperature residency stats per thermal zone. + */ +message VendorTempResidencyStats { + optional string reverse_domain_name = 1; + // Thermal zone name + optional string sensor_name = 2; + + // Time since last collect of this thermal zone + optional int64 since_last_update_ms = 3; + + // Temperature residency stats is measured by time in ms that a temperature zone's temperature + // lay within some temperature thresholds + // e.g. + // With temperature thresholds predefined as thresholds_i, thresholds_i+1, + // temp_residency_ms_bucket_i measures how much time the sensor lay within this two thresholds + optional int64 temp_residency_ms_bucket_1 = 4; + optional int64 temp_residency_ms_bucket_2 = 5; + optional int64 temp_residency_ms_bucket_3 = 6; + optional int64 temp_residency_ms_bucket_4 = 7; + optional int64 temp_residency_ms_bucket_5 = 8; + optional int64 temp_residency_ms_bucket_6 = 9; + optional int64 temp_residency_ms_bucket_7 = 10; + optional int64 temp_residency_ms_bucket_8 = 11; + optional int64 temp_residency_ms_bucket_9 = 12; + optional int64 temp_residency_ms_bucket_10 = 13; + optional int64 temp_residency_ms_bucket_11 = 14; + optional int64 temp_residency_ms_bucket_12 = 15; + optional int64 temp_residency_ms_bucket_13 = 16; + optional int64 temp_residency_ms_bucket_14 = 17; + optional int64 temp_residency_ms_bucket_15 = 18; + optional int64 temp_residency_ms_bucket_16 = 19; + optional int64 temp_residency_ms_bucket_17 = 20; + optional int64 temp_residency_ms_bucket_18 = 21; + optional int64 temp_residency_ms_bucket_19 = 22; + optional int64 temp_residency_ms_bucket_20 = 23; +} + +/** + * Logs the Resume Latency stats. + */ +message VendorResumeLatencyStats { + optional string reverse_domain_name = 1; + optional int64 max_latency_ms = 2; + optional int64 avg_latency_ms = 3; + + // Resume Latency stats is measured by count of resumes that lay within some latency thresholds + // e.g. + // With resume times thresholds predefined as thresholds_i, thresholds_i+1, + // resume_count_bucket_i measures count of resumes that lay within this two thresholds + optional int64 resume_count_bucket_1 = 4; + optional int64 resume_count_bucket_2 = 5; + optional int64 resume_count_bucket_3 = 6; + optional int64 resume_count_bucket_4 = 7; + optional int64 resume_count_bucket_5 = 8; + optional int64 resume_count_bucket_6 = 9; + optional int64 resume_count_bucket_7 = 10; + optional int64 resume_count_bucket_8 = 11; + optional int64 resume_count_bucket_9 = 12; + optional int64 resume_count_bucket_10 = 13; + optional int64 resume_count_bucket_11 = 14; + optional int64 resume_count_bucket_12 = 15; + optional int64 resume_count_bucket_13 = 16; + optional int64 resume_count_bucket_14 = 17; + optional int64 resume_count_bucket_15 = 18; + optional int64 resume_count_bucket_16 = 19; + optional int64 resume_count_bucket_17 = 20; + optional int64 resume_count_bucket_18 = 21; + optional int64 resume_count_bucket_19 = 22; + optional int64 resume_count_bucket_20 = 23; + optional int64 resume_count_bucket_21 = 24; + optional int64 resume_count_bucket_22 = 25; + optional int64 resume_count_bucket_23 = 26; + optional int64 resume_count_bucket_24 = 27; + optional int64 resume_count_bucket_25 = 28; + optional int64 resume_count_bucket_26 = 29; + optional int64 resume_count_bucket_27 = 30; + optional int64 resume_count_bucket_28 = 31; + optional int64 resume_count_bucket_29 = 32; + optional int64 resume_count_bucket_30 = 33; + optional int64 resume_count_bucket_31 = 34; + optional int64 resume_count_bucket_32 = 35; + optional int64 resume_count_bucket_33 = 36; + optional int64 resume_count_bucket_34 = 37; + optional int64 resume_count_bucket_35 = 38; + optional int64 resume_count_bucket_36 = 39; +} diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp index cac641a..14cbf01 100644 --- a/power-libperfmgr/aidl/PowerHintSession.cpp +++ b/power-libperfmgr/aidl/PowerHintSession.cpp @@ -265,14 +265,10 @@ ndk::ScopedAStatus PowerHintSession::close() { } // Remove the session from PowerSessionManager first to avoid racing. PowerSessionManager::getInstance()->removePowerSession(this); - setSessionUclampMin(0); - { - std::lock_guard<std::mutex> guard(mSessionLock); - mSessionClosed.store(true); - } - mDescriptor->is_active.store(false); mEarlyBoostHandler->setSessionDead(); mStaleTimerHandler->setSessionDead(); + setSessionUclampMin(0); + mDescriptor->is_active.store(false); updateUniveralBoostMode(); return ndk::ScopedAStatus::ok(); } @@ -414,24 +410,29 @@ void PowerHintSession::setStale() { void PowerHintSession::wakeup() { std::lock_guard<std::mutex> guard(mSessionLock); - // We only wake up non-paused and stale sessions - if (mSessionClosed || !isActive() || !isTimeout()) + // We only wake up non-paused session + if (mSessionClosed || !isActive()) { return; + } + // Update session's timer + mStaleTimerHandler->updateTimer(); + // Skip uclamp update for stale session + if (!isTimeout()) { + return; + } if (ATRACE_ENABLED()) { std::string tag = StringPrintf("wakeup.%s(a:%d,s:%d)", getIdString().c_str(), isActive(), isTimeout()); ATRACE_NAME(tag.c_str()); } std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile(); - int min = std::max(mDescriptor->current_min, static_cast<int>(adpfConfig->mUclampMinInit)); - mDescriptor->current_min = min; - PowerSessionManager::getInstance()->setUclampMinLocked(this, min); - mStaleTimerHandler->updateTimer(); + mDescriptor->current_min = + std::max(mDescriptor->current_min, static_cast<int>(adpfConfig->mUclampMinInit)); if (ATRACE_ENABLED()) { const std::string idstr = getIdString(); std::string sz = StringPrintf("adpf.%s-min", idstr.c_str()); - ATRACE_INT(sz.c_str(), min); + ATRACE_INT(sz.c_str(), mDescriptor->current_min); } } @@ -500,6 +501,7 @@ void PowerHintSession::StaleTimerHandler::updateTimer(time_point<steady_clock> s } void PowerHintSession::StaleTimerHandler::handleMessage(const Message &) { + std::lock_guard<std::mutex> guard(mClosedLock); if (mIsSessionDead) { return; } @@ -529,7 +531,7 @@ void PowerHintSession::StaleTimerHandler::handleMessage(const Message &) { } void PowerHintSession::StaleTimerHandler::setSessionDead() { - std::lock_guard<std::mutex> guard(mStaleLock); + std::lock_guard<std::mutex> guard(mClosedLock); mIsSessionDead = true; PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler); } diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h index 96b445e..9bd9a2c 100644 --- a/power-libperfmgr/aidl/PowerHintSession.h +++ b/power-libperfmgr/aidl/PowerHintSession.h @@ -105,7 +105,7 @@ class PowerHintSession : public BnPowerHintSession { private: PowerHintSession *mSession; - std::mutex mStaleLock; + std::mutex mClosedLock; std::mutex mMessageLock; std::atomic<time_point<steady_clock>> mStaleTime; std::atomic<bool> mIsMonitoring; diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp index 516942a..947208b 100644 --- a/power-libperfmgr/aidl/PowerSessionManager.cpp +++ b/power-libperfmgr/aidl/PowerSessionManager.cpp @@ -33,6 +33,7 @@ namespace power { namespace impl { namespace pixel { +using ::android::perfmgr::AdpfConfig; using ::android::perfmgr::HintManager; namespace { @@ -103,7 +104,28 @@ void PowerSessionManager::updateHintBoost(const std::string &boost, int32_t dura void PowerSessionManager::wakeSessions() { std::lock_guard<std::mutex> guard(mLock); - for (PowerHintSession *s : mSessions) { + std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile(); + std::unordered_set<PowerHintSession *> wakeupList; + const int wakeupBoostValue = static_cast<int>(adpfConfig->mUclampMinInit); + for (auto &it : mTidSessionListMap) { + int tid = it.first; + int maxboost = -1; + // Find the max boost value among all the sessions that include the same TID. + for (PowerHintSession *s : it.second) { + if (!s->isActive()) + continue; + // all active sessions need to be awakened. + wakeupList.insert(s); + if (s->isTimeout()) { + maxboost = std::max(maxboost, s->getUclampMin()); + } + } + // Found the max boost and actally set to the task. + if (maxboost != -1) { + set_uclamp_min(tid, std::max(maxboost, wakeupBoostValue)); + } + } + for (PowerHintSession *s : wakeupList) { s->wakeup(); } } diff --git a/power-libperfmgr/libperfmgr/HintManager.cc b/power-libperfmgr/libperfmgr/HintManager.cc index 73ebc43..8c84c68 100644 --- a/power-libperfmgr/libperfmgr/HintManager.cc +++ b/power-libperfmgr/libperfmgr/HintManager.cc @@ -125,6 +125,11 @@ void HintManager::EndHintStatus(const std::string &hint_type) { void HintManager::DoHintAction(const std::string &hint_type) { for (auto &action : actions_.at(hint_type).hint_actions) { + if (!action.enable_property.empty() && + !android::base::GetBoolProperty(action.enable_property, true)) { + // Disabled action based on its control property + continue; + } switch (action.type) { case HintActionType::DoHint: DoHint(action.value); @@ -537,6 +542,7 @@ std::unordered_map<std::string, Hint> HintManager::ParseActions( HintActionType action_type = HintActionType::Node; std::string type_string = actions[i]["Type"].asString(); + std::string enable_property = actions[i]["EnableProperty"].asString(); LOG(VERBOSE) << "Action[" << i << "]'s Type: " << type_string; if (type_string.empty()) { LOG(VERBOSE) << "Failed to read " @@ -599,7 +605,7 @@ std::unordered_map<std::string, Hint> HintManager::ParseActions( } } actions_parsed[hint_type].node_actions.emplace_back( - node_index, value_index, std::chrono::milliseconds(duration)); + node_index, value_index, std::chrono::milliseconds(duration), enable_property); } else { const std::string &hint_value = actions[i]["Value"].asString(); @@ -610,7 +616,8 @@ std::unordered_map<std::string, Hint> HintManager::ParseActions( actions_parsed.clear(); return actions_parsed; } - actions_parsed[hint_type].hint_actions.emplace_back(action_type, hint_value); + actions_parsed[hint_type].hint_actions.emplace_back(action_type, hint_value, + enable_property); } ++total_parsed; diff --git a/power-libperfmgr/libperfmgr/NodeLooperThread.cc b/power-libperfmgr/libperfmgr/NodeLooperThread.cc index 39ffc2e..d38501d 100644 --- a/power-libperfmgr/libperfmgr/NodeLooperThread.cc +++ b/power-libperfmgr/libperfmgr/NodeLooperThread.cc @@ -21,6 +21,7 @@ #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/properties.h> #include <utils/Trace.h> namespace android { @@ -39,6 +40,11 @@ bool NodeLooperThread::Request(const std::vector<NodeAction>& actions, bool ret = true; ::android::AutoMutex _l(lock_); for (const auto& a : actions) { + if (!a.enable_property.empty() && + !android::base::GetBoolProperty(a.enable_property, true)) { + // Disabled action based on its control property + continue; + } if (a.node_index >= nodes_.size()) { LOG(ERROR) << "Node index out of bound: " << a.node_index << " ,size: " << nodes_.size(); diff --git a/power-libperfmgr/libperfmgr/config_schema.json b/power-libperfmgr/libperfmgr/config_schema.json deleted file mode 100644 index 712af50..0000000 --- a/power-libperfmgr/libperfmgr/config_schema.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "definitions": {}, - "$schema": "http://json-schema.org/draft-06/schema#", - "type": "object", - "id": "config_schema.json", - "required": [ - "Nodes", - "Actions" - ], - "properties": { - "Nodes": { - "type": "array", - "id": "/properties/Nodes", - "minItems": 1, - "uniqueItems": true, - "items": { - "type": "object", - "id": "/properties/Nodes/items", - "required": [ - "Name", - "Path", - "Values" - ], - "properties": { - "Name": { - "type": "string", - "id": "/properties/Nodes/items/properties/Name", - "title": "The Name Schema.", - "description": "The name of the node.", - "minLength": 1 - }, - "Path": { - "type": "string", - "id": "/properties/Nodes/items/properties/Path", - "title": "The Path Schema.", - "description": "For File type node, it is filesystem path of the file; for Property type node, it is the key of the property.", - "minLength": 1 - }, - "Values": { - "type": "array", - "id": "/properties/Nodes/items/properties/Values", - "minItems": 1, - "uniqueItems": true, - "items": { - "type": "string", - "id": "/properties/Nodes/items/properties/Values/items", - "title": "The Values Schema.", - "description": "The Values array lists all possible values that can be set in the Actions section, and the list of values is sorted based on their priority, with the highest priority first." - } - }, - "DefaultIndex": { - "type": "integer", - "id": "/properties/Nodes/items/properties/DefaultIndex", - "title": "The Default Index Schema.", - "description": "The default index of the node, if not present, it will be set to max index of Values.", - "minimum": 0 - }, - "ResetOnInit": { - "type": "boolean", - "id": "/properties/Nodes/items/properties/ResetOnInit", - "title": "The Reset On Init Schema.", - "description": "Flag if node will be set to default value on initialization; if not present, it will be set to false." - }, - "Type": { - "type": "string", - "id": "/properties/Nodes/items/properties/Type", - "title": "The type Schema.", - "description": "Type of Node (File or Property), if not present, it will be set to File." - }, - "HoldFd": { - "type": "boolean", - "id": "/properties/Nodes/items/properties/HoldFd", - "title": "The Hold Fd Schema.", - "description": "Flag if node will hold the file descriptor on non-default values; if not present, it will be set to false. This is only honoured for File type node." - } - } - } - }, - "Actions": { - "type": "array", - "id": "/properties/Actions", - "minItems": 1, - "uniqueItems": true, - "items": { - "type": "object", - "id": "/properties/Actions/items", - "required": [ - "PowerHint", - "Node", - "ValueIndex", - "Duration" - ], - "properties": { - "PowerHint": { - "type": "string", - "id": "/properties/Actions/items/properties/PowerHint", - "title": "The PowerHint Schema.", - "description": "The PowerHint name of the action.", - "minLength": 1 - }, - "Node": { - "type": "string", - "id": "/properties/Actions/items/properties/Node", - "title": "The Node Schema.", - "description": "The Node name of the action, which is defined in Nodes.", - "minLength": 1 - }, - "Value": { - "type": "string", - "id": "/properties/Actions/items/properties/Value", - "title": "The Value Index Schema.", - "description": "The value of action, which is defined in Nodes.", - "minLength": 1 - }, - "Duration": { - "type": "integer", - "id": "/properties/Actions/items/properties/Duration", - "title": "The Duration Schema.", - "description": "The number of milliseconds that this action will be active (zero means forever).", - "minimum": 0 - } - } - } - } - } -} diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h index d360fea..13211a1 100644 --- a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h +++ b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h @@ -61,9 +61,11 @@ struct HintStatus { enum class HintActionType { Node, DoHint, EndHint, MaskHint }; struct HintAction { - HintAction(HintActionType t, const std::string &v) : type(t), value(v) {} + HintAction(HintActionType t, const std::string &v, const std::string &p) + : type(t), value(v), enable_property(p) {} HintActionType type; std::string value; + std::string enable_property; }; struct Hint { diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/NodeLooperThread.h b/power-libperfmgr/libperfmgr/include/perfmgr/NodeLooperThread.h index eba0fc7..bad233c 100644 --- a/power-libperfmgr/libperfmgr/include/perfmgr/NodeLooperThread.h +++ b/power-libperfmgr/libperfmgr/include/perfmgr/NodeLooperThread.h @@ -34,13 +34,15 @@ namespace perfmgr { // timeout for this action: struct NodeAction { NodeAction(std::size_t node_index, std::size_t value_index, - std::chrono::milliseconds timeout_ms) + std::chrono::milliseconds timeout_ms, const std::string &enable_property = "") : node_index(node_index), value_index(value_index), - timeout_ms(timeout_ms) {} + timeout_ms(timeout_ms), + enable_property(enable_property) {} std::size_t node_index; std::size_t value_index; std::chrono::milliseconds timeout_ms; // 0ms for forever + std::string enable_property; // boolean property to control action on/off. }; // The NodeLooperThread is responsible for managing each of the sysfs nodes diff --git a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc index 48a8494..979e7dc 100644 --- a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc +++ b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc @@ -68,6 +68,16 @@ constexpr char kJSON_RAW[] = R"( "NONE" ], "Type": "Property" + }, + { + "Name": "TestEnableProperty", + "Path": "vendor.pwhal.enable.test", + "Values": [ + "0", + "1" + ], + "Type": "Property", + "ResetOnInit": true } ], "Actions": [ @@ -87,6 +97,7 @@ constexpr char kJSON_RAW[] = R"( "PowerHint": "LAUNCH", "Node": "CPUCluster0MinFreq", "Value": "1134000", + "EnableProperty": "vendor.pwhal.enable.no_exist", "Duration": 500 }, { @@ -99,9 +110,16 @@ constexpr char kJSON_RAW[] = R"( "PowerHint": "LAUNCH", "Node": "CPUCluster1MinFreq", "Value": "1512000", + "EnableProperty": "vendor.pwhal.enable.test", "Duration": 2000 }, { + "PowerHint": "DISABLE_LAUNCH_ACT2", + "Node": "TestEnableProperty", + "Value": "0", + "Duration": 0 + }, + { "PowerHint": "MASK_LAUNCH_MODE", "Type": "MaskHint", "Value": "LAUNCH" @@ -410,7 +428,7 @@ TEST_F(HintManagerTest, HintStatsTest) { TEST_F(HintManagerTest, ParseNodesTest) { std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_); - EXPECT_EQ(3u, nodes.size()); + EXPECT_EQ(4u, nodes.size()); EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName()); EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName()); EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath()); @@ -503,7 +521,7 @@ TEST_F(HintManagerTest, ParsePropertyNodesEmptyValueTest) { json_doc_.replace(start_pos, from.length(), ""); std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_); - EXPECT_EQ(3u, nodes.size()); + EXPECT_EQ(4u, nodes.size()); EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName()); EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName()); EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath()); @@ -546,7 +564,7 @@ TEST_F(HintManagerTest, ParseActionsTest) { std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_); std::unordered_map<std::string, Hint> actions = HintManager::ParseActions(json_doc_, nodes); - EXPECT_EQ(6u, actions.size()); + EXPECT_EQ(7u, actions.size()); EXPECT_EQ(2u, actions["INTERACTION"].node_actions.size()); EXPECT_EQ(1u, actions["INTERACTION"].node_actions[0].node_index); @@ -575,6 +593,7 @@ TEST_F(HintManagerTest, ParseActionsTest) { EXPECT_EQ(0u, actions["LAUNCH"].node_actions[2].value_index); EXPECT_EQ(std::chrono::milliseconds(2000).count(), actions["LAUNCH"].node_actions[2].timeout_ms.count()); + EXPECT_EQ("vendor.pwhal.enable.test", actions["LAUNCH"].node_actions[2].enable_property); EXPECT_EQ(1u, actions["MASK_LAUNCH_MODE"].hint_actions.size()); EXPECT_EQ(HintActionType::MaskHint, actions["MASK_LAUNCH_MODE"].hint_actions[0].type); @@ -604,7 +623,7 @@ TEST_F(HintManagerTest, ParseActionDuplicateFileNodeTest) { json_doc_.replace(start_pos, from.length(), R"("Node": "CPUCluster1MinFreq")"); std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_); - EXPECT_EQ(3u, nodes.size()); + EXPECT_EQ(4u, nodes.size()); auto actions = HintManager::ParseActions(json_doc_, nodes); EXPECT_EQ(0u, actions.size()); } @@ -615,7 +634,7 @@ TEST_F(HintManagerTest, ParseActionDuplicatePropertyNodeTest) { size_t start_pos = json_doc_.find(from); json_doc_.replace(start_pos, from.length(), R"("Node": "ModeProperty")"); auto nodes = HintManager::ParseNodes(json_doc_); - EXPECT_EQ(3u, nodes.size()); + EXPECT_EQ(4u, nodes.size()); auto actions = HintManager::ParseActions(json_doc_, nodes); EXPECT_EQ(0u, actions.size()); } @@ -660,6 +679,7 @@ TEST_F(HintManagerTest, GetFromJSONTest) { _VerifyPathValue(files_[1 + 2]->path, "1134000"); _VerifyPropertyValue(prop_, "LOW"); // Do LAUNCH + _VerifyPropertyValue("vendor.pwhal.enable.test", "1"); EXPECT_TRUE(hm->DoHint("LAUNCH")); std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); _VerifyPathValue(files_[0 + 2]->path, "1134000"); @@ -682,6 +702,21 @@ TEST_F(HintManagerTest, GetFromJSONTest) { _VerifyPathValue(files_[1 + 2]->path, "384000"); _VerifyPropertyValue(prop_, "NONE"); + // Disable action[2] of LAUNCH + EXPECT_TRUE(hm->EndHint("LAUNCH")); + _VerifyPropertyValue("vendor.pwhal.enable.test", "1"); + EXPECT_TRUE(hm->DoHint("DISABLE_LAUNCH_ACT2")); + std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); + _VerifyPropertyValue("vendor.pwhal.enable.test", "0"); + EXPECT_TRUE(hm->DoHint("LAUNCH")); + std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); + _VerifyPathValue(files_[0 + 2]->path, "1134000"); + // action[2] have no effect. + _VerifyPathValue(files_[1 + 2]->path, "384000"); + _VerifyPropertyValue(prop_, "HIGH"); + EXPECT_TRUE(hm->EndHint("LAUNCH")); + EXPECT_TRUE(hm->EndHint("DISABLE_LAUNCH_ACT2")); + // Mask LAUNCH and do LAUNCH EXPECT_TRUE(hm->DoHint("MASK_LAUNCH_MODE")); EXPECT_FALSE(hm->DoHint("LAUNCH")); // should fail diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp index 4b9e5cf..d3dc032 100644 --- a/thermal/Thermal.cpp +++ b/thermal/Thermal.cpp @@ -282,7 +282,7 @@ void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) { } void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { - *dump_buf << "VirtualSensorInfo:" << std::endl; + *dump_buf << "getVirtualSensorInfo:" << std::endl; const auto &map = thermal_helper_.GetSensorInfoMap(); for (const auto &sensor_info_pair : map) { if (sensor_info_pair.second.virtual_sensor_info != nullptr) { @@ -337,7 +337,7 @@ void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { } void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) { - *dump_buf << "Throttling Info:" << std::endl; + *dump_buf << "getThrottlingInfo:" << std::endl; const auto &map = thermal_helper_.GetSensorInfoMap(); const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap(); for (const auto &name_info_pair : map) { @@ -464,7 +464,7 @@ void Thermal::dumpThrottlingRequestStatus(std::ostringstream *dump_buf) { if (!thermal_throttling_status_map.size()) { return; } - *dump_buf << "Throttling Request Status " << std::endl; + *dump_buf << "getThrottlingRequestStatus:" << std::endl; for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) { *dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl; if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) { @@ -513,7 +513,7 @@ void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) { const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap(); const auto &power_status_map = thermal_helper_.GetPowerStatusMap(); - *dump_buf << "Power Rail Info " << std::endl; + *dump_buf << "getPowerRailInfo:" << std::endl; for (const auto &power_rail_pair : power_rail_info_map) { *dump_buf << " Power Rail: " << power_rail_pair.first << std::endl; *dump_buf << " Power Sample Count: " << power_rail_pair.second.power_sample_count @@ -583,7 +583,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin hidl_vec<CpuUsage> cpu_usages; dump_buf << "getCpuUsages:" << std::endl; if (!thermal_helper_.fillCpuUsages(&cpu_usages)) { - dump_buf << "Failed to get CPU usages." << std::endl; + dump_buf << " Failed to get CPU usages." << std::endl; } for (const auto &usage : cpu_usages) { @@ -679,7 +679,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin hidl_vec<CoolingDevice_2_0> cooling_devices; if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU, &cooling_devices)) { - dump_buf << "Failed to getCurrentCoolingDevices." << std::endl; + dump_buf << " Failed to getCurrentCoolingDevices." << std::endl; } for (const auto &c : cooling_devices) { @@ -688,7 +688,8 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin } } { - dump_buf << "Callbacks: Total " << callbacks_.size() << std::endl; + dump_buf << "getCallbacks:" << std::endl; + dump_buf << " Total: " << callbacks_.size() << std::endl; for (const auto &c : callbacks_) { dump_buf << " IsFilter: " << c.is_filter_type << " Type: " << android::hardware::thermal::V2_0::toString(c.type) @@ -696,7 +697,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin } } { - dump_buf << "SendCallback" << std::endl; + dump_buf << "sendCallback:" << std::endl; dump_buf << " Enabled List: "; const auto &map = thermal_helper_.GetSensorInfoMap(); for (const auto &name_info_pair : map) { @@ -707,7 +708,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin dump_buf << std::endl; } { - dump_buf << "SendPowerHint" << std::endl; + dump_buf << "sendPowerHint:" << std::endl; dump_buf << " Enabled List: "; const auto &map = thermal_helper_.GetSensorInfoMap(); for (const auto &name_info_pair : map) { @@ -722,11 +723,12 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin dumpThrottlingRequestStatus(&dump_buf); dumpPowerRailInfo(&dump_buf); { - dump_buf << "AIDL Power Hal exist: " << std::boolalpha - << thermal_helper_.isAidlPowerHalExist() << std::endl; - dump_buf << "AIDL Power Hal connected: " << std::boolalpha + dump_buf << "getAIDLPowerHalInfo:" << std::endl; + dump_buf << " Exist: " << std::boolalpha << thermal_helper_.isAidlPowerHalExist() + << std::endl; + dump_buf << " Connected: " << std::boolalpha << thermal_helper_.isPowerHalConnected() << std::endl; - dump_buf << "AIDL Power Hal Ext connected: " << std::boolalpha + dump_buf << " Ext connected: " << std::boolalpha << thermal_helper_.isPowerHalExtConnected() << std::endl; } } diff --git a/thermal/android.hardware.thermal@2.0-service.pixel.rc b/thermal/android.hardware.thermal@2.0-service.pixel.rc index 0e23bef..3ed7865 100644 --- a/thermal/android.hardware.thermal@2.0-service.pixel.rc +++ b/thermal/android.hardware.thermal@2.0-service.pixel.rc @@ -4,11 +4,9 @@ on property:vendor.thermal.link_ready=1 trigger enable-thermal-hal on enable-thermal-hal - start vendor.thermal-hal-2-0 + restart vendor.thermal-hal-2-0 service vendor.thermal-hal-2-0 /vendor/bin/hw/android.hardware.thermal@2.0-service.pixel - interface android.hardware.thermal@1.0::IThermal default - interface android.hardware.thermal@2.0::IThermal default class hal user system group system diff --git a/thermal/pid_1_0/Thermal.cpp b/thermal/pid_1_0/Thermal.cpp index 2f41445..7e6f7c0 100644 --- a/thermal/pid_1_0/Thermal.cpp +++ b/thermal/pid_1_0/Thermal.cpp @@ -282,7 +282,7 @@ void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) { } void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { - *dump_buf << "VirtualSensorInfo:" << std::endl; + *dump_buf << "getVirtualSensorInfo:" << std::endl; const auto &map = thermal_helper_.GetSensorInfoMap(); for (const auto &sensor_info_pair : map) { if (sensor_info_pair.second.virtual_sensor_info != nullptr) { @@ -331,7 +331,7 @@ void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { } void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) { - *dump_buf << "Throttling Info:" << std::endl; + *dump_buf << "getThrottlingInfo:" << std::endl; const auto &map = thermal_helper_.GetSensorInfoMap(); const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap(); for (const auto &name_info_pair : map) { @@ -458,7 +458,7 @@ void Thermal::dumpThrottlingRequestStatus(std::ostringstream *dump_buf) { if (!thermal_throttling_status_map.size()) { return; } - *dump_buf << "Throttling Request Status " << std::endl; + *dump_buf << "getThrottlingRequestStatus:" << std::endl; for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) { *dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl; if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) { @@ -507,7 +507,7 @@ void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) { const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap(); const auto &power_status_map = thermal_helper_.GetPowerStatusMap(); - *dump_buf << "Power Rail Info " << std::endl; + *dump_buf << "getPowerRailInfo:" << std::endl; for (const auto &power_rail_pair : power_rail_info_map) { *dump_buf << " Power Rail: " << power_rail_pair.first << std::endl; *dump_buf << " Power Sample Count: " << power_rail_pair.second.power_sample_count @@ -577,7 +577,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin hidl_vec<CpuUsage> cpu_usages; dump_buf << "getCpuUsages:" << std::endl; if (!thermal_helper_.fillCpuUsages(&cpu_usages)) { - dump_buf << "Failed to get CPU usages." << std::endl; + dump_buf << " Failed to get CPU usages." << std::endl; } for (const auto &usage : cpu_usages) { @@ -673,7 +673,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin hidl_vec<CoolingDevice_2_0> cooling_devices; if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU, &cooling_devices)) { - dump_buf << "Failed to getCurrentCoolingDevices." << std::endl; + dump_buf << " Failed to getCurrentCoolingDevices." << std::endl; } for (const auto &c : cooling_devices) { @@ -682,7 +682,8 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin } } { - dump_buf << "Callbacks: Total " << callbacks_.size() << std::endl; + dump_buf << "getCallbacks:" << std::endl; + dump_buf << " Total: " << callbacks_.size() << std::endl; for (const auto &c : callbacks_) { dump_buf << " IsFilter: " << c.is_filter_type << " Type: " << android::hardware::thermal::V2_0::toString(c.type) @@ -690,7 +691,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin } } { - dump_buf << "SendCallback" << std::endl; + dump_buf << "sendCallback:" << std::endl; dump_buf << " Enabled List: "; const auto &map = thermal_helper_.GetSensorInfoMap(); for (const auto &name_info_pair : map) { @@ -701,7 +702,7 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin dump_buf << std::endl; } { - dump_buf << "SendPowerHint" << std::endl; + dump_buf << "sendPowerHint:" << std::endl; dump_buf << " Enabled List: "; const auto &map = thermal_helper_.GetSensorInfoMap(); for (const auto &name_info_pair : map) { @@ -716,11 +717,12 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin dumpThrottlingRequestStatus(&dump_buf); dumpPowerRailInfo(&dump_buf); { - dump_buf << "AIDL Power Hal exist: " << std::boolalpha - << thermal_helper_.isAidlPowerHalExist() << std::endl; - dump_buf << "AIDL Power Hal connected: " << std::boolalpha + dump_buf << "getAIDLPowerHalInfo:" << std::endl; + dump_buf << " Exist: " << std::boolalpha << thermal_helper_.isAidlPowerHalExist() + << std::endl; + dump_buf << " Connected: " << std::boolalpha << thermal_helper_.isPowerHalConnected() << std::endl; - dump_buf << "AIDL Power Hal Ext connected: " << std::boolalpha + dump_buf << " Ext connected: " << std::boolalpha << thermal_helper_.isPowerHalExtConnected() << std::endl; } } diff --git a/thermal/pid_1_0/utils/powerhal_helper.cpp b/thermal/pid_1_0/utils/powerhal_helper.cpp index 515fa2d..e20f518 100644 --- a/thermal/pid_1_0/utils/powerhal_helper.cpp +++ b/thermal/pid_1_0/utils/powerhal_helper.cpp @@ -48,11 +48,14 @@ PowerHalService::PowerHalService() bool PowerHalService::connect() { std::lock_guard<std::mutex> lock(lock_); - if (!power_hal_aidl_exist_) + + if (!power_hal_aidl_exist_) { return false; + } - if (power_hal_aidl_ != nullptr) + if (power_hal_aidl_ && power_hal_ext_aidl_) { return true; + } const std::string kInstance = std::string(IPower::descriptor) + "/default"; ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str())); @@ -90,14 +93,13 @@ bool PowerHalService::connect() { bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingSeverity &t) { bool isSupported = false; - if (!isPowerHalConnected()) { + if (!connect()) { return false; } std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); lock_.lock(); if (!power_hal_ext_aidl_->isModeSupported(power_hint, &isSupported).isOk()) { LOG(ERROR) << "Fail to check supported mode, Hint: " << power_hint; - power_hal_aidl_exist_ = false; power_hal_ext_aidl_ = nullptr; power_hal_aidl_ = nullptr; lock_.unlock(); @@ -108,20 +110,23 @@ bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingS } void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity &t, - const bool &enable) { - if (!isPowerHalConnected()) { + const bool &enable, const bool error_on_exit) { + if (!connect()) { return; } std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); - LOG(INFO) << "Send Hint " << power_hint << " Enable: " << std::boolalpha << enable; + LOG(INFO) << (error_on_exit ? "Resend Hint " : "Send Hint ") << power_hint + << " Enable: " << std::boolalpha << enable; lock_.lock(); if (!power_hal_ext_aidl_->setMode(power_hint, enable).isOk()) { LOG(ERROR) << "Fail to set mode, Hint: " << power_hint; - power_hal_aidl_exist_ = false; power_hal_ext_aidl_ = nullptr; power_hal_aidl_ = nullptr; lock_.unlock(); + if (!error_on_exit) { + setMode(type, t, enable, true); + } return; } lock_.unlock(); diff --git a/thermal/pid_1_0/utils/powerhal_helper.h b/thermal/pid_1_0/utils/powerhal_helper.h index 761e315..d629cef 100644 --- a/thermal/pid_1_0/utils/powerhal_helper.h +++ b/thermal/pid_1_0/utils/powerhal_helper.h @@ -51,7 +51,8 @@ class PowerHalService { bool isModeSupported(const std::string &type, const ThrottlingSeverity &t); bool isPowerHalConnected() { return power_hal_aidl_ != nullptr; } bool isPowerHalExtConnected() { return power_hal_ext_aidl_ != nullptr; } - void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable); + void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable, + const bool error_on_exit = false); private: bool power_hal_aidl_exist_; diff --git a/thermal/pixel-thermal-symlinks.rc b/thermal/pixel-thermal-symlinks.rc index 9404331..132ec5f 100644 --- a/thermal/pixel-thermal-symlinks.rc +++ b/thermal/pixel-thermal-symlinks.rc @@ -4,20 +4,7 @@ on boot mkdir /dev/thermal/cdev-by-name 0750 system system start vendor.thermal.symlinks -on charger - # Wait for insmod_sh to finish all common modules - wait_for_prop vendor.common.modules.ready 1 - - # Wait for insmod_sh to finish all device specific modules - wait_for_prop vendor.device.modules.ready 1 - - mkdir /dev/thermal 0750 system system - mkdir /dev/thermal/tz-by-name 0750 system system - mkdir /dev/thermal/cdev-by-name 0750 system system - start vendor.thermal.symlinks - service vendor.thermal.symlinks /vendor/bin/thermal_symlinks - class hal user system group system oneshot diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp index ae58daa..27fa3b5 100644 --- a/thermal/thermal-helper.cpp +++ b/thermal/thermal-helper.cpp @@ -334,7 +334,8 @@ bool ThermalHelper::readCoolingDevice(std::string_view cooling_device, bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_0 *out) { // Return fail if the thermal sensor cannot be read. float temp; - if (!readThermalSensor(sensor_name, &temp, false)) { + std::map<std::string, float> sensor_log_map; + if (!readThermalSensor(sensor_name, &temp, false, &sensor_log_map)) { LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name; return false; } @@ -353,6 +354,14 @@ bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_ sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SHUTDOWN)]; out->vrThrottlingThreshold = sensor_info.vr_threshold; + if (sensor_info.is_watch) { + std::ostringstream sensor_log; + for (const auto &sensor_log_pair : sensor_log_map) { + sensor_log << sensor_log_pair.first << ":" << sensor_log_pair.second << " "; + } + LOG(INFO) << sensor_name.data() << ":" << out->currentValue + << " raw data: " << sensor_log.str(); + } return true; } @@ -362,8 +371,9 @@ bool ThermalHelper::readTemperature( const bool force_no_cache) { // Return fail if the thermal sensor cannot be read. float temp; + std::map<std::string, float> sensor_log_map; - if (!readThermalSensor(sensor_name, &temp, force_no_cache)) { + if (!readThermalSensor(sensor_name, &temp, force_no_cache, &sensor_log_map)) { LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name; return false; } @@ -395,6 +405,13 @@ bool ThermalHelper::readTemperature( out->throttlingStatus = static_cast<size_t>(status.first) > static_cast<size_t>(status.second) ? status.first : status.second; + if (sensor_info.is_watch) { + std::ostringstream sensor_log; + for (const auto &sensor_log_pair : sensor_log_map) { + sensor_log << sensor_log_pair.first << ":" << sensor_log_pair.second << " "; + } + LOG(INFO) << sensor_name.data() << ":" << out->value << " raw data: " << sensor_log.str(); + } return true; } @@ -438,6 +455,7 @@ void ThermalHelper::updateCoolingDevices(const std::vector<std::string> &updated } } if (cooling_devices_.writeCdevFile(target_cdev, std::to_string(max_state))) { + ATRACE_INT(target_cdev.c_str(), max_state); LOG(INFO) << "Successfully update cdev " << target_cdev << " sysfs to " << max_state; } else { LOG(ERROR) << "Failed to update cdev " << target_cdev << " sysfs to " << max_state; @@ -784,10 +802,10 @@ bool ThermalHelper::fillCpuUsages(hidl_vec<CpuUsage> *cpu_usages) const { } bool ThermalHelper::readThermalSensor(std::string_view sensor_name, float *temp, - const bool force_no_cache) { + const bool force_no_cache, + std::map<std::string, float> *sensor_log_map) { float temp_val = 0.0; std::string file_reading; - std::string log_buf; boot_clock::time_point now = boot_clock::now(); ATRACE_NAME(StringPrintf("ThermalHelper::readThermalSensor - %s", sensor_name.data()).c_str()); @@ -799,14 +817,15 @@ bool ThermalHelper::readThermalSensor(std::string_view sensor_name, float *temp, const auto &sensor_info = sensor_info_map_.at(sensor_name.data()); auto &sensor_status = sensor_status_map_.at(sensor_name.data()); - // Check if thermal data need to be read from buffer + // Check if thermal data need to be read from cache if (!force_no_cache && (sensor_status.thermal_cached.timestamp != boot_clock::time_point::min()) && (std::chrono::duration_cast<std::chrono::milliseconds>( now - sensor_status.thermal_cached.timestamp) < sensor_info.time_resolution) && !isnan(sensor_status.thermal_cached.temp)) { *temp = sensor_status.thermal_cached.temp; - LOG(VERBOSE) << "read " << sensor_name.data() << " from buffer, value:" << *temp; + (*sensor_log_map)[sensor_name.data()] = *temp; + ATRACE_INT((sensor_name.data() + std::string("-cached")).c_str(), static_cast<int>(*temp)); return true; } @@ -825,12 +844,9 @@ bool ThermalHelper::readThermalSensor(std::string_view sensor_name, float *temp, for (size_t i = 0; i < sensor_info.virtual_sensor_info->linked_sensors.size(); i++) { float sensor_reading = 0.0; if (!readThermalSensor(sensor_info.virtual_sensor_info->linked_sensors[i], - &sensor_reading, force_no_cache)) { + &sensor_reading, force_no_cache, sensor_log_map)) { return false; } - log_buf.append(StringPrintf("(%s: %0.2f)", - sensor_info.virtual_sensor_info->linked_sensors[i].c_str(), - sensor_reading)); if (std::isnan(sensor_info.virtual_sensor_info->coefficients[i])) { return false; } @@ -861,9 +877,10 @@ bool ThermalHelper::readThermalSensor(std::string_view sensor_name, float *temp, break; } } - LOG(VERBOSE) << sensor_name.data() << "'s sub sensors:" << log_buf; *temp = (temp_val + sensor_info.virtual_sensor_info->offset); } + (*sensor_log_map)[sensor_name.data()] = *temp; + ATRACE_INT(sensor_name.data(), static_cast<int>(*temp)); { std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_); @@ -994,10 +1011,8 @@ std::chrono::milliseconds ThermalHelper::thermalWatcherCallbackFunc( } if (sensor_status.severity == ThrottlingSeverity::NONE) { - LOG(VERBOSE) << temp.name << ": " << temp.value; thermal_throttling_.clearThrottlingData(name_status_pair.first, sensor_info); } else { - LOG(INFO) << temp.name << ": " << temp.value; // update thermal throttling request thermal_throttling_.thermalThrottlingUpdate( temp, sensor_info, sensor_status.severity, time_elapsed_ms, diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h index 6b7cd9a..fe5ebb8 100644 --- a/thermal/thermal-helper.h +++ b/thermal/thermal-helper.h @@ -150,7 +150,8 @@ class ThermalHelper { ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity, float value) const; // Read temperature data according to thermal sensor's info - bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs); + bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs, + std::map<std::string, float> *sensor_log_map); bool connectToPowerHal(); void updateSupportedPowerHints(); void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update); diff --git a/thermal/utils/powerhal_helper.cpp b/thermal/utils/powerhal_helper.cpp index 515fa2d..e20f518 100644 --- a/thermal/utils/powerhal_helper.cpp +++ b/thermal/utils/powerhal_helper.cpp @@ -48,11 +48,14 @@ PowerHalService::PowerHalService() bool PowerHalService::connect() { std::lock_guard<std::mutex> lock(lock_); - if (!power_hal_aidl_exist_) + + if (!power_hal_aidl_exist_) { return false; + } - if (power_hal_aidl_ != nullptr) + if (power_hal_aidl_ && power_hal_ext_aidl_) { return true; + } const std::string kInstance = std::string(IPower::descriptor) + "/default"; ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str())); @@ -90,14 +93,13 @@ bool PowerHalService::connect() { bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingSeverity &t) { bool isSupported = false; - if (!isPowerHalConnected()) { + if (!connect()) { return false; } std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); lock_.lock(); if (!power_hal_ext_aidl_->isModeSupported(power_hint, &isSupported).isOk()) { LOG(ERROR) << "Fail to check supported mode, Hint: " << power_hint; - power_hal_aidl_exist_ = false; power_hal_ext_aidl_ = nullptr; power_hal_aidl_ = nullptr; lock_.unlock(); @@ -108,20 +110,23 @@ bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingS } void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity &t, - const bool &enable) { - if (!isPowerHalConnected()) { + const bool &enable, const bool error_on_exit) { + if (!connect()) { return; } std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); - LOG(INFO) << "Send Hint " << power_hint << " Enable: " << std::boolalpha << enable; + LOG(INFO) << (error_on_exit ? "Resend Hint " : "Send Hint ") << power_hint + << " Enable: " << std::boolalpha << enable; lock_.lock(); if (!power_hal_ext_aidl_->setMode(power_hint, enable).isOk()) { LOG(ERROR) << "Fail to set mode, Hint: " << power_hint; - power_hal_aidl_exist_ = false; power_hal_ext_aidl_ = nullptr; power_hal_aidl_ = nullptr; lock_.unlock(); + if (!error_on_exit) { + setMode(type, t, enable, true); + } return; } lock_.unlock(); diff --git a/thermal/utils/powerhal_helper.h b/thermal/utils/powerhal_helper.h index 761e315..d629cef 100644 --- a/thermal/utils/powerhal_helper.h +++ b/thermal/utils/powerhal_helper.h @@ -51,7 +51,8 @@ class PowerHalService { bool isModeSupported(const std::string &type, const ThrottlingSeverity &t); bool isPowerHalConnected() { return power_hal_aidl_ != nullptr; } bool isPowerHalExtConnected() { return power_hal_ext_aidl_ != nullptr; } - void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable); + void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable, + const bool error_on_exit = false); private: bool power_hal_aidl_exist_; diff --git a/thermal/utils/thermal_throttling.cpp b/thermal/utils/thermal_throttling.cpp index 6f2d120..0a5f926 100644 --- a/thermal/utils/thermal_throttling.cpp +++ b/thermal/utils/thermal_throttling.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL) #include "thermal_throttling.h" @@ -168,6 +169,7 @@ float ThermalThrottling::updatePowerBudget(const Temperature_2_0 &temp, bool target_changed = false; float budget_transient = 0.0; auto &throttling_status = thermal_throttling_status_map_.at(temp.name); + std::string sensor_name = temp.name; if (curr_severity == ThrottlingSeverity::NONE) { return power_budget; @@ -231,6 +233,23 @@ float ThermalThrottling::updatePowerBudget(const Temperature_2_0 &temp, << " i=" << throttling_status.i_budget << " d=" << d << " budget transient=" << budget_transient << " control target=" << target_state; + ATRACE_INT((sensor_name + std::string("-power_budget")).c_str(), + static_cast<int>(power_budget)); + ATRACE_INT((sensor_name + std::string("-s_power")).c_str(), + static_cast<int>(sensor_info.throttling_info->s_power[target_state])); + ATRACE_INT((sensor_name + std::string("-time_elapsed_ms")).c_str(), + static_cast<int>(time_elapsed_ms.count())); + ATRACE_INT((sensor_name + std::string("-budget_transient")).c_str(), + static_cast<int>(budget_transient)); + ATRACE_INT((sensor_name + std::string("-i")).c_str(), + static_cast<int>(throttling_status.i_budget)); + ATRACE_INT((sensor_name + std::string("-target_state")).c_str(), + static_cast<int>(target_state)); + + ATRACE_INT((sensor_name + std::string("-err")).c_str(), static_cast<int>(err)); + ATRACE_INT((sensor_name + std::string("-p")).c_str(), static_cast<int>(p)); + ATRACE_INT((sensor_name + std::string("-temp")).c_str(), static_cast<int>(temp.value)); + throttling_status.prev_power_budget = power_budget; return power_budget; @@ -253,8 +272,13 @@ float ThermalThrottling::computeExcludedPower( "(%s: %0.2f mW, cdev_weight: %f)", excluded_power_info_pair.first.c_str(), last_updated_avg_power, excluded_power_info_pair.second[static_cast<size_t>(curr_severity)])); + + ATRACE_INT((excluded_power_info_pair.first + std::string("-avg_power")).c_str(), + static_cast<int>(last_updated_avg_power)); } } + + ATRACE_INT("excluded_power", static_cast<int>(excluded_power)); return excluded_power; } @@ -326,6 +350,10 @@ bool ThermalThrottling::allocatePowerToCdev( power_data_invalid = true; break; } + + ATRACE_INT((binded_cdev_info_pair.second.power_rail + std::string("-avg_power")) + .c_str(), + static_cast<int>(last_updated_avg_power)); } else { power_data_invalid = true; break; @@ -346,18 +374,21 @@ bool ThermalThrottling::allocatePowerToCdev( allocated_power += last_updated_avg_power; allocated_weight += cdev_weight; allocated_cdev.insert(binded_cdev_info_pair.first); - log_buf.append(StringPrintf("(%s: %0.2f mW)", - binded_cdev_info_pair.second.power_rail.c_str(), - last_updated_avg_power)); - + if (!binded_cdev_info_pair.second.power_rail.empty()) { + log_buf.append(StringPrintf("(%s: %0.2f mW)", + binded_cdev_info_pair.second.power_rail.c_str(), + last_updated_avg_power)); + } LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first << " has been already at min state 0"; } } else { const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first); - log_buf.append(StringPrintf("(%s: %0.2f mW)", - binded_cdev_info_pair.second.power_rail.c_str(), - last_updated_avg_power)); + if (!binded_cdev_info_pair.second.power_rail.empty()) { + log_buf.append(StringPrintf("(%s: %0.2f mW)", + binded_cdev_info_pair.second.power_rail.c_str(), + last_updated_avg_power)); + } // Ignore the power distribution if the CDEV has no space to reduce power if ((cdev_power_adjustment < 0 && thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( @@ -528,6 +559,13 @@ bool ThermalThrottling::throttlingReleaseUpdate( << binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)] << ", avg power = " << avg_power; + ATRACE_INT( + (binded_cdev_info_pair.second.power_rail + std::string("-power_threshold")).c_str(), + static_cast<int>( + binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)])); + ATRACE_INT((binded_cdev_info_pair.second.power_rail + std::string("-avg_power")).c_str(), + avg_power); + switch (binded_cdev_info_pair.second.release_logic) { case ReleaseLogic::INCREASE: if (!is_over_budget) { @@ -646,6 +684,14 @@ void ThermalThrottling::computeCoolingDevicesRequest( << " cdev_floor_with_power_link=" << cdev_floor << " cdev_ceiling=" << cdev_ceiling; + ATRACE_INT((cdev_request_pair.first + std::string("-pid_request")).c_str(), + pid_cdev_request); + ATRACE_INT((cdev_request_pair.first + std::string("-hardlimit_request")).c_str(), + hardlimit_cdev_request); + ATRACE_INT((cdev_request_pair.first + std::string("-release_step")).c_str(), release_step); + ATRACE_INT((cdev_request_pair.first + std::string("-cdev_floor")).c_str(), cdev_floor); + ATRACE_INT((cdev_request_pair.first + std::string("-cdev_ceiling")).c_str(), cdev_ceiling); + auto request_state = std::max(pid_cdev_request, hardlimit_cdev_request); if (release_step) { if (release_step >= request_state) { diff --git a/vibrator/cs40l25/Vibrator.cpp b/vibrator/cs40l25/Vibrator.cpp index 669258f..ba4efad 100644 --- a/vibrator/cs40l25/Vibrator.cpp +++ b/vibrator/cs40l25/Vibrator.cpp @@ -112,7 +112,7 @@ static constexpr float PWLE_FREQUENCY_MAX_HZ = 999.0f; static constexpr float PWLE_BW_MAP_SIZE = 1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ); static constexpr float RAMP_DOWN_CONSTANT = 1048.576f; -static constexpr float RAMP_DOWN_TIME_MS = 50.0f; +static constexpr float RAMP_DOWN_TIME_MS = 0.0f; static struct pcm_config haptic_nohost_config = { .channels = 1, @@ -1276,12 +1276,18 @@ void Vibrator::setPwleRampDown() { // where Trd is the desired ramp down time in seconds // pwle_ramp_down accepts only 24 bit integers values - const float seconds = RAMP_DOWN_TIME_MS / 1000; - const auto ramp_down_coefficient = static_cast<uint32_t>(RAMP_DOWN_CONSTANT / seconds); - - if (!mHwApi->setPwleRampDown(ramp_down_coefficient)) { - ALOGE("Failed to write \"%d\" to pwle_ramp_down (%d): %s", ramp_down_coefficient, errno, - strerror(errno)); + if (RAMP_DOWN_TIME_MS != 0.0) { + const float seconds = RAMP_DOWN_TIME_MS / 1000; + const auto ramp_down_coefficient = static_cast<uint32_t>(RAMP_DOWN_CONSTANT / seconds); + if (!mHwApi->setPwleRampDown(ramp_down_coefficient)) { + ALOGE("Failed to write \"%d\" to pwle_ramp_down (%d): %s", ramp_down_coefficient, errno, + strerror(errno)); + } + } else { + // Turn off the low level PWLE Ramp Down feature + if (!mHwApi->setPwleRampDown(0)) { + ALOGE("Failed to write 0 to pwle_ramp_down (%d): %s", errno, strerror(errno)); + } } } diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h index fec5c8b..2143b96 100644 --- a/vibrator/cs40l26/Hardware.h +++ b/vibrator/cs40l26/Hardware.h @@ -220,7 +220,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase { ALOGE("Invalid waveform index for OWT erase: %d", effectIndex); return false; } - + // Turn off the waiting time for SVC init phase to complete since chip + // should already under STOP state + setMinOnOffInterval(0); + // Do erase flow if (effectIndex < WAVEFORM_MAX_INDEX) { /* Normal situation. Only erase the effect which we just played. */ if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) { @@ -248,6 +251,8 @@ class HwApi : public Vibrator::HwApi, private HwApiBase { (*effect)[i].id = -1; } } + // Turn on the waiting time for SVC init phase to complete + setMinOnOffInterval(Vibrator::MIN_ON_OFF_INTERVAL_US); return true; } @@ -327,6 +332,16 @@ class HwCal : public Vibrator::HwCal, private HwCalBase { bool getSupportedPrimitives(uint32_t *value) override { return getProperty("supported_primitives", value, (uint32_t)0); } + bool isF0CompEnabled() override { + bool value; + getProperty("f0.comp.enabled", &value, true); + return value; + } + bool isRedcCompEnabled() override { + bool value; + getProperty("redc.comp.enabled", &value, true); + return value; + } void debug(int fd) override { HwCalBase::debug(fd); } }; diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp index c57cca6..2501224 100644 --- a/vibrator/cs40l26/Vibrator.cpp +++ b/vibrator/cs40l26/Vibrator.cpp @@ -48,7 +48,6 @@ static constexpr uint32_t WAVEFORM_LONG_VIBRATION_THRESHOLD_MS = 50; static constexpr uint8_t VOLTAGE_SCALE_MAX = 100; static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby -static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling static constexpr uint32_t MAX_TIME_MS = UINT16_MAX; @@ -100,11 +99,6 @@ static uint16_t amplitudeToScale(float amplitude, float maximum) { return std::round(ratio); } -enum class AlwaysOnId : uint32_t { - GPIO_RISE, - GPIO_FALL, -}; - enum WaveformBankID : uint8_t { RAM_WVFRM_BANK, ROM_WVFRM_BANK, @@ -148,6 +142,8 @@ enum vibe_state { VIBE_STATE_ASP, }; +std::mutex mActiveId_mutex; // protects mActiveId + static int min(int x, int y) { return x < y ? x : y; } @@ -347,8 +343,8 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal) ALOGD("Unsupported calibration version: %u!", calVer); } - mHwApi->setF0CompEnable(true); - mHwApi->setRedcCompEnable(true); + mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled()); + mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled()); mIsUnderExternalControl = false; @@ -375,8 +371,8 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) { ATRACE_NAME("Vibrator::getCapabilities"); int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK | - IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_ALWAYS_ON_CONTROL | - IVibrator::CAP_GET_RESONANT_FREQUENCY | IVibrator::CAP_GET_Q_FACTOR; + IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY | + IVibrator::CAP_GET_Q_FACTOR; if (hasHapticAlsaDevice()) { ret |= IVibrator::CAP_EXTERNAL_CONTROL; } else { @@ -394,28 +390,36 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) { ndk::ScopedAStatus Vibrator::off() { ATRACE_NAME("Vibrator::off"); - if (mActiveId < 0) { - ALOGD("Vibrator is already off"); - return ndk::ScopedAStatus::ok(); - } + bool ret{true}; + const std::scoped_lock<std::mutex> lock(mActiveId_mutex); + + if (mActiveId >= 0) { + /* Stop the active effect. */ + if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) { + ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno)); + ret = false; + } - /* Stop the active effect. */ - if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) { - ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno)); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && + (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) { + ALOGE("Failed to clean up the composed effect %d", mActiveId); + ret = false; + } + } else { + ALOGV("Vibrator is already off"); } - if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && - (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) { - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } mActiveId = -1; setGlobalAmplitude(false); if (mF0Offset) { mHwApi->setF0Offset(0); } - return ndk::ScopedAStatus::ok(); + if (ret) { + return ndk::ScopedAStatus::ok(); + } else { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } } ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, @@ -604,13 +608,11 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem if (effectIndex >= FF_MAX_EFFECTS) { ALOGE("Invalid waveform index %d", effectIndex); - status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - goto end; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (mAsyncHandle.wait_for(ASYNC_COMPLETION_TIMEOUT) != std::future_status::ready) { ALOGE("Previous vibration pending: prev: %d, curr: %d", mActiveId, effectIndex); - status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - goto end; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (ch) { @@ -647,23 +649,20 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex], static_cast<uint16_t>(timeoutMs))) { ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno)); - status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - goto end; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } + const std::scoped_lock<std::mutex> lock(mActiveId_mutex); mActiveId = effectIndex; /* Play the event now. */ if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) { ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno)); - status = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - goto end; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback); - -end: - return status; + return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) { @@ -683,47 +682,15 @@ ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) { return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX); } -ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> *_aidl_return) { - *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK}; - return ndk::ScopedAStatus::ok(); +ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> * /*_aidl_return*/) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } -ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) { - ndk::ScopedAStatus status; - uint32_t effectIndex; - uint32_t timeMs; - uint32_t volLevel; - uint16_t scale; - status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel); - if (!status.isOk()) { - return status; - } - - scale = amplitudeToScale(volLevel, VOLTAGE_SCALE_MAX); - - switch (static_cast<AlwaysOnId>(id)) { - case AlwaysOnId::GPIO_RISE: - // mHwApi->setGpioRiseIndex(effectIndex); - // mHwApi->setGpioRiseScale(scale); - return ndk::ScopedAStatus::ok(); - case AlwaysOnId::GPIO_FALL: - // mHwApi->setGpioFallIndex(effectIndex); - // mHwApi->setGpioFallScale(scale); - return ndk::ScopedAStatus::ok(); - } - +ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/, + EffectStrength /*strength*/) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } -ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) { - switch (static_cast<AlwaysOnId>(id)) { - case AlwaysOnId::GPIO_RISE: - // mHwApi->setGpioRiseIndex(0); - return ndk::ScopedAStatus::ok(); - case AlwaysOnId::GPIO_FALL: - // mHwApi->setGpioFallIndex(0); - return ndk::ScopedAStatus::ok(); - } - +ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } @@ -1308,9 +1275,21 @@ void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) { } mHwApi->pollVibeState(VIBE_STATE_STOPPED); - if (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) { - mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects); + const std::scoped_lock<std::mutex> lock(mActiveId_mutex); + uint32_t effectCount = WAVEFORM_MAX_PHYSICAL_INDEX; + if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && + (!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) { + ALOGE("Failed to clean up the composed effect %d", mActiveId); + } else { + ALOGD("waitForComplete: Vibrator is already off"); } + mHwApi->getEffectCount(&effectCount); + // Do waveform number checking + if ((effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) && + (!mHwApi->eraseOwtEffect(mInputFd, WAVEFORM_MAX_INDEX, &mFfEffects))) { + ALOGE("Failed to forcibly clean up all composed effect"); + } + mActiveId = -1; if (callback) { diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h index f4a5e85..27ee283 100644 --- a/vibrator/cs40l26/Vibrator.h +++ b/vibrator/cs40l26/Vibrator.h @@ -110,6 +110,10 @@ class Vibrator : public BnVibrator { virtual bool isChirpEnabled() = 0; // Obtains the supported primitive effects. virtual bool getSupportedPrimitives(uint32_t *value) = 0; + // Checks if the f0 compensation feature needs to be enabled. + virtual bool isF0CompEnabled() = 0; + // Checks if the redc compensation feature needs to be enabled. + virtual bool isRedcCompEnabled() = 0; // Emit diagnostic information to the given file. virtual void debug(int fd) = 0; }; @@ -150,6 +154,9 @@ class Vibrator : public BnVibrator { binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; + // SVC initialization time + static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; + private: ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch, const std::shared_ptr<IVibratorCallback> &callback); diff --git a/vibrator/cs40l26/tests/mocks.h b/vibrator/cs40l26/tests/mocks.h index c0dbcd9..21466a0 100644 --- a/vibrator/cs40l26/tests/mocks.h +++ b/vibrator/cs40l26/tests/mocks.h @@ -61,6 +61,8 @@ class MockCal : public ::aidl::android::hardware::vibrator::Vibrator::HwCal { MOCK_METHOD1(getLongVolLevels, bool(std::array<uint32_t, 2> *value)); MOCK_METHOD0(isChirpEnabled, bool()); MOCK_METHOD1(getSupportedPrimitives, bool(uint32_t *value)); + MOCK_METHOD0(isF0CompEnabled, bool()); + MOCK_METHOD0(isRedcCompEnabled, bool()); MOCK_METHOD1(debug, void(int fd)); ~MockCal() override { destructor(); }; diff --git a/vibrator/cs40l26/tests/test-vibrator.cpp b/vibrator/cs40l26/tests/test-vibrator.cpp index ecc1523..a8bedd5 100644 --- a/vibrator/cs40l26/tests/test-vibrator.cpp +++ b/vibrator/cs40l26/tests/test-vibrator.cpp @@ -298,6 +298,8 @@ class VibratorTest : public Test { EXPECT_CALL(*mMockCal, getLongVolLevels(_)).Times(times); EXPECT_CALL(*mMockCal, isChirpEnabled()).Times(times); EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).Times(times); + EXPECT_CALL(*mMockCal, isF0CompEnabled()).Times(times); + EXPECT_CALL(*mMockCal, isRedcCompEnabled()).Times(times); EXPECT_CALL(*mMockCal, debug(_)).Times(times); } @@ -350,7 +352,9 @@ TEST_F(VibratorTest, Constructor) { volGet = EXPECT_CALL(*mMockCal, getLongVolLevels(_)).WillOnce(DoDefault()); } + EXPECT_CALL(*mMockCal, isF0CompEnabled()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setF0CompEnable(true)).WillOnce(Return(true)); + EXPECT_CALL(*mMockCal, isRedcCompEnabled()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setRedcCompEnable(true)).WillOnce(Return(true)); EXPECT_CALL(*mMockCal, isChirpEnabled()).WillOnce(Return(true)); @@ -377,6 +381,8 @@ TEST_F(VibratorTest, on) { } TEST_F(VibratorTest, off) { + Sequence s1; + EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault()); EXPECT_TRUE(mVibrator->off().isOk()); } @@ -545,26 +551,6 @@ TEST_P(EffectsTest, perform) { } } -TEST_P(EffectsTest, alwaysOnEnable) { - // No real function now in P22+ - auto param = GetParam(); - auto effect = std::get<0>(param); - auto strength = std::get<1>(param); - auto scale = EFFECT_SCALE.find(param); - bool supported = (scale != EFFECT_SCALE.end()); - - if (supported) { - // Do nothing - } - - ndk::ScopedAStatus status = mVibrator->alwaysOnEnable(0, effect, strength); - if (supported) { - EXPECT_EQ(EX_NONE, status.getExceptionCode()); - } else { - EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode()); - } -} - const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(), ndk::enum_range<Effect>().end()}; const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(), @@ -690,50 +676,6 @@ const std::vector<ComposeParam> kComposeParams = { INSTANTIATE_TEST_CASE_P(VibratorTests, ComposeTest, ValuesIn(kComposeParams.begin(), kComposeParams.end()), ComposeTest::PrintParam); - -class AlwaysOnTest : public VibratorTest, public WithParamInterface<int32_t> { - public: - static auto PrintParam(const TestParamInfo<ParamType> &info) { - return std::to_string(info.param); - } -}; - -TEST_P(AlwaysOnTest, alwaysOnEnable) { - auto param = GetParam(); - auto scale = EFFECT_SCALE.begin(); - - std::advance(scale, std::rand() % EFFECT_SCALE.size()); - - auto effect = std::get<0>(scale->first); - auto strength = std::get<1>(scale->first); - - switch (param) { - case 0: - case 1: - // Do nothing - break; - } - - ndk::ScopedAStatus status = mVibrator->alwaysOnEnable(param, effect, strength); - EXPECT_EQ(EX_NONE, status.getExceptionCode()); -} - -TEST_P(AlwaysOnTest, alwaysOnDisable) { - auto param = GetParam(); - - switch (param) { - case 0: - case 1: - // Do nothing - break; - } - - ndk::ScopedAStatus status = mVibrator->alwaysOnDisable(param); - EXPECT_EQ(EX_NONE, status.getExceptionCode()); -} - -INSTANTIATE_TEST_CASE_P(VibratorTests, AlwaysOnTest, Range(0, 1), AlwaysOnTest::PrintParam); - } // namespace vibrator } // namespace hardware } // namespace android |