1 /*
2 * Copyright (c) 2021-2022 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 "inspector.h"
17
18 #include <chrono>
19 #include <shared_mutex>
20 #if defined(OHOS_PLATFORM)
21 #include <syscall.h>
22 #endif
23 #include <thread>
24 #if defined(OHOS_PLATFORM)
25 #include <unistd.h>
26 #endif
27 #include <unordered_map>
28
29 #include "common/log_wrapper.h"
30 #include "library_loader.h"
31
32 #if defined(IOS_PLATFORM)
33 #include "tooling/debugger_service.h"
34 #endif
35
36 #if defined(ENABLE_FFRT_INTERFACES)
37 #include "ffrt.h"
38 #endif
39
40 namespace OHOS::ArkCompiler::Toolchain {
41 namespace {
42 enum DispatchStatus : int32_t {
43 UNKNOWN = 0,
44 DISPATCHING,
45 DISPATCHED
46 };
47
48 using InitializeDebugger = void(*)(void*, const std::function<void(const void*, const std::string&)>&);
49 using UninitializeDebugger = void(*)(void*);
50 using WaitForDebugger = void(*)(void*);
51 using OnMessage = void(*)(void*, std::string&&);
52 using ProcessMessage = void(*)(void*);
53 using GetDispatchStatus = int32_t(*)(void*);
54
55 OnMessage g_onMessage = nullptr;
56 InitializeDebugger g_initializeDebugger = nullptr;
57 UninitializeDebugger g_uninitializeDebugger = nullptr;
58 WaitForDebugger g_waitForDebugger = nullptr;
59 ProcessMessage g_processMessage = nullptr;
60 GetDispatchStatus g_getDispatchStatus = nullptr;
61
62 std::atomic<bool> g_hasArkFuncsInited = false;
63 std::unordered_map<const void*, Inspector*> g_inspectors;
64 std::unordered_map<int, std::pair<void*, const DebuggerPostTask>> g_debuggerInfo;
65 std::shared_mutex g_mutex;
66
67 #if !defined(IOS_PLATFORM)
68 thread_local void* g_handle = nullptr;
69 #endif
70 thread_local void* g_vm = nullptr;
71
72 #if !defined(IOS_PLATFORM)
73 #if defined(WINDOWS_PLATFORM)
74 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_tooling.dll";
75 #elif defined(MAC_PLATFORM)
76 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_tooling.dylib";
77 #else
78 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_tooling.so";
79 #endif
80 #endif
81
HandleClient(void* const server)82 void* HandleClient(void* const server)
83 {
84 LOGI("HandleClient");
85 if (server == nullptr) {
86 LOGE("HandleClient server nullptr");
87 return nullptr;
88 }
89
90 #if defined(IOS_PLATFORM) || defined(MAC_PLATFORM)
91 pthread_setname_np("OS_DebugThread");
92 #else
93 pthread_setname_np(pthread_self(), "OS_DebugThread");
94 #endif
95
96 static_cast<WsServer*>(server)->RunServer();
97 return nullptr;
98 }
99
100 #if !defined(IOS_PLATFORM)
LoadArkDebuggerLibrary()101 bool LoadArkDebuggerLibrary()
102 {
103 if (g_handle != nullptr) {
104 LOGI("LoadArkDebuggerLibrary, handle has already loaded");
105 return true;
106 }
107 g_handle = Load(ARK_DEBUGGER_SHARED_LIB);
108 if (g_handle == nullptr) {
109 LOGE("LoadArkDebuggerLibrary, handle load failed");
110 return false;
111 }
112 return true;
113 }
114
GetArkDynFunction(const char* symbol)115 void* GetArkDynFunction(const char* symbol)
116 {
117 return ResolveSymbol(g_handle, symbol);
118 }
119 #endif
120
SendReply(const void* vm, const std::string& message)121 void SendReply(const void* vm, const std::string& message)
122 {
123 std::shared_lock<std::shared_mutex> lock(g_mutex);
124 auto iter = g_inspectors.find(vm);
125 if (iter != g_inspectors.end() && iter->second != nullptr &&
126 iter->second->websocketServer_ != nullptr) {
127 iter->second->websocketServer_->SendReply(message);
128 }
129 }
130
ResetServiceLocked(void *vm, bool isCloseHandle)131 void ResetServiceLocked(void *vm, bool isCloseHandle)
132 {
133 auto iter = g_inspectors.find(vm);
134 if (iter != g_inspectors.end() && iter->second != nullptr &&
135 iter->second->websocketServer_ != nullptr) {
136 iter->second->websocketServer_->StopServer();
137 delete iter->second;
138 iter->second = nullptr;
139 g_inspectors.erase(iter);
140 }
141 #if !defined(IOS_PLATFORM)
142 if (g_handle != nullptr && isCloseHandle) {
143 CloseHandle(g_handle);
144 g_handle = nullptr;
145 }
146 #endif
147 }
148
InitializeInspector( void* vm, const DebuggerPostTask& debuggerPostTask, const DebugInfo& debugInfo, int tidForSocketPair = 0)149 bool InitializeInspector(
150 void* vm, const DebuggerPostTask& debuggerPostTask, const DebugInfo& debugInfo, int tidForSocketPair = 0)
151 {
152 std::unique_lock<std::shared_mutex> lock(g_mutex);
153 auto iter = g_inspectors.find(vm);
154 if (iter != g_inspectors.end()) {
155 LOGW("Inspector already exist!");
156 return true;
157 }
158
159 Inspector *newInspector = new Inspector();
160 g_inspectors.emplace(vm, newInspector);
161
162 newInspector->tidForSocketPair_ = tidForSocketPair;
163 newInspector->tid_ = pthread_self();
164 newInspector->vm_ = vm;
165 newInspector->debuggerPostTask_ = debuggerPostTask;
166 newInspector->websocketServer_ = std::make_unique<WsServer>(debugInfo,
167 std::bind(&Inspector::OnMessage, newInspector, std::placeholders::_1));
168
169 pthread_t tid;
170 if (pthread_create(&tid, nullptr, &HandleClient, static_cast<void *>(
171 newInspector->websocketServer_.get())) != 0) {
172 LOGE("Create inspector thread failed");
173 return false;
174 }
175 newInspector->websocketServer_->tid_ = tid;
176
177 return true;
178 }
179
180 #if !defined(IOS_PLATFORM)
InitializeArkFunctionsOthers()181 bool InitializeArkFunctionsOthers()
182 {
183 g_initializeDebugger = reinterpret_cast<InitializeDebugger>(
184 GetArkDynFunction("InitializeDebugger"));
185 if (g_initializeDebugger == nullptr) {
186 ResetServiceLocked(g_vm, true);
187 return false;
188 }
189 g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(
190 GetArkDynFunction("UninitializeDebugger"));
191 if (g_uninitializeDebugger == nullptr) {
192 ResetServiceLocked(g_vm, true);
193 return false;
194 }
195 g_waitForDebugger = reinterpret_cast<WaitForDebugger>(
196 GetArkDynFunction("WaitForDebugger"));
197 if (g_waitForDebugger == nullptr) {
198 ResetServiceLocked(g_vm, true);
199 return false;
200 }
201 g_onMessage = reinterpret_cast<OnMessage>(
202 GetArkDynFunction("OnMessage"));
203 if (g_onMessage == nullptr) {
204 ResetServiceLocked(g_vm, true);
205 return false;
206 }
207 g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(
208 GetArkDynFunction("GetDispatchStatus"));
209 if (g_getDispatchStatus == nullptr) {
210 ResetServiceLocked(g_vm, true);
211 return false;
212 }
213 g_processMessage = reinterpret_cast<ProcessMessage>(
214 GetArkDynFunction("ProcessMessage"));
215 if (g_processMessage == nullptr) {
216 ResetServiceLocked(g_vm, true);
217 return false;
218 }
219 return true;
220 }
221 #else
InitializeArkFunctionsIOS()222 bool InitializeArkFunctionsIOS()
223 {
224 using namespace panda::ecmascript;
225 g_initializeDebugger = reinterpret_cast<InitializeDebugger>(&tooling::InitializeDebugger);
226 g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(&tooling::UninitializeDebugger);
227 g_waitForDebugger = reinterpret_cast<WaitForDebugger>(&tooling::WaitForDebugger);
228 g_onMessage = reinterpret_cast<OnMessage>(&tooling::OnMessage);
229 g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(&tooling::GetDispatchStatus);
230 g_processMessage = reinterpret_cast<ProcessMessage>(&tooling::ProcessMessage);
231 return true;
232 }
233 #endif
234
InitializeArkFunctions()235 bool InitializeArkFunctions()
236 {
237 // no need to initialize again in case of multi-instance
238 if (g_hasArkFuncsInited) {
239 return true;
240 }
241
242 std::unique_lock<std::shared_mutex> lock(g_mutex);
243 if (g_hasArkFuncsInited) {
244 return true;
245 }
246 #if !defined(IOS_PLATFORM)
247 if (!InitializeArkFunctionsOthers()) {
248 return false;
249 }
250 #else
251 InitializeArkFunctionsIOS()
252 #endif
253
254 g_hasArkFuncsInited = true;
255 return true;
256 }
257
258 } // namespace
259
OnMessage(std::string&& msg)260 void Inspector::OnMessage(std::string&& msg)
261 {
262 g_onMessage(vm_, std::move(msg));
263
264 // message will be processed soon if the debugger thread is in running or waiting status
265 if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
266 return;
267 }
268 std::this_thread::sleep_for(std::chrono::microseconds(DELAY_CHECK_DISPATCH_STATUS));
269 if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
270 return;
271 }
272
273 // the debugger thread maybe in idle status, so try to post a task to wake it up
274 if (debuggerPostTask_ != nullptr) {
275 if (tidForSocketPair_ == 0) {
276 debuggerPostTask_([tid = tid_, vm = vm_] {
277 if (tid != pthread_self()) {
278 LOGE("Task not in debugger thread");
279 return;
280 }
281 g_processMessage(vm);
282 });
283 } else {
284 #if defined(OHOS_PLATFORM)
285 debuggerPostTask_([tid = tidForSocketPair_, vm = vm_, this] {
286 uint64_t threadOrTaskId = GetThreadOrTaskId();
287 if (tid != static_cast<pid_t>(threadOrTaskId)) {
288 LOGE("Task not in debugger thread for socketpair");
289 return;
290 }
291 g_processMessage(vm);
292 });
293 #endif // defined(OHOS_PLATFORM)
294 }
295 } else {
296 LOGW("No debuggerPostTask provided");
297 }
298 }
299
300 #if defined(OHOS_PLATFORM)
GetThreadOrTaskId()301 uint64_t Inspector::GetThreadOrTaskId()
302 {
303 #if defined(ENABLE_FFRT_INTERFACES)
304 uint64_t threadOrTaskId = ffrt_this_task_get_id();
305 if (threadOrTaskId != 0) {
306 return threadOrTaskId;
307 } else {
308 return static_cast<uint64_t>(getproctid());
309 }
310 #else
311 return static_cast<uint64_t>(getproctid());
312 #endif // defined(ENABLE_FFRT_INTERFACES)
313 }
314 #endif // defined(OHOS_PLATFORM)
315
GetDebuggerPostTask(int tid)316 const DebuggerPostTask &GetDebuggerPostTask(int tid)
317 {
318 std::shared_lock<std::shared_mutex> lock(g_mutex);
319 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
320 static DebuggerPostTask tempTask;
321 return tempTask;
322 }
323 return g_debuggerInfo[tid].second;
324 }
325
GetEcmaVM(int tid)326 void *GetEcmaVM(int tid)
327 {
328 std::shared_lock<std::shared_mutex> lock(g_mutex);
329 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
330 return nullptr;
331 }
332 return g_debuggerInfo[tid].first;
333 }
334
InitializeDebuggerForSocketpair(void* vm)335 bool InitializeDebuggerForSocketpair(void* vm)
336 {
337 #if !defined(IOS_PLATFORM)
338 if (!LoadArkDebuggerLibrary()) {
339 return false;
340 }
341 #endif
342 if (!InitializeArkFunctions()) {
343 LOGE("Initialize ark functions failed");
344 return false;
345 }
346 g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2));
347 return true;
348 }
349
350 // for ohos platform.
StartDebugForSocketpair(int tid, int socketfd)351 bool StartDebugForSocketpair(int tid, int socketfd)
352 {
353 LOGI("StartDebugForSocketpair, tid = %{private}d, socketfd = %{private}d", tid, socketfd);
354 void* vm = GetEcmaVM(tid);
355 if (vm == nullptr) {
356 LOGD("VM has already been destroyed");
357 return false;
358 }
359 g_vm = vm;
360 if (!InitializeDebuggerForSocketpair(vm)) {
361 return false;
362 }
363 const DebuggerPostTask &debuggerPostTask = GetDebuggerPostTask(tid);
364 DebugInfo debugInfo = {socketfd};
365 if (!InitializeInspector(vm, debuggerPostTask, debugInfo, tid)) {
366 LOGE("Initialize inspector failed");
367 return false;
368 }
369
370 return true;
371 }
372
373 // for cross-platform, previewer and old process of StartDebugger.
StartDebug(const std::string& componentName, void* vm, bool isDebugMode, int32_t instanceId, const DebuggerPostTask& debuggerPostTask, int port)374 bool StartDebug(const std::string& componentName, void* vm, bool isDebugMode,
375 int32_t instanceId, const DebuggerPostTask& debuggerPostTask, int port)
376 {
377 LOGI("StartDebug, componentName = %{private}s, isDebugMode = %{private}d, instanceId = %{private}d",
378 componentName.c_str(), isDebugMode, instanceId);
379 g_vm = vm;
380 #if !defined(IOS_PLATFORM)
381 if (!LoadArkDebuggerLibrary()) {
382 return false;
383 }
384 #endif
385 if (!InitializeArkFunctions()) {
386 LOGE("Initialize ark functions failed");
387 return false;
388 }
389
390 g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2));
391
392 int startDebugInOldProcess = -2; // start debug in old process.
393 DebugInfo debugInfo = {startDebugInOldProcess, componentName, instanceId, port};
394 if (!InitializeInspector(vm, debuggerPostTask, debugInfo)) {
395 LOGE("Initialize inspector failed");
396 return false;
397 }
398
399 if (isDebugMode && port > 0) {
400 LOGI("Wait for debugger for previewer");
401 g_waitForDebugger(vm);
402 }
403 return true;
404 }
405
WaitForDebugger(void* vm)406 void WaitForDebugger(void* vm)
407 {
408 LOGI("WaitForDebugger");
409 g_waitForDebugger(vm);
410 }
411
StopDebug(void* vm)412 void StopDebug(void* vm)
413 {
414 LOGI("StopDebug start, vm is %{private}p", vm);
415 std::unique_lock<std::shared_mutex> lock(g_mutex);
416 auto iter = g_inspectors.find(vm);
417 if (iter == g_inspectors.end() || iter->second == nullptr) {
418 return;
419 }
420 #ifdef PANDA_TARGET_MACOS
421 uint32_t tid = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(g_inspectors[vm]->tid_));
422 #else
423 uint32_t tid = g_inspectors[vm]->tid_;
424 #endif
425 auto debuggerInfo = g_debuggerInfo.find(tid);
426 if (debuggerInfo != g_debuggerInfo.end()) {
427 g_debuggerInfo.erase(debuggerInfo);
428 }
429 g_uninitializeDebugger(vm);
430 ResetServiceLocked(vm, true);
431 LOGI("StopDebug end");
432 }
433
StopOldDebug(int tid, const std::string& componentName)434 void StopOldDebug(int tid, const std::string& componentName)
435 {
436 LOGI("StopDebug start, componentName = %{private}s, tid = %{private}d", componentName.c_str(), tid);
437 void* vm = GetEcmaVM(tid);
438 if (vm == nullptr) {
439 return;
440 }
441 std::unique_lock<std::shared_mutex> lock(g_mutex);
442 auto iter = g_inspectors.find(vm);
443 if (iter == g_inspectors.end() || iter->second == nullptr) {
444 return;
445 }
446
447 ResetServiceLocked(vm, false);
448 LOGI("StopDebug end");
449 }
450
451 // for socketpair process.
StoreDebuggerInfo(int tid, void* vm, const DebuggerPostTask& debuggerPostTask)452 void StoreDebuggerInfo(int tid, void* vm, const DebuggerPostTask& debuggerPostTask)
453 {
454 std::unique_lock<std::shared_mutex> lock(g_mutex);
455 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
456 g_debuggerInfo.emplace(tid, std::make_pair(vm, debuggerPostTask));
457 }
458 }
459 } // namespace OHOS::ArkCompiler::Toolchain
460