diff --git a/ability_runtime.gni b/ability_runtime.gni index 51031932ab..a78b111c05 100644 --- a/ability_runtime.gni +++ b/ability_runtime.gni @@ -123,6 +123,7 @@ declare_args() { ability_runtime_media_library_enable = true ability_runtime_no_screen = false ability_runtime_hitrace_enable = true + ability_runtime_hiperf_enable = true if (!defined(global_parts_info) || defined(global_parts_info.account_os_account)) { @@ -231,4 +232,9 @@ declare_args() { !defined(global_parts_info.hiviewdfx_hitrace)) { ability_runtime_hitrace_enable = false } + + if (defined(global_parts_info) && + !defined(global_parts_info.developtools_hiperf)) { + ability_runtime_hiperf_enable = false + } } diff --git a/bundle.json b/bundle.json index fed9812464..c02e112679 100644 --- a/bundle.json +++ b/bundle.json @@ -111,7 +111,8 @@ "udmf", "webview", "window_manager", - "zlib" + "zlib", + "hiperf" ], "third_party": [ "libjpeg-turbo" diff --git a/frameworks/native/appkit/BUILD.gn b/frameworks/native/appkit/BUILD.gn index 4db9c2a784..ebb0dc003b 100644 --- a/frameworks/native/appkit/BUILD.gn +++ b/frameworks/native/appkit/BUILD.gn @@ -229,6 +229,18 @@ ohos_shared_library("appkit_native") { deps += [ "${ability_runtime_innerkits_path}/child_process_manager:child_process_manager" ] } + if (ability_runtime_hiperf_enable) { + defines += [ "SUPPORT_HIPERF" ] + sources += [ + "${ability_runtime_native_path}/appkit/dfr/appcapture_perf.cpp", + ] + external_deps += [ + "hiperf:hiperf_local", + "faultloggerd:libunwinder", + "faultloggerd:libstack_printer", + ] + } + if (ability_runtime_graphics) { external_deps += [ "ace_engine:ace_forward_compatibility", diff --git a/frameworks/native/appkit/app/main_thread.cpp b/frameworks/native/appkit/app/main_thread.cpp index 66223570af..d840e73b04 100644 --- a/frameworks/native/appkit/app/main_thread.cpp +++ b/frameworks/native/appkit/app/main_thread.cpp @@ -96,6 +96,9 @@ #include "js_runtime_utils.h" #include "context/application_context.h" #include "os_account_manager_wrapper.h" +#ifdef SUPPORT_HIPERF +#include "appcapture_perf.h" +#endif #if defined(NWEB) #include @@ -3570,6 +3573,12 @@ int32_t MainThread::ScheduleNotifyAppFault(const FaultData &faultData) return AppExecFwk::AppfreezeInner::GetInstance()->AppfreezeHandle(faultData, false); } +#ifdef SUPPORT_HIPERF + if (faultData.faultType == FaultDataType::CPU_LOAD) { + return AppExecFwk::AppCapturePerf::GetInstance()->CapturePerf(faultData); + } +#endif + wptr weak = this; auto task = [weak, faultData] { auto appThread = weak.promote(); diff --git a/frameworks/native/appkit/dfr/appcapture_perf.cpp b/frameworks/native/appkit/dfr/appcapture_perf.cpp new file mode 100644 index 0000000000..9d299257c3 --- /dev/null +++ b/frameworks/native/appkit/dfr/appcapture_perf.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "appcapture_perf.h" + +#include +#include +#include + +#include "hisysevent.h" +#include "lperf.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AppExecFwk { +const int32_t CAPTURE_DURATION = 1000; +const int32_t FREQ = 100; + +std::shared_ptr AppCapturePerf::instance_ = nullptr; +std::mutex AppCapturePerf::singletonMutex_; + +std::shared_ptr AppCapturePerf::GetInstance() +{ + if (instance_ == nullptr) { + std::lock_guard lock(singletonMutex_); + if (instance_ == nullptr) { + instance_ = std::make_shared(); + } + } + return instance_; +} + +std::vector AppCapturePerf::SplitStr(const std::string &s, char delimiter) +{ + std::vector tokens; + std::string token; + std::istringstream tokenStream(s); + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + return tokens; +} + +int32_t AppCapturePerf::CapturePerf(const FaultData &faultData) +{ + std::vector tids; + std::vector threads = SplitStr(faultData.errorObject.stack, ','); + for (int32_t i = 0; i < threads.size(); i++) { + if (threads[i] == "") { + continue; + } + tids.push_back(stoi(threads[i])); + } + int res = -2; + auto &instance = Developtools::HiPerf::HiPerfLocal::Lperf::GetInstance(); + res = instance.StartProcessStackSampling(tids, FREQ, CAPTURE_DURATION, false); + if (res != 0) { + TAG_LOGE(AAFwkTag::APPDFR, "hiperf stack capture failed"); + return 0; + } + std::vector perf; + for (int32_t i = 0; i < tids.size(); i++) { + std::string stack; + res = instance.CollectSampleStackByTid(tids[i], stack); + if (res != 0) { + perf.push_back(""); + continue; + } + perf.push_back(stack); + } + res = instance.FinishProcessStackSampling(); + if (res != 0) { + TAG_LOGE(AAFwkTag::APPDFR, "hiperf stack clean failed"); + } + int64_t perfId = std::strtoll(faultData.timeoutMarkers.c_str(), nullptr, 10); + int ret = HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RELIABILITY, "CPU_LOAD_CAPTURE_STACK", + HiviewDFX::HiSysEvent::EventType::STATISTIC, "APP_NAME", faultData.errorObject.name, + "TIDS", tids, "PERF", perf, "PERFID", perfId); + if (ret != 0) { + TAG_LOGE(AAFwkTag::APPDFR, "HiSysEventWrite CPU_LOAD_CAPTURE_STACK failed"); + } + return 0; +} + +void AppCapturePerf::DestroyInstance() +{ + std::lock_guard lock(singletonMutex_); + if (instance_ != nullptr) { + instance_.reset(); + instance_ = nullptr; + } +} + +} // namespace AppExecFwk +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/inner_api/app_manager/include/appmgr/fault_data.h b/interfaces/inner_api/app_manager/include/appmgr/fault_data.h index b698fc028a..ea1faee711 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/fault_data.h +++ b/interfaces/inner_api/app_manager/include/appmgr/fault_data.h @@ -35,6 +35,7 @@ enum class FaultDataType { JS_ERROR, CJ_ERROR, APP_FREEZE, + CPU_LOAD, PERFORMANCE_CONTROL, RESOURCE_CONTROL }; diff --git a/interfaces/kits/native/appkit/dfr/appcapture_perf.h b/interfaces/kits/native/appkit/dfr/appcapture_perf.h new file mode 100644 index 0000000000..31f7bd035e --- /dev/null +++ b/interfaces/kits/native/appkit/dfr/appcapture_perf.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OHOS_ABILITY_ABILITY_APP_CAPTURE_PERF_H +#define OHOS_ABILITY_ABILITY_APP_CAPTURE_PERF_H + +#include +#include +#include + +#include "fault_data.h" + +namespace OHOS { +namespace AppExecFwk { +class AppCapturePerf { +public: + AppCapturePerf() {} + ~AppCapturePerf() {} + static std::shared_ptr GetInstance(); + static void DestroyInstance(); + int32_t CapturePerf(const FaultData &faultData); + std::vector SplitStr(const std::string &s, char delimiter); +private: + static std::shared_ptr instance_; + static std::mutex singletonMutex_; +}; +} // namespace AppExecFwk +} // namespace OHOS +#endif // OHOS_ABILITY_ABILITY_APP_CAPTURE_PERF_H \ No newline at end of file diff --git a/test/unittest/dfr_test/BUILD.gn b/test/unittest/dfr_test/BUILD.gn index fc3693ed84..ae41f4848c 100644 --- a/test/unittest/dfr_test/BUILD.gn +++ b/test/unittest/dfr_test/BUILD.gn @@ -23,5 +23,6 @@ group("unittest") { "cpu_data_processor_test:unittest", "dump_proc_helper_test:unittest", "watchdog_test:unittest", + "appcapture_perf_test:unittest", ] } diff --git a/test/unittest/dfr_test/appcapture_perf_test/BUILD.gn b/test/unittest/dfr_test/appcapture_perf_test/BUILD.gn new file mode 100644 index 0000000000..8ee0e27eed --- /dev/null +++ b/test/unittest/dfr_test/appcapture_perf_test/BUILD.gn @@ -0,0 +1,125 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") +import("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +module_output_path = "ability_runtime/freeze_checker" + +############################################################################### +config("module_context_config") { + visibility = [ ":*" ] + include_dirs = [ + "${ability_runtime_innerkits_path}/app_manager/include/appmgr", + "${ability_runtime_test_path}/mock/frameworks_kits_appkit_native_test/include", + "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", + "${ability_runtime_path}/interfaces/kits/native/appkit/app/task", + "${ability_runtime_path}/interfaces/kits/native/ability/native", + ] + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + defines = [ "AMS_LOG_TAG = \"ApplicationUnitTest\"" ] +} + +config("ability_start_setting_config") { + visibility = [ ":*" ] + include_dirs = [ + "${ability_runtime_path}/interfaces/kits/native/appkit/app", + "${ability_runtime_path}/interfaces/kits/native/appkit/dfr", + "${ability_runtime_innerkits_path}/ability_manager/include", + ] +} + +ohos_unittest("appcapture_perf_test") { + module_out_path = module_output_path + + include_dirs = [ + "${ability_runtime_path}/interfaces/kits/native/appkit/dfr", + "${ability_runtime_path}/utils/global/time/include", + ] + + configs = [ + ":module_context_config", + ":ability_start_setting_config", + ] + + sources = [ + "${ability_runtime_native_path}/appkit/dfr/appcapture_perf.cpp", + "appcapture_perf_test.cpp", + ] + + deps = [ + "${ability_runtime_abilitymgr_path}/:abilityms", + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/app_manager:app_manager", + "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/ability/native:ability_thread", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_native_path}/ability/native:uiabilitykit_native", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_native_path}/appkit:app_context_utils", + "${ability_runtime_native_path}/appkit:appkit_native", + "${ability_runtime_path}/js_environment/frameworks/js_environment:js_environment", + ] + + external_deps = [ + "ability_base:configuration", + "ability_base:extractresourcemanager", + "ability_base:string_utils", + "ability_base:want", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "eventhandler:libeventhandler", + "faultloggerd:libbacktrace_local", + "faultloggerd:libdfx_procinfo", + "faultloggerd:libfaultloggerd", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "graphic_2d:librender_service_client", + "hicollie:libhicollie", + "hilog:libhilog", + "hisysevent:libhisysevent", + "hitrace:hitrace_meter", + "init:libbegetutil", + "input:libmmi-client", + "ipc:ipc_core", + "json:nlohmann_json_static", + "napi:ace_napi", + "resource_management:global_resmgr", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + "hiperf:hiperf_local", + "faultloggerd:libunwinder", + "faultloggerd:libstack_printer" + ] + + if (ability_runtime_upms) { + deps += [ + "${ability_runtime_innerkits_path}/uri_permission:uri_permission_mgr", + ] + } +} + +############################################################################### + +group("unittest") { + testonly = true + deps = [ ":appcapture_perf_test" ] +} \ No newline at end of file diff --git a/test/unittest/dfr_test/appcapture_perf_test/appcapture_perf_test.cpp b/test/unittest/dfr_test/appcapture_perf_test/appcapture_perf_test.cpp new file mode 100644 index 0000000000..1e09451787 --- /dev/null +++ b/test/unittest/dfr_test/appcapture_perf_test/appcapture_perf_test.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 + +#define private public +#include "appcapture_perf.h" +#undef private +#include "cpp/mutex.h" + +using namespace testing; +using namespace testing::ext; +using namespace OHOS; +using namespace OHOS::AppExecFwk; + +namespace OHOS { +namespace AppExecFwk { +class AppCapturePerfTest : public testing::Test { +public: + AppCapturePerfTest() + {} + ~AppCapturePerfTest() + {} + std::shared_ptr appCapturePerf = nullptr; + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void AppCapturePerfTest::SetUpTestCase(void) +{} + +void AppCapturePerfTest::TearDownTestCase(void) +{} + +void AppCapturePerfTest::SetUp(void) +{ + appCapturePerf = AppCapturePerf::GetInstance(); +} + +void AppCapturePerfTest::TearDown(void) +{ + AppCapturePerf::DestroyInstance(); +} + +/** + * @tc.number: AppCapturePerfTest001 + * @tc.desc: add testcase codecoverage + * @tc.type: FUNC + */ +HWTEST_F(AppCapturePerfTest, AppCapturePerfTest001, TestSize.Level0) +{ + ASSERT_TRUE(appCapturePerf != nullptr); + GTEST_LOG_(INFO) << "test AppCapturePerfTest001 Data.\n"; + FaultData faultData; + faultData.errorObject.name = "testapp"; + faultData.errorObject.message = "test"; + faultData.errorObject.stack = ""; + int32_t ret = appCapturePerf->CapturePerf(faultData); + EXPECT_EQ(ret, 0); +} + +/** + * @tc.number: AppCapturePerfTest002 + * @tc.desc: add testcase codecoverage + * @tc.type: FUNC + */ +HWTEST_F(AppCapturePerfTest, AppCapturePerfTest002, TestSize.Level0) +{ + ASSERT_TRUE(appCapturePerf != nullptr); + GTEST_LOG_(INFO) << "test AppCapturePerfTest002 Data.\n"; + FaultData faultData; + faultData.errorObject.name = "testapp"; + faultData.errorObject.message = "test"; + faultData.errorObject.stack = "123,,1478"; + int32_t ret = appCapturePerf->CapturePerf(faultData); + EXPECT_EQ(ret, 0); +} +} // namespace AppExecFwk +} // namespace OHOS \ No newline at end of file