sdm710-common: Import power libperfmgr from hardware/xiaomi
This commit is contained in:
parent
09f92402a8
commit
c4e5b44832
59 changed files with 200 additions and 184475 deletions
|
@ -1,118 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright (C) 2018 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.
|
|
||||||
|
|
||||||
package {
|
|
||||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_library {
|
|
||||||
name: "libdisppower-lenovo-sdm710",
|
|
||||||
proprietary: true,
|
|
||||||
srcs: [
|
|
||||||
"disp-power/DisplayLowPower.cpp",
|
|
||||||
"disp-power/InteractionHandler.cpp",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"libbase",
|
|
||||||
"libcutils",
|
|
||||||
"liblog",
|
|
||||||
"libperfmgr",
|
|
||||||
"libutils",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_library {
|
|
||||||
name: "libadaptivecpu-lenovo-sdm710",
|
|
||||||
proprietary: true,
|
|
||||||
vendor: true,
|
|
||||||
srcs: [
|
|
||||||
"adaptivecpu/AdaptiveCpu.cpp",
|
|
||||||
"adaptivecpu/AdaptiveCpuConfig.cpp",
|
|
||||||
"adaptivecpu/AdaptiveCpuStats.cpp",
|
|
||||||
"adaptivecpu/CpuFrequencyReader.cpp",
|
|
||||||
"adaptivecpu/CpuLoadReaderProcStat.cpp",
|
|
||||||
"adaptivecpu/CpuLoadReaderSysDevices.cpp",
|
|
||||||
"adaptivecpu/Device.cpp",
|
|
||||||
"adaptivecpu/KernelCpuFeatureReader.cpp",
|
|
||||||
"adaptivecpu/Model.cpp",
|
|
||||||
"adaptivecpu/RealFilesystem.cpp",
|
|
||||||
"adaptivecpu/ThrottleDecision.cpp",
|
|
||||||
"adaptivecpu/TimeSource.cpp",
|
|
||||||
"adaptivecpu/WorkDurationProcessor.cpp",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"android.hardware.power-V3-ndk",
|
|
||||||
"libbase",
|
|
||||||
"liblog",
|
|
||||||
"libperfmgr",
|
|
||||||
"libutils",
|
|
||||||
"libcutils",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_test {
|
|
||||||
name: "libadaptivecpu_test-lenovo-sdm710",
|
|
||||||
proprietary: true,
|
|
||||||
vendor: true,
|
|
||||||
srcs: [
|
|
||||||
"adaptivecpu/tests/AdaptiveCpuConfigTest.cpp",
|
|
||||||
"adaptivecpu/tests/AdaptiveCpuStatsTest.cpp",
|
|
||||||
"adaptivecpu/tests/CpuFrequencyReaderTest.cpp",
|
|
||||||
"adaptivecpu/tests/CpuLoadReaderProcStatTest.cpp",
|
|
||||||
"adaptivecpu/tests/CpuLoadReaderSysDevicesTest.cpp",
|
|
||||||
"adaptivecpu/tests/KernelCpuFeatureReaderTest.cpp",
|
|
||||||
"adaptivecpu/tests/ModelTest.cpp",
|
|
||||||
"adaptivecpu/tests/WorkDurationProcessorTest.cpp",
|
|
||||||
],
|
|
||||||
static_libs: [
|
|
||||||
"libadaptivecpu-lenovo-sdm710",
|
|
||||||
"libgmock",
|
|
||||||
"android.hardware.power-V3-ndk",
|
|
||||||
],
|
|
||||||
shared_libs: [
|
|
||||||
"liblog",
|
|
||||||
"libbase",
|
|
||||||
"libcutils",
|
|
||||||
],
|
|
||||||
test_suites: ["device-tests"],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_binary {
|
|
||||||
name: "android.hardware.power-service.lenovo-sdm710-libperfmgr",
|
|
||||||
relative_install_path: "hw",
|
|
||||||
init_rc: ["aidl/android.hardware.power-service.lenovo-sdm710-libperfmgr.rc"],
|
|
||||||
vintf_fragments: ["aidl/android.hardware.power-service.lenovo-sdm710.xml"],
|
|
||||||
vendor: true,
|
|
||||||
shared_libs: [
|
|
||||||
"android.hardware.power-V3-ndk",
|
|
||||||
"libadaptivecpu-lenovo-sdm710",
|
|
||||||
"libbase",
|
|
||||||
"libcutils",
|
|
||||||
"liblog",
|
|
||||||
"libutils",
|
|
||||||
"libbinder_ndk",
|
|
||||||
"libdisppower-lenovo-sdm710",
|
|
||||||
"libperfmgr",
|
|
||||||
"libprocessgroup",
|
|
||||||
"pixel-power-ext-V1-ndk",
|
|
||||||
],
|
|
||||||
srcs: [
|
|
||||||
"aidl/service.cpp",
|
|
||||||
"aidl/Power.cpp",
|
|
||||||
"aidl/PowerExt.cpp",
|
|
||||||
"aidl/PowerHintSession.cpp",
|
|
||||||
"aidl/PowerSessionManager.cpp",
|
|
||||||
],
|
|
||||||
}
|
|
66
power-libperfmgr/Android.mk
Normal file
66
power-libperfmgr/Android.mk
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021 The LineageOS 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
ifneq (,$(findstring hardware/google/interfaces, $(PRODUCT_SOONG_NAMESPACES)))
|
||||||
|
ifneq (,$(findstring hardware/google/pixel, $(PRODUCT_SOONG_NAMESPACES)))
|
||||||
|
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE_RELATIVE_PATH := hw
|
||||||
|
|
||||||
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
|
android.hardware.power-V2-ndk \
|
||||||
|
libbase \
|
||||||
|
libbinder_ndk \
|
||||||
|
libcutils \
|
||||||
|
libdl \
|
||||||
|
liblog \
|
||||||
|
libperfmgr \
|
||||||
|
libprocessgroup \
|
||||||
|
libutils \
|
||||||
|
pixel-power-ext-V1-ndk
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
service.cpp \
|
||||||
|
InteractionHandler.cpp \
|
||||||
|
Power.cpp \
|
||||||
|
PowerExt.cpp \
|
||||||
|
PowerHintSession.cpp \
|
||||||
|
PowerSessionManager.cpp
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -Wno-unused-parameter -Wno-unused-variable
|
||||||
|
|
||||||
|
ifneq ($(TARGET_POWERHAL_MODE_EXT),)
|
||||||
|
LOCAL_CFLAGS += -DMODE_EXT
|
||||||
|
LOCAL_SRC_FILES += ../../../../$(TARGET_POWERHAL_MODE_EXT)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(TARGET_TAP_TO_WAKE_NODE),)
|
||||||
|
LOCAL_CFLAGS += -DTAP_TO_WAKE_NODE=\"$(TARGET_TAP_TO_WAKE_NODE)\"
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_MODULE := android.hardware.power-service.lenovo-sdm710-libperfmgr
|
||||||
|
LOCAL_INIT_RC := android.hardware.power-service.lenovo-sdm710-libperfmgr.rc
|
||||||
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
LOCAL_VENDOR_MODULE := true
|
||||||
|
LOCAL_VINTF_FRAGMENTS := android.hardware.power-service.lenovo-sdm710.xml
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
endif
|
||||||
|
endif
|
|
@ -14,8 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LOG_TAG "powerhal-libperfmgr"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
||||||
|
#define LOG_TAG "powerhal-libperfmgr"
|
||||||
|
|
||||||
#include "InteractionHandler.h"
|
#include "InteractionHandler.h"
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
#include "PowerHintSession.h"
|
#include "PowerHintSession.h"
|
||||||
#include "PowerSessionManager.h"
|
#include "PowerSessionManager.h"
|
||||||
#include "disp-power/DisplayLowPower.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace google {
|
namespace google {
|
||||||
|
@ -42,14 +41,17 @@ namespace pixel {
|
||||||
using ::aidl::google::hardware::power::impl::pixel::PowerHintSession;
|
using ::aidl::google::hardware::power::impl::pixel::PowerHintSession;
|
||||||
using ::android::perfmgr::HintManager;
|
using ::android::perfmgr::HintManager;
|
||||||
|
|
||||||
|
#ifdef MODE_EXT
|
||||||
|
extern bool isDeviceSpecificModeSupported(Mode type, bool* _aidl_return);
|
||||||
|
extern bool setDeviceSpecificMode(Mode type, bool enabled);
|
||||||
|
#endif
|
||||||
|
|
||||||
constexpr char kPowerHalStateProp[] = "vendor.powerhal.state";
|
constexpr char kPowerHalStateProp[] = "vendor.powerhal.state";
|
||||||
constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio";
|
constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio";
|
||||||
constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering";
|
constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering";
|
||||||
|
|
||||||
Power::Power(std::shared_ptr<DisplayLowPower> dlpw, std::shared_ptr<AdaptiveCpu> adaptiveCpu)
|
Power::Power()
|
||||||
: mDisplayLowPower(dlpw),
|
: mInteractionHandler(nullptr),
|
||||||
mAdaptiveCpu(adaptiveCpu),
|
|
||||||
mInteractionHandler(nullptr),
|
|
||||||
mSustainedPerfModeOn(false) {
|
mSustainedPerfModeOn(false) {
|
||||||
mInteractionHandler = std::make_unique<InteractionHandler>();
|
mInteractionHandler = std::make_unique<InteractionHandler>();
|
||||||
mInteractionHandler->Init();
|
mInteractionHandler->Init();
|
||||||
|
@ -76,46 +78,23 @@ Power::Power(std::shared_ptr<DisplayLowPower> dlpw, std::shared_ptr<AdaptiveCpu>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sysfs_write(const char *path, const char *s)
|
|
||||||
{
|
|
||||||
char buf[80];
|
|
||||||
int len;
|
|
||||||
int ret = 0;
|
|
||||||
int fd = open(path, O_WRONLY);
|
|
||||||
|
|
||||||
if (fd < 0) {
|
|
||||||
strerror_r(errno, buf, sizeof(buf));
|
|
||||||
ALOGE("Error opening %s: %s\n", path, buf);
|
|
||||||
return -1 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = write(fd, s, strlen(s));
|
|
||||||
if (len < 0) {
|
|
||||||
strerror_r(errno, buf, sizeof(buf));
|
|
||||||
ALOGE("Error writing to %s: %s\n", path, buf);
|
|
||||||
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
|
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
|
||||||
LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
|
LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
|
||||||
if (HintManager::GetInstance()->GetAdpfProfile() &&
|
if (HintManager::GetInstance()->GetAdpfProfile() &&
|
||||||
HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
|
HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
|
||||||
PowerSessionManager::getInstance()->updateHintMode(toString(type), enabled);
|
PowerSessionManager::getInstance()->updateHintMode(toString(type), enabled);
|
||||||
}
|
}
|
||||||
switch (type) {
|
#ifdef MODE_EXT
|
||||||
case Mode::DOUBLE_TAP_TO_WAKE:
|
if (setDeviceSpecificMode(type, enabled)) {
|
||||||
{
|
return ndk::ScopedAStatus::ok();
|
||||||
sysfs_write("/sys/class/touch/tp_dev/gesture_on", enabled ? "1" : "0");
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
switch (type) {
|
||||||
|
#ifdef TAP_TO_WAKE_NODE
|
||||||
|
case Mode::DOUBLE_TAP_TO_WAKE:
|
||||||
|
::android::base::WriteStringToFile(enabled ? "1" : "0", TAP_TO_WAKE_NODE, true);
|
||||||
break;
|
break;
|
||||||
case Mode::LOW_POWER:
|
#endif
|
||||||
break;
|
|
||||||
case Mode::SUSTAINED_PERFORMANCE:
|
case Mode::SUSTAINED_PERFORMANCE:
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE");
|
HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE");
|
||||||
|
@ -127,6 +106,10 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
#ifndef TAP_TO_WAKE_NODE
|
||||||
|
case Mode::DOUBLE_TAP_TO_WAKE:
|
||||||
|
[[fallthrough]];
|
||||||
|
#endif
|
||||||
case Mode::FIXED_PERFORMANCE:
|
case Mode::FIXED_PERFORMANCE:
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case Mode::EXPENSIVE_RENDERING:
|
case Mode::EXPENSIVE_RENDERING:
|
||||||
|
@ -139,8 +122,6 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case Mode::AUDIO_STREAMING_LOW_LATENCY:
|
case Mode::AUDIO_STREAMING_LOW_LATENCY:
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case Mode::GAME_LOADING:
|
|
||||||
[[fallthrough]];
|
|
||||||
default:
|
default:
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
HintManager::GetInstance()->DoHint(toString(type));
|
HintManager::GetInstance()->DoHint(toString(type));
|
||||||
|
@ -154,11 +135,18 @@ ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
|
ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
|
||||||
|
#ifdef MODE_EXT
|
||||||
|
if (isDeviceSpecificModeSupported(type, _aidl_return)) {
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
|
bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
|
||||||
// LOW_POWER and DOUBLE_TAP_TO_WAKE handled insides PowerHAL specifically
|
#ifdef TAP_TO_WAKE_NODE
|
||||||
if (type == Mode::LOW_POWER || type == Mode::DOUBLE_TAP_TO_WAKE) {
|
if (type == Mode::DOUBLE_TAP_TO_WAKE) {
|
||||||
supported = true;
|
supported = true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported;
|
LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported;
|
||||||
*_aidl_return = supported;
|
*_aidl_return = supported;
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
|
@ -221,7 +209,6 @@ binder_status_t Power::dump(int fd, const char **, uint32_t) {
|
||||||
// Dump nodes through libperfmgr
|
// Dump nodes through libperfmgr
|
||||||
HintManager::GetInstance()->DumpToFd(fd);
|
HintManager::GetInstance()->DumpToFd(fd);
|
||||||
PowerSessionManager::getInstance()->dumpToFd(fd);
|
PowerSessionManager::getInstance()->dumpToFd(fd);
|
||||||
mAdaptiveCpu->DumpToFd(fd);
|
|
||||||
if (!::android::base::WriteStringToFd(buf, fd)) {
|
if (!::android::base::WriteStringToFd(buf, fd)) {
|
||||||
PLOG(ERROR) << "Failed to dump state to fd";
|
PLOG(ERROR) << "Failed to dump state to fd";
|
||||||
}
|
}
|
||||||
|
@ -244,7 +231,7 @@ ndk::ScopedAStatus Power::createHintSession(int32_t tgid, int32_t uid,
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||||
}
|
}
|
||||||
std::shared_ptr<IPowerHintSession> session = ndk::SharedRefBase::make<PowerHintSession>(
|
std::shared_ptr<IPowerHintSession> session = ndk::SharedRefBase::make<PowerHintSession>(
|
||||||
mAdaptiveCpu, tgid, uid, threadIds, durationNanos);
|
tgid, uid, threadIds, durationNanos);
|
||||||
*_aidl_return = session;
|
*_aidl_return = session;
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
|
@ -22,9 +22,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "adaptivecpu/AdaptiveCpu.h"
|
#include "InteractionHandler.h"
|
||||||
#include "disp-power/DisplayLowPower.h"
|
|
||||||
#include "disp-power/InteractionHandler.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace google {
|
namespace google {
|
||||||
|
@ -39,7 +37,7 @@ using ::aidl::android::hardware::power::Mode;
|
||||||
|
|
||||||
class Power : public ::aidl::android::hardware::power::BnPower {
|
class Power : public ::aidl::android::hardware::power::BnPower {
|
||||||
public:
|
public:
|
||||||
Power(std::shared_ptr<DisplayLowPower> dlpw, std::shared_ptr<AdaptiveCpu> adaptiveCpu);
|
Power();
|
||||||
ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
|
ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
|
||||||
ndk::ScopedAStatus isModeSupported(Mode type, bool *_aidl_return) override;
|
ndk::ScopedAStatus isModeSupported(Mode type, bool *_aidl_return) override;
|
||||||
ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
|
ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
|
||||||
|
@ -52,8 +50,6 @@ class Power : public ::aidl::android::hardware::power::BnPower {
|
||||||
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
|
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<DisplayLowPower> mDisplayLowPower;
|
|
||||||
std::shared_ptr<AdaptiveCpu> mAdaptiveCpu;
|
|
||||||
std::unique_ptr<InteractionHandler> mInteractionHandler;
|
std::unique_ptr<InteractionHandler> mInteractionHandler;
|
||||||
std::atomic<bool> mSustainedPerfModeOn;
|
std::atomic<bool> mSustainedPerfModeOn;
|
||||||
};
|
};
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LOG_TAG "android.hardware.power-service.pixel.ext-libperfmgr"
|
#define LOG_TAG "android.hardware.power-service.lenovo-sdm710-libperfmgr"
|
||||||
|
|
||||||
#include "PowerExt.h"
|
#include "PowerExt.h"
|
||||||
|
|
||||||
|
@ -52,11 +52,6 @@ ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) {
|
||||||
PowerSessionManager::getInstance()->updateHintMode(mode, enabled);
|
PowerSessionManager::getInstance()->updateHintMode(mode, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == AdaptiveCpu::HINT_NAME) {
|
|
||||||
LOG(DEBUG) << "AdaptiveCpu intercepted hint";
|
|
||||||
mAdaptiveCpu->HintReceived(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <aidl/google/hardware/power/extension/pixel/BnPowerExt.h>
|
|
||||||
#include <perfmgr/HintManager.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "adaptivecpu/AdaptiveCpu.h"
|
#include <aidl/google/hardware/power/extension/pixel/BnPowerExt.h>
|
||||||
#include "disp-power/DisplayLowPower.h"
|
#include <perfmgr/HintManager.h>
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace google {
|
namespace google {
|
||||||
|
@ -35,16 +32,13 @@ namespace pixel {
|
||||||
|
|
||||||
class PowerExt : public ::aidl::google::hardware::power::extension::pixel::BnPowerExt {
|
class PowerExt : public ::aidl::google::hardware::power::extension::pixel::BnPowerExt {
|
||||||
public:
|
public:
|
||||||
PowerExt(std::shared_ptr<DisplayLowPower> dlpw, std::shared_ptr<AdaptiveCpu> acpu)
|
PowerExt() {}
|
||||||
: mDisplayLowPower(dlpw), mAdaptiveCpu(acpu) {}
|
|
||||||
ndk::ScopedAStatus setMode(const std::string &mode, bool enabled) override;
|
ndk::ScopedAStatus setMode(const std::string &mode, bool enabled) override;
|
||||||
ndk::ScopedAStatus isModeSupported(const std::string &mode, bool *_aidl_return) override;
|
ndk::ScopedAStatus isModeSupported(const std::string &mode, bool *_aidl_return) override;
|
||||||
ndk::ScopedAStatus setBoost(const std::string &boost, int32_t durationMs) override;
|
ndk::ScopedAStatus setBoost(const std::string &boost, int32_t durationMs) override;
|
||||||
ndk::ScopedAStatus isBoostSupported(const std::string &boost, bool *_aidl_return) override;
|
ndk::ScopedAStatus isBoostSupported(const std::string &boost, bool *_aidl_return) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<DisplayLowPower> mDisplayLowPower;
|
|
||||||
std::shared_ptr<AdaptiveCpu> mAdaptiveCpu;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pixel
|
} // namespace pixel
|
|
@ -52,11 +52,14 @@ static inline int64_t ns_to_100us(int64_t ns) {
|
||||||
return ns / 100000;
|
return ns / 100000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t convertWorkDurationToBoostByPid(std::shared_ptr<AdpfConfig> adpfConfig,
|
} // namespace
|
||||||
nanoseconds targetDuration,
|
|
||||||
const std::vector<WorkDuration> &actualDurations,
|
int64_t PowerHintSession::convertWorkDurationToBoostByPid(
|
||||||
int64_t *integral_error, int64_t *previous_error,
|
const std::vector<WorkDuration> &actualDurations) {
|
||||||
const std::string &idstr) {
|
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
|
||||||
|
const nanoseconds &targetDuration = mDescriptor->duration;
|
||||||
|
int64_t &integral_error = mDescriptor->integral_error;
|
||||||
|
int64_t &previous_error = mDescriptor->previous_error;
|
||||||
uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
|
uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
|
||||||
uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
|
uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
|
||||||
uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
|
uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
|
||||||
|
@ -80,70 +83,50 @@ static int64_t convertWorkDurationToBoostByPid(std::shared_ptr<AdpfConfig> adpfC
|
||||||
// PID control algorithm
|
// PID control algorithm
|
||||||
int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
|
int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
|
||||||
if (i >= d_start) {
|
if (i >= d_start) {
|
||||||
derivative_sum += error - (*previous_error);
|
derivative_sum += error - previous_error;
|
||||||
}
|
}
|
||||||
if (i >= p_start) {
|
if (i >= p_start) {
|
||||||
err_sum += error;
|
err_sum += error;
|
||||||
}
|
}
|
||||||
if (i >= i_start) {
|
if (i >= i_start) {
|
||||||
*integral_error = *integral_error + error * dt;
|
integral_error += error * dt;
|
||||||
*integral_error = std::min(adpfConfig->getPidIHighDivI(), *integral_error);
|
integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error);
|
||||||
*integral_error = std::max(adpfConfig->getPidILowDivI(), *integral_error);
|
integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error);
|
||||||
}
|
}
|
||||||
*previous_error = error;
|
previous_error = error;
|
||||||
}
|
}
|
||||||
int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : adpfConfig->mPidPu) *
|
int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : adpfConfig->mPidPu) *
|
||||||
err_sum / (length - p_start));
|
err_sum / (length - p_start));
|
||||||
int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * (*integral_error));
|
int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * integral_error);
|
||||||
int64_t dOut =
|
int64_t dOut =
|
||||||
static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
|
static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
|
||||||
derivative_sum / dt / (length - d_start));
|
derivative_sum / dt / (length - d_start));
|
||||||
|
|
||||||
int64_t output = pOut + iOut + dOut;
|
int64_t output = pOut + iOut + dOut;
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
std::string sz = StringPrintf("adpf.%s-pid.err", idstr.c_str());
|
traceSessionVal("pid.err", err_sum / (length - p_start));
|
||||||
ATRACE_INT(sz.c_str(), err_sum / (length - p_start));
|
traceSessionVal("pid.integral", integral_error);
|
||||||
sz = StringPrintf("adpf.%s-pid.integral", idstr.c_str());
|
traceSessionVal("pid.derivative", derivative_sum / dt / (length - d_start));
|
||||||
ATRACE_INT(sz.c_str(), *integral_error);
|
traceSessionVal("pid.pOut", pOut);
|
||||||
sz = StringPrintf("adpf.%s-pid.derivative", idstr.c_str());
|
traceSessionVal("pid.iOut", iOut);
|
||||||
ATRACE_INT(sz.c_str(), derivative_sum / dt / (length - d_start));
|
traceSessionVal("pid.dOut", dOut);
|
||||||
sz = StringPrintf("adpf.%s-pid.pOut", idstr.c_str());
|
traceSessionVal("pid.output", output);
|
||||||
ATRACE_INT(sz.c_str(), pOut);
|
|
||||||
sz = StringPrintf("adpf.%s-pid.iOut", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), iOut);
|
|
||||||
sz = StringPrintf("adpf.%s-pid.dOut", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), dOut);
|
|
||||||
sz = StringPrintf("adpf.%s-pid.output", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), output);
|
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
|
||||||
|
int64_t durationNanos) {
|
||||||
PowerHintSession::PowerHintSession(std::shared_ptr<AdaptiveCpu> adaptiveCpu, int32_t tgid,
|
|
||||||
int32_t uid, const std::vector<int32_t> &threadIds,
|
|
||||||
int64_t durationNanos)
|
|
||||||
: mAdaptiveCpu(adaptiveCpu) {
|
|
||||||
mDescriptor = new AppHintDesc(tgid, uid, threadIds);
|
mDescriptor = new AppHintDesc(tgid, uid, threadIds);
|
||||||
mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
|
mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
|
||||||
|
mIdString = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid,
|
||||||
|
mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
|
||||||
mStaleTimerHandler = sp<StaleTimerHandler>(new StaleTimerHandler(this));
|
mStaleTimerHandler = sp<StaleTimerHandler>(new StaleTimerHandler(this));
|
||||||
mEarlyBoostHandler = sp<EarlyBoostHandler>(new EarlyBoostHandler(this));
|
|
||||||
mPowerManagerHandler = PowerSessionManager::getInstance();
|
mPowerManagerHandler = PowerSessionManager::getInstance();
|
||||||
mLastUpdatedTime.store(std::chrono::steady_clock::now());
|
mLastUpdatedTime.store(std::chrono::steady_clock::now());
|
||||||
mLastStartedTimeNs =
|
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
||||||
(std::chrono::steady_clock::now() - mDescriptor->duration).time_since_epoch())
|
|
||||||
.count();
|
|
||||||
mLastDurationNs = durationNanos;
|
|
||||||
mWorkPeriodNs = durationNanos;
|
|
||||||
|
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("target", mDescriptor->duration.count());
|
||||||
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
|
traceSessionVal("active", mDescriptor->is_active.load());
|
||||||
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
|
|
||||||
sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
|
|
||||||
}
|
}
|
||||||
PowerSessionManager::getInstance()->addPowerSession(this);
|
PowerSessionManager::getInstance()->addPowerSession(this);
|
||||||
// init boost
|
// init boost
|
||||||
|
@ -155,21 +138,15 @@ PowerHintSession::~PowerHintSession() {
|
||||||
close();
|
close();
|
||||||
ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
|
ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("target", 0);
|
||||||
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
|
traceSessionVal("actl_last", 0);
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
traceSessionVal("active", 0);
|
||||||
sz = StringPrintf("adpf.%s-actl_last", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
|
||||||
sz = sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
|
||||||
}
|
}
|
||||||
delete mDescriptor;
|
delete mDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PowerHintSession::getIdString() const {
|
void PowerHintSession::traceSessionVal(char const *identifier, int64_t val) const {
|
||||||
std::string idstr = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid,
|
ATRACE_INT(StringPrintf("adpf.%s-%s", mIdString.c_str(), identifier).c_str(), val);
|
||||||
mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
|
|
||||||
return idstr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PowerHintSession::isAppSession() {
|
bool PowerHintSession::isAppSession() {
|
||||||
|
@ -182,7 +159,7 @@ void PowerHintSession::updateUniveralBoostMode() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", getIdString().c_str());
|
const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", mIdString.c_str());
|
||||||
ATRACE_BEGIN(tag.c_str());
|
ATRACE_BEGIN(tag.c_str());
|
||||||
}
|
}
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
|
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
|
||||||
|
@ -191,6 +168,15 @@ void PowerHintSession::updateUniveralBoostMode() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PowerHintSession::tryToSendPowerHint(std::string hint) {
|
||||||
|
if (!mSupportedHints[hint].has_value()) {
|
||||||
|
mSupportedHints[hint] = HintManager::GetInstance()->IsHintSupported(hint);
|
||||||
|
}
|
||||||
|
if (mSupportedHints[hint].value()) {
|
||||||
|
HintManager::GetInstance()->DoHint(hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int PowerHintSession::setSessionUclampMin(int32_t min) {
|
int PowerHintSession::setSessionUclampMin(int32_t min) {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(mSessionLock);
|
std::lock_guard<std::mutex> guard(mSessionLock);
|
||||||
|
@ -202,9 +188,7 @@ int PowerHintSession::setSessionUclampMin(int32_t min) {
|
||||||
PowerSessionManager::getInstance()->setUclampMin(this, min);
|
PowerSessionManager::getInstance()->setUclampMin(this, min);
|
||||||
|
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("min", min);
|
||||||
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), min);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +198,7 @@ int PowerHintSession::getUclampMin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::dumpToStream(std::ostream &stream) {
|
void PowerHintSession::dumpToStream(std::ostream &stream) {
|
||||||
stream << "ID.Min.Act.Timeout(" << getIdString();
|
stream << "ID.Min.Act.Timeout(" << mIdString;
|
||||||
stream << ", " << mDescriptor->current_min;
|
stream << ", " << mDescriptor->current_min;
|
||||||
stream << ", " << mDescriptor->is_active;
|
stream << ", " << mDescriptor->is_active;
|
||||||
stream << ", " << isTimeout() << ")";
|
stream << ", " << isTimeout() << ")";
|
||||||
|
@ -231,9 +215,7 @@ ndk::ScopedAStatus PowerHintSession::pause() {
|
||||||
mDescriptor->is_active.store(false);
|
mDescriptor->is_active.store(false);
|
||||||
setStale();
|
setStale();
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("active", mDescriptor->is_active.load());
|
||||||
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
|
|
||||||
}
|
}
|
||||||
updateUniveralBoostMode();
|
updateUniveralBoostMode();
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
|
@ -250,9 +232,7 @@ ndk::ScopedAStatus PowerHintSession::resume() {
|
||||||
// resume boost
|
// resume boost
|
||||||
setSessionUclampMin(mDescriptor->current_min);
|
setSessionUclampMin(mDescriptor->current_min);
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("active", mDescriptor->is_active.load());
|
||||||
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
|
|
||||||
}
|
}
|
||||||
updateUniveralBoostMode();
|
updateUniveralBoostMode();
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
|
@ -265,14 +245,9 @@ ndk::ScopedAStatus PowerHintSession::close() {
|
||||||
}
|
}
|
||||||
// Remove the session from PowerSessionManager first to avoid racing.
|
// Remove the session from PowerSessionManager first to avoid racing.
|
||||||
PowerSessionManager::getInstance()->removePowerSession(this);
|
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();
|
mStaleTimerHandler->setSessionDead();
|
||||||
|
setSessionUclampMin(0);
|
||||||
|
mDescriptor->is_active.store(false);
|
||||||
updateUniveralBoostMode();
|
updateUniveralBoostMode();
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
@ -292,9 +267,7 @@ ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDura
|
||||||
|
|
||||||
mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
|
mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("target", mDescriptor->duration.count());
|
||||||
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
|
@ -322,22 +295,20 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
|
||||||
mDescriptor->update_count++;
|
mDescriptor->update_count++;
|
||||||
bool isFirstFrame = isTimeout();
|
bool isFirstFrame = isTimeout();
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("batch_size", actualDurations.size());
|
||||||
std::string sz = StringPrintf("adpf.%s-batch_size", idstr.c_str());
|
traceSessionVal("actl_last", actualDurations.back().durationNanos);
|
||||||
ATRACE_INT(sz.c_str(), actualDurations.size());
|
traceSessionVal("target", mDescriptor->duration.count());
|
||||||
sz = StringPrintf("adpf.%s-actl_last", idstr.c_str());
|
traceSessionVal("hint.count", mDescriptor->update_count);
|
||||||
ATRACE_INT(sz.c_str(), actualDurations.back().durationNanos);
|
traceSessionVal("hint.overtime",
|
||||||
sz = StringPrintf("adpf.%s-target", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
|
|
||||||
sz = StringPrintf("adpf.%s-hint.count", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), mDescriptor->update_count);
|
|
||||||
sz = StringPrintf("adpf.%s-hint.overtime", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(),
|
|
||||||
actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
|
actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
|
||||||
|
traceSessionVal("session_hint", -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
mLastUpdatedTime.store(std::chrono::steady_clock::now());
|
mLastUpdatedTime.store(std::chrono::steady_clock::now());
|
||||||
if (isFirstFrame) {
|
if (isFirstFrame) {
|
||||||
|
if (isAppSession()) {
|
||||||
|
tryToSendPowerHint("ADPF_FIRST_FRAME");
|
||||||
|
}
|
||||||
updateUniveralBoostMode();
|
updateUniveralBoostMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,22 +316,13 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
|
||||||
setSessionUclampMin(adpfConfig->mUclampMinHigh);
|
setSessionUclampMin(adpfConfig->mUclampMinHigh);
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
int64_t output = convertWorkDurationToBoostByPid(
|
int64_t output = convertWorkDurationToBoostByPid(actualDurations);
|
||||||
adpfConfig, mDescriptor->duration, actualDurations, &(mDescriptor->integral_error),
|
|
||||||
&(mDescriptor->previous_error), getIdString());
|
|
||||||
|
|
||||||
/* apply to all the threads in the group */
|
/* apply to all the threads in the group */
|
||||||
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
|
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
|
||||||
mDescriptor->current_min + static_cast<int>(output));
|
mDescriptor->current_min + static_cast<int>(output));
|
||||||
next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
|
next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
|
||||||
setSessionUclampMin(next_min);
|
setSessionUclampMin(next_min);
|
||||||
mStaleTimerHandler->updateTimer(getStaleTime());
|
|
||||||
if (HintManager::GetInstance()->GetAdpfProfile()->mEarlyBoostOn) {
|
|
||||||
updateWorkPeriod(actualDurations);
|
|
||||||
mEarlyBoostHandler->updateTimer(getEarlyBoostTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
mAdaptiveCpu->ReportWorkDurations(actualDurations, mDescriptor->duration);
|
|
||||||
|
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
@ -392,7 +354,12 @@ bool PowerHintSession::isActive() {
|
||||||
|
|
||||||
bool PowerHintSession::isTimeout() {
|
bool PowerHintSession::isTimeout() {
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
return now >= getStaleTime();
|
time_point<steady_clock> staleTime =
|
||||||
|
mLastUpdatedTime.load() +
|
||||||
|
nanoseconds(static_cast<int64_t>(
|
||||||
|
mDescriptor->duration.count() *
|
||||||
|
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
|
||||||
|
return now >= staleTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<int> &PowerHintSession::getTidList() const {
|
const std::vector<int> &PowerHintSession::getTidList() const {
|
||||||
|
@ -405,101 +372,30 @@ void PowerHintSession::setStale() {
|
||||||
// Deliver a task to check if all sessions are inactive.
|
// Deliver a task to check if all sessions are inactive.
|
||||||
updateUniveralBoostMode();
|
updateUniveralBoostMode();
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = getIdString();
|
traceSessionVal("min", 0);
|
||||||
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::wakeup() {
|
|
||||||
std::lock_guard<std::mutex> guard(mSessionLock);
|
|
||||||
|
|
||||||
// We only wake up non-paused and stale sessions
|
|
||||||
if (mSessionClosed || !isActive() || !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();
|
|
||||||
|
|
||||||
if (ATRACE_ENABLED()) {
|
|
||||||
const std::string idstr = getIdString();
|
|
||||||
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), min);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PowerHintSession::updateWorkPeriod(const std::vector<WorkDuration> &actualDurations) {
|
|
||||||
if (actualDurations.size() == 0)
|
|
||||||
return;
|
|
||||||
if (actualDurations.size() >= 2) {
|
|
||||||
const WorkDuration &last = actualDurations[actualDurations.size() - 2];
|
|
||||||
mLastStartedTimeNs = last.timeStampNanos - last.durationNanos;
|
|
||||||
}
|
|
||||||
const WorkDuration ¤t = actualDurations.back();
|
|
||||||
int64_t curr_start = current.timeStampNanos - current.durationNanos;
|
|
||||||
int64_t period = curr_start - mLastStartedTimeNs;
|
|
||||||
if (period > 0 && period < mDescriptor->duration.count() * 2) {
|
|
||||||
// Accounting workload period with moving average for the last 10 workload.
|
|
||||||
mWorkPeriodNs = 0.9 * mWorkPeriodNs + 0.1 * period;
|
|
||||||
if (ATRACE_ENABLED()) {
|
|
||||||
const std::string idstr = getIdString();
|
|
||||||
std::string sz = StringPrintf("adpf.%s-timer.period", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), mWorkPeriodNs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mLastStartedTimeNs = curr_start;
|
|
||||||
mLastDurationNs = current.durationNanos;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_point<steady_clock> PowerHintSession::getEarlyBoostTime() {
|
|
||||||
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
|
|
||||||
int64_t earlyBoostTimeoutNs =
|
|
||||||
(int64_t)mDescriptor->duration.count() * adpfConfig->mEarlyBoostTimeFactor;
|
|
||||||
time_point<steady_clock> nextStartTime =
|
|
||||||
mLastUpdatedTime.load() + nanoseconds(mWorkPeriodNs - mLastDurationNs);
|
|
||||||
return nextStartTime + nanoseconds(earlyBoostTimeoutNs);
|
|
||||||
}
|
|
||||||
|
|
||||||
time_point<steady_clock> PowerHintSession::getStaleTime() {
|
|
||||||
return mLastUpdatedTime.load() +
|
|
||||||
nanoseconds(static_cast<int64_t>(
|
|
||||||
mDescriptor->duration.count() *
|
|
||||||
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PowerHintSession::StaleTimerHandler::updateTimer() {
|
void PowerHintSession::StaleTimerHandler::updateTimer() {
|
||||||
time_point<steady_clock> staleTime =
|
auto now = std::chrono::steady_clock::now();
|
||||||
std::chrono::steady_clock::now() +
|
nanoseconds staleDuration = std::chrono::nanoseconds(
|
||||||
nanoseconds(static_cast<int64_t>(
|
static_cast<int64_t>(mSession->mDescriptor->duration.count() *
|
||||||
mSession->mDescriptor->duration.count() *
|
|
||||||
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
|
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
|
||||||
updateTimer(staleTime);
|
mStaleTime.store(now + staleDuration);
|
||||||
}
|
int64_t next = static_cast<int64_t>(staleDuration.count());
|
||||||
|
|
||||||
void PowerHintSession::StaleTimerHandler::updateTimer(time_point<steady_clock> staleTime) {
|
|
||||||
mStaleTime.store(staleTime);
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(mMessageLock);
|
std::lock_guard<std::mutex> guard(mMessageLock);
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
|
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mSession->mStaleTimerHandler,
|
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
|
||||||
NULL);
|
next, mSession->mStaleTimerHandler, NULL);
|
||||||
}
|
}
|
||||||
mIsMonitoring.store(true);
|
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = mSession->getIdString();
|
mSession->traceSessionVal("timer.stale", 0);
|
||||||
std::string sz = StringPrintf("adpf.%s-timer.stale", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::StaleTimerHandler::handleMessage(const Message &) {
|
void PowerHintSession::StaleTimerHandler::handleMessage(const Message &) {
|
||||||
|
std::lock_guard<std::mutex> guard(mClosedLock);
|
||||||
if (mIsSessionDead) {
|
if (mIsSessionDead) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -514,80 +410,21 @@ void PowerHintSession::StaleTimerHandler::handleMessage(const Message &) {
|
||||||
next, mSession->mStaleTimerHandler, NULL);
|
next, mSession->mStaleTimerHandler, NULL);
|
||||||
} else {
|
} else {
|
||||||
mSession->setStale();
|
mSession->setStale();
|
||||||
mIsMonitoring.store(false);
|
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = mSession->getIdString();
|
mSession->traceSessionVal("session_hint", -1);
|
||||||
std::string sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
const std::string idstr = mSession->getIdString();
|
mSession->traceSessionVal("timer.stale", next > 0 ? 0 : 1);
|
||||||
std::string sz = StringPrintf("adpf.%s-timer.stale", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), mIsMonitoring ? 0 : 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::StaleTimerHandler::setSessionDead() {
|
void PowerHintSession::StaleTimerHandler::setSessionDead() {
|
||||||
std::lock_guard<std::mutex> guard(mStaleLock);
|
std::lock_guard<std::mutex> guard(mClosedLock);
|
||||||
mIsSessionDead = true;
|
mIsSessionDead = true;
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
|
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleTimerHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::EarlyBoostHandler::updateTimer(time_point<steady_clock> boostTime) {
|
|
||||||
mBoostTime.store(boostTime);
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard(mMessageLock);
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mEarlyBoostHandler);
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mSession->mEarlyBoostHandler,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
mIsMonitoring.store(true);
|
|
||||||
if (ATRACE_ENABLED()) {
|
|
||||||
const std::string idstr = mSession->getIdString();
|
|
||||||
std::string sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PowerHintSession::EarlyBoostHandler::handleMessage(const Message &) {
|
|
||||||
std::lock_guard<std::mutex> guard(mBoostLock);
|
|
||||||
if (mIsSessionDead) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto now = std::chrono::steady_clock::now();
|
|
||||||
int64_t next =
|
|
||||||
static_cast<int64_t>(duration_cast<nanoseconds>(mBoostTime.load() - now).count());
|
|
||||||
if (next > 0) {
|
|
||||||
if (ATRACE_ENABLED()) {
|
|
||||||
const std::string idstr = mSession->getIdString();
|
|
||||||
std::string sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 1);
|
|
||||||
}
|
|
||||||
std::lock_guard<std::mutex> guard(mMessageLock);
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mEarlyBoostHandler);
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
|
|
||||||
next, mSession->mEarlyBoostHandler, NULL);
|
|
||||||
} else {
|
|
||||||
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
|
|
||||||
PowerSessionManager::getInstance()->setUclampMin(mSession, adpfConfig->mUclampMinHigh);
|
|
||||||
mIsMonitoring.store(false);
|
|
||||||
if (ATRACE_ENABLED()) {
|
|
||||||
const std::string idstr = mSession->getIdString();
|
|
||||||
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), adpfConfig->mUclampMinHigh);
|
|
||||||
sz = StringPrintf("adpf.%s-timer.earlyboost", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PowerHintSession::EarlyBoostHandler::setSessionDead() {
|
|
||||||
std::lock_guard<std::mutex> guard(mBoostLock);
|
|
||||||
mIsSessionDead = true;
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mEarlyBoostHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
} // namespace pixel
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
} // namespace power
|
} // namespace power
|
|
@ -24,8 +24,6 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "adaptivecpu/AdaptiveCpu.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace google {
|
namespace google {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -70,8 +68,8 @@ struct AppHintDesc {
|
||||||
|
|
||||||
class PowerHintSession : public BnPowerHintSession {
|
class PowerHintSession : public BnPowerHintSession {
|
||||||
public:
|
public:
|
||||||
explicit PowerHintSession(std::shared_ptr<AdaptiveCpu> adaptiveCpu, int32_t tgid, int32_t uid,
|
explicit PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
|
||||||
const std::vector<int32_t> &threadIds, int64_t durationNanos);
|
int64_t durationNanos);
|
||||||
~PowerHintSession();
|
~PowerHintSession();
|
||||||
ndk::ScopedAStatus close() override;
|
ndk::ScopedAStatus close() override;
|
||||||
ndk::ScopedAStatus pause() override;
|
ndk::ScopedAStatus pause() override;
|
||||||
|
@ -81,7 +79,6 @@ class PowerHintSession : public BnPowerHintSession {
|
||||||
const std::vector<WorkDuration> &actualDurations) override;
|
const std::vector<WorkDuration> &actualDurations) override;
|
||||||
bool isActive();
|
bool isActive();
|
||||||
bool isTimeout();
|
bool isTimeout();
|
||||||
void wakeup();
|
|
||||||
void setStale();
|
void setStale();
|
||||||
// Is this hint session for a user application
|
// Is this hint session for a user application
|
||||||
bool isAppSession();
|
bool isAppSession();
|
||||||
|
@ -89,62 +86,39 @@ class PowerHintSession : public BnPowerHintSession {
|
||||||
int getUclampMin();
|
int getUclampMin();
|
||||||
void dumpToStream(std::ostream &stream);
|
void dumpToStream(std::ostream &stream);
|
||||||
|
|
||||||
void updateWorkPeriod(const std::vector<WorkDuration> &actualDurations);
|
|
||||||
time_point<steady_clock> getEarlyBoostTime();
|
|
||||||
time_point<steady_clock> getStaleTime();
|
time_point<steady_clock> getStaleTime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class StaleTimerHandler : public MessageHandler {
|
class StaleTimerHandler : public MessageHandler {
|
||||||
public:
|
public:
|
||||||
StaleTimerHandler(PowerHintSession *session)
|
StaleTimerHandler(PowerHintSession *session) : mSession(session), mIsSessionDead(false) {}
|
||||||
: mSession(session), mIsMonitoring(false), mIsSessionDead(false) {}
|
|
||||||
void updateTimer();
|
void updateTimer();
|
||||||
void updateTimer(time_point<steady_clock> staleTime);
|
|
||||||
void handleMessage(const Message &message) override;
|
void handleMessage(const Message &message) override;
|
||||||
void setSessionDead();
|
void setSessionDead();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PowerHintSession *mSession;
|
PowerHintSession *mSession;
|
||||||
std::mutex mStaleLock;
|
std::mutex mClosedLock;
|
||||||
std::mutex mMessageLock;
|
std::mutex mMessageLock;
|
||||||
std::atomic<time_point<steady_clock>> mStaleTime;
|
std::atomic<time_point<steady_clock>> mStaleTime;
|
||||||
std::atomic<bool> mIsMonitoring;
|
|
||||||
bool mIsSessionDead;
|
|
||||||
};
|
|
||||||
|
|
||||||
class EarlyBoostHandler : public MessageHandler {
|
|
||||||
public:
|
|
||||||
EarlyBoostHandler(PowerHintSession *session)
|
|
||||||
: mSession(session), mIsMonitoring(false), mIsSessionDead(false) {}
|
|
||||||
void updateTimer(time_point<steady_clock> boostTime);
|
|
||||||
void handleMessage(const Message &message) override;
|
|
||||||
void setSessionDead();
|
|
||||||
|
|
||||||
private:
|
|
||||||
PowerHintSession *mSession;
|
|
||||||
std::mutex mBoostLock;
|
|
||||||
std::mutex mMessageLock;
|
|
||||||
std::atomic<time_point<steady_clock>> mBoostTime;
|
|
||||||
std::atomic<bool> mIsMonitoring;
|
|
||||||
bool mIsSessionDead;
|
bool mIsSessionDead;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateUniveralBoostMode();
|
void updateUniveralBoostMode();
|
||||||
int setSessionUclampMin(int32_t min);
|
int setSessionUclampMin(int32_t min);
|
||||||
std::string getIdString() const;
|
void tryToSendPowerHint(std::string hint);
|
||||||
const std::shared_ptr<AdaptiveCpu> mAdaptiveCpu;
|
int64_t convertWorkDurationToBoostByPid(const std::vector<WorkDuration> &actualDurations);
|
||||||
|
void traceSessionVal(char const *identifier, int64_t val) const;
|
||||||
AppHintDesc *mDescriptor = nullptr;
|
AppHintDesc *mDescriptor = nullptr;
|
||||||
sp<StaleTimerHandler> mStaleTimerHandler;
|
sp<StaleTimerHandler> mStaleTimerHandler;
|
||||||
sp<EarlyBoostHandler> mEarlyBoostHandler;
|
|
||||||
std::atomic<time_point<steady_clock>> mLastUpdatedTime;
|
std::atomic<time_point<steady_clock>> mLastUpdatedTime;
|
||||||
sp<MessageHandler> mPowerManagerHandler;
|
sp<MessageHandler> mPowerManagerHandler;
|
||||||
std::mutex mSessionLock;
|
std::mutex mSessionLock;
|
||||||
std::atomic<bool> mSessionClosed = false;
|
std::atomic<bool> mSessionClosed = false;
|
||||||
// These 3 variables are for earlyboost work period estimation.
|
std::string mIdString;
|
||||||
int64_t mLastStartedTimeNs;
|
// To cache the status of whether ADPF hints are supported.
|
||||||
int64_t mLastDurationNs;
|
std::unordered_map<std::string, std::optional<bool>> mSupportedHints;
|
||||||
int64_t mWorkPeriodNs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pixel
|
} // namespace pixel
|
|
@ -33,6 +33,7 @@ namespace power {
|
||||||
namespace impl {
|
namespace impl {
|
||||||
namespace pixel {
|
namespace pixel {
|
||||||
|
|
||||||
|
using ::android::perfmgr::AdpfConfig;
|
||||||
using ::android::perfmgr::HintManager;
|
using ::android::perfmgr::HintManager;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -96,16 +97,6 @@ void PowerSessionManager::updateHintBoost(const std::string &boost, int32_t dura
|
||||||
ATRACE_CALL();
|
ATRACE_CALL();
|
||||||
ALOGV("PowerSessionManager::updateHintBoost: boost: %s, durationMs: %d", boost.c_str(),
|
ALOGV("PowerSessionManager::updateHintBoost: boost: %s, durationMs: %d", boost.c_str(),
|
||||||
durationMs);
|
durationMs);
|
||||||
if (boost.compare("DISPLAY_UPDATE_IMMINENT") == 0) {
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mWakeupHandler, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PowerSessionManager::wakeSessions() {
|
|
||||||
std::lock_guard<std::mutex> guard(mLock);
|
|
||||||
for (PowerHintSession *s : mSessions) {
|
|
||||||
s->wakeup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PowerSessionManager::getDisplayRefreshRate() {
|
int PowerSessionManager::getDisplayRefreshRate() {
|
||||||
|
@ -201,10 +192,6 @@ void PowerSessionManager::handleMessage(const Message &) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerSessionManager::WakeupHandler::handleMessage(const Message &) {
|
|
||||||
PowerSessionManager::getInstance()->wakeSessions();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PowerSessionManager::dumpToFd(int fd) {
|
void PowerSessionManager::dumpToFd(int fd) {
|
||||||
std::ostringstream dump_buf;
|
std::ostringstream dump_buf;
|
||||||
std::lock_guard<std::mutex> guard(mLock);
|
std::lock_guard<std::mutex> guard(mLock);
|
|
@ -62,14 +62,6 @@ class PowerSessionManager : public MessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class WakeupHandler : public MessageHandler {
|
|
||||||
public:
|
|
||||||
WakeupHandler() {}
|
|
||||||
void handleMessage(const Message &message) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
void wakeSessions();
|
|
||||||
std::optional<bool> isAnyAppSessionActive();
|
std::optional<bool> isAnyAppSessionActive();
|
||||||
void disableSystemTopAppBoost();
|
void disableSystemTopAppBoost();
|
||||||
void enableSystemTopAppBoost();
|
void enableSystemTopAppBoost();
|
||||||
|
@ -78,7 +70,6 @@ class PowerSessionManager : public MessageHandler {
|
||||||
std::unordered_set<PowerHintSession *> mSessions; // protected by mLock
|
std::unordered_set<PowerHintSession *> mSessions; // protected by mLock
|
||||||
std::unordered_map<int, int> mTidRefCountMap; // protected by mLock
|
std::unordered_map<int, int> mTidRefCountMap; // protected by mLock
|
||||||
std::unordered_map<int, std::unordered_set<PowerHintSession *>> mTidSessionListMap;
|
std::unordered_map<int, std::unordered_set<PowerHintSession *>> mTidSessionListMap;
|
||||||
sp<WakeupHandler> mWakeupHandler;
|
|
||||||
bool mActive; // protected by mLock
|
bool mActive; // protected by mLock
|
||||||
/**
|
/**
|
||||||
* mLock to pretect the above data objects opertions.
|
* mLock to pretect the above data objects opertions.
|
||||||
|
@ -90,9 +81,7 @@ class PowerSessionManager : public MessageHandler {
|
||||||
: kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
|
: kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
|
||||||
"ADPF_DISABLE_TA_BOOST")),
|
"ADPF_DISABLE_TA_BOOST")),
|
||||||
mActive(false),
|
mActive(false),
|
||||||
mDisplayRefreshRate(60) {
|
mDisplayRefreshRate(60) {}
|
||||||
mWakeupHandler = sp<WakeupHandler>(new WakeupHandler());
|
|
||||||
}
|
|
||||||
PowerSessionManager(PowerSessionManager const &) = delete;
|
PowerSessionManager(PowerSessionManager const &) = delete;
|
||||||
void operator=(PowerSessionManager const &) = delete;
|
void operator=(PowerSessionManager const &) = delete;
|
||||||
};
|
};
|
|
@ -1,233 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "AdaptiveCpu.h"
|
|
||||||
|
|
||||||
#include <android-base/file.h>
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <android-base/properties.h>
|
|
||||||
#include <perfmgr/HintManager.h>
|
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <deque>
|
|
||||||
#include <numeric>
|
|
||||||
|
|
||||||
#include "CpuLoadReaderSysDevices.h"
|
|
||||||
#include "Model.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
using ::android::perfmgr::HintManager;
|
|
||||||
|
|
||||||
// We pass the previous N ModelInputs to the model, including the most recent ModelInput.
|
|
||||||
constexpr uint32_t kNumHistoricalModelInputs = 3;
|
|
||||||
|
|
||||||
// TODO(b/207662659): Add config for changing between different reader types.
|
|
||||||
AdaptiveCpu::AdaptiveCpu() {}
|
|
||||||
|
|
||||||
bool AdaptiveCpu::IsEnabled() const {
|
|
||||||
return mIsEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpu::HintReceived(bool enable) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
LOG(INFO) << "AdaptiveCpu received hint: enable=" << enable;
|
|
||||||
if (enable) {
|
|
||||||
StartThread();
|
|
||||||
} else {
|
|
||||||
SuspendThread();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpu::StartThread() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
std::lock_guard lock(mThreadCreationMutex);
|
|
||||||
LOG(INFO) << "Starting AdaptiveCpu thread";
|
|
||||||
mIsEnabled = true;
|
|
||||||
mShouldReloadConfig = true;
|
|
||||||
mLastEnabledHintTime = mTimeSource.GetTime();
|
|
||||||
if (!mLoopThread.joinable()) {
|
|
||||||
mLoopThread = std::thread([&]() {
|
|
||||||
pthread_setname_np(pthread_self(), "AdaptiveCpu");
|
|
||||||
// Parent threads may have higher priorities, so we reset to the default.
|
|
||||||
int ret = setpriority(PRIO_PROCESS, 0, 0);
|
|
||||||
if (ret != 0) {
|
|
||||||
PLOG(ERROR) << "setpriority on AdaptiveCpu thread failed: " << ret;
|
|
||||||
}
|
|
||||||
LOG(INFO) << "Started AdaptiveCpu thread successfully";
|
|
||||||
RunMainLoop();
|
|
||||||
LOG(ERROR) << "AdaptiveCpu thread ended, this should never happen!";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpu::SuspendThread() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
LOG(INFO) << "Stopping AdaptiveCpu thread";
|
|
||||||
// This stops the thread from receiving work durations in ReportWorkDurations, which means the
|
|
||||||
// thread blocks indefinitely.
|
|
||||||
mIsEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpu::ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
|
|
||||||
std::chrono::nanoseconds targetDuration) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
if (!mIsEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!mWorkDurationProcessor.ReportWorkDurations(workDurations, targetDuration)) {
|
|
||||||
mIsEnabled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mWorkDurationsAvailableCondition.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpu::WaitForEnabledAndWorkDurations() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
std::unique_lock<std::mutex> lock(mWaitMutex);
|
|
||||||
// TODO(b/188770301) Once the gating logic is implemented, don't block indefinitely.
|
|
||||||
mWorkDurationsAvailableCondition.wait(
|
|
||||||
lock, [&] { return mIsEnabled && mWorkDurationProcessor.HasWorkDurations(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpu::RunMainLoop() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
|
|
||||||
std::deque<ModelInput> historicalModelInputs;
|
|
||||||
ThrottleDecision previousThrottleDecision = ThrottleDecision::NO_THROTTLE;
|
|
||||||
while (true) {
|
|
||||||
ATRACE_NAME("loop");
|
|
||||||
WaitForEnabledAndWorkDurations();
|
|
||||||
|
|
||||||
if (mLastEnabledHintTime + mConfig.enabledHintTimeout < mTimeSource.GetTime()) {
|
|
||||||
LOG(INFO) << "Adaptive CPU hint timed out, last enabled time="
|
|
||||||
<< mLastEnabledHintTime.count() << "ns";
|
|
||||||
mIsEnabled = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mShouldReloadConfig) {
|
|
||||||
if (!AdaptiveCpuConfig::ReadFromSystemProperties(&mConfig)) {
|
|
||||||
mIsEnabled = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
LOG(INFO) << "Read config: " << mConfig;
|
|
||||||
mShouldReloadConfig = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ATRACE_BEGIN("compute");
|
|
||||||
mAdaptiveCpuStats.RegisterStartRun();
|
|
||||||
|
|
||||||
if (!mIsInitialized) {
|
|
||||||
if (!mKernelCpuFeatureReader.Init()) {
|
|
||||||
mIsEnabled = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mDevice = ReadDevice();
|
|
||||||
mIsInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelInput modelInput;
|
|
||||||
modelInput.previousThrottleDecision = previousThrottleDecision;
|
|
||||||
|
|
||||||
modelInput.workDurationFeatures = mWorkDurationProcessor.GetFeatures();
|
|
||||||
LOG(VERBOSE) << "Got work durations: count=" << modelInput.workDurationFeatures.numDurations
|
|
||||||
<< ", average=" << modelInput.workDurationFeatures.averageDuration.count()
|
|
||||||
<< "ns";
|
|
||||||
if (modelInput.workDurationFeatures.numDurations == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mKernelCpuFeatureReader.GetRecentCpuFeatures(&modelInput.cpuPolicyAverageFrequencyHz,
|
|
||||||
&modelInput.cpuCoreIdleTimesPercentage)) {
|
|
||||||
mIsEnabled = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
modelInput.LogToAtrace();
|
|
||||||
historicalModelInputs.push_back(modelInput);
|
|
||||||
if (historicalModelInputs.size() > kNumHistoricalModelInputs) {
|
|
||||||
historicalModelInputs.pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
const ThrottleDecision throttleDecision = mModel.Run(historicalModelInputs, mConfig);
|
|
||||||
LOG(VERBOSE) << "Model decision: " << static_cast<uint32_t>(throttleDecision);
|
|
||||||
ATRACE_INT("AdaptiveCpu_throttleDecision", static_cast<uint32_t>(throttleDecision));
|
|
||||||
|
|
||||||
{
|
|
||||||
ATRACE_NAME("sendHints");
|
|
||||||
const auto now = mTimeSource.GetTime();
|
|
||||||
// Resend the throttle hints, even if they've not changed, if the previous send is close
|
|
||||||
// to timing out. We define "close to" as half the hint timeout, as we can't guarantee
|
|
||||||
// we will run again before the actual timeout.
|
|
||||||
const bool throttleHintMayTimeout =
|
|
||||||
now - mLastThrottleHintTime > mConfig.hintTimeout / 2;
|
|
||||||
if (throttleDecision != previousThrottleDecision || throttleHintMayTimeout) {
|
|
||||||
ATRACE_NAME("sendNewHints");
|
|
||||||
mLastThrottleHintTime = now;
|
|
||||||
for (const auto &hintName : THROTTLE_DECISION_TO_HINT_NAMES.at(throttleDecision)) {
|
|
||||||
HintManager::GetInstance()->DoHint(hintName, mConfig.hintTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (throttleDecision != previousThrottleDecision) {
|
|
||||||
ATRACE_NAME("endOldHints");
|
|
||||||
for (const auto &hintName :
|
|
||||||
THROTTLE_DECISION_TO_HINT_NAMES.at(previousThrottleDecision)) {
|
|
||||||
HintManager::GetInstance()->EndHint(hintName);
|
|
||||||
}
|
|
||||||
previousThrottleDecision = throttleDecision;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mAdaptiveCpuStats.RegisterSuccessfulRun(previousThrottleDecision, throttleDecision,
|
|
||||||
modelInput.workDurationFeatures, mConfig);
|
|
||||||
ATRACE_END(); // compute
|
|
||||||
{
|
|
||||||
ATRACE_NAME("sleep");
|
|
||||||
std::this_thread::sleep_for(mConfig.iterationSleepDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpu::DumpToFd(int fd) const {
|
|
||||||
std::stringstream result;
|
|
||||||
result << "========== Begin Adaptive CPU stats ==========\n";
|
|
||||||
result << "Enabled: " << mIsEnabled << "\n";
|
|
||||||
result << "Config: " << mConfig << "\n";
|
|
||||||
mKernelCpuFeatureReader.DumpToStream(result);
|
|
||||||
mAdaptiveCpuStats.DumpToStream(result);
|
|
||||||
result << "========== End Adaptive CPU stats ==========\n";
|
|
||||||
if (!::android::base::WriteStringToFd(result.str(), fd)) {
|
|
||||||
PLOG(ERROR) << "Failed to dump state to fd";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <aidl/android/hardware/power/WorkDuration.h>
|
|
||||||
#include <perfmgr/HintManager.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "AdaptiveCpuConfig.h"
|
|
||||||
#include "AdaptiveCpuStats.h"
|
|
||||||
#include "Device.h"
|
|
||||||
#include "KernelCpuFeatureReader.h"
|
|
||||||
#include "Model.h"
|
|
||||||
#include "WorkDurationProcessor.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
using std::chrono_literals::operator""ms;
|
|
||||||
using ::aidl::android::hardware::power::WorkDuration;
|
|
||||||
using ::android::perfmgr::HintManager;
|
|
||||||
|
|
||||||
// Applies CPU frequency hints infered by an ML model based on the recent CPU statistics and work
|
|
||||||
// durations.
|
|
||||||
// This class's public members are not synchronised and should not be used from multiple threads,
|
|
||||||
// with the exception of ReportWorkDuration, which can be called from an arbitrary thread.
|
|
||||||
class AdaptiveCpu {
|
|
||||||
public:
|
|
||||||
AdaptiveCpu();
|
|
||||||
|
|
||||||
bool IsEnabled() const;
|
|
||||||
|
|
||||||
// Called when the Adaptive CPU hint is received. This method enables/disables the Adaptive CPU
|
|
||||||
// thread.
|
|
||||||
void HintReceived(bool enable);
|
|
||||||
|
|
||||||
// Reports work durations for processing. This method returns immediately as work durations are
|
|
||||||
// processed asynchonuously.
|
|
||||||
void ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
|
|
||||||
std::chrono::nanoseconds targetDuration);
|
|
||||||
|
|
||||||
// Dump info to a file descriptor. Called when dumping service info.
|
|
||||||
void DumpToFd(int fd) const;
|
|
||||||
|
|
||||||
// When PowerExt receives a hint with this name, HintReceived() is called.
|
|
||||||
static constexpr char HINT_NAME[] = "ADAPTIVE_CPU";
|
|
||||||
|
|
||||||
private:
|
|
||||||
void StartThread();
|
|
||||||
|
|
||||||
void SuspendThread();
|
|
||||||
|
|
||||||
// The main loop of Adaptive CPU, which runs in a separate thread.
|
|
||||||
void RunMainLoop();
|
|
||||||
|
|
||||||
void WaitForEnabledAndWorkDurations();
|
|
||||||
|
|
||||||
Model mModel;
|
|
||||||
WorkDurationProcessor mWorkDurationProcessor;
|
|
||||||
KernelCpuFeatureReader mKernelCpuFeatureReader;
|
|
||||||
AdaptiveCpuStats mAdaptiveCpuStats;
|
|
||||||
const TimeSource mTimeSource;
|
|
||||||
|
|
||||||
// The thread in which work durations are processed.
|
|
||||||
std::thread mLoopThread;
|
|
||||||
|
|
||||||
// Guards against creating multiple threads in the case HintReceived(true) is called on separate
|
|
||||||
// threads simultaneously.
|
|
||||||
std::mutex mThreadCreationMutex;
|
|
||||||
// Used when waiting in WaitForEnabledAndWorkDurations().
|
|
||||||
std::mutex mWaitMutex;
|
|
||||||
|
|
||||||
// A condition variable that will be notified when new work durations arrive.
|
|
||||||
std::condition_variable mWorkDurationsAvailableCondition;
|
|
||||||
|
|
||||||
volatile bool mIsEnabled = false;
|
|
||||||
bool mIsInitialized = false;
|
|
||||||
volatile bool mShouldReloadConfig = false;
|
|
||||||
std::chrono::nanoseconds mLastEnabledHintTime;
|
|
||||||
std::chrono::nanoseconds mLastThrottleHintTime;
|
|
||||||
Device mDevice;
|
|
||||||
AdaptiveCpuConfig mConfig = AdaptiveCpuConfig::DEFAULT;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "AdaptiveCpuConfig.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <android-base/properties.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
using std::chrono_literals::operator""ms;
|
|
||||||
using std::chrono_literals::operator""min;
|
|
||||||
|
|
||||||
constexpr std::string_view kIterationSleepDurationProperty(
|
|
||||||
"debug.adaptivecpu.iteration_sleep_duration_ms");
|
|
||||||
static const std::chrono::milliseconds kIterationSleepDurationMin = 20ms;
|
|
||||||
constexpr std::string_view kHintTimeoutProperty("debug.adaptivecpu.hint_timeout_ms");
|
|
||||||
// "percent" as range is 0-100, while the in-memory is "probability" as range is 0-1.
|
|
||||||
constexpr std::string_view kRandomThrottleDecisionPercentProperty(
|
|
||||||
"debug.adaptivecpu.random_throttle_decision_percent");
|
|
||||||
constexpr std::string_view kRandomThrottleOptionsProperty(
|
|
||||||
"debug.adaptivecpu.random_throttle_options");
|
|
||||||
constexpr std::string_view kEnabledHintTimeoutProperty("debug.adaptivecpu.enabled_hint_timeout_ms");
|
|
||||||
|
|
||||||
bool ParseThrottleDecisions(const std::string &input, std::vector<ThrottleDecision> *output);
|
|
||||||
std::string FormatThrottleDecisions(const std::vector<ThrottleDecision> &throttleDecisions);
|
|
||||||
|
|
||||||
const AdaptiveCpuConfig AdaptiveCpuConfig::DEFAULT{
|
|
||||||
// N.B.: The model will typically be trained with this value set to 25ms. We set it to 1s as
|
|
||||||
// a safety measure, but best performance will be seen at 25ms.
|
|
||||||
.iterationSleepDuration = 1000ms,
|
|
||||||
.hintTimeout = 2000ms,
|
|
||||||
.randomThrottleDecisionProbability = 0,
|
|
||||||
.randomThrottleOptions = {ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_50,
|
|
||||||
ThrottleDecision::THROTTLE_60, ThrottleDecision::THROTTLE_70,
|
|
||||||
ThrottleDecision::THROTTLE_80, ThrottleDecision::THROTTLE_90},
|
|
||||||
.enabledHintTimeout = 120min,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool AdaptiveCpuConfig::ReadFromSystemProperties(AdaptiveCpuConfig *output) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
|
|
||||||
output->iterationSleepDuration = std::chrono::milliseconds(
|
|
||||||
::android::base::GetUintProperty<uint32_t>(kIterationSleepDurationProperty.data(),
|
|
||||||
DEFAULT.iterationSleepDuration.count()));
|
|
||||||
output->iterationSleepDuration =
|
|
||||||
std::max(output->iterationSleepDuration, kIterationSleepDurationMin);
|
|
||||||
|
|
||||||
output->hintTimeout = std::chrono::milliseconds(::android::base::GetUintProperty<uint32_t>(
|
|
||||||
kHintTimeoutProperty.data(), DEFAULT.hintTimeout.count()));
|
|
||||||
|
|
||||||
output->randomThrottleDecisionProbability =
|
|
||||||
static_cast<double>(::android::base::GetUintProperty<uint32_t>(
|
|
||||||
kRandomThrottleDecisionPercentProperty.data(),
|
|
||||||
DEFAULT.randomThrottleDecisionProbability * 100)) /
|
|
||||||
100;
|
|
||||||
if (output->randomThrottleDecisionProbability > 1.0) {
|
|
||||||
LOG(ERROR) << "Received bad value for " << kRandomThrottleDecisionPercentProperty << ": "
|
|
||||||
<< output->randomThrottleDecisionProbability;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string randomThrottleOptionsStr =
|
|
||||||
::android::base::GetProperty(kRandomThrottleOptionsProperty.data(),
|
|
||||||
FormatThrottleDecisions(DEFAULT.randomThrottleOptions));
|
|
||||||
output->randomThrottleOptions.clear();
|
|
||||||
if (!ParseThrottleDecisions(randomThrottleOptionsStr, &output->randomThrottleOptions)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->enabledHintTimeout =
|
|
||||||
std::chrono::milliseconds(::android::base::GetUintProperty<uint32_t>(
|
|
||||||
kEnabledHintTimeoutProperty.data(), DEFAULT.enabledHintTimeout.count()));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AdaptiveCpuConfig::operator==(const AdaptiveCpuConfig &other) const {
|
|
||||||
return iterationSleepDuration == other.iterationSleepDuration &&
|
|
||||||
hintTimeout == other.hintTimeout &&
|
|
||||||
randomThrottleDecisionProbability == other.randomThrottleDecisionProbability &&
|
|
||||||
enabledHintTimeout == other.enabledHintTimeout &&
|
|
||||||
randomThrottleOptions == other.randomThrottleOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &stream, const AdaptiveCpuConfig &config) {
|
|
||||||
stream << "AdaptiveCpuConfig(";
|
|
||||||
stream << "iterationSleepDuration=" << config.iterationSleepDuration.count() << "ms, ";
|
|
||||||
stream << "hintTimeout=" << config.hintTimeout.count() << "ms, ";
|
|
||||||
stream << "randomThrottleDecisionProbability=" << config.randomThrottleDecisionProbability
|
|
||||||
<< ", ";
|
|
||||||
stream << "enabledHintTimeout=" << config.enabledHintTimeout.count() << "ms, ";
|
|
||||||
stream << "randomThrottleOptions=[" << FormatThrottleDecisions(config.randomThrottleOptions)
|
|
||||||
<< "]";
|
|
||||||
stream << ")";
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParseThrottleDecisions(const std::string &input, std::vector<ThrottleDecision> *output) {
|
|
||||||
std::stringstream ss(input);
|
|
||||||
while (ss.good()) {
|
|
||||||
std::string throttleDecisionStr;
|
|
||||||
if (std::getline(ss, throttleDecisionStr, ',').fail()) {
|
|
||||||
LOG(ERROR) << "Failed to getline on throttle decisions string: " << input;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t throttleDecisionInt;
|
|
||||||
int scanEnd;
|
|
||||||
if (std::sscanf(throttleDecisionStr.c_str(), "%" PRIu32 "%n", &throttleDecisionInt,
|
|
||||||
&scanEnd) != 1 ||
|
|
||||||
scanEnd != throttleDecisionStr.size()) {
|
|
||||||
LOG(ERROR) << "Failed to parse as int: str=" << throttleDecisionStr
|
|
||||||
<< ", input=" << input << ", scanEnd=" << scanEnd;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (throttleDecisionInt < static_cast<uint32_t>(ThrottleDecision::FIRST) ||
|
|
||||||
throttleDecisionInt > static_cast<uint32_t>(ThrottleDecision::LAST)) {
|
|
||||||
LOG(ERROR) << "Failed to parse throttle decision: throttleDecision="
|
|
||||||
<< throttleDecisionInt << ", input=" << input;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
output->push_back(static_cast<ThrottleDecision>(throttleDecisionInt));
|
|
||||||
}
|
|
||||||
if (output->empty()) {
|
|
||||||
LOG(ERROR) << "Failed to find any throttle decisions, must have at least one: " << input;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FormatThrottleDecisions(const std::vector<ThrottleDecision> &throttleDecisions) {
|
|
||||||
std::stringstream ss;
|
|
||||||
for (size_t i = 0; i < throttleDecisions.size(); i++) {
|
|
||||||
ss << static_cast<uint32_t>(throttleDecisions[i]);
|
|
||||||
if (i < throttleDecisions.size() - 1) {
|
|
||||||
ss << ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "ThrottleDecision.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
struct AdaptiveCpuConfig {
|
|
||||||
static bool ReadFromSystemProperties(AdaptiveCpuConfig *output);
|
|
||||||
static const AdaptiveCpuConfig DEFAULT;
|
|
||||||
|
|
||||||
// How long to sleep for between Adaptive CPU runs.
|
|
||||||
std::chrono::milliseconds iterationSleepDuration;
|
|
||||||
// Timeout applied to hints. If Adaptive CPU doesn't receive any frames in this time, CPU
|
|
||||||
// throttling hints are cancelled.
|
|
||||||
std::chrono::milliseconds hintTimeout;
|
|
||||||
// Instead of throttling based on model output, choose a random throttle X% of the time. Must be
|
|
||||||
// between 0 and 1 inclusive.
|
|
||||||
double randomThrottleDecisionProbability;
|
|
||||||
std::vector<ThrottleDecision> randomThrottleOptions;
|
|
||||||
// Setting AdaptiveCpu to enabled only lasts this long. For a continuous run, AdaptiveCpu needs
|
|
||||||
// to receive the enabled hint more frequently than this value.
|
|
||||||
std::chrono::milliseconds enabledHintTimeout;
|
|
||||||
|
|
||||||
bool operator==(const AdaptiveCpuConfig &other) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const AdaptiveCpuConfig &config);
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,131 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "AdaptiveCpuStats.h"
|
|
||||||
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include "AdaptiveCpu.h"
|
|
||||||
|
|
||||||
using std::chrono_literals::operator""ns;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
void AdaptiveCpuStats::RegisterStartRun() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
mNumStartedRuns++;
|
|
||||||
mLastRunStartTime = mTimeSource->GetTime();
|
|
||||||
if (mStartTime == 0ns) {
|
|
||||||
mStartTime = mLastRunStartTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpuStats::RegisterSuccessfulRun(ThrottleDecision previousThrottleDecision,
|
|
||||||
ThrottleDecision throttleDecision,
|
|
||||||
WorkDurationFeatures workDurationFeatures,
|
|
||||||
const AdaptiveCpuConfig &config) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
mNumSuccessfulRuns++;
|
|
||||||
mNumThrottles[throttleDecision]++;
|
|
||||||
const auto runSuccessTime = mTimeSource->GetTime();
|
|
||||||
mTotalRunDuration += runSuccessTime - mLastRunStartTime;
|
|
||||||
// Don't update previousThrottleDecision entries if we haven't run successfully before.
|
|
||||||
if (mLastRunSuccessTime != 0ns) {
|
|
||||||
mThrottleDurations[previousThrottleDecision] +=
|
|
||||||
std::min(runSuccessTime - mLastRunSuccessTime,
|
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(config.hintTimeout));
|
|
||||||
mNumDurations[previousThrottleDecision] += workDurationFeatures.numDurations;
|
|
||||||
mNumMissedDeadlines[previousThrottleDecision] += workDurationFeatures.numMissedDeadlines;
|
|
||||||
}
|
|
||||||
mLastRunSuccessTime = runSuccessTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdaptiveCpuStats::DumpToStream(std::ostream &stream) const {
|
|
||||||
stream << "Stats:\n";
|
|
||||||
stream << "- Successful runs / total runs: " << mNumSuccessfulRuns << " / " << mNumStartedRuns
|
|
||||||
<< "\n";
|
|
||||||
stream << "- Total run duration: " << FormatDuration(mTotalRunDuration) << "\n";
|
|
||||||
stream << "- Average run duration: " << FormatDuration(mTotalRunDuration / mNumSuccessfulRuns)
|
|
||||||
<< "\n";
|
|
||||||
stream << "- Running time fraction: "
|
|
||||||
<< static_cast<double>(mTotalRunDuration.count()) /
|
|
||||||
(mTimeSource->GetTime() - mStartTime).count()
|
|
||||||
<< "\n";
|
|
||||||
|
|
||||||
stream << "- Number of throttles:\n";
|
|
||||||
size_t totalNumThrottles = 0;
|
|
||||||
for (const auto &[throttleDecision, numThrottles] : mNumThrottles) {
|
|
||||||
stream << " - " << ThrottleString(throttleDecision) << ": " << numThrottles << "\n";
|
|
||||||
totalNumThrottles += numThrottles;
|
|
||||||
}
|
|
||||||
stream << " - Total: " << totalNumThrottles << "\n";
|
|
||||||
|
|
||||||
stream << "- Time spent throttling:\n";
|
|
||||||
std::chrono::nanoseconds totalThrottleDuration;
|
|
||||||
for (const auto &[throttleDecision, throttleDuration] : mThrottleDurations) {
|
|
||||||
stream << " - " << ThrottleString(throttleDecision) << ": "
|
|
||||||
<< FormatDuration(throttleDuration) << "\n";
|
|
||||||
totalThrottleDuration += throttleDuration;
|
|
||||||
}
|
|
||||||
stream << " - Total: " << FormatDuration(totalThrottleDuration) << "\n";
|
|
||||||
|
|
||||||
stream << "- Missed deadlines per throttle:\n";
|
|
||||||
size_t totalNumDurations = 0;
|
|
||||||
size_t totalNumMissedDeadlines = 0;
|
|
||||||
for (const auto &[throttleDecision, numDurations] : mNumDurations) {
|
|
||||||
const size_t numMissedDeadlines = mNumMissedDeadlines.at(throttleDecision);
|
|
||||||
stream << " - " << ThrottleString(throttleDecision) << ": " << numMissedDeadlines << " / "
|
|
||||||
<< numDurations << " (" << static_cast<double>(numMissedDeadlines) / numDurations
|
|
||||||
<< ")\n";
|
|
||||||
totalNumDurations += numDurations;
|
|
||||||
totalNumMissedDeadlines += numMissedDeadlines;
|
|
||||||
}
|
|
||||||
stream << " - Total: " << totalNumMissedDeadlines << " / " << totalNumDurations << " ("
|
|
||||||
<< static_cast<double>(totalNumMissedDeadlines) / totalNumDurations << ")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AdaptiveCpuStats::FormatDuration(std::chrono::nanoseconds duration) {
|
|
||||||
double count = static_cast<double>(duration.count());
|
|
||||||
std::string suffix;
|
|
||||||
if (count < 1000.0) {
|
|
||||||
suffix = "ns";
|
|
||||||
} else if (count < 1000.0 * 1000) {
|
|
||||||
suffix = "us";
|
|
||||||
count /= 1000;
|
|
||||||
} else if (count < 1000.0 * 1000 * 100) {
|
|
||||||
suffix = "ms";
|
|
||||||
count /= 1000 * 1000;
|
|
||||||
} else {
|
|
||||||
suffix = "s";
|
|
||||||
count /= 1000 * 1000 * 1000;
|
|
||||||
}
|
|
||||||
return std::to_string(count) + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
#include "AdaptiveCpuConfig.h"
|
|
||||||
#include "ITimeSource.h"
|
|
||||||
#include "Model.h"
|
|
||||||
#include "TimeSource.h"
|
|
||||||
#include "WorkDurationProcessor.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
// Collects statistics about Adaptive CPU.
|
|
||||||
// These are only Used during a dumpsys to improve bug report quality.
|
|
||||||
class AdaptiveCpuStats {
|
|
||||||
public:
|
|
||||||
AdaptiveCpuStats() : mTimeSource(std::make_unique<TimeSource>()) {}
|
|
||||||
AdaptiveCpuStats(std::unique_ptr<ITimeSource> timeSource)
|
|
||||||
: mTimeSource(std::move(timeSource)) {}
|
|
||||||
|
|
||||||
void RegisterStartRun();
|
|
||||||
void RegisterSuccessfulRun(ThrottleDecision previousThrottleDecision,
|
|
||||||
ThrottleDecision throttleDecision,
|
|
||||||
WorkDurationFeatures workDurationFeatures,
|
|
||||||
const AdaptiveCpuConfig &config);
|
|
||||||
void DumpToStream(std::ostream &stream) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::unique_ptr<ITimeSource> mTimeSource;
|
|
||||||
|
|
||||||
size_t mNumStartedRuns = 0;
|
|
||||||
size_t mNumSuccessfulRuns = 0;
|
|
||||||
std::chrono::nanoseconds mStartTime;
|
|
||||||
std::chrono::nanoseconds mLastRunStartTime;
|
|
||||||
std::chrono::nanoseconds mLastRunSuccessTime;
|
|
||||||
std::chrono::nanoseconds mTotalRunDuration;
|
|
||||||
|
|
||||||
std::map<ThrottleDecision, size_t> mNumThrottles;
|
|
||||||
std::map<ThrottleDecision, std::chrono::nanoseconds> mThrottleDurations;
|
|
||||||
|
|
||||||
std::map<ThrottleDecision, size_t> mNumDurations;
|
|
||||||
std::map<ThrottleDecision, size_t> mNumMissedDeadlines;
|
|
||||||
|
|
||||||
static std::string FormatDuration(std::chrono::nanoseconds duration);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,149 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "CpuFrequencyReader.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
using std::chrono_literals::operator""ms;
|
|
||||||
|
|
||||||
constexpr std::string_view kCpuPolicyDirectory("/sys/devices/system/cpu/cpufreq");
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
bool CpuFrequencyReader::Init() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
mCpuPolicyIds.clear();
|
|
||||||
if (!ReadCpuPolicyIds(&mCpuPolicyIds)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mPreviousCpuPolicyFrequencies.clear();
|
|
||||||
return ReadCpuPolicyFrequencies(&mPreviousCpuPolicyFrequencies);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuFrequencyReader::GetRecentCpuPolicyFrequencies(
|
|
||||||
std::vector<CpuPolicyAverageFrequency> *result) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> cpuPolicyFrequencies;
|
|
||||||
if (!ReadCpuPolicyFrequencies(&cpuPolicyFrequencies)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto &[policyId, cpuFrequencies] : cpuPolicyFrequencies) {
|
|
||||||
const auto &previousCpuFrequencies = mPreviousCpuPolicyFrequencies.find(policyId);
|
|
||||||
if (previousCpuFrequencies == mPreviousCpuPolicyFrequencies.end()) {
|
|
||||||
LOG(ERROR) << "Couldn't find policy " << policyId << " in previous frequencies";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint64_t weightedFrequenciesSumHz = 0;
|
|
||||||
std::chrono::milliseconds timeSum = 0ms;
|
|
||||||
for (const auto &[frequencyHz, time] : cpuFrequencies) {
|
|
||||||
const auto &previousCpuFrequency = previousCpuFrequencies->second.find(frequencyHz);
|
|
||||||
if (previousCpuFrequency == previousCpuFrequencies->second.end()) {
|
|
||||||
LOG(ERROR) << "Couldn't find frequency " << frequencyHz
|
|
||||||
<< " in previous frequencies";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const std::chrono::milliseconds recentTime = time - previousCpuFrequency->second;
|
|
||||||
weightedFrequenciesSumHz += frequencyHz * recentTime.count();
|
|
||||||
timeSum += recentTime;
|
|
||||||
}
|
|
||||||
const uint64_t averageFrequencyHz =
|
|
||||||
timeSum != 0ms ? weightedFrequenciesSumHz / timeSum.count() : 0;
|
|
||||||
result->push_back({.policyId = policyId, .averageFrequencyHz = averageFrequencyHz});
|
|
||||||
}
|
|
||||||
mPreviousCpuPolicyFrequencies = cpuPolicyFrequencies;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>>
|
|
||||||
CpuFrequencyReader::GetPreviousCpuPolicyFrequencies() const {
|
|
||||||
return mPreviousCpuPolicyFrequencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuFrequencyReader::ReadCpuPolicyFrequencies(
|
|
||||||
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> *result) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
for (const uint32_t cpuPolicyId : mCpuPolicyIds) {
|
|
||||||
std::stringstream timeInStatePath;
|
|
||||||
timeInStatePath << "/sys/devices/system/cpu/cpufreq/policy" << cpuPolicyId
|
|
||||||
<< "/stats/time_in_state";
|
|
||||||
std::unique_ptr<std::istream> timeInStateFile;
|
|
||||||
if (!mFilesystem->ReadFileStream(timeInStatePath.str(), &timeInStateFile)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<uint64_t, std::chrono::milliseconds> cpuFrequencies;
|
|
||||||
std::string timeInStateLine;
|
|
||||||
while (std::getline(*timeInStateFile, timeInStateLine)) {
|
|
||||||
// Time format in time_in_state is 10s of milliseconds:
|
|
||||||
// https://www.kernel.org/doc/Documentation/cpu-freq/cpufreq-stats.txt
|
|
||||||
uint64_t frequencyHz, time10Ms;
|
|
||||||
if (std::sscanf(timeInStateLine.c_str(), "%" PRIu64 " %" PRIu64 "\n", &frequencyHz,
|
|
||||||
&time10Ms) != 2) {
|
|
||||||
LOG(ERROR) << "Failed to parse time_in_state line: " << timeInStateLine;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cpuFrequencies[frequencyHz] = time10Ms * 10ms;
|
|
||||||
}
|
|
||||||
if (cpuFrequencies.size() > 500) {
|
|
||||||
LOG(ERROR) << "Found " << cpuFrequencies.size() << " frequencies for policy "
|
|
||||||
<< cpuPolicyId << ", aborting";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
(*result)[cpuPolicyId] = cpuFrequencies;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuFrequencyReader::ReadCpuPolicyIds(std::vector<uint32_t> *result) const {
|
|
||||||
ATRACE_CALL();
|
|
||||||
std::vector<std::string> entries;
|
|
||||||
if (!mFilesystem->ListDirectory(kCpuPolicyDirectory.data(), &entries)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto &entry : entries) {
|
|
||||||
uint32_t cpuPolicyId;
|
|
||||||
if (!sscanf(entry.c_str(), "policy%d", &cpuPolicyId)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result->push_back(cpuPolicyId);
|
|
||||||
}
|
|
||||||
// Sort the list, so that getRecentCpuPolicyFrequencies always returns frequencies sorted by
|
|
||||||
// policy ID.
|
|
||||||
std::sort(result->begin(), result->end());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,91 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <chrono>
|
|
||||||
#include <map>
|
|
||||||
#include <ostream>
|
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "IFilesystem.h"
|
|
||||||
#include "RealFilesystem.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
struct CpuPolicyAverageFrequency {
|
|
||||||
const uint32_t policyId;
|
|
||||||
const uint64_t averageFrequencyHz;
|
|
||||||
|
|
||||||
bool operator==(const CpuPolicyAverageFrequency &other) const {
|
|
||||||
return policyId == other.policyId && averageFrequencyHz == other.averageFrequencyHz;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CpuFrequencyReader {
|
|
||||||
public:
|
|
||||||
CpuFrequencyReader() : mFilesystem(std::make_unique<RealFilesystem>()) {}
|
|
||||||
CpuFrequencyReader(std::unique_ptr<IFilesystem> filesystem)
|
|
||||||
: mFilesystem(std::move(filesystem)) {}
|
|
||||||
|
|
||||||
// Initialize reading, must be done before calling other methods.
|
|
||||||
// Work is not done in constructor as it accesses files.
|
|
||||||
// Returns true on success.
|
|
||||||
bool Init();
|
|
||||||
|
|
||||||
// Gets the average frequency each CPU policy was using, since this method was last called.
|
|
||||||
// Results are returned sorted by policyId.
|
|
||||||
// Returns true on success.
|
|
||||||
bool GetRecentCpuPolicyFrequencies(std::vector<CpuPolicyAverageFrequency> *result);
|
|
||||||
|
|
||||||
// The most recently read frequencies for each CPU policy. See readCpuPolicyFrequencies for type
|
|
||||||
// explanation. Used for dumping to bug reports.
|
|
||||||
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>>
|
|
||||||
GetPreviousCpuPolicyFrequencies() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// CPU policy IDs read from /sys. Initialized in #init(). Sorted ascending.
|
|
||||||
std::vector<uint32_t> mCpuPolicyIds;
|
|
||||||
// The CPU frequencies when #getRecentCpuPolicyFrequencies was last called (or #init if it has
|
|
||||||
// not been called yet).
|
|
||||||
// See readCpuPolicyFrequencies for explanation of type.
|
|
||||||
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> mPreviousCpuPolicyFrequencies;
|
|
||||||
const std::unique_ptr<IFilesystem> mFilesystem;
|
|
||||||
|
|
||||||
// Reads, from the /sys filesystem, the CPU frequencies used by each policy.
|
|
||||||
// - The outer map's key is the CPU policy ID.
|
|
||||||
// - The inner map's key is the CPU frequency in Hz.
|
|
||||||
// - The inner map's value is the time the policy has been running at that frequency, aggregated
|
|
||||||
// since boot.
|
|
||||||
// Returns true on success.
|
|
||||||
bool ReadCpuPolicyFrequencies(
|
|
||||||
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> *result);
|
|
||||||
|
|
||||||
bool ReadCpuPolicyIds(std::vector<uint32_t> *result) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "CpuLoadReaderProcStat.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
bool CpuLoadReaderProcStat::Init() {
|
|
||||||
mPreviousCpuTimes.clear();
|
|
||||||
return ReadCpuTimes(&mPreviousCpuTimes);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuLoadReaderProcStat::GetRecentCpuLoads(
|
|
||||||
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
if (cpuCoreIdleTimesPercentage == nullptr) {
|
|
||||||
LOG(ERROR) << "Got nullptr output in getRecentCpuLoads";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::map<uint32_t, CpuTime> cpuTimes;
|
|
||||||
if (!ReadCpuTimes(&cpuTimes)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cpuTimes.empty()) {
|
|
||||||
LOG(ERROR) << "Failed to find any CPU times";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto &[cpuId, cpuTime] : cpuTimes) {
|
|
||||||
const auto previousCpuTime = mPreviousCpuTimes.find(cpuId);
|
|
||||||
if (previousCpuTime == mPreviousCpuTimes.end()) {
|
|
||||||
LOG(ERROR) << "Couldn't find CPU " << cpuId << " in previous CPU times";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto recentIdleTimeMs = cpuTime.idleTimeMs - previousCpuTime->second.idleTimeMs;
|
|
||||||
const auto recentTotalTimeMs = cpuTime.totalTimeMs - previousCpuTime->second.totalTimeMs;
|
|
||||||
if (recentIdleTimeMs > recentTotalTimeMs) {
|
|
||||||
LOG(ERROR) << "Found more recent idle time than total time: idle=" << recentIdleTimeMs
|
|
||||||
<< ", total=" << recentTotalTimeMs;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const double idleTimePercentage =
|
|
||||||
static_cast<double>(recentIdleTimeMs) / (recentTotalTimeMs);
|
|
||||||
LOG(VERBOSE) << "Read CPU idle time: cpuId=" << cpuId
|
|
||||||
<< ", idleTimePercentage=" << idleTimePercentage;
|
|
||||||
(*cpuCoreIdleTimesPercentage)[cpuId] = idleTimePercentage;
|
|
||||||
}
|
|
||||||
mPreviousCpuTimes = cpuTimes;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuLoadReaderProcStat::ReadCpuTimes(std::map<uint32_t, CpuTime> *result) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
|
|
||||||
std::unique_ptr<std::istream> file;
|
|
||||||
if (!mFilesystem->ReadFileStream("/proc/stat", &file)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::string line;
|
|
||||||
ATRACE_BEGIN("loop");
|
|
||||||
while (std::getline(*file, line)) {
|
|
||||||
ATRACE_NAME("parse");
|
|
||||||
uint32_t cpuId;
|
|
||||||
// Times reported when the CPU is active.
|
|
||||||
uint64_t user, nice, system, irq, softIrq, steal, guest, guestNice;
|
|
||||||
// Times reported when the CPU is idle.
|
|
||||||
uint64_t idle, ioWait;
|
|
||||||
// Order & values taken from `fs/proc/stat.c`.
|
|
||||||
if (std::sscanf(line.c_str(),
|
|
||||||
"cpu%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
|
|
||||||
" %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " ",
|
|
||||||
&cpuId, &user, &nice, &system, &idle, &ioWait, &irq, &softIrq, &steal,
|
|
||||||
&guest, &guestNice) != 11) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uint64_t idleTimeJiffies = idle + ioWait;
|
|
||||||
uint64_t totalTimeJiffies =
|
|
||||||
user + nice + system + irq + softIrq + steal + guest + guestNice + idleTimeJiffies;
|
|
||||||
(*result)[cpuId] = {.idleTimeMs = JiffiesToMs(idleTimeJiffies),
|
|
||||||
.totalTimeMs = JiffiesToMs(totalTimeJiffies)};
|
|
||||||
}
|
|
||||||
ATRACE_END();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CpuLoadReaderProcStat::DumpToStream(std::stringstream &stream) const {
|
|
||||||
stream << "CPU loads from /proc/stat:\n";
|
|
||||||
for (const auto &[cpuId, cpuTime] : mPreviousCpuTimes) {
|
|
||||||
stream << "- CPU=" << cpuId << ", idleTime=" << cpuTime.idleTimeMs
|
|
||||||
<< "ms, totalTime=" << cpuTime.totalTimeMs << "ms\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t CpuLoadReaderProcStat::JiffiesToMs(uint64_t jiffies) {
|
|
||||||
return (jiffies * 1000) / sysconf(_SC_CLK_TCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,63 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <map>
|
|
||||||
|
|
||||||
#include "ICpuLoadReader.h"
|
|
||||||
#include "IFilesystem.h"
|
|
||||||
#include "RealFilesystem.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
struct CpuTime {
|
|
||||||
uint64_t idleTimeMs;
|
|
||||||
uint64_t totalTimeMs;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reads CPU idle stats from /proc/stat.
|
|
||||||
class CpuLoadReaderProcStat : public ICpuLoadReader {
|
|
||||||
public:
|
|
||||||
CpuLoadReaderProcStat() : mFilesystem(std::make_unique<RealFilesystem>()) {}
|
|
||||||
CpuLoadReaderProcStat(std::unique_ptr<IFilesystem> filesystem)
|
|
||||||
: mFilesystem(std::move(filesystem)) {}
|
|
||||||
|
|
||||||
bool Init() override;
|
|
||||||
bool GetRecentCpuLoads(std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) override;
|
|
||||||
void DumpToStream(std::stringstream &stream) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<uint32_t, CpuTime> mPreviousCpuTimes;
|
|
||||||
const std::unique_ptr<IFilesystem> mFilesystem;
|
|
||||||
|
|
||||||
bool ReadCpuTimes(std::map<uint32_t, CpuTime> *result);
|
|
||||||
// Converts jiffies to milliseconds. Jiffies is the granularity the kernel reports times in,
|
|
||||||
// including the timings in CPU statistics.
|
|
||||||
static uint64_t JiffiesToMs(uint64_t jiffies);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,151 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "CpuLoadReaderSysDevices.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
std::chrono::nanoseconds getKernelTime() {
|
|
||||||
timespec ts;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
return std::chrono::nanoseconds(ts.tv_sec * 1000000000UL + ts.tv_nsec);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuLoadReaderSysDevices::Init() {
|
|
||||||
mIdleStateNames.clear();
|
|
||||||
if (!ReadIdleStateNames(&mIdleStateNames)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ReadCpuTimes(&mPreviousCpuTimes);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuLoadReaderSysDevices::GetRecentCpuLoads(
|
|
||||||
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
if (cpuCoreIdleTimesPercentage == nullptr) {
|
|
||||||
LOG(ERROR) << "Got nullptr output in getRecentCpuLoads";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::array<CpuTime, NUM_CPU_CORES> cpuTimes;
|
|
||||||
if (!ReadCpuTimes(&cpuTimes)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cpuTimes.empty()) {
|
|
||||||
LOG(ERROR) << "Failed to find any CPU times";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
|
|
||||||
const auto cpuTime = cpuTimes[cpuId];
|
|
||||||
const auto previousCpuTime = mPreviousCpuTimes[cpuId];
|
|
||||||
auto recentIdleTime = cpuTime.idleTime - previousCpuTime.idleTime;
|
|
||||||
const auto recentTotalTime = cpuTime.totalTime - previousCpuTime.totalTime;
|
|
||||||
if (recentIdleTime > recentTotalTime) {
|
|
||||||
// This happens occasionally, as we use the idle time from the kernel, and the current
|
|
||||||
// time from userspace.
|
|
||||||
recentIdleTime = recentTotalTime;
|
|
||||||
}
|
|
||||||
const double idleTimePercentage =
|
|
||||||
static_cast<double>(recentIdleTime.count()) / recentTotalTime.count();
|
|
||||||
(*cpuCoreIdleTimesPercentage)[cpuId] = idleTimePercentage;
|
|
||||||
}
|
|
||||||
mPreviousCpuTimes = cpuTimes;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CpuLoadReaderSysDevices::DumpToStream(std::stringstream &stream) const {
|
|
||||||
stream << "CPU loads from /sys/devices/system/cpu/cpuN/cpuidle:\n";
|
|
||||||
for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
|
|
||||||
stream << "- CPU=" << cpuId << ", idleTime=" << mPreviousCpuTimes[cpuId].idleTime.count()
|
|
||||||
<< "ms, totalTime=" << mPreviousCpuTimes[cpuId].totalTime.count() << "ms\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuLoadReaderSysDevices::ReadCpuTimes(std::array<CpuTime, NUM_CPU_CORES> *result) const {
|
|
||||||
ATRACE_CALL();
|
|
||||||
const auto totalTime = mTimeSource->GetTime();
|
|
||||||
|
|
||||||
for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
|
|
||||||
std::chrono::microseconds idleTime;
|
|
||||||
for (const auto &idleStateName : mIdleStateNames) {
|
|
||||||
std::stringstream cpuIdlePath;
|
|
||||||
cpuIdlePath << "/sys/devices/system/cpu/"
|
|
||||||
<< "cpu" << cpuId << "/cpuidle/" << idleStateName << "/time";
|
|
||||||
std::unique_ptr<std::istream> file;
|
|
||||||
if (!mFilesystem->ReadFileStream(cpuIdlePath.str(), &file)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Times are reported in microseconds:
|
|
||||||
// https://www.kernel.org/doc/Documentation/cpuidle/sysfs.txt
|
|
||||||
std::string idleTimeUs(std::istreambuf_iterator<char>(*file), {});
|
|
||||||
idleTime += std::chrono::microseconds(std::atoi(idleTimeUs.c_str()));
|
|
||||||
}
|
|
||||||
(*result)[cpuId] = {
|
|
||||||
.idleTime = idleTime,
|
|
||||||
.totalTime = std::chrono::duration_cast<std::chrono::microseconds>(totalTime),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CpuLoadReaderSysDevices::ReadIdleStateNames(std::vector<std::string> *result) const {
|
|
||||||
std::vector<std::string> idleStateNames;
|
|
||||||
if (!mFilesystem->ListDirectory("/sys/devices/system/cpu/cpu0/cpuidle", &idleStateNames)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto &idleStateName : idleStateNames) {
|
|
||||||
if (idleStateName.length() == 0 || idleStateName[0] == '.') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::vector<std::string> files;
|
|
||||||
if (!mFilesystem->ListDirectory(
|
|
||||||
std::string("/sys/devices/system/cpu/cpu0/cpuidle/") + idleStateName, &files)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (std::find(files.begin(), files.end(), "time") == files.end()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result->push_back(idleStateName);
|
|
||||||
}
|
|
||||||
if (idleStateNames.empty()) {
|
|
||||||
LOG(ERROR) << "Found no idle state names";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,71 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <chrono>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "ICpuLoadReader.h"
|
|
||||||
#include "IFilesystem.h"
|
|
||||||
#include "ITimeSource.h"
|
|
||||||
#include "Model.h"
|
|
||||||
#include "RealFilesystem.h"
|
|
||||||
#include "TimeSource.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
struct CpuTime {
|
|
||||||
std::chrono::microseconds idleTime;
|
|
||||||
std::chrono::microseconds totalTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reads CPU idle stats from /sys/devices/system/cpu/cpuN/cpuidle.
|
|
||||||
class CpuLoadReaderSysDevices : public ICpuLoadReader {
|
|
||||||
public:
|
|
||||||
CpuLoadReaderSysDevices()
|
|
||||||
: mFilesystem(std::make_unique<RealFilesystem>()),
|
|
||||||
mTimeSource(std::make_unique<TimeSource>()) {}
|
|
||||||
CpuLoadReaderSysDevices(std::unique_ptr<IFilesystem> filesystem,
|
|
||||||
std::unique_ptr<ITimeSource> timeSource)
|
|
||||||
: mFilesystem(std::move(filesystem)), mTimeSource(std::move(timeSource)) {}
|
|
||||||
|
|
||||||
bool Init() override;
|
|
||||||
bool GetRecentCpuLoads(std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) override;
|
|
||||||
void DumpToStream(std::stringstream &stream) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::unique_ptr<IFilesystem> mFilesystem;
|
|
||||||
const std::unique_ptr<ITimeSource> mTimeSource;
|
|
||||||
|
|
||||||
std::array<CpuTime, NUM_CPU_CORES> mPreviousCpuTimes;
|
|
||||||
std::vector<std::string> mIdleStateNames;
|
|
||||||
|
|
||||||
bool ReadCpuTimes(std::array<CpuTime, NUM_CPU_CORES> *result) const;
|
|
||||||
bool ReadIdleStateNames(std::vector<std::string> *result) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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 "powerhal-libperfmgr"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "Device.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <android-base/properties.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
Device ReadDevice() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
const std::string deviceProperty = ::android::base::GetProperty("ro.product.device", "");
|
|
||||||
Device device;
|
|
||||||
if (deviceProperty == "raven") {
|
|
||||||
device = Device::RAVEN;
|
|
||||||
} else if (deviceProperty == "oriole") {
|
|
||||||
device = Device::ORIOLE;
|
|
||||||
} else {
|
|
||||||
LOG(WARNING) << "Failed to parse device property, setting to UNKNOWN: " << deviceProperty;
|
|
||||||
device = Device::UNKNOWN;
|
|
||||||
}
|
|
||||||
LOG(DEBUG) << "Parsed device: deviceProperty=" << deviceProperty
|
|
||||||
<< ", device=" << static_cast<uint32_t>(device);
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
enum class Device { UNKNOWN = 0, RAVEN = 1, ORIOLE = 2 };
|
|
||||||
|
|
||||||
Device ReadDevice();
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,49 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <array>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "Model.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
class ICpuLoadReader {
|
|
||||||
public:
|
|
||||||
// Initialize reading, must be done before calling other methods.
|
|
||||||
// Work is not done in constructor as it accesses files.
|
|
||||||
virtual bool Init() = 0;
|
|
||||||
// Get the load of each CPU, since the last time this method was called.
|
|
||||||
virtual bool GetRecentCpuLoads(
|
|
||||||
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) = 0;
|
|
||||||
// Dump internal state to a string stream. Used for dumpsys.
|
|
||||||
virtual void DumpToStream(std::stringstream &stream) const = 0;
|
|
||||||
virtual ~ICpuLoadReader() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,49 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <ostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
// Abstracted so we can mock in tests.
|
|
||||||
class IFilesystem {
|
|
||||||
public:
|
|
||||||
virtual ~IFilesystem() {}
|
|
||||||
virtual bool ListDirectory(const std::string &path, std::vector<std::string> *result) const = 0;
|
|
||||||
virtual bool ReadFileStream(const std::string &path,
|
|
||||||
std::unique_ptr<std::istream> *result) const = 0;
|
|
||||||
// Resets the file stream, so that the next read will read from the beginning.
|
|
||||||
// This function exists in IFilesystem rather than using istream::seekg directly. This is
|
|
||||||
// so we can mock this function in tests, allowing us to return different data on reset.
|
|
||||||
virtual bool ResetFileStream(const std::unique_ptr<std::istream> &fileStream) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,41 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <chrono>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
// Abstracted so we can mock in tests.
|
|
||||||
class ITimeSource {
|
|
||||||
public:
|
|
||||||
virtual ~ITimeSource() {}
|
|
||||||
virtual std::chrono::nanoseconds GetTime() const = 0;
|
|
||||||
virtual std::chrono::nanoseconds GetKernelTime() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,135 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "KernelCpuFeatureReader.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
constexpr std::string_view kKernelFilePath("/proc/vendor_sched/acpu_stats");
|
|
||||||
constexpr size_t kReadBufferSize = sizeof(acpu_stats) * NUM_CPU_CORES;
|
|
||||||
|
|
||||||
bool KernelCpuFeatureReader::Init() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
if (!OpenStatsFile(&mStatsFile)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ReadStats(&mPreviousStats, &mPreviousReadTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KernelCpuFeatureReader::GetRecentCpuFeatures(
|
|
||||||
std::array<double, NUM_CPU_POLICIES> *cpuPolicyAverageFrequencyHz,
|
|
||||||
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> stats;
|
|
||||||
std::chrono::nanoseconds readTime;
|
|
||||||
if (!ReadStats(&stats, &readTime)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const std::chrono::nanoseconds timeDelta = readTime - mPreviousReadTime;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < NUM_CPU_POLICIES; i++) {
|
|
||||||
// acpu_stats has data per-CPU, but frequency data is equivalent for all CPUs in a policy.
|
|
||||||
// So, we only read the first CPU in each policy.
|
|
||||||
const size_t statsIdx = CPU_POLICY_INDICES[i];
|
|
||||||
if (stats[statsIdx].weighted_sum_freq < mPreviousStats[statsIdx].weighted_sum_freq) {
|
|
||||||
LOG(WARNING) << "New weighted_sum_freq is less than old: new="
|
|
||||||
<< stats[statsIdx].weighted_sum_freq
|
|
||||||
<< ", old=" << mPreviousStats[statsIdx].weighted_sum_freq;
|
|
||||||
mPreviousStats[statsIdx].weighted_sum_freq = stats[statsIdx].weighted_sum_freq;
|
|
||||||
}
|
|
||||||
(*cpuPolicyAverageFrequencyHz)[i] =
|
|
||||||
static_cast<double>(stats[statsIdx].weighted_sum_freq -
|
|
||||||
mPreviousStats[statsIdx].weighted_sum_freq) /
|
|
||||||
timeDelta.count();
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < NUM_CPU_CORES; i++) {
|
|
||||||
if (stats[i].total_idle_time_ns < mPreviousStats[i].total_idle_time_ns) {
|
|
||||||
LOG(WARNING) << "New total_idle_time_ns is less than old: new="
|
|
||||||
<< stats[i].total_idle_time_ns
|
|
||||||
<< ", old=" << mPreviousStats[i].total_idle_time_ns;
|
|
||||||
mPreviousStats[i].total_idle_time_ns = stats[i].total_idle_time_ns;
|
|
||||||
}
|
|
||||||
(*cpuCoreIdleTimesPercentage)[i] =
|
|
||||||
static_cast<double>(stats[i].total_idle_time_ns -
|
|
||||||
mPreviousStats[i].total_idle_time_ns) /
|
|
||||||
timeDelta.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
mPreviousStats = stats;
|
|
||||||
mPreviousReadTime = readTime;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KernelCpuFeatureReader::OpenStatsFile(std::unique_ptr<std::istream> *file) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
return mFilesystem->ReadFileStream(kKernelFilePath.data(), file);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KernelCpuFeatureReader::ReadStats(std::array<acpu_stats, NUM_CPU_CORES> *stats,
|
|
||||||
std::chrono::nanoseconds *readTime) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
*readTime = mTimeSource->GetKernelTime();
|
|
||||||
if (!mFilesystem->ResetFileStream(mStatsFile)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char buffer[kReadBufferSize];
|
|
||||||
{
|
|
||||||
ATRACE_NAME("read");
|
|
||||||
if (!mStatsFile->read(buffer, kReadBufferSize).good()) {
|
|
||||||
LOG(ERROR) << "Failed to read stats file";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const size_t bytesRead = mStatsFile->gcount();
|
|
||||||
if (bytesRead != kReadBufferSize) {
|
|
||||||
LOG(ERROR) << "Didn't read full data: expected=" << kReadBufferSize
|
|
||||||
<< ", actual=" << bytesRead;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto kernelStructs = reinterpret_cast<acpu_stats *>(buffer);
|
|
||||||
std::copy(kernelStructs, kernelStructs + NUM_CPU_CORES, stats->begin());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KernelCpuFeatureReader::DumpToStream(std::ostream &stream) const {
|
|
||||||
ATRACE_CALL();
|
|
||||||
stream << "CPU features from acpu_stats:\n";
|
|
||||||
for (size_t i = 0; i < NUM_CPU_CORES; i++) {
|
|
||||||
stream << "- CPU " << i << ": weighted_sum_freq=" << mPreviousStats[i].weighted_sum_freq
|
|
||||||
<< ", total_idle_time_ns=" << mPreviousStats[i].total_idle_time_ns << "\n";
|
|
||||||
}
|
|
||||||
stream << "- Last read time: " << mPreviousReadTime.count() << "ns\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
#include "IFilesystem.h"
|
|
||||||
#include "ITimeSource.h"
|
|
||||||
#include "Model.h"
|
|
||||||
#include "RealFilesystem.h"
|
|
||||||
#include "TimeSource.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
// Kernel <-> Userspace ABI for CPU features. See kernel/sched/acpu.c.
|
|
||||||
// Contains CPU statistics for a single CPU. The kernel reports an `acpu_stats` struct for each CPU
|
|
||||||
// on the system.
|
|
||||||
struct acpu_stats {
|
|
||||||
// Sum of the CPU frequencies that the CPU used, multiplied by how much time was spent in each
|
|
||||||
// frequency. Measured in ns*KHz. E.g.:
|
|
||||||
// 10ns at 100MHz, 2ns at 50MHz = 10*100,000 + 2*50,000 = 1,100,000
|
|
||||||
// This is used to calculate the average frequency the CPU was running at between two times:
|
|
||||||
// (new.weighted_sum_freq - old.weighted_sum_freq) / elapsed_time_ns
|
|
||||||
uint64_t weighted_sum_freq;
|
|
||||||
// The total time (in nanoseconds) that the CPU was idle.
|
|
||||||
// This is ued to calculate the percent of time the CPU was idle between two times:
|
|
||||||
// (new.total_idle_time_ns - old.total_idle_time_ns) / elapsed_time_ns
|
|
||||||
uint64_t total_idle_time_ns;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KernelCpuFeatureReader {
|
|
||||||
public:
|
|
||||||
KernelCpuFeatureReader()
|
|
||||||
: mFilesystem(std::make_unique<RealFilesystem>()),
|
|
||||||
mTimeSource(std::make_unique<TimeSource>()) {}
|
|
||||||
KernelCpuFeatureReader(std::unique_ptr<IFilesystem> filesystem,
|
|
||||||
std::unique_ptr<ITimeSource> timeSource)
|
|
||||||
: mFilesystem(std::move(filesystem)), mTimeSource(std::move(timeSource)) {}
|
|
||||||
|
|
||||||
bool Init();
|
|
||||||
bool GetRecentCpuFeatures(std::array<double, NUM_CPU_POLICIES> *cpuPolicyAverageFrequencyHz,
|
|
||||||
std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage);
|
|
||||||
void DumpToStream(std::ostream &stream) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::unique_ptr<IFilesystem> mFilesystem;
|
|
||||||
const std::unique_ptr<ITimeSource> mTimeSource;
|
|
||||||
// We only open the stats file once and reuse the file descriptor. We find this reduces
|
|
||||||
// ReadStats runtime by 2x.
|
|
||||||
std::unique_ptr<std::istream> mStatsFile;
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> mPreviousStats;
|
|
||||||
std::chrono::nanoseconds mPreviousReadTime;
|
|
||||||
bool OpenStatsFile(std::unique_ptr<std::istream> *file);
|
|
||||||
bool ReadStats(std::array<acpu_stats, NUM_CPU_CORES> *stats,
|
|
||||||
std::chrono::nanoseconds *readTime);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,106 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "Model.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
bool ModelInput::SetCpuFreqiencies(
|
|
||||||
const std::vector<CpuPolicyAverageFrequency> &cpuPolicyAverageFrequencies) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
if (cpuPolicyAverageFrequencies.size() != cpuPolicyAverageFrequencyHz.size()) {
|
|
||||||
LOG(ERROR) << "Received incorrect amount of CPU policy frequencies, expected "
|
|
||||||
<< cpuPolicyAverageFrequencyHz.size() << ", received "
|
|
||||||
<< cpuPolicyAverageFrequencies.size();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int32_t previousPolicyId = -1;
|
|
||||||
for (uint32_t i = 0; i < cpuPolicyAverageFrequencies.size(); i++) {
|
|
||||||
if (previousPolicyId >= static_cast<int32_t>(cpuPolicyAverageFrequencies[i].policyId)) {
|
|
||||||
LOG(ERROR) << "CPU frequencies weren't sorted by policy ID, found " << previousPolicyId
|
|
||||||
<< " " << cpuPolicyAverageFrequencies[i].policyId;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
previousPolicyId = cpuPolicyAverageFrequencies[i].policyId;
|
|
||||||
cpuPolicyAverageFrequencyHz[i] = cpuPolicyAverageFrequencies[i].averageFrequencyHz;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelInput::LogToAtrace() const {
|
|
||||||
if (!ATRACE_ENABLED()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ATRACE_CALL();
|
|
||||||
for (int i = 0; i < cpuPolicyAverageFrequencyHz.size(); i++) {
|
|
||||||
ATRACE_INT((std::string("ModelInput_frequency_") + std::to_string(i)).c_str(),
|
|
||||||
static_cast<int>(cpuPolicyAverageFrequencyHz[i]));
|
|
||||||
}
|
|
||||||
for (int i = 0; i < cpuCoreIdleTimesPercentage.size(); i++) {
|
|
||||||
ATRACE_INT((std::string("ModelInput_idle_") + std::to_string(i)).c_str(),
|
|
||||||
static_cast<int>(cpuCoreIdleTimesPercentage[i] * 100));
|
|
||||||
}
|
|
||||||
ATRACE_INT("ModelInput_workDurations_averageDurationNs",
|
|
||||||
workDurationFeatures.averageDuration.count());
|
|
||||||
ATRACE_INT("ModelInput_workDurations_maxDurationNs", workDurationFeatures.maxDuration.count());
|
|
||||||
ATRACE_INT("ModelInput_workDurations_numMissedDeadlines",
|
|
||||||
workDurationFeatures.numMissedDeadlines);
|
|
||||||
ATRACE_INT("ModelInput_workDurations_numDurations", workDurationFeatures.numDurations);
|
|
||||||
ATRACE_INT("ModelInput_prevThrottle", (int)previousThrottleDecision);
|
|
||||||
ATRACE_INT("ModelInput_device", static_cast<int>(device));
|
|
||||||
}
|
|
||||||
|
|
||||||
ThrottleDecision Model::Run(const std::deque<ModelInput> &modelInputs,
|
|
||||||
const AdaptiveCpuConfig &config) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
if (config.randomThrottleDecisionProbability > 0 &&
|
|
||||||
mShouldRandomThrottleDistribution(mGenerator) < config.randomThrottleDecisionProbability) {
|
|
||||||
std::uniform_int_distribution<uint32_t> optionDistribution(
|
|
||||||
0, config.randomThrottleOptions.size() - 1);
|
|
||||||
const ThrottleDecision throttleDecision =
|
|
||||||
config.randomThrottleOptions[optionDistribution(mGenerator)];
|
|
||||||
LOG(VERBOSE) << "Randomly overrided throttle decision: "
|
|
||||||
<< static_cast<uint32_t>(throttleDecision);
|
|
||||||
ATRACE_INT("AdaptiveCpu_randomThrottleDecision", static_cast<uint32_t>(throttleDecision));
|
|
||||||
return throttleDecision;
|
|
||||||
}
|
|
||||||
ATRACE_INT("AdaptiveCpu_randomThrottleDecision", -1);
|
|
||||||
return RunDecisionTree(modelInputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
ThrottleDecision Model::RunDecisionTree(const std::deque<ModelInput> &modelInputs
|
|
||||||
__attribute__((unused))) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
#include "models/model.inc"
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,88 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <chrono>
|
|
||||||
#include <deque>
|
|
||||||
#include <random>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "AdaptiveCpuConfig.h"
|
|
||||||
#include "CpuFrequencyReader.h"
|
|
||||||
#include "Device.h"
|
|
||||||
#include "ThrottleDecision.h"
|
|
||||||
#include "WorkDurationProcessor.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
// Currently Adaptive CPU is targeted to only raven/oriole, so we can hardcode the CPU architecture.
|
|
||||||
// If we extend to other architectures, this will have to vary per-device or be dynamically loaded.
|
|
||||||
constexpr uint32_t NUM_CPU_CORES = 8;
|
|
||||||
constexpr uint32_t NUM_CPU_POLICIES = 3;
|
|
||||||
constexpr std::array<uint32_t, NUM_CPU_POLICIES> CPU_POLICY_INDICES{0, 4, 6};
|
|
||||||
|
|
||||||
struct ModelInput {
|
|
||||||
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
|
|
||||||
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
|
|
||||||
WorkDurationFeatures workDurationFeatures;
|
|
||||||
ThrottleDecision previousThrottleDecision;
|
|
||||||
Device device;
|
|
||||||
|
|
||||||
bool SetCpuFreqiencies(
|
|
||||||
const std::vector<CpuPolicyAverageFrequency> &cpuPolicyAverageFrequencies);
|
|
||||||
|
|
||||||
void LogToAtrace() const;
|
|
||||||
|
|
||||||
bool operator==(const ModelInput &other) const {
|
|
||||||
return cpuPolicyAverageFrequencyHz == other.cpuPolicyAverageFrequencyHz &&
|
|
||||||
cpuCoreIdleTimesPercentage == other.cpuCoreIdleTimesPercentage &&
|
|
||||||
workDurationFeatures == other.workDurationFeatures &&
|
|
||||||
previousThrottleDecision == other.previousThrottleDecision;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Model {
|
|
||||||
public:
|
|
||||||
Model()
|
|
||||||
: mShouldRandomThrottleDistribution(0, 1),
|
|
||||||
mRandomThrottleDistribution(static_cast<uint32_t>(ThrottleDecision::FIRST),
|
|
||||||
static_cast<uint32_t>(ThrottleDecision::LAST)) {}
|
|
||||||
ThrottleDecision Run(const std::deque<ModelInput> &modelInputs,
|
|
||||||
const AdaptiveCpuConfig &config);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::default_random_engine mGenerator;
|
|
||||||
std::uniform_real_distribution<double> mShouldRandomThrottleDistribution;
|
|
||||||
std::uniform_int_distribution<uint32_t> mRandomThrottleDistribution;
|
|
||||||
|
|
||||||
ThrottleDecision RunDecisionTree(const std::deque<ModelInput> &modelInputs);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "RealFilesystem.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <istream>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
bool RealFilesystem::ListDirectory(const std::string &path,
|
|
||||||
std::vector<std::string> *result) const {
|
|
||||||
ATRACE_CALL();
|
|
||||||
// We can't use std::filesystem, see aosp/894015 & b/175635923.
|
|
||||||
auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir(path.c_str()), closedir};
|
|
||||||
if (!dir) {
|
|
||||||
LOG(ERROR) << "Failed to open directory " << path;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dirent *entry;
|
|
||||||
while ((entry = readdir(&*dir)) != nullptr) {
|
|
||||||
result->emplace_back(entry->d_name);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RealFilesystem::ReadFileStream(const std::string &path,
|
|
||||||
std::unique_ptr<std::istream> *result) const {
|
|
||||||
ATRACE_CALL();
|
|
||||||
*result = std::make_unique<std::ifstream>(path);
|
|
||||||
if ((*result)->fail()) {
|
|
||||||
LOG(ERROR) << "Failed to read file stream: " << path;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RealFilesystem::ResetFileStream(const std::unique_ptr<std::istream> &fileStream) const {
|
|
||||||
if (fileStream->seekg(0).bad()) {
|
|
||||||
LOG(ERROR) << "Failed to reset file stream";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,46 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <memory>
|
|
||||||
#include <ostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "IFilesystem.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
class RealFilesystem : public IFilesystem {
|
|
||||||
public:
|
|
||||||
virtual ~RealFilesystem() {}
|
|
||||||
bool ListDirectory(const std::string &path, std::vector<std::string> *result) const override;
|
|
||||||
bool ReadFileStream(const std::string &path,
|
|
||||||
std::unique_ptr<std::istream> *result) const override;
|
|
||||||
bool ResetFileStream(const std::unique_ptr<std::istream> &fileStream) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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 "ThrottleDecision.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
std::string ThrottleString(ThrottleDecision throttleDecision) {
|
|
||||||
switch (throttleDecision) {
|
|
||||||
case ThrottleDecision::NO_THROTTLE:
|
|
||||||
return "NO_THROTTLE";
|
|
||||||
case ThrottleDecision::THROTTLE_60:
|
|
||||||
return "THROTTLE_60";
|
|
||||||
case ThrottleDecision::THROTTLE_70:
|
|
||||||
return "THROTTLE_70";
|
|
||||||
case ThrottleDecision::THROTTLE_80:
|
|
||||||
return "THROTTLE_80";
|
|
||||||
case ThrottleDecision::THROTTLE_90:
|
|
||||||
return "THROTTLE_90";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,62 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
enum class ThrottleDecision {
|
|
||||||
NO_THROTTLE = 0,
|
|
||||||
THROTTLE_50 = 1,
|
|
||||||
THROTTLE_60 = 2,
|
|
||||||
THROTTLE_70 = 3,
|
|
||||||
THROTTLE_80 = 4,
|
|
||||||
THROTTLE_90 = 5,
|
|
||||||
FIRST = NO_THROTTLE,
|
|
||||||
LAST = THROTTLE_90,
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string ThrottleString(ThrottleDecision throttleDecision);
|
|
||||||
|
|
||||||
static const std::unordered_map<ThrottleDecision, std::vector<std::string>>
|
|
||||||
THROTTLE_DECISION_TO_HINT_NAMES = {
|
|
||||||
{ThrottleDecision::NO_THROTTLE, {}},
|
|
||||||
{ThrottleDecision::THROTTLE_50,
|
|
||||||
{"LOW_POWER_LITTLE_CLUSTER_50", "LOW_POWER_MID_CLUSTER_50", "LOW_POWER_CPU_50"}},
|
|
||||||
{ThrottleDecision::THROTTLE_60,
|
|
||||||
{"LOW_POWER_LITTLE_CLUSTER_60", "LOW_POWER_MID_CLUSTER_60", "LOW_POWER_CPU_60"}},
|
|
||||||
{ThrottleDecision::THROTTLE_70,
|
|
||||||
{"LOW_POWER_LITTLE_CLUSTER_70", "LOW_POWER_MID_CLUSTER_70", "LOW_POWER_CPU_70"}},
|
|
||||||
{ThrottleDecision::THROTTLE_80,
|
|
||||||
{"LOW_POWER_LITTLE_CLUSTER_80", "LOW_POWER_MID_CLUSTER_80", "LOW_POWER_CPU_80"}},
|
|
||||||
{ThrottleDecision::THROTTLE_90,
|
|
||||||
{"LOW_POWER_LITTLE_CLUSTER_90", "LOW_POWER_MID_CLUSTER_90", "LOW_POWER_CPU_90"}}};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "TimeSource.h"
|
|
||||||
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
std::chrono::nanoseconds TimeSource::GetTime() const {
|
|
||||||
ATRACE_CALL();
|
|
||||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::nanoseconds TimeSource::GetKernelTime() const {
|
|
||||||
ATRACE_CALL();
|
|
||||||
timespec ts;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
return std::chrono::nanoseconds(ts.tv_sec * 1000000000UL + ts.tv_nsec);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,40 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 "ITimeSource.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
class TimeSource : public ITimeSource {
|
|
||||||
public:
|
|
||||||
~TimeSource() override {}
|
|
||||||
std::chrono::nanoseconds GetTime() const override;
|
|
||||||
std::chrono::nanoseconds GetKernelTime() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "powerhal-adaptivecpu"
|
|
||||||
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
||||||
|
|
||||||
#include "WorkDurationProcessor.h"
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
using std::chrono_literals::operator""ms;
|
|
||||||
using std::chrono_literals::operator""ns;
|
|
||||||
|
|
||||||
// The standard target duration, based on 60 FPS. Durations submitted with different targets are
|
|
||||||
// normalized against this target. For example, a duration that was at 80% of its target will be
|
|
||||||
// scaled to 0.8 * kNormalTargetDuration.
|
|
||||||
constexpr std::chrono::nanoseconds kNormalTargetDuration = 16666666ns;
|
|
||||||
|
|
||||||
// All durations shorter than this are ignored.
|
|
||||||
constexpr std::chrono::nanoseconds kMinDuration = 0ns;
|
|
||||||
|
|
||||||
// All durations longer than this are ignored.
|
|
||||||
constexpr std::chrono::nanoseconds kMaxDuration = 600 * kNormalTargetDuration;
|
|
||||||
|
|
||||||
// If we haven't processed a lot of batches, stop accepting new ones. In cases where the processing
|
|
||||||
// thread has crashed, but the reporting thread is still reporting, this prevents consuming large
|
|
||||||
// amounts of memory.
|
|
||||||
// TODO(b/213160386): Move to AdaptiveCpuConfig.
|
|
||||||
constexpr size_t kMaxUnprocessedBatches = 1000;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
bool WorkDurationProcessor::ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
|
|
||||||
std::chrono::nanoseconds targetDuration) {
|
|
||||||
ATRACE_CALL();
|
|
||||||
LOG(VERBOSE) << "Received " << workDurations.size() << " work durations with target "
|
|
||||||
<< targetDuration.count() << "ns";
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mWorkDurationBatches.size() >= kMaxUnprocessedBatches) {
|
|
||||||
LOG(ERROR) << "Adaptive CPU isn't processing work durations fast enough";
|
|
||||||
mWorkDurationBatches.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mWorkDurationBatches.emplace_back(workDurations, targetDuration);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkDurationFeatures WorkDurationProcessor::GetFeatures() {
|
|
||||||
ATRACE_CALL();
|
|
||||||
|
|
||||||
std::vector<WorkDurationBatch> workDurationBatches;
|
|
||||||
{
|
|
||||||
ATRACE_NAME("lock");
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
mWorkDurationBatches.swap(workDurationBatches);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::nanoseconds durationsSum = 0ns;
|
|
||||||
std::chrono::nanoseconds maxDuration = 0ns;
|
|
||||||
uint32_t numMissedDeadlines = 0;
|
|
||||||
uint32_t numDurations = 0;
|
|
||||||
for (const WorkDurationBatch &batch : workDurationBatches) {
|
|
||||||
for (const WorkDuration workDuration : batch.workDurations) {
|
|
||||||
std::chrono::nanoseconds duration(workDuration.durationNanos);
|
|
||||||
if (duration < kMinDuration || duration > kMaxDuration) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalise the duration and add it to the total.
|
|
||||||
// kMaxDuration * kStandardTarget.count() fits comfortably within int64_t.
|
|
||||||
std::chrono::nanoseconds durationNormalized =
|
|
||||||
(duration * kNormalTargetDuration.count()) / batch.targetDuration.count();
|
|
||||||
durationsSum += durationNormalized;
|
|
||||||
maxDuration = std::max(maxDuration, durationNormalized);
|
|
||||||
if (duration > batch.targetDuration) {
|
|
||||||
++numMissedDeadlines;
|
|
||||||
}
|
|
||||||
++numDurations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::nanoseconds averageDuration = durationsSum / numDurations;
|
|
||||||
return {
|
|
||||||
.averageDuration = averageDuration,
|
|
||||||
.maxDuration = maxDuration,
|
|
||||||
.numMissedDeadlines = numMissedDeadlines,
|
|
||||||
.numDurations = numDurations,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WorkDurationProcessor::HasWorkDurations() {
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
return !mWorkDurationBatches.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,76 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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/hardware/power/WorkDuration.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
using ::aidl::android::hardware::power::WorkDuration;
|
|
||||||
|
|
||||||
struct WorkDurationBatch {
|
|
||||||
WorkDurationBatch(const std::vector<WorkDuration> &workDurations,
|
|
||||||
std::chrono::nanoseconds targetDuration)
|
|
||||||
: workDurations(workDurations), targetDuration(targetDuration) {}
|
|
||||||
const std::vector<WorkDuration> workDurations;
|
|
||||||
const std::chrono::nanoseconds targetDuration;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WorkDurationFeatures {
|
|
||||||
std::chrono::nanoseconds averageDuration;
|
|
||||||
std::chrono::nanoseconds maxDuration;
|
|
||||||
uint32_t numMissedDeadlines;
|
|
||||||
uint32_t numDurations;
|
|
||||||
|
|
||||||
bool operator==(const WorkDurationFeatures &other) const {
|
|
||||||
return averageDuration == other.averageDuration && maxDuration == other.maxDuration &&
|
|
||||||
numMissedDeadlines == other.numMissedDeadlines && numDurations == other.numDurations;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class WorkDurationProcessor {
|
|
||||||
public:
|
|
||||||
bool ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
|
|
||||||
std::chrono::nanoseconds targetDuration);
|
|
||||||
|
|
||||||
WorkDurationFeatures GetFeatures();
|
|
||||||
|
|
||||||
// True if ReportWorkDurations has been called since GetFeatures was last called.
|
|
||||||
bool HasWorkDurations();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// The work durations reported since GetFeatures() was last called.
|
|
||||||
// Ordered from least recent to most recent.
|
|
||||||
std::vector<WorkDurationBatch> mWorkDurationBatches;
|
|
||||||
// Guards reading/writing mWorkDurationBatches.
|
|
||||||
std::mutex mMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1 +0,0 @@
|
||||||
exclude_files=.*
|
|
|
@ -1,4 +0,0 @@
|
||||||
benm@google.com
|
|
||||||
miloslav@google.com
|
|
||||||
mishaw@google.com
|
|
||||||
silviavinyes@google.com
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,131 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <android-base/properties.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/AdaptiveCpuConfig.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
using std::chrono_literals::operator""ms;
|
|
||||||
using std::chrono_literals::operator""min;
|
|
||||||
|
|
||||||
class AdaptiveCpuConfigTest : public ::testing::Test {
|
|
||||||
protected:
|
|
||||||
void SetUp() override {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.hint_timeout_ms", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.enabled_hint_timeout_ms", "");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, valid) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "25");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.hint_timeout_ms", "500");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "25");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,3,4");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.enabled_hint_timeout_ms", "1000");
|
|
||||||
const AdaptiveCpuConfig expectedConfig{
|
|
||||||
.iterationSleepDuration = 25ms,
|
|
||||||
.hintTimeout = 500ms,
|
|
||||||
.randomThrottleDecisionProbability = 0.25,
|
|
||||||
.enabledHintTimeout = 1000ms,
|
|
||||||
.randomThrottleOptions = {ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_70,
|
|
||||||
ThrottleDecision::THROTTLE_80},
|
|
||||||
};
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
ASSERT_EQ(actualConfig, expectedConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, defaultConfig) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.hint_timeout_ms", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "");
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.enabled_hint_timeout_ms", "");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
ASSERT_EQ(actualConfig, AdaptiveCpuConfig::DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, iterationSleepDuration_belowMin) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "2");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
ASSERT_EQ(actualConfig.iterationSleepDuration, 20ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, iterationSleepDuration_negative) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.iteration_sleep_duration_ms", "-100");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
ASSERT_EQ(actualConfig.iterationSleepDuration, 1000ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, randomThrottleDecisionProbability_float) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "0.5");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_TRUE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
ASSERT_EQ(actualConfig.randomThrottleDecisionProbability, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, randomThrottleDecisionProbability_tooBig) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_decision_percent", "101");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_invalidThrottle) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1,2,9");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_wrongDelim) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1.2,3");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_wrongNumber) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1,2a,3");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuConfigTest, randomThrottleOptions_straySpace) {
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1 ,2,3");
|
|
||||||
AdaptiveCpuConfig actualConfig;
|
|
||||||
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
android::base::SetProperty("debug.adaptivecpu.random_throttle_options", "0,1,2,3 ");
|
|
||||||
ASSERT_FALSE(AdaptiveCpuConfig::ReadFromSystemProperties(&actualConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,129 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/AdaptiveCpuStats.h"
|
|
||||||
#include "mocks.h"
|
|
||||||
|
|
||||||
using testing::HasSubstr;
|
|
||||||
using testing::Return;
|
|
||||||
using std::chrono_literals::operator""ns;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuStatsTest, singleRun) {
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
|
|
||||||
EXPECT_CALL(*timeSource, GetTime())
|
|
||||||
.Times(3)
|
|
||||||
.WillOnce(Return(1000ns))
|
|
||||||
.WillOnce(Return(1100ns))
|
|
||||||
.WillOnce(Return(1200ns));
|
|
||||||
|
|
||||||
AdaptiveCpuStats stats(std::move(timeSource));
|
|
||||||
stats.RegisterStartRun();
|
|
||||||
stats.RegisterSuccessfulRun(ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_60, {},
|
|
||||||
AdaptiveCpuConfig::DEFAULT);
|
|
||||||
|
|
||||||
std::stringstream stream;
|
|
||||||
stats.DumpToStream(stream);
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Successful runs / total runs: 1 / 1\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Total run duration: 100.000000ns\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Average run duration: 100.000000ns\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Running time fraction: 0.5\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_60: 1\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuStatsTest, multipleRuns) {
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
|
|
||||||
EXPECT_CALL(*timeSource, GetTime())
|
|
||||||
.Times(9)
|
|
||||||
.WillOnce(Return(1000ns)) // start #1
|
|
||||||
.WillOnce(Return(1100ns)) // success #1
|
|
||||||
.WillOnce(Return(2000ns)) // start #2
|
|
||||||
.WillOnce(Return(2200ns)) // success #2
|
|
||||||
.WillOnce(Return(3000ns)) // start #3
|
|
||||||
.WillOnce(Return(3100ns)) // success #3
|
|
||||||
.WillOnce(Return(4000ns)) // start #4
|
|
||||||
.WillOnce(Return(4800ns)) // success #4
|
|
||||||
.WillOnce(Return(5000ns)); // dump
|
|
||||||
|
|
||||||
AdaptiveCpuStats stats(std::move(timeSource));
|
|
||||||
stats.RegisterStartRun();
|
|
||||||
stats.RegisterSuccessfulRun(ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_60,
|
|
||||||
// Ignored, as this is the first call.
|
|
||||||
{.numDurations = 100000, .numMissedDeadlines = 123},
|
|
||||||
AdaptiveCpuConfig::DEFAULT);
|
|
||||||
stats.RegisterStartRun();
|
|
||||||
stats.RegisterSuccessfulRun(ThrottleDecision::THROTTLE_60, ThrottleDecision::THROTTLE_70,
|
|
||||||
{.numDurations = 100, .numMissedDeadlines = 10},
|
|
||||||
AdaptiveCpuConfig::DEFAULT);
|
|
||||||
stats.RegisterStartRun();
|
|
||||||
stats.RegisterSuccessfulRun(ThrottleDecision::THROTTLE_70, ThrottleDecision::THROTTLE_60,
|
|
||||||
{.numDurations = 50, .numMissedDeadlines = 1},
|
|
||||||
AdaptiveCpuConfig::DEFAULT);
|
|
||||||
stats.RegisterStartRun();
|
|
||||||
stats.RegisterSuccessfulRun(ThrottleDecision::THROTTLE_60, ThrottleDecision::THROTTLE_80,
|
|
||||||
{.numDurations = 200, .numMissedDeadlines = 20},
|
|
||||||
AdaptiveCpuConfig::DEFAULT);
|
|
||||||
|
|
||||||
std::stringstream stream;
|
|
||||||
stats.DumpToStream(stream);
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Successful runs / total runs: 4 / 4\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Total run duration: 1.200000us\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Average run duration: 300.000000ns\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Running time fraction: 0.3\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_60: 2\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_70: 1\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_60: 30 / 300 (0.1)\n"));
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- THROTTLE_70: 1 / 50 (0.02)\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AdaptiveCpuStatsTest, failedRun) {
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
|
|
||||||
EXPECT_CALL(*timeSource, GetTime())
|
|
||||||
.Times(4)
|
|
||||||
.WillOnce(Return(1000ns))
|
|
||||||
.WillOnce(Return(1100ns))
|
|
||||||
.WillOnce(Return(1200ns))
|
|
||||||
.WillOnce(Return(1300ns));
|
|
||||||
|
|
||||||
AdaptiveCpuStats stats(std::move(timeSource));
|
|
||||||
stats.RegisterStartRun();
|
|
||||||
stats.RegisterStartRun();
|
|
||||||
stats.RegisterSuccessfulRun(ThrottleDecision::NO_THROTTLE, ThrottleDecision::THROTTLE_60, {},
|
|
||||||
AdaptiveCpuConfig::DEFAULT);
|
|
||||||
|
|
||||||
std::stringstream stream;
|
|
||||||
stats.DumpToStream(stream);
|
|
||||||
EXPECT_THAT(stream.str(), HasSubstr("- Successful runs / total runs: 1 / 2\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,159 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/CpuFrequencyReader.h"
|
|
||||||
#include "mocks.h"
|
|
||||||
|
|
||||||
using testing::_;
|
|
||||||
using testing::ByMove;
|
|
||||||
using testing::Return;
|
|
||||||
using std::chrono_literals::operator""ms;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &stream, const CpuPolicyAverageFrequency &frequency) {
|
|
||||||
return stream << "CpuPolicyAverageFrequency(" << frequency.policyId << ", "
|
|
||||||
<< frequency.averageFrequencyHz << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CpuFrequencyReaderTest, cpuPolicyIds) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::vector<std::string>{"ignored1", "policy1", "ignored2",
|
|
||||||
"policy5", "policy10", "policybad"};
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream(_, _))
|
|
||||||
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1 2\n3 4\n");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuFrequencyReader reader(std::move(filesystem));
|
|
||||||
EXPECT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::map<uint32_t, std::map<uint64_t, std::chrono::milliseconds>> expected = {
|
|
||||||
{1, {{1, 20ms}, {3, 40ms}}}, {5, {{1, 20ms}, {3, 40ms}}}, {10, {{1, 20ms}, {3, 40ms}}}};
|
|
||||||
EXPECT_EQ(reader.GetPreviousCpuPolicyFrequencies(), expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CpuFrequencyReaderTest, getRecentCpuPolicyFrequencies) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::vector<std::string>{"policy1", "policy2"};
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem,
|
|
||||||
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy1/stats/time_in_state", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1000 5\n2000 4");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1000 7\n2000 10");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem,
|
|
||||||
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy2/stats/time_in_state", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1500 1\n2500 23");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1500 5\n2500 23");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuFrequencyReader reader(std::move(filesystem));
|
|
||||||
EXPECT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::vector<CpuPolicyAverageFrequency> actual;
|
|
||||||
EXPECT_TRUE(reader.GetRecentCpuPolicyFrequencies(&actual));
|
|
||||||
EXPECT_EQ(actual, std::vector<CpuPolicyAverageFrequency>({
|
|
||||||
{.policyId = 1, .averageFrequencyHz = 1750},
|
|
||||||
{.policyId = 2, .averageFrequencyHz = 1500},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CpuFrequencyReaderTest, getRecentCpuPolicyFrequencies_frequenciesChange) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::vector<std::string>{"policy1"};
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem,
|
|
||||||
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy1/stats/time_in_state", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1000 5\n2000 4");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1000 6\n2001 4");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuFrequencyReader reader(std::move(filesystem));
|
|
||||||
EXPECT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::vector<CpuPolicyAverageFrequency> actual;
|
|
||||||
EXPECT_FALSE(reader.GetRecentCpuPolicyFrequencies(&actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CpuFrequencyReaderTest, getRecentCpuPolicyFrequencies_badFormat) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ListDirectory("/sys/devices/system/cpu/cpufreq", _))
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::vector<std::string>{"policy1"};
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem,
|
|
||||||
ReadFileStream("/sys/devices/system/cpu/cpufreq/policy1/stats/time_in_state", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1000 1");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1000 2\nfoo");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuFrequencyReader reader(std::move(filesystem));
|
|
||||||
EXPECT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::vector<CpuPolicyAverageFrequency> actual;
|
|
||||||
EXPECT_FALSE(reader.GetRecentCpuPolicyFrequencies(&actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,139 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/CpuLoadReaderProcStat.h"
|
|
||||||
#include "mocks.h"
|
|
||||||
|
|
||||||
#define LOG_TAG "powerhal-adaptivecpu"
|
|
||||||
|
|
||||||
using testing::_;
|
|
||||||
using testing::ByMove;
|
|
||||||
using testing::Return;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "bad line\n";
|
|
||||||
ss << "cpu1 100 0 0 50 0 0 0 0 0 0\n";
|
|
||||||
ss << "cpu2 200 0 0 50 0 0 0 0 0 0\n";
|
|
||||||
*result = std::make_unique<std::istringstream>(ss.str());
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "bad line\n";
|
|
||||||
ss << "cpu1 200 0 0 150 0 0 0 0 0 0\n";
|
|
||||||
ss << "cpu2 500 0 0 150 0 0 0 0 0 0\n";
|
|
||||||
*result = std::make_unique<std::istringstream>(ss.str());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuLoadReaderProcStat reader(std::move(filesystem));
|
|
||||||
reader.Init();
|
|
||||||
|
|
||||||
std::array<double, NUM_CPU_CORES> actualPercentages;
|
|
||||||
ASSERT_TRUE(reader.GetRecentCpuLoads(&actualPercentages));
|
|
||||||
std::array<double, NUM_CPU_CORES> expectedPercentages({0, 0.5, 0.25, 0, 0, 0, 0, 0});
|
|
||||||
ASSERT_EQ(actualPercentages, expectedPercentages);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads_failsWithMissingValues) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "bad line\n";
|
|
||||||
ss << "cpu1 100 0 0 50 0 0 0\n";
|
|
||||||
ss << "cpu2 200 0 0 50 0 0 0\n";
|
|
||||||
*result = std::make_unique<std::istringstream>(ss.str());
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "bad line\n";
|
|
||||||
ss << "cpu1 200 0 0 150 0 0 0\n";
|
|
||||||
ss << "cpu2 500 0 0 150 0 0 0\n";
|
|
||||||
*result = std::make_unique<std::istringstream>(ss.str());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuLoadReaderProcStat reader(std::move(filesystem));
|
|
||||||
reader.Init();
|
|
||||||
std::array<double, NUM_CPU_CORES> actualPercentages;
|
|
||||||
ASSERT_FALSE(reader.GetRecentCpuLoads(&actualPercentages));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads_failsWithEmptyFile) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuLoadReaderProcStat reader(std::move(filesystem));
|
|
||||||
reader.Init();
|
|
||||||
std::array<double, NUM_CPU_CORES> actualPercentages;
|
|
||||||
ASSERT_FALSE(reader.GetRecentCpuLoads(&actualPercentages));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CpuLoadReaderProcStatTest, GetRecentCpuLoads_failsWithDifferentCpus) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/stat", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "bad line\n";
|
|
||||||
ss << "cpu1 100 0 0 50 0 0 0 0 0 0\n";
|
|
||||||
ss << "cpu2 200 0 0 50 0 0 0 0 0 0\n";
|
|
||||||
*result = std::make_unique<std::istringstream>(ss.str());
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "bad line\n";
|
|
||||||
ss << "cpu1 200 0 0 150 0 0 0 0 0 0\n";
|
|
||||||
ss << "cpu3 500 0 0 150 0 0 0 0 0 0\n";
|
|
||||||
*result = std::make_unique<std::istringstream>(ss.str());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
CpuLoadReaderProcStat reader(std::move(filesystem));
|
|
||||||
reader.Init();
|
|
||||||
std::array<double, NUM_CPU_CORES> actualPercentages;
|
|
||||||
ASSERT_FALSE(reader.GetRecentCpuLoads(&actualPercentages));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/CpuLoadReaderSysDevices.h"
|
|
||||||
#include "mocks.h"
|
|
||||||
|
|
||||||
#define LOG_TAG "powerhal-adaptivecpu"
|
|
||||||
|
|
||||||
using testing::_;
|
|
||||||
using testing::ByMove;
|
|
||||||
using testing::MatchesRegex;
|
|
||||||
using testing::Return;
|
|
||||||
using std::chrono_literals::operator""ms;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
TEST(CpuLoadReaderSysDevicesTest, GetRecentCpuLoads) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
EXPECT_CALL(*filesystem, ListDirectory(MatchesRegex("/sys/devices/system/cpu/cpu0/cpuidle"), _))
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::vector<std::string>{"foo", "bar", "baz"};
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem,
|
|
||||||
ListDirectory(MatchesRegex("/sys/devices/system/cpu/cpu0/cpuidle/(foo|bar)"), _))
|
|
||||||
.Times(2)
|
|
||||||
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::vector<std::string>{"abc", "time", "xyz"};
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem,
|
|
||||||
ListDirectory(MatchesRegex("/sys/devices/system/cpu/cpu0/cpuidle/baz"), _))
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::vector<std::string>{"abc", "xyz"};
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu0/cpuidle/foo/time", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("100");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("200");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu0/cpuidle/bar/time", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("500");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("700");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu1/cpuidle/foo/time", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1000");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("1010");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/sys/devices/system/cpu/cpu1/cpuidle/bar/time", _))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("50");
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("70");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem,
|
|
||||||
ReadFileStream(
|
|
||||||
MatchesRegex("/sys/devices/system/cpu/cpu[2-7]/cpuidle/(foo|bar)/time"), _))
|
|
||||||
.WillRepeatedly([](auto _path __attribute__((unused)), auto result) {
|
|
||||||
*result = std::make_unique<std::istringstream>("0");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_CALL(*timeSource, GetTime()).Times(2).WillOnce(Return(1ms)).WillOnce(Return(2ms));
|
|
||||||
|
|
||||||
CpuLoadReaderSysDevices reader(std::move(filesystem), std::move(timeSource));
|
|
||||||
ASSERT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::array<double, NUM_CPU_CORES> actualPercentage;
|
|
||||||
reader.GetRecentCpuLoads(&actualPercentage);
|
|
||||||
|
|
||||||
std::array<double, NUM_CPU_CORES> expectedPercentage{0.3, 0.03, 0, 0, 0, 0, 0, 0};
|
|
||||||
ASSERT_EQ(actualPercentage, expectedPercentage);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,211 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/KernelCpuFeatureReader.h"
|
|
||||||
#include "mocks.h"
|
|
||||||
|
|
||||||
using testing::_;
|
|
||||||
using testing::ByMove;
|
|
||||||
using testing::MatchesRegex;
|
|
||||||
using testing::Return;
|
|
||||||
using std::chrono_literals::operator""ns;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
TEST(KernelCpuFeatureReaderTest, valid) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
|
|
||||||
EXPECT_CALL(*timeSource, GetKernelTime())
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce(Return(100ns))
|
|
||||||
.WillOnce(Return(200ns));
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
|
|
||||||
.WillOnce([](auto path __attribute__((unused)), auto result) {
|
|
||||||
// Empty file, we will populate in ResetFileStream.
|
|
||||||
*result = std::make_unique<std::istringstream>("");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ResetFileStream(_))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto &result) {
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 100},
|
|
||||||
{.weighted_sum_freq = 200, .total_idle_time_ns = 200},
|
|
||||||
{.weighted_sum_freq = 200, .total_idle_time_ns = 200},
|
|
||||||
{.weighted_sum_freq = 300, .total_idle_time_ns = 200},
|
|
||||||
{.weighted_sum_freq = 300, .total_idle_time_ns = 200},
|
|
||||||
}};
|
|
||||||
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
|
|
||||||
static_cast<std::istringstream &>(*result).str(
|
|
||||||
std::string(bytes, bytes + sizeof(acpuStats)));
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto &result) {
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
|
|
||||||
{.weighted_sum_freq = 200, .total_idle_time_ns = 150},
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
|
|
||||||
{.weighted_sum_freq = 300, .total_idle_time_ns = 300},
|
|
||||||
{.weighted_sum_freq = 200, .total_idle_time_ns = 300},
|
|
||||||
{.weighted_sum_freq = 400, .total_idle_time_ns = 300},
|
|
||||||
{.weighted_sum_freq = 300, .total_idle_time_ns = 300},
|
|
||||||
}};
|
|
||||||
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
|
|
||||||
static_cast<std::istringstream &>(*result).str(
|
|
||||||
std::string(bytes, bytes + sizeof(acpuStats)));
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
|
|
||||||
ASSERT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
|
|
||||||
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
|
|
||||||
ASSERT_TRUE(
|
|
||||||
reader.GetRecentCpuFeatures(&cpuPolicyAverageFrequencyHz, &cpuCoreIdleTimesPercentage));
|
|
||||||
std::array<double, NUM_CPU_POLICIES> expectedFrequencies{{1, 1, 1}};
|
|
||||||
std::array<double, NUM_CPU_CORES> expectedIdleTimes{{0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1}};
|
|
||||||
ASSERT_EQ(cpuPolicyAverageFrequencyHz, expectedFrequencies);
|
|
||||||
ASSERT_EQ(cpuCoreIdleTimesPercentage, expectedIdleTimes);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(KernelCpuFeatureReaderTest, noFile) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
|
|
||||||
.WillOnce(Return(false));
|
|
||||||
|
|
||||||
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
|
|
||||||
ASSERT_FALSE(reader.Init());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(KernelCpuFeatureReaderTest, frequencies_capsNegativeDiff) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
|
|
||||||
EXPECT_CALL(*timeSource, GetKernelTime())
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce(Return(100ns))
|
|
||||||
.WillOnce(Return(200ns));
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
|
|
||||||
.WillOnce([](auto path __attribute__((unused)), auto result) {
|
|
||||||
// Empty file, we will populate in ResetFileStream.
|
|
||||||
*result = std::make_unique<std::istringstream>("");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ResetFileStream(_))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto &result) {
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
|
|
||||||
{.weighted_sum_freq = 200, .total_idle_time_ns = 100},
|
|
||||||
}};
|
|
||||||
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
|
|
||||||
static_cast<std::istringstream &>(*result).str(
|
|
||||||
std::string(bytes, bytes + sizeof(acpuStats)));
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto &result) {
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
|
|
||||||
}};
|
|
||||||
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
|
|
||||||
static_cast<std::istringstream &>(*result).str(
|
|
||||||
std::string(bytes, bytes + sizeof(acpuStats)));
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
|
|
||||||
ASSERT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
|
|
||||||
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
|
|
||||||
ASSERT_TRUE(
|
|
||||||
reader.GetRecentCpuFeatures(&cpuPolicyAverageFrequencyHz, &cpuCoreIdleTimesPercentage));
|
|
||||||
std::array<double, NUM_CPU_POLICIES> expectedFrequencies{{0}};
|
|
||||||
ASSERT_EQ(cpuPolicyAverageFrequencyHz, expectedFrequencies);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(KernelCpuFeatureReaderTest, idleTimes_capsNegativeDiff) {
|
|
||||||
std::unique_ptr<MockFilesystem> filesystem = std::make_unique<MockFilesystem>();
|
|
||||||
std::unique_ptr<MockTimeSource> timeSource = std::make_unique<MockTimeSource>();
|
|
||||||
|
|
||||||
EXPECT_CALL(*timeSource, GetKernelTime())
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce(Return(100ns))
|
|
||||||
.WillOnce(Return(200ns));
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ReadFileStream("/proc/vendor_sched/acpu_stats", _))
|
|
||||||
.WillOnce([](auto path __attribute__((unused)), auto result) {
|
|
||||||
// Empty file, we will populate in ResetFileStream.
|
|
||||||
*result = std::make_unique<std::istringstream>("");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_CALL(*filesystem, ResetFileStream(_))
|
|
||||||
.Times(2)
|
|
||||||
.WillOnce([](auto &result) {
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
|
|
||||||
{.weighted_sum_freq = 100, .total_idle_time_ns = 150},
|
|
||||||
}};
|
|
||||||
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
|
|
||||||
static_cast<std::istringstream &>(*result).str(
|
|
||||||
std::string(bytes, bytes + sizeof(acpuStats)));
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.WillOnce([](auto &result) {
|
|
||||||
std::array<acpu_stats, NUM_CPU_CORES> acpuStats{{
|
|
||||||
{.weighted_sum_freq = 200, .total_idle_time_ns = 100},
|
|
||||||
}};
|
|
||||||
char *bytes = reinterpret_cast<char *>(acpuStats.begin());
|
|
||||||
static_cast<std::istringstream &>(*result).str(
|
|
||||||
std::string(bytes, bytes + sizeof(acpuStats)));
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
KernelCpuFeatureReader reader(std::move(filesystem), std::move(timeSource));
|
|
||||||
ASSERT_TRUE(reader.Init());
|
|
||||||
|
|
||||||
std::array<double, NUM_CPU_POLICIES> cpuPolicyAverageFrequencyHz;
|
|
||||||
std::array<double, NUM_CPU_CORES> cpuCoreIdleTimesPercentage;
|
|
||||||
ASSERT_TRUE(
|
|
||||||
reader.GetRecentCpuFeatures(&cpuPolicyAverageFrequencyHz, &cpuCoreIdleTimesPercentage));
|
|
||||||
std::array<double, NUM_CPU_CORES> expectedIdleTimes{{0}};
|
|
||||||
ASSERT_EQ(cpuCoreIdleTimesPercentage, expectedIdleTimes);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "adaptivecpu/Model.h"
|
|
||||||
#include "mocks.h"
|
|
||||||
|
|
||||||
using std::chrono_literals::operator""ns;
|
|
||||||
using testing::_;
|
|
||||||
using testing::ByMove;
|
|
||||||
using testing::Return;
|
|
||||||
using testing::UnorderedElementsAre;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
TEST(ModelTest, ModelInput_SetCpuFreqiencies) {
|
|
||||||
const ModelInput expected{
|
|
||||||
.cpuPolicyAverageFrequencyHz = {100, 101, 102},
|
|
||||||
};
|
|
||||||
ModelInput actual;
|
|
||||||
ASSERT_TRUE(actual.SetCpuFreqiencies({
|
|
||||||
{.policyId = 0, .averageFrequencyHz = 100},
|
|
||||||
{.policyId = 4, .averageFrequencyHz = 101},
|
|
||||||
{.policyId = 6, .averageFrequencyHz = 102},
|
|
||||||
}));
|
|
||||||
ASSERT_EQ(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModelTest, ModelInput_SetCpuFreqiencies_failsWithOutOfOrderFrquencies) {
|
|
||||||
ASSERT_FALSE(ModelInput().SetCpuFreqiencies({
|
|
||||||
{.policyId = 0, .averageFrequencyHz = 100},
|
|
||||||
{.policyId = 6, .averageFrequencyHz = 102},
|
|
||||||
{.policyId = 4, .averageFrequencyHz = 101},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModelTest, Run_randomInputs) {
|
|
||||||
std::default_random_engine generator;
|
|
||||||
std::uniform_real_distribution<double> frequencyDistribution(0, 1e6);
|
|
||||||
std::uniform_real_distribution<double> idleTimesDistribution(0, 1);
|
|
||||||
std::uniform_int_distribution<uint32_t> frameTimeDistribution(1, 100);
|
|
||||||
std::uniform_int_distribution<uint16_t> numRenderedFramesDistribution(1, 20);
|
|
||||||
std::uniform_int_distribution<uint32_t> throttleDecisionDistribution(0, 3);
|
|
||||||
|
|
||||||
const auto randomModelInput = [&]() {
|
|
||||||
return ModelInput{
|
|
||||||
.cpuPolicyAverageFrequencyHz = {frequencyDistribution(generator),
|
|
||||||
frequencyDistribution(generator),
|
|
||||||
frequencyDistribution(generator)},
|
|
||||||
.cpuCoreIdleTimesPercentage =
|
|
||||||
{idleTimesDistribution(generator), idleTimesDistribution(generator),
|
|
||||||
idleTimesDistribution(generator), idleTimesDistribution(generator),
|
|
||||||
idleTimesDistribution(generator), idleTimesDistribution(generator),
|
|
||||||
idleTimesDistribution(generator), idleTimesDistribution(generator)},
|
|
||||||
.workDurationFeatures =
|
|
||||||
{.averageDuration =
|
|
||||||
std::chrono::nanoseconds(frameTimeDistribution(generator)),
|
|
||||||
.maxDuration = std::chrono::nanoseconds(frameTimeDistribution(generator)),
|
|
||||||
.numMissedDeadlines = numRenderedFramesDistribution(generator),
|
|
||||||
.numDurations = numRenderedFramesDistribution(generator)},
|
|
||||||
.previousThrottleDecision =
|
|
||||||
static_cast<ThrottleDecision>(throttleDecisionDistribution(generator)),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
std::deque<ModelInput> modelInputs{randomModelInput(), randomModelInput(),
|
|
||||||
randomModelInput()};
|
|
||||||
Model().Run(modelInputs, AdaptiveCpuConfig::DEFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModelTest, Run_randomThrottling) {
|
|
||||||
ModelInput modelInput{
|
|
||||||
.cpuPolicyAverageFrequencyHz = {0, 0, 0},
|
|
||||||
.cpuCoreIdleTimesPercentage = {0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
.workDurationFeatures = {.averageDuration = 0ns,
|
|
||||||
.maxDuration = 0ns,
|
|
||||||
.numMissedDeadlines = 0,
|
|
||||||
.numDurations = 0},
|
|
||||||
.previousThrottleDecision = ThrottleDecision::NO_THROTTLE,
|
|
||||||
};
|
|
||||||
std::deque<ModelInput> modelInputs{modelInput, modelInput, modelInput};
|
|
||||||
|
|
||||||
AdaptiveCpuConfig config = AdaptiveCpuConfig::DEFAULT;
|
|
||||||
config.randomThrottleOptions = {ThrottleDecision::THROTTLE_70, ThrottleDecision::THROTTLE_80};
|
|
||||||
config.randomThrottleDecisionProbability = 1;
|
|
||||||
|
|
||||||
std::set<ThrottleDecision> actualThrottleDecisions;
|
|
||||||
Model model;
|
|
||||||
for (int i = 0; i < 100; i++) {
|
|
||||||
ThrottleDecision throttleDecision = model.Run(modelInputs, config);
|
|
||||||
actualThrottleDecisions.insert(throttleDecision);
|
|
||||||
}
|
|
||||||
ASSERT_THAT(actualThrottleDecisions,
|
|
||||||
UnorderedElementsAre(ThrottleDecision::THROTTLE_70, ThrottleDecision::THROTTLE_80));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/WorkDurationProcessor.h"
|
|
||||||
|
|
||||||
using ::aidl::android::hardware::power::WorkDuration;
|
|
||||||
using std::chrono_literals::operator""ns;
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
static const std::chrono::nanoseconds kNormalTargetDuration = 16666666ns;
|
|
||||||
|
|
||||||
TEST(WorkDurationProcessorTest, GetFeatures) {
|
|
||||||
WorkDurationProcessor processor;
|
|
||||||
ASSERT_TRUE(processor.ReportWorkDurations(
|
|
||||||
std::vector<WorkDuration>{
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()},
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 3}},
|
|
||||||
kNormalTargetDuration));
|
|
||||||
|
|
||||||
const WorkDurationFeatures expected = {.averageDuration = kNormalTargetDuration * 2,
|
|
||||||
.maxDuration = kNormalTargetDuration * 3,
|
|
||||||
.numMissedDeadlines = 1,
|
|
||||||
.numDurations = 2};
|
|
||||||
const WorkDurationFeatures actual = processor.GetFeatures();
|
|
||||||
ASSERT_EQ(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(WorkDurationProcessorTest, GetFeatures_multipleBatches) {
|
|
||||||
WorkDurationProcessor processor;
|
|
||||||
ASSERT_TRUE(processor.ReportWorkDurations(
|
|
||||||
std::vector<WorkDuration>{
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()},
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 3}},
|
|
||||||
kNormalTargetDuration));
|
|
||||||
ASSERT_TRUE(processor.ReportWorkDurations(
|
|
||||||
std::vector<WorkDuration>{
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 6},
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 2}},
|
|
||||||
kNormalTargetDuration));
|
|
||||||
|
|
||||||
const WorkDurationFeatures expected = {.averageDuration = kNormalTargetDuration * 3,
|
|
||||||
.maxDuration = kNormalTargetDuration * 6,
|
|
||||||
.numMissedDeadlines = 3,
|
|
||||||
.numDurations = 4};
|
|
||||||
const WorkDurationFeatures actual = processor.GetFeatures();
|
|
||||||
ASSERT_EQ(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(WorkDurationProcessorTest, GetFeatures_scalesDifferentTargetDurations) {
|
|
||||||
WorkDurationProcessor processor;
|
|
||||||
ASSERT_TRUE(processor.ReportWorkDurations(
|
|
||||||
std::vector<WorkDuration>{
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 2},
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count() * 6}},
|
|
||||||
kNormalTargetDuration * 2));
|
|
||||||
|
|
||||||
const WorkDurationFeatures expected = {.averageDuration = kNormalTargetDuration * 2,
|
|
||||||
.maxDuration = kNormalTargetDuration * 3,
|
|
||||||
.numMissedDeadlines = 1,
|
|
||||||
.numDurations = 2};
|
|
||||||
const WorkDurationFeatures actual = processor.GetFeatures();
|
|
||||||
ASSERT_EQ(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(WorkDurationProcessorTest, GetFeatures_noFrames) {
|
|
||||||
WorkDurationProcessor processor;
|
|
||||||
const WorkDurationFeatures expected = {
|
|
||||||
.averageDuration = 0ns, .maxDuration = 0ns, .numMissedDeadlines = 0, .numDurations = 0};
|
|
||||||
const WorkDurationFeatures actual = processor.GetFeatures();
|
|
||||||
ASSERT_EQ(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(WorkDurationProcessorTest, HasWorkDurations) {
|
|
||||||
WorkDurationProcessor processor;
|
|
||||||
ASSERT_FALSE(processor.HasWorkDurations());
|
|
||||||
ASSERT_TRUE(processor.ReportWorkDurations(
|
|
||||||
std::vector<WorkDuration>{
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()}},
|
|
||||||
kNormalTargetDuration * 2));
|
|
||||||
ASSERT_TRUE(processor.HasWorkDurations());
|
|
||||||
processor.GetFeatures();
|
|
||||||
ASSERT_FALSE(processor.HasWorkDurations());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(WorkDurationProcessorTest, GetFeatures_tooManyUnprocessedThenClears) {
|
|
||||||
WorkDurationProcessor processor;
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
ASSERT_TRUE(processor.ReportWorkDurations(
|
|
||||||
std::vector<WorkDuration>{
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()}},
|
|
||||||
kNormalTargetDuration));
|
|
||||||
}
|
|
||||||
ASSERT_FALSE(processor.ReportWorkDurations(
|
|
||||||
std::vector<WorkDuration>{
|
|
||||||
{.timeStampNanos = 0, .durationNanos = kNormalTargetDuration.count()}},
|
|
||||||
kNormalTargetDuration));
|
|
||||||
ASSERT_EQ(processor.GetFeatures().numDurations, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <gmock/gmock.h>
|
|
||||||
|
|
||||||
#include "adaptivecpu/IFilesystem.h"
|
|
||||||
#include "adaptivecpu/ITimeSource.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
class MockFilesystem : public IFilesystem {
|
|
||||||
public:
|
|
||||||
~MockFilesystem() override {}
|
|
||||||
MOCK_METHOD(bool, ListDirectory, (const std::string &path, std::vector<std::string> *result),
|
|
||||||
(const, override));
|
|
||||||
MOCK_METHOD(bool, ReadFileStream,
|
|
||||||
(const std::string &path, std::unique_ptr<std::istream> *result),
|
|
||||||
(const, override));
|
|
||||||
MOCK_METHOD(bool, ResetFileStream, (const std::unique_ptr<std::istream> &fileStream),
|
|
||||||
(const, override));
|
|
||||||
};
|
|
||||||
|
|
||||||
class MockTimeSource : public ITimeSource {
|
|
||||||
public:
|
|
||||||
~MockTimeSource() override {}
|
|
||||||
MOCK_METHOD(std::chrono::nanoseconds, GetTime, (), (const, override));
|
|
||||||
MOCK_METHOD(std::chrono::nanoseconds, GetKernelTime, (), (const, override));
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,5 +0,0 @@
|
||||||
BOARD_SEPOLICY_DIRS += hardware/google/pixel-sepolicy/power-libperfmgr
|
|
||||||
|
|
||||||
# power HAL
|
|
||||||
PRODUCT_PACKAGES += \
|
|
||||||
android.hardware.power-service.pixel-libperfmgr
|
|
|
@ -1,7 +1,7 @@
|
||||||
<manifest version="1.0" type="device">
|
<manifest version="1.0" type="device">
|
||||||
<hal format="aidl">
|
<hal format="aidl">
|
||||||
<name>android.hardware.power</name>
|
<name>android.hardware.power</name>
|
||||||
<version>3</version>
|
<version>2</version>
|
||||||
<fqname>IPower/default</fqname>
|
<fqname>IPower/default</fqname>
|
||||||
</hal>
|
</hal>
|
||||||
</manifest>
|
</manifest>
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 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 "powerhal-libperfmgr"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <cutils/sockets.h>
|
|
||||||
#include <log/log.h>
|
|
||||||
|
|
||||||
#include "DisplayLowPower.h"
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
DisplayLowPower::DisplayLowPower() : mFossStatus(false) {}
|
|
||||||
|
|
||||||
void DisplayLowPower::Init() {
|
|
||||||
ConnectPpsDaemon();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayLowPower::SetDisplayLowPower(bool enable) {
|
|
||||||
SetFoss(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayLowPower::ConnectPpsDaemon() {
|
|
||||||
constexpr const char kPpsDaemon[] = "pps";
|
|
||||||
|
|
||||||
mPpsSocket.reset(
|
|
||||||
socket_local_client(kPpsDaemon, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
|
|
||||||
if (mPpsSocket.get() < 0) {
|
|
||||||
ALOGW("Connecting to PPS daemon failed (%s)", strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int DisplayLowPower::SendPpsCommand(const std::string_view cmd) {
|
|
||||||
if (TEMP_FAILURE_RETRY(write(mPpsSocket.get(), cmd.data(), cmd.size())) < 0) {
|
|
||||||
ALOGE("Failed to send pps command '%s' over socket (%s)", cmd.data(), strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayLowPower::SetFoss(bool enable) {
|
|
||||||
if (mPpsSocket.get() < 0 || mFossStatus == enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALOGI("%s foss", (enable) ? "Enable" : "Disable");
|
|
||||||
|
|
||||||
std::string_view foss_cmd;
|
|
||||||
if (enable) {
|
|
||||||
foss_cmd = "foss:on";
|
|
||||||
} else {
|
|
||||||
foss_cmd = "foss:off";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SendPpsCommand(foss_cmd)) {
|
|
||||||
mFossStatus = enable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <android-base/unique_fd.h>
|
|
||||||
|
|
||||||
namespace aidl {
|
|
||||||
namespace google {
|
|
||||||
namespace hardware {
|
|
||||||
namespace power {
|
|
||||||
namespace impl {
|
|
||||||
namespace pixel {
|
|
||||||
|
|
||||||
class DisplayLowPower {
|
|
||||||
public:
|
|
||||||
DisplayLowPower();
|
|
||||||
~DisplayLowPower() {}
|
|
||||||
void Init();
|
|
||||||
void SetDisplayLowPower(bool enable);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void ConnectPpsDaemon();
|
|
||||||
int SendPpsCommand(const std::string_view cmd);
|
|
||||||
void SetFoss(bool enable);
|
|
||||||
|
|
||||||
::android::base::unique_fd mPpsSocket;
|
|
||||||
bool mFossStatus;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pixel
|
|
||||||
} // namespace impl
|
|
||||||
} // namespace power
|
|
||||||
} // namespace hardware
|
|
||||||
} // namespace google
|
|
||||||
} // namespace aidl
|
|
|
@ -28,11 +28,7 @@
|
||||||
#include "Power.h"
|
#include "Power.h"
|
||||||
#include "PowerExt.h"
|
#include "PowerExt.h"
|
||||||
#include "PowerSessionManager.h"
|
#include "PowerSessionManager.h"
|
||||||
#include "adaptivecpu/AdaptiveCpu.h"
|
|
||||||
#include "disp-power/DisplayLowPower.h"
|
|
||||||
|
|
||||||
using aidl::google::hardware::power::impl::pixel::AdaptiveCpu;
|
|
||||||
using aidl::google::hardware::power::impl::pixel::DisplayLowPower;
|
|
||||||
using aidl::google::hardware::power::impl::pixel::Power;
|
using aidl::google::hardware::power::impl::pixel::Power;
|
||||||
using aidl::google::hardware::power::impl::pixel::PowerExt;
|
using aidl::google::hardware::power::impl::pixel::PowerExt;
|
||||||
using aidl::google::hardware::power::impl::pixel::PowerHintMonitor;
|
using aidl::google::hardware::power::impl::pixel::PowerHintMonitor;
|
||||||
|
@ -48,20 +44,16 @@ int main() {
|
||||||
LOG(FATAL) << "HintManager Init failed";
|
LOG(FATAL) << "HintManager Init failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<DisplayLowPower> dlpw = std::make_shared<DisplayLowPower>();
|
|
||||||
|
|
||||||
// single thread
|
// single thread
|
||||||
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
||||||
|
|
||||||
std::shared_ptr<AdaptiveCpu> adaptiveCpu = std::make_shared<AdaptiveCpu>();
|
|
||||||
|
|
||||||
// core service
|
// core service
|
||||||
std::shared_ptr<Power> pw = ndk::SharedRefBase::make<Power>(dlpw, adaptiveCpu);
|
std::shared_ptr<Power> pw = ndk::SharedRefBase::make<Power>();
|
||||||
ndk::SpAIBinder pwBinder = pw->asBinder();
|
ndk::SpAIBinder pwBinder = pw->asBinder();
|
||||||
AIBinder_setMinSchedulerPolicy(pwBinder.get(), SCHED_NORMAL, -20);
|
AIBinder_setMinSchedulerPolicy(pwBinder.get(), SCHED_NORMAL, -20);
|
||||||
|
|
||||||
// extension service
|
// extension service
|
||||||
std::shared_ptr<PowerExt> pwExt = ndk::SharedRefBase::make<PowerExt>(dlpw, adaptiveCpu);
|
std::shared_ptr<PowerExt> pwExt = ndk::SharedRefBase::make<PowerExt>();
|
||||||
auto pwExtBinder = pwExt->asBinder();
|
auto pwExtBinder = pwExt->asBinder();
|
||||||
AIBinder_setMinSchedulerPolicy(pwExtBinder.get(), SCHED_NORMAL, -20);
|
AIBinder_setMinSchedulerPolicy(pwExtBinder.get(), SCHED_NORMAL, -20);
|
||||||
|
|
||||||
|
@ -71,7 +63,7 @@ int main() {
|
||||||
const std::string instance = std::string() + Power::descriptor + "/default";
|
const std::string instance = std::string() + Power::descriptor + "/default";
|
||||||
binder_status_t status = AServiceManager_addService(pw->asBinder().get(), instance.c_str());
|
binder_status_t status = AServiceManager_addService(pw->asBinder().get(), instance.c_str());
|
||||||
CHECK(status == STATUS_OK);
|
CHECK(status == STATUS_OK);
|
||||||
LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is started.";
|
LOG(INFO) << "Lenovo Power HAL AIDL Service with Extension is started.";
|
||||||
|
|
||||||
if (HintManager::GetInstance()->GetAdpfProfile()) {
|
if (HintManager::GetInstance()->GetAdpfProfile()) {
|
||||||
PowerHintMonitor::getInstance()->start();
|
PowerHintMonitor::getInstance()->start();
|
||||||
|
@ -80,13 +72,12 @@ int main() {
|
||||||
std::thread initThread([&]() {
|
std::thread initThread([&]() {
|
||||||
::android::base::WaitForProperty(kPowerHalInitProp.data(), "1");
|
::android::base::WaitForProperty(kPowerHalInitProp.data(), "1");
|
||||||
HintManager::GetInstance()->Start();
|
HintManager::GetInstance()->Start();
|
||||||
dlpw->Init();
|
|
||||||
});
|
});
|
||||||
initThread.detach();
|
initThread.detach();
|
||||||
|
|
||||||
ABinderProcess_joinThreadPool();
|
ABinderProcess_joinThreadPool();
|
||||||
|
|
||||||
// should not reach
|
// should not reach
|
||||||
LOG(ERROR) << "Pixel Power HAL AIDL Service with Extension just died.";
|
LOG(ERROR) << "Lenovo Power HAL AIDL Service with Extension just died.";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
Loading…
Reference in a new issue