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 }