From 8308ff56e08da46cc4f66c08ea163f25400f023f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=AD=E6=9D=BE=E6=9D=BE?= Date: Wed, 23 Apr 2025 14:37:24 +0800 Subject: [PATCH] add ipc thread reclaim policy in low-load scenarios --- .../include/collect/common_event_collect.h | 11 +- .../source/collect/common_event_collect.cpp | 124 ++++++++++++++++++ .../src/common_event_collect_test.cpp | 114 ++++++++++++++++ 3 files changed, 248 insertions(+), 1 deletion(-) diff --git a/services/samgr/native/include/collect/common_event_collect.h b/services/samgr/native/include/collect/common_event_collect.h index 983fb863..cd2625e8 100644 --- a/services/samgr/native/include/collect/common_event_collect.h +++ b/services/samgr/native/include/collect/common_event_collect.h @@ -17,6 +17,7 @@ #include #include +#include #include "common_event_subscriber.h" #include "ffrt_handler.h" @@ -59,12 +60,18 @@ public: const std::string& eventName = "") override; void RemoveWhiteCommonEvent() override; void StartReclaimIpcThreadWork(const EventFwk::CommonEventData& data); + void StartMonitorThread(); + void StopMonitorThread(); + private: int64_t GenerateExtraDataIdLocked(); std::vector AddCommonEventName(const std::vector& events); void AddSkillsEvent(EventFwk::MatchingSkills& skill); void CleanFailedEventLocked(const std::vector& eventNames); void SendKernalReclaimIpcThread(); + bool GetCpuTimes(const char* file, uint64_t& total, uint64_t& idle); + float GetCpuUsage(const char* file, uint32_t interval); + void MonitorCpuUsageThread(); std::mutex commomEventLock_; std::mutex commonEventSubscriberLock_; sptr commonEventDeath_; @@ -84,6 +91,8 @@ private: std::atomic isAwakeNotified_ {false}; std::atomic isTriggerTaskStart_ {false}; std::atomic isCancel_{false}; + std::atomic keepRunning_ {false}; + std::thread monitorThread_; }; class CommonEventListener : public SystemAbilityStatusChangeStub { @@ -126,4 +135,4 @@ private: wptr collect_; }; } // namespace OHOS -#endif // SYSTEM_ABILITY_MANAGER_COMMON_EVENT_COLLECT_H \ No newline at end of file +#endif // SYSTEM_ABILITY_MANAGER_COMMON_EVENT_COLLECT_H diff --git a/services/samgr/native/source/collect/common_event_collect.cpp b/services/samgr/native/source/collect/common_event_collect.cpp index f1349b12..004daa2b 100644 --- a/services/samgr/native/source/collect/common_event_collect.cpp +++ b/services/samgr/native/source/collect/common_event_collect.cpp @@ -14,6 +14,9 @@ */ #include +#include +#include +#include "securec.h" #include "common_event_collect.h" @@ -43,6 +46,14 @@ constexpr int64_t MAX_EXTRA_DATA_ID = 1000000000; constexpr int32_t COMMON_EVENT_SERVICE_ID = 3299; constexpr int32_t TRIGGER_THREAD_RECLAIM_DELAY_TIME = 130; constexpr int32_t TRIGGER_THREAD_RECLAIM_DURATION_TIME = 2; +constexpr int32_t CPU_STAT_MIN_FIELDS = 7; +constexpr int32_t CPU_STAT_CHECK_INTERVAL = 5; +constexpr int32_t CPU_LOAD_SHIFT = 16; +constexpr int32_t CPU_LOAD_CHECK_INTERVAL = 300; +constexpr float CPU_LOAD_INVALID = 0.0f; +constexpr float CPU_LOAD_IDLE_THRESHOLD = 10.0f; +constexpr float CPU_LOAD_PERCENT = 100.0f; +constexpr const char* CPU_STAT_INFO = "/proc/stat"; constexpr const char* UID = "uid"; constexpr const char* NET_TYPE = "NetType"; constexpr const char* BUNDLE_NAME = "bundleName"; @@ -94,6 +105,7 @@ int32_t CommonEventCollect::OnStart() workHandler_ = std::make_shared(this); unsubHandler_ = std::make_shared(this); workHandler_->SendEvent(INIT_EVENT); + StartMonitorThread(); return ERR_OK; } @@ -105,6 +117,7 @@ int32_t CommonEventCollect::OnStop() if (unsubHandler_ != nullptr) { unsubHandler_ = nullptr; } + StopMonitorThread(); return ERR_OK; } @@ -703,4 +716,115 @@ void CommonEventSubscriber::OnReceiveEvent(const EventFwk::CommonEventData& data collect->ReportEvent(event); collect->StartReclaimIpcThreadWork(data); } + +bool CommonEventCollect::GetCpuTimes(const char* file, uint64_t& total, uint64_t& idle) +{ + if (file == nullptr) { + HILOGE("Invalid file name"); + return false; + } + + std::ifstream cpuStatFile(file); + if (!cpuStatFile.is_open()) { + HILOGE("Failed to open %{public}s", file); + return false; + } + + std::string line; + if (!std::getline(cpuStatFile, line)) { + HILOGE("Failed to read %{public}s", file); + return false; + } + + uint64_t user, nice, system, iowait, irq, softirq, steal, guest = 0, guestNice = 0; + int num = sscanf_s(line.c_str(), "cpu %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 + " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, + &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, &guestNice); + + if (num < CPU_STAT_MIN_FIELDS) { + HILOGE("Failed to parse %{public}s (got %{public}d fields)", file, num); + return false; + } + + total = user + nice + system + idle + iowait + irq + softirq + steal; + if (num > CPU_STAT_MIN_FIELDS) { + total += guest + guestNice; + } + + return true; +} + +float CommonEventCollect::GetCpuUsage(const char* file, uint32_t interval) +{ + if (file == nullptr) { + HILOGE("Invalid file name"); + return CPU_LOAD_INVALID; + } + + if (interval <= 0) { + HILOGE("Invalid interval"); + return CPU_LOAD_INVALID; + } + + uint64_t totalPre = 0, idlePre = 0, total = 0, idle = 0; + if (!GetCpuTimes(file, totalPre, idlePre)) { + HILOGE("Failed to get pre CPU times"); + return CPU_LOAD_INVALID; + } + + std::this_thread::sleep_for(std::chrono::seconds(interval)); + if (!GetCpuTimes(file, total, idle)) { + HILOGE("Failed to get CPU times"); + return CPU_LOAD_INVALID; + } + + uint64_t totalDelta = total - totalPre; + uint64_t idleDelta = idle - idlePre; + if (totalDelta == 0) { + return CPU_LOAD_INVALID; + } + + float usage = static_cast(totalDelta - idleDelta) / totalDelta; + return usage * CPU_LOAD_PERCENT; +} + +void CommonEventCollect::MonitorCpuUsageThread() +{ + struct sysinfo info; + uint64_t coreNum = sysconf(_SC_NPROCESSORS_ONLN); + uint64_t baseLoad = coreNum << CPU_LOAD_SHIFT; + pthread_setname_np(pthread_self(), "OS_CPU_MONITOR"); + + while (keepRunning_) { + std::this_thread::sleep_for(std::chrono::seconds(CPU_LOAD_CHECK_INTERVAL - CPU_STAT_CHECK_INTERVAL)); + float usage = GetCpuUsage(CPU_STAT_INFO, CPU_STAT_CHECK_INTERVAL); + if (sysinfo(&info) == 0) { + HILOGI("cpu usage: %{public}f 1min %{public}lu 5min %{public}lu", usage, info.loads[0], info.loads[1]); + if (info.loads[0] - baseLoad < (1 << CPU_LOAD_SHIFT) && // 1min avg load <= (logic core num + 1) + info.loads[0] < info.loads[1] && // 1min avg load less than 5min avg load + usage > CPU_LOAD_INVALID && // cpu usage > 0% and <= 10% + usage <= CPU_LOAD_IDLE_THRESHOLD) { + HILOGI("cpu idle TriggerSystemIPCThreadReclaim"); + IPCSkeleton::TriggerSystemIPCThreadReclaim(); + } + } + } +} + +void CommonEventCollect::StartMonitorThread() +{ + keepRunning_ = true; + monitorThread_ = std::thread(&CommonEventCollect::MonitorCpuUsageThread, this); + monitorThread_.detach(); +} + +void CommonEventCollect::StopMonitorThread() +{ + if (keepRunning_) { + keepRunning_ = false; + if (monitorThread_.joinable()) { + monitorThread_.join(); + } + } +} } // namespace OHOS \ No newline at end of file diff --git a/services/samgr/native/test/unittest/src/common_event_collect_test.cpp b/services/samgr/native/test/unittest/src/common_event_collect_test.cpp index 71effd7c..8e17f9f0 100644 --- a/services/samgr/native/test/unittest/src/common_event_collect_test.cpp +++ b/services/samgr/native/test/unittest/src/common_event_collect_test.cpp @@ -18,6 +18,8 @@ #include "sa_profiles.h" #include "string_ex.h" #include "test_log.h" +#include +#include #define private public #include "common_event_collect.h" @@ -34,6 +36,7 @@ namespace OHOS { namespace { constexpr uint32_t COMMON_DIED_EVENT = 11; constexpr const char* COMMON_EVENT_ACTION_NAME = "common_event_action_name"; +constexpr const char* TEST_CPU_STAT_FILE = "test_cpu_stat"; } @@ -50,11 +53,14 @@ void CommonEventCollectTest::TearDownTestCase() void CommonEventCollectTest::SetUp() { DTEST_LOG << "SetUp" << std::endl; + std::ofstream outfile(TEST_CPU_STAT_FILE); + outfile.close(); } void CommonEventCollectTest::TearDown() { DTEST_LOG << "TearDown" << std::endl; + remove(TEST_CPU_STAT_FILE); } /** @@ -893,4 +899,112 @@ HWTEST_F(CommonEventCollectTest, StartReclaimIpcThreadWork001, TestSize.Level3) EXPECT_NE(nullptr, commonEventCollect->workHandler_); DTEST_LOG<<"StartReclaimIpcThreadWork001 END"<< std::endl; } + +HWTEST_F(CommonEventCollectTest, GetCpuTimesWork001, TestSize.Level3) { + // Prepare test data + DTEST_LOG<<"GetCpuTimesWork001 BEGIN"<< std::endl; + std::ofstream outfile(TEST_CPU_STAT_FILE); + outfile << "cpu 1000 200 300 4000 500 0 0 0 0 0"; + outfile.close(); + uint64_t total = 0, idle = 0; + sptr commonEventCollect = new CommonEventCollect(nullptr); + bool ret = commonEventCollect->GetCpuTimes(TEST_CPU_STAT_FILE, total, idle); + EXPECT_TRUE(ret); + EXPECT_EQ(idle, 4000); + EXPECT_EQ(total, 1000 + 200 + 300 + 4000 + 500); + DTEST_LOG<<"GetCpuTimesWork001 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuTimesWork002, TestSize.Level3) { + // Prepare test data with guest times + DTEST_LOG<<"GetCpuTimesWork002 BEGIN"<< std::endl; + std::ofstream outfile(TEST_CPU_STAT_FILE); + outfile << "cpu 1000 200 300 4000 500 0 0 0 100 50"; + outfile.close(); + uint64_t total = 0, idle = 0; + sptr commonEventCollect = new CommonEventCollect(nullptr); + bool ret = commonEventCollect->GetCpuTimes(TEST_CPU_STAT_FILE, total, idle); + EXPECT_TRUE(ret); + EXPECT_EQ(idle, 4000); + EXPECT_EQ(total, 1000 + 200 + 300 + 4000 + 500 + 100 + 50); + DTEST_LOG<<"GetCpuTimesWork002 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuTimesWork003, TestSize.Level3) { + // Don't create the file to simulate open failure + DTEST_LOG<<"GetCpuTimesWork003 BEGIN"<< std::endl; + uint64_t total = 0, idle = 0; + sptr commonEventCollect = new CommonEventCollect(nullptr); + bool ret = commonEventCollect->GetCpuTimes(TEST_CPU_STAT_FILE, total, idle); + EXPECT_FALSE(ret); + DTEST_LOG<<"GetCpuTimesWork003 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuTimesWork004, TestSize.Level3) { + // Create empty file to simulate read failure + DTEST_LOG<<"GetCpuTimesWork004 BEGIN"<< std::endl; + std::ofstream outfile(TEST_CPU_STAT_FILE); + outfile.close(); + uint64_t total = 0, idle = 0; + sptr commonEventCollect = new CommonEventCollect(nullptr); + bool ret = commonEventCollect->GetCpuTimes(TEST_CPU_STAT_FILE, total, idle); + EXPECT_FALSE(ret); + DTEST_LOG<<"GetCpuTimesWork004 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuTimesWork005, TestSize.Level3) { + // Prepare data with insufficient fields + DTEST_LOG<<"GetCpuTimesWork005 BEGIN"<< std::endl; + std::ofstream outfile(TEST_CPU_STAT_FILE); + outfile << "cpu 1000 200"; // Only 2 fields + outfile.close(); + uint64_t total = 0, idle = 0; + sptr commonEventCollect = new CommonEventCollect(nullptr); + bool ret = commonEventCollect->GetCpuTimes(TEST_CPU_STAT_FILE, total, idle); + EXPECT_FALSE(ret); + DTEST_LOG<<"GetCpuTimesWork005 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuTimesWork006, TestSize.Level3) { + // Prepare data with invalid file + DTEST_LOG<<"GetCpuTimesWork006 BEGIN"<< std::endl; + sptr commonEventCollect = new CommonEventCollect(nullptr); + uint64_t total = 0, idle = 0; + bool ret = commonEventCollect->GetCpuTimes(nullptr, total, idle); + EXPECT_FALSE(ret); + DTEST_LOG<<"GetCpuTimesWork006 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuUsageWork001, TestSize.Level3) { + // Prepare data with insufficient fields + DTEST_LOG<<"GetCpuUsageWork001 BEGIN"<< std::endl; + std::ofstream outfile(TEST_CPU_STAT_FILE); + outfile << "cpu 1000 200"; // Only 2 fields + outfile.close(); + sptr commonEventCollect = new CommonEventCollect(nullptr); + float usage = commonEventCollect->GetCpuUsage(TEST_CPU_STAT_FILE, 5); + EXPECT_TRUE(usage == 0.0f); + DTEST_LOG<<"GetCpuUsageWork001 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuUsageWork002, TestSize.Level3) { + // Prepare data with invlaid file + DTEST_LOG<<"GetCpuUsageWork002 BEGIN"<< std::endl; + sptr commonEventCollect = new CommonEventCollect(nullptr); + float usage = commonEventCollect->GetCpuUsage(nullptr, 5); + EXPECT_TRUE(usage == 0.0f); + DTEST_LOG<<"GetCpuUsageWork002 END"<< std::endl; +} + +HWTEST_F(CommonEventCollectTest, GetCpuUsageWork003, TestSize.Level3) { + // Prepare data with invlaid file + DTEST_LOG<<"GetCpuUsageWork003 BEGIN"<< std::endl; + std::ofstream outfile(TEST_CPU_STAT_FILE); + outfile << "cpu 1000 200"; // Only 2 fields + outfile.close(); + sptr commonEventCollect = new CommonEventCollect(nullptr); + float usage = commonEventCollect->GetCpuUsage(TEST_CPU_STAT_FILE, 0); + EXPECT_TRUE(usage == 0.0f); + DTEST_LOG<<"GetCpuUsageWork003 END"<< std::endl; +} } // namespace OHOS \ No newline at end of file -- Gitee