• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "cj_runtime.h"
17 
18 #include <dlfcn.h>
19 #include <unistd.h>
20 #include <filesystem>
21 #include <regex>
22 
23 #include "cj_envsetup.h"
24 #include "hilog_tag_wrapper.h"
25 #include "hitrace_meter.h"
26 #include "hdc_register.h"
27 #include "parameters.h"
28 #include "bundle_constants.h"
29 #include "connect_server_manager.h"
30 #include "faultloggerd_client.h"
31 
32 using namespace OHOS::AbilityRuntime;
33 
34 
35 namespace {
36 const std::string DEBUGGER = "@Debugger";
37 } // namespace
38 
39 #define LIB_NAME "libcj_environment.z.so"
40 #define GET_ENV_INS_NAME "OHOS_GetCJEnvInstance"
41 
42 namespace OHOS {
LoadInstance()43 CJEnvMethods* CJEnv::LoadInstance()
44 {
45     auto handle = dlopen(LIB_NAME, RTLD_NOW);
46     if (!handle) {
47         TAG_LOGE(AAFwkTag::CJRUNTIME, "dlopen failed %{public}s, %{public}s", LIB_NAME, dlerror());
48         return nullptr;
49     }
50     auto symbol = dlsym(handle, GET_ENV_INS_NAME);
51     if (!symbol) {
52         TAG_LOGE(AAFwkTag::CJRUNTIME, "dlsym failed %{public}s, %{public}s", GET_ENV_INS_NAME, dlerror());
53         dlclose(handle);
54         return nullptr;
55     }
56     auto func = reinterpret_cast<CJEnvMethods* (*)()>(symbol);
57     return func();
58 }
59 }
60 AppLibPathVec CJRuntime::appLibPaths_;
61 
62 std::string CJRuntime::packageName_;
63 
Create(const Options & options)64 std::unique_ptr<CJRuntime> CJRuntime::Create(const Options& options)
65 {
66     auto instance = std::make_unique<CJRuntime>();
67     if (!instance || !instance->Initialize(options)) {
68         return nullptr;
69     }
70     return instance;
71 }
72 
SetAppLibPath(const AppLibPathMap & appLibPaths)73 void CJRuntime::SetAppLibPath(const AppLibPathMap& appLibPaths)
74 {
75     HITRACE_METER_NAME(HITRACE_TAG_APP, "Initialize cangjie runtime and namespace");
76     std::string appPath = "";
77     for (const auto& kv : appLibPaths) {
78         for (const auto& libPath : kv.second) {
79             TAG_LOGD(AAFwkTag::CJRUNTIME, "SetCJAppLibPath: %{public}s.", libPath.c_str());
80             CJRuntime::appLibPaths_.emplace_back(libPath);
81             appPath += appPath.empty() ? libPath : ":" + libPath;
82         }
83     }
84     auto cjEnv = OHOS::CJEnv::LoadInstance();
85     if (cjEnv == nullptr) {
86         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
87         return;
88     }
89 
90     cjEnv->initCJAppNS(appPath);
91 }
92 
Initialize(const Options & options)93 bool CJRuntime::Initialize(const Options& options)
94 {
95     if (options.lang != GetLanguage()) {
96         TAG_LOGE(AAFwkTag::CJRUNTIME, "language mismatch");
97         return false;
98     }
99     auto cjEnv = OHOS::CJEnv::LoadInstance();
100     if (cjEnv == nullptr) {
101         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
102         return false;
103     }
104     if (!cjEnv->startRuntime()) {
105         TAG_LOGE(AAFwkTag::CJRUNTIME, "start cj runtime failed");
106         return false;
107     }
108     if (!cjEnv->startUIScheduler()) {
109         TAG_LOGE(AAFwkTag::CJRUNTIME, "start cj ui context failed");
110         return false;
111     }
112     if (!LoadCJAppLibrary(CJRuntime::appLibPaths_)) {
113         TAG_LOGE(AAFwkTag::CJRUNTIME, "load app library fail");
114         return false;
115     }
116     bundleName_ = options.bundleName;
117     instanceId_ = static_cast<uint32_t>(getproctid());
118     return true;
119 }
120 
RegisterUncaughtExceptionHandler(const CJUncaughtExceptionInfo & uncaughtExceptionInfo)121 void CJRuntime::RegisterUncaughtExceptionHandler(const CJUncaughtExceptionInfo& uncaughtExceptionInfo)
122 {
123     auto cjEnv = OHOS::CJEnv::LoadInstance();
124     if (cjEnv == nullptr) {
125         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
126         return;
127     }
128     cjEnv->registerCJUncaughtExceptionHandler(uncaughtExceptionInfo);
129 }
130 
IsCJAbility(const std::string & info)131 bool CJRuntime::IsCJAbility(const std::string& info)
132 {
133     // in cj application, the srcEntry format should be packageName.AbilityClassName.
134     std::string pattern = "^([a-zA-Z0-9_]+\\.)+[a-zA-Z0-9_]+$";
135     return std::regex_match(info, std::regex(pattern));
136 }
137 
LoadCJAppLibrary(const AppLibPathVec & appLibPaths)138 bool CJRuntime::LoadCJAppLibrary(const AppLibPathVec& appLibPaths)
139 {
140     HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__);
141     auto cjEnv = OHOS::CJEnv::LoadInstance();
142     if (cjEnv == nullptr) {
143         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
144         return false;
145     }
146     void* handle = nullptr;
147     // According to the OHOS rule, the format of the SO name is as follows
148     auto targetSoName = "lib" + packageName_ + ".so";
149 
150     for (const auto& libPath : appLibPaths) {
151         for (auto& itor : std::filesystem::directory_iterator(libPath)) {
152             // According to the convention, the names of cj generated products must contain the following keywords
153             if (itor.path().string().find(targetSoName) == std::string::npos) {
154                 continue;
155             }
156             handle = cjEnv->loadCJLibrary(itor.path().c_str());
157             if (handle == nullptr) {
158                 char* errMsg = dlerror();
159                 TAG_LOGE(AAFwkTag::CJRUNTIME,
160                     "load %{public}s failed, reason: %{public}s", itor.path().c_str(), errMsg ? errMsg : "null");
161                 return false;
162             }
163         }
164     }
165     appLibLoaded_ = true;
166     return true;
167 }
168 
SetPackageName(std::string srcEntryName)169 void CJRuntime::SetPackageName(std::string srcEntryName)
170 {
171     // According to the srcEntry rule in the Cangjie application,
172     // the last '.' The previous strings were all package names
173     packageName_ = srcEntryName.substr(0, srcEntryName.find_last_of("."));
174 }
175 
SetSanitizerVersion(SanitizerKind kind)176 void CJRuntime::SetSanitizerVersion(SanitizerKind kind)
177 {
178     auto cjEnv = OHOS::CJEnv::LoadInstance();
179     if (cjEnv == nullptr) {
180         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
181         return;
182     }
183     cjEnv->setSanitizerKindRuntimeVersion(kind);
184 }
185 
RegisterCangjieCallback()186 bool CJRuntime::RegisterCangjieCallback()
187 {
188     auto cjEnv = OHOS::CJEnv::LoadInstance();
189     if (cjEnv == nullptr) {
190         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
191         return false;
192     }
193     constexpr char CANGJIE_DEBUGGER_LIB_PATH[] = "libark_connect_inspector.z.so";
194     #define LIBARARYKIND_SYS 0
195     auto handlerConnectServerSo = cjEnv->loadLibrary(LIBARARYKIND_SYS, CANGJIE_DEBUGGER_LIB_PATH);
196     if (handlerConnectServerSo == nullptr) {
197         TAG_LOGE(AAFwkTag::CJRUNTIME, "null handlerConnectServerSo: %{public}s", dlerror());
198         return false;
199     }
200     using SendMsgCB = const std::function<void(const std::string& message)>;
201     using SetCangjieCallback = void(*)(const std::function<void(const std::string& message, SendMsgCB)>);
202     using CangjieCallback = void(*)(const std::string& message, SendMsgCB);
203     auto setCangjieCallback = reinterpret_cast<SetCangjieCallback>(
204         cjEnv->getSymbol(handlerConnectServerSo, "SetCangjieCallback"));
205     if (setCangjieCallback == nullptr) {
206         TAG_LOGE(AAFwkTag::CJRUNTIME, "null setCangjieCallback: %{public}s", dlerror());
207         return false;
208     }
209     #define RTLIB_NAME "libcangjie-runtime.so"
210     #define LIBARARYKIND_SDK 1
211     auto dso = cjEnv->loadLibrary(LIBARARYKIND_SDK, RTLIB_NAME);
212     if (!dso) {
213         TAG_LOGE(AAFwkTag::CJRUNTIME, "load library failed: %{public}s", RTLIB_NAME);
214         return false;
215     }
216     TAG_LOGE(AAFwkTag::CJRUNTIME, "load libcangjie-runtime.so success");
217     #define PROFILERAGENT "ProfilerAgent"
218     CangjieCallback cangjieCallback = reinterpret_cast<CangjieCallback>(cjEnv->getSymbol(dso, PROFILERAGENT));
219     if (cangjieCallback == nullptr) {
220         TAG_LOGE(AAFwkTag::CJRUNTIME, "runtime api not found: %{public}s", PROFILERAGENT);
221         dlclose(handlerConnectServerSo);
222         handlerConnectServerSo = nullptr;
223         return false;
224     }
225     TAG_LOGE(AAFwkTag::CJRUNTIME, "find runtime api success");
226     setCangjieCallback(cangjieCallback);
227     dlclose(handlerConnectServerSo);
228     handlerConnectServerSo = nullptr;
229     return true;
230 }
231 
StartProfiler(const DebugOption dOption)232 void CJRuntime::StartProfiler(const DebugOption dOption)
233 {
234     if (!dOption.isDebugFromLocal && !dOption.isDeveloperMode) {
235         TAG_LOGE(AAFwkTag::CJRUNTIME, "developer Mode false");
236         return;
237     }
238     bool isStartWithDebug = dOption.isStartWithDebug;
239     bool isDebugApp = dOption.isDebugApp;
240     const std::string bundleName = bundleName_;
241     int32_t instanceId = static_cast<int32_t>(instanceId_);
242     std::string appProvisionType = dOption.appProvisionType;
243     std::string inputProcessName = bundleName_ != dOption.processName ? dOption.processName : "";
244 
245     HdcRegister::Get().StartHdcRegister(bundleName_, inputProcessName, isDebugApp,
246         HdcRegister::DebugRegisterMode::HDC_DEBUG_REG,
247         [bundleName, isStartWithDebug, isDebugApp, instanceId, appProvisionType](int socketFd, std::string option) {
248             TAG_LOGI(AAFwkTag::CJRUNTIME, "hdcRegister callback call, socket fd: %{public}d, option: %{public}s.",
249                 socketFd, option.c_str());
250             bool isSystemDebuggable = system::GetBoolParameter("const.secure", true) == false &&
251             system::GetBoolParameter("const.debuggable", false) == true;
252             // Don't start any server if (system not in debuggable mode) and app is release version
253             // Starting ConnectServer in release app on debuggable system
254             // is only for debug mode, not for profiling mode.
255             if ((!isSystemDebuggable) && appProvisionType == AppExecFwk::Constants::APP_PROVISION_TYPE_RELEASE) {
256                 TAG_LOGE(AAFwkTag::CJRUNTIME, "not support release app");
257                 return;
258             }
259             if (option.find(DEBUGGER) == std::string::npos) {
260                 ConnectServerManager::Get().StopConnectServer(false);
261                 TAG_LOGI(AAFwkTag::CJRUNTIME, "start SendInstanceMessage");
262                 ConnectServerManager::Get().SendInstanceMessage(instanceId, instanceId, bundleName);
263                 ConnectServerManager::Get().SendDebuggerInfo(isStartWithDebug, isDebugApp);
264                 ConnectServerManager::Get().StartConnectServer(bundleName, socketFd, false);
265                 CJRuntime::RegisterCangjieCallback();
266             } else {
267                 TAG_LOGE(AAFwkTag::CJRUNTIME, "debugger service unexpected option: %{public}s", option.c_str());
268             }
269         });
270 }
271 
StartDebugMode(const DebugOption dOption)272 void CJRuntime::StartDebugMode(const DebugOption dOption)
273 {
274     if (debugModel_) {
275         TAG_LOGI(AAFwkTag::CJRUNTIME, "already debug mode");
276         return;
277     }
278     if (!dOption.isDebugFromLocal && !dOption.isDeveloperMode) {
279         TAG_LOGE(AAFwkTag::CJRUNTIME, "developer Mode false");
280         return;
281     }
282 
283     bool isStartWithDebug = dOption.isStartWithDebug;
284     bool isDebugApp = dOption.isDebugApp;
285     const std::string bundleName = bundleName_;
286     int32_t instanceId = static_cast<int32_t>(instanceId_);
287     std::string appProvisionType = dOption.appProvisionType;
288     std::string inputProcessName = bundleName_ != dOption.processName ? dOption.processName : "";
289 
290     TAG_LOGI(AAFwkTag::CJRUNTIME, "StartDebugMode %{public}s", bundleName_.c_str());
291 
292     HdcRegister::Get().StartHdcRegister(bundleName_, inputProcessName, isDebugApp,
293         HdcRegister::DebugRegisterMode::HDC_DEBUG_REG,
294         [bundleName, isStartWithDebug, isDebugApp, instanceId, appProvisionType](int socketFd, std::string option) {
295             TAG_LOGI(AAFwkTag::CJRUNTIME, "hdcRegister callback call, socket fd: %{public}d, option: %{public}s.",
296                 socketFd, option.c_str());
297                     // system is debuggable when const.secure is false and const.debuggable is true
298             bool isSystemDebuggable = system::GetBoolParameter("const.secure", true) == false &&
299             system::GetBoolParameter("const.debuggable", false) == true;
300             // Don't start any server if (system not in debuggable mode) and app is release version
301             // Starting ConnectServer in release app on debuggable system
302             // is only for debug mode, not for profiling mode.
303             if ((!isSystemDebuggable) && appProvisionType == AppExecFwk::Constants::APP_PROVISION_TYPE_RELEASE) {
304                 TAG_LOGE(AAFwkTag::CJRUNTIME, "not support release app");
305                 return;
306             }
307             if (option.find(DEBUGGER) == std::string::npos) {
308                 ConnectServerManager::Get().StopConnectServer(false);
309                 TAG_LOGI(AAFwkTag::CJRUNTIME, "start SendInstanceMessage");
310                 ConnectServerManager::Get().SendInstanceMessage(instanceId, instanceId, bundleName);
311                 ConnectServerManager::Get().SendDebuggerInfo(isStartWithDebug, isDebugApp);
312                 ConnectServerManager::Get().StartConnectServer(bundleName, socketFd, false);
313                 CJRuntime::RegisterCangjieCallback();
314             } else {
315                 TAG_LOGE(AAFwkTag::CJRUNTIME, "debugger service unexpected option: %{public}s", option.c_str());
316             }
317         });
318     if (isDebugApp) {
319         ConnectServerManager::Get().StartConnectServer(bundleName_, -1, true);
320     }
321     ConnectServerManager::Get().AddInstance(instanceId_, instanceId_);
322 
323     debugModel_ = StartDebugger();
324 }
325 
StartDebugger()326 bool CJRuntime::StartDebugger()
327 {
328     auto cjEnv = OHOS::CJEnv::LoadInstance();
329     if (cjEnv == nullptr) {
330         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
331         return false;
332     }
333     return cjEnv->startDebugger();
334 }
335 
UnLoadCJAppLibrary()336 void CJRuntime::UnLoadCJAppLibrary()
337 {
338     TAG_LOGI(AAFwkTag::CJRUNTIME, "UnLoadCJAppLibrary not support yet");
339 }
340 
SetAppVersion(std::string & version)341 void CJRuntime::SetAppVersion(std::string& version)
342 {
343     auto cjEnv = OHOS::CJEnv::LoadInstance();
344     if (cjEnv == nullptr) {
345         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
346         return;
347     }
348     return cjEnv->setAppVersion(version);
349 }
350 
DumpHeapSnapshot(uint32_t tid,bool isFullGC,bool isBinary)351 void CJRuntime::DumpHeapSnapshot(uint32_t tid, bool isFullGC, bool isBinary)
352 {
353     auto cjEnv = OHOS::CJEnv::LoadInstance();
354     if (cjEnv == nullptr) {
355         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
356         return;
357     }
358     int32_t fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::CJ_HEAP_SNAPSHOT));
359     if (fd < 0) {
360         TAG_LOGE(AAFwkTag::CJRUNTIME, "fd:%{public}d.\n", fd);
361         return;
362     }
363     cjEnv->dumpHeapSnapshot(fd);
364     close(fd);
365 }
366 
ForceFullGC(uint32_t tid)367 void CJRuntime::ForceFullGC(uint32_t tid)
368 {
369     auto cjEnv = OHOS::CJEnv::LoadInstance();
370     if (cjEnv == nullptr) {
371         TAG_LOGE(AAFwkTag::CJRUNTIME, "null cjEnv");
372         return;
373     }
374     cjEnv->forceFullGC();
375 }