diff --git a/etc/samgr_standard.cfg b/etc/samgr_standard.cfg index a3ce2cd8893ee7a5e182b9844dc3ea45bd01416e..3b7f03bf794b57f6b6c80c7137af49aea3bb5274 100644 --- a/etc/samgr_standard.cfg +++ b/etc/samgr_standard.cfg @@ -12,6 +12,7 @@ "critical" : [1, 1, 60], "uid" : "samgr", "gid" : ["samgr", "system", "readproc", "data_reserve"], + "writepid" : ["/dev/memcg/perf_sensitive/cgroup.procs"], "file" : ["/dev/kmsg rd 0660 root system"], "bootevents":"bootevent.samgr.ready", "permission": [ diff --git a/interfaces/innerkits/common/BUILD.gn b/interfaces/innerkits/common/BUILD.gn index 837fb697d014faa475dbb0677fb7152b05b674e3..639f675d70fcb3bf724b31ea9f8e363aea69bbc0 100644 --- a/interfaces/innerkits/common/BUILD.gn +++ b/interfaces/innerkits/common/BUILD.gn @@ -79,7 +79,7 @@ ohos_shared_library("samgr_common") { external_deps += [ "hicollie:libhicollie" ] defines += [ "HICOLLIE_ENABLE" ] } - public_external_deps = [ "json:nlohmann_json_static" ] + external_deps += [ "json:nlohmann_json_static" ] part_name = "samgr" } diff --git a/services/samgr/native/BUILD.gn b/services/samgr/native/BUILD.gn index e9acf73f4ccf8dac4aad142e907246e7748184da..34cccf3648d6bd7a6b280ec6b42459cb64cbb89e 100644 --- a/services/samgr/native/BUILD.gn +++ b/services/samgr/native/BUILD.gn @@ -105,6 +105,7 @@ ohos_executable("samgr") { "init:libbegetutil", "ipc:ipc_core", "ipc:libdbinder", + "json:nlohmann_json_static", "safwk:system_ability_ondemand_reason", ] diff --git a/services/samgr/native/include/collect/common_event_collect.h b/services/samgr/native/include/collect/common_event_collect.h index 983fb8630e3c085a86cca8908cd442eed597b8cc..36ab8104516bd76f09ad92fda5a113e6c57184a5 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 { diff --git a/services/samgr/native/source/collect/common_event_collect.cpp b/services/samgr/native/source/collect/common_event_collect.cpp index f1349b1282acde1e0ba98b64b7289e828b58c190..9573fbfdbba4d871a203eed663e14847fc543013 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,128 @@ 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; + uint64_t nice; + uint64_t system; + uint64_t iowait; + uint64_t irq; + uint64_t softirq; + uint64_t steal; + uint64_t guest = 0; + uint64_t 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; + uint64_t idlePre = 0; + uint64_t total = 0; + uint64_t 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/source/collect/ref_count_collect.cpp b/services/samgr/native/source/collect/ref_count_collect.cpp index 66b62b1cb49df3a615d1f30fccbd445fd70b9e82..bafb0e616813a597dda56600e0249b3f6b7ee7b1 100644 --- a/services/samgr/native/source/collect/ref_count_collect.cpp +++ b/services/samgr/native/source/collect/ref_count_collect.cpp @@ -17,7 +17,7 @@ namespace OHOS { namespace { -constexpr int32_t REF_RESIDENT_TIMER_INTERVAL = 1000 * 60 * 10; +constexpr int32_t REF_RESIDENT_TIMER_INTERVAL = 1000 * 60 * 60; constexpr int32_t REF_ONDEMAND_TIMER_INTERVAL = 1000 * 60 * 1; } 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 71effd7c3f0c4c5ff7c899f198ea373eb8af19f9..8e17f9f077902f4cc654a564ae95064e5d972d0a 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 diff --git a/test/fuzztest/samgr_fuzzer/BUILD.gn b/test/fuzztest/samgr_fuzzer/BUILD.gn index 2656669458a08f3224485d51e8523a9b2e33280c..62412f668815382a516618b63d11fcd187469db1 100644 --- a/test/fuzztest/samgr_fuzzer/BUILD.gn +++ b/test/fuzztest/samgr_fuzzer/BUILD.gn @@ -181,6 +181,7 @@ foreach(item, samgr_fuzztests) { "init:libbegetutil", "ipc:ipc_core", "ipc:libdbinder", + "json:nlohmann_json_static", "safwk:system_ability_fwk", "samgr:samgr_common", "samgr:samgr_proxy", diff --git a/test/fuzztest/samgrcoverage_fuzzer/BUILD.gn b/test/fuzztest/samgrcoverage_fuzzer/BUILD.gn index db80705ad00cc63dc3b6bb8820b87da90dbe8c44..328c79b681e4bdec54b1eeec6e576043c7c7d4fd 100644 --- a/test/fuzztest/samgrcoverage_fuzzer/BUILD.gn +++ b/test/fuzztest/samgrcoverage_fuzzer/BUILD.gn @@ -84,6 +84,7 @@ ohos_fuzztest("SamgrCoverageFuzzTest") { "init:libbegetutil", "ipc:ipc_single", "ipc:libdbinder", + "json:nlohmann_json_static", "safwk:system_ability_fwk", "samgr:samgr_common", "samgr:samgr_proxy", diff --git a/test/fuzztest/samgrdumper_fuzzer/BUILD.gn b/test/fuzztest/samgrdumper_fuzzer/BUILD.gn index 1d6123d81d4acdfbc0ee935626acbfd205048cd5..d644470c220aaeede36a4d86d68087ad25ce9479 100644 --- a/test/fuzztest/samgrdumper_fuzzer/BUILD.gn +++ b/test/fuzztest/samgrdumper_fuzzer/BUILD.gn @@ -83,6 +83,7 @@ ohos_fuzztest("SamgrDumperFuzzTest") { "init:libbegetutil", "ipc:ipc_single", "ipc:libdbinder", + "json:nlohmann_json_static", "safwk:system_ability_fwk", "samgr:samgr_common", "samgr:samgr_proxy", diff --git a/test/fuzztest/samgrdumper_fuzzer/samgrdumper_fuzzer.cpp b/test/fuzztest/samgrdumper_fuzzer/samgrdumper_fuzzer.cpp index c81daef52609c6e69597f3d0b1cf68467b6400c8..aa01728bfacf071ce5c9d09bf84ee7a6ee2dfc9a 100644 --- a/test/fuzztest/samgrdumper_fuzzer/samgrdumper_fuzzer.cpp +++ b/test/fuzztest/samgrdumper_fuzzer/samgrdumper_fuzzer.cpp @@ -67,6 +67,13 @@ std::string BuildStringFromData(const uint8_t* data, size_t size) return strVal; } +void InitData(const uint8_t* data, size_t size) +{ + g_baseFuzzData = data; + g_baseFuzzSize = size; + g_baseFuzzPos = 0; +} + void SamgrDumperFuzzTest(const uint8_t* data, size_t size) { SamMockPermission::MockProcess(HIDUMPER_PROCESS_NAME); @@ -103,12 +110,9 @@ void SamgrDumperFuzzTest(const uint8_t* data, size_t size) manager->IpcDumpSingleProcess(fd, cmd, processName); } -void FuzzListenerDumpProc(const uint8_t* data, size_t size) +void FuzzListenerDumpProc() { SamMockPermission::MockProcess(HIDUMPER_PROCESS_NAME); - g_baseFuzzData = data; - g_baseFuzzSize = size; - g_baseFuzzPos = 0; int32_t pid1 = GetData(); int32_t pid2 = GetData(); std::map> dumpListeners; @@ -149,11 +153,8 @@ void FuzzListenerDumpProc(const uint8_t* data, size_t size) args.clear(); } -void FuzzFfrtLoadMetrics(const uint8_t* data, size_t size) +void FuzzFfrtLoadMetrics() { - g_baseFuzzData = data; - g_baseFuzzSize = size; - g_baseFuzzPos = 0; int32_t pid = GetData(); std::shared_ptr scheduler = std::make_shared(); int32_t fd = -1; @@ -182,9 +183,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) return 0; } + OHOS::Samgr::InitData(data, size); OHOS::Samgr::SamgrDumperFuzzTest(data, size); - OHOS::Samgr::FuzzListenerDumpProc(data, size); - OHOS::Samgr::FuzzFfrtLoadMetrics(data, size); + OHOS::Samgr::FuzzListenerDumpProc(); + OHOS::Samgr::FuzzFfrtLoadMetrics(); return 0; } diff --git a/test/fuzztest/systemabilitymanager_fuzzer/BUILD.gn b/test/fuzztest/systemabilitymanager_fuzzer/BUILD.gn index c5357e938bf82a3c98bd7bd53ccfe5180e02322d..bd0e4c0702956a7eaef765e722846a0188508974 100644 --- a/test/fuzztest/systemabilitymanager_fuzzer/BUILD.gn +++ b/test/fuzztest/systemabilitymanager_fuzzer/BUILD.gn @@ -83,6 +83,7 @@ ohos_fuzztest("SystemAbilityManagerFuzzTest") { "init:libbegetutil", "ipc:ipc_core", "ipc:libdbinder", + "json:nlohmann_json_static", "safwk:system_ability_fwk", "samgr:samgr_common", "samgr:samgr_proxy",