1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "dump_runtime_helper.h"
17 
18 #include "app_mgr_client.h"
19 #include "faultloggerd_client.h"
20 #include "hilog_tag_wrapper.h"
21 #include "js_runtime.h"
22 #include "js_runtime_utils.h"
23 #include "singleton.h"
24 #include "dfx_jsnapi.h"
25 
26 namespace OHOS {
27 namespace AppExecFwk {
28 const char *MODULE_NAME = "hiviewdfx.jsLeakWatcher";
29 const char *CHECK = "check";
30 const char *REQUIRE_NAPI = "requireNapi";
31 
DumpRuntimeHelper(const std::shared_ptr<OHOSApplication> & application)32 DumpRuntimeHelper::DumpRuntimeHelper(const std::shared_ptr<OHOSApplication> &application)
33     : application_(application)
34 {}
35 
SetAppFreezeFilterCallback()36 void DumpRuntimeHelper::SetAppFreezeFilterCallback()
37 {
38     if (application_ == nullptr) {
39         TAG_LOGE(AAFwkTag::APPKIT, "OHOSApplication is nullptr");
40         return;
41     }
42     auto& runtime = application_->GetRuntime();
43     if (runtime == nullptr) {
44         TAG_LOGE(AAFwkTag::APPKIT, "Runtime is nullptr");
45         return;
46     }
47     auto appfreezeFilterCallback = [] (const int32_t pid) -> bool {
48         auto client = DelayedSingleton<AppExecFwk::AppMgrClient>::GetInstance();
49         if (client == nullptr) {
50             TAG_LOGE(AAFwkTag::APPKIT, "client is nullptr");
51             return false;
52         }
53         return client->SetAppFreezeFilter(pid);
54     };
55     auto vm = (static_cast<AbilityRuntime::JsRuntime&>(*runtime)).GetEcmaVm();
56     panda::DFXJSNApi::SetAppFreezeFilterCallback(vm, appfreezeFilterCallback);
57 }
58 
DumpJsHeap(const OHOS::AppExecFwk::JsHeapDumpInfo & info)59 void DumpRuntimeHelper::DumpJsHeap(const OHOS::AppExecFwk::JsHeapDumpInfo &info)
60 {
61     if (application_ == nullptr) {
62         TAG_LOGE(AAFwkTag::APPKIT, "null application");
63         return;
64     }
65     auto& runtime = application_->GetRuntime();
66     if (runtime == nullptr) {
67         TAG_LOGE(AAFwkTag::APPKIT, "null runtime");
68         return;
69     }
70     if (info.needLeakobj) {
71         std::string checkList = "";
72         GetCheckList(runtime, checkList);
73         WriteCheckList(checkList);
74     }
75 
76     if (info.needSnapshot == true) {
77         runtime->DumpHeapSnapshot(info.tid, info.needGc);
78     } else {
79         if (info.needGc == true) {
80             runtime->ForceFullGC(info.tid);
81         }
82     }
83 }
84 
GetCheckList(const std::unique_ptr<AbilityRuntime::Runtime> & runtime,std::string & checkList)85 void DumpRuntimeHelper::GetCheckList(const std::unique_ptr<AbilityRuntime::Runtime> &runtime, std::string &checkList)
86 {
87     if (runtime->GetLanguage() != AbilityRuntime::Runtime::Language::JS) {
88         TAG_LOGE(AAFwkTag::APPKIT, "current language is not js");
89         return;
90     }
91     AbilityRuntime::JsRuntime &jsruntime = static_cast<AbilityRuntime::JsRuntime&>(*runtime);
92     AbilityRuntime::HandleScope handleScope(jsruntime);
93     auto env = jsruntime.GetNapiEnv();
94 
95     napi_value global = nullptr;
96     napi_get_global(env, &global);
97     napi_value requireValue = GetJsLeakModule(env, global);
98     if (requireValue == nullptr) {
99         TAG_LOGE(AAFwkTag::APPKIT, "get jsLeak module fail");
100         return;
101     }
102     napi_value result = GetMethodCheck(env, requireValue, global);
103     if (result == nullptr) {
104         TAG_LOGE(AAFwkTag::APPKIT, "get method check fail");
105         return;
106     }
107 
108     size_t checkListSize = 0;
109     napi_get_value_string_utf8(env, result, nullptr, 0, &checkListSize);
110     checkList.resize(checkListSize + 1);
111     napi_get_value_string_utf8(env, result, &checkList[0], checkListSize + 1, &checkListSize);
112 }
113 
GetJsLeakModule(napi_env env,napi_value global)114 napi_value DumpRuntimeHelper::GetJsLeakModule(napi_env env, napi_value global)
115 {
116     napi_value napiFunc = nullptr;
117     napi_status status = napi_get_named_property(env, global, REQUIRE_NAPI, &napiFunc);
118     if (status != napi_ok) {
119         TAG_LOGE(AAFwkTag::APPKIT, "napi get requireNapi fail, %{public}d", status);
120         return nullptr;
121     }
122     napi_value moduleName = nullptr;
123     napi_create_string_utf8(env, MODULE_NAME, strlen(MODULE_NAME), &moduleName);
124     napi_value param[1] = {moduleName};
125     napi_value requireValue = nullptr;
126     status = napi_call_function(env, global, napiFunc, 1, &param[0], &requireValue);
127     if (status != napi_ok) {
128         TAG_LOGE(AAFwkTag::APPKIT, "call jsLeak fail, %{public}d", status);
129         return nullptr;
130     }
131     return requireValue;
132 }
133 
GetMethodCheck(napi_env env,napi_value requireValue,napi_value global)134 napi_value DumpRuntimeHelper::GetMethodCheck(napi_env env, napi_value requireValue, napi_value global)
135 {
136     napi_value methodCheck = nullptr;
137     napi_status status = napi_get_named_property(env, requireValue, CHECK, &methodCheck);
138     if (status != napi_ok) {
139         TAG_LOGE(AAFwkTag::APPKIT, "napi get check fail, %{public}d", status);
140         return nullptr;
141     }
142     napi_valuetype valuetype = napi_undefined;
143     status = napi_typeof(env, methodCheck, &valuetype);
144     if (status != napi_ok) {
145         TAG_LOGE(AAFwkTag::APPKIT, "napi_typeof failed, %{public}d", status);
146         return nullptr;
147     }
148     napi_value result = nullptr;
149     status = napi_call_function(env, global, methodCheck, 0, nullptr, &result);
150     if (status != napi_ok) {
151         TAG_LOGE(AAFwkTag::APPKIT, "napi call check fail, %{public}d", status);
152         return nullptr;
153     }
154     return result;
155 }
156 
WriteCheckList(const std::string & checkList)157 void DumpRuntimeHelper::WriteCheckList(const std::string &checkList)
158 {
159     TAG_LOGD(AAFwkTag::APPKIT, "called");
160     int32_t fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_HEAP_LEAK_LIST));
161     if (fd < 0) {
162         TAG_LOGE(AAFwkTag::APPKIT, "request fd failed, fd:%{public}d.\n", fd);
163         return;
164     }
165     if (write(fd, checkList.c_str(), strlen(checkList.c_str())) == -1) {
166         TAG_LOGE(AAFwkTag::APPKIT, "write failed, fd:%{public}d, errno:%{public}d.\n", fd, errno);
167         close(fd);
168         return;
169     }
170     close(fd);
171 }
172 
173 } // namespace AppExecFwk
174 } // namespace OHOS
175