1 /*
2 * Copyright (c) 2021-2025 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 #include "init_static.h"
18
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
28 #include "common/log_wrapper.h"
29 #include "library_loader.h"
30
31 #if defined(IOS_PLATFORM)
32 #include "tooling/dynamic/debugger_service.h"
33 #endif
34
35 #if defined(ENABLE_FFRT_INTERFACES)
36 #include "ffrt.h"
37 #endif
38
39 #include <string>
40 #include <regex>
41
42 namespace OHOS::ArkCompiler::Toolchain {
43 namespace {
44 enum DispatchStatus : int32_t {
45 UNKNOWN = 0,
46 DISPATCHING,
47 DISPATCHED
48 };
49
50 using InitializeDebugger = void(*)(void*, const std::function<void(const void*, const std::string&)>&);
51 using UninitializeDebugger = void(*)(void*);
52 using WaitForDebugger = void(*)(void*);
53 using OnMessage = void(*)(void*, std::string&&);
54 using ProcessMessage = void(*)(void*);
55 using GetDispatchStatus = int32_t(*)(void*);
56 using GetCallFrames = char*(*)(void*);
57 using OperateDebugMessage = char*(*)(void*, const char*);
58
59 OnMessage g_onMessage = nullptr;
60 InitializeDebugger g_initializeDebugger = nullptr;
61 UninitializeDebugger g_uninitializeDebugger = nullptr;
62 WaitForDebugger g_waitForDebugger = nullptr;
63 ProcessMessage g_processMessage = nullptr;
64 GetDispatchStatus g_getDispatchStatus = nullptr;
65 GetCallFrames g_getCallFrames = nullptr;
66 OperateDebugMessage g_operateDebugMessage = nullptr;
67
68 std::atomic<bool> g_hasArkFuncsInited = false;
69 std::unordered_map<const void*, Inspector*> g_inspectors;
70 std::unordered_map<int, std::pair<void*, const DebuggerPostTask>> g_debuggerInfo;
71 std::shared_mutex g_mutex;
72
73 #if !defined(IOS_PLATFORM)
74 thread_local void* g_handle = nullptr;
75 #endif
76 thread_local void* g_vm = nullptr;
77
78 #if !defined(IOS_PLATFORM)
79 #if defined(WINDOWS_PLATFORM)
80 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_tooling.dll";
81 #elif defined(MAC_PLATFORM)
82 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_tooling.dylib";
83 #else
84 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_tooling.so";
85 #endif
86 #endif
87
88 struct ClientArgs {
89 void* server;
90 bool isHybrid;
91 };
92
HandleHybridClient(void * arg)93 void* HandleHybridClient(void* arg)
94 {
95 ClientArgs* args = static_cast<ClientArgs*>(arg);
96 auto server = args->server;
97 LOGI("HandleClient");
98 if (server == nullptr) {
99 LOGE("HandleClient server nullptr");
100 return nullptr;
101 }
102
103 #if defined(IOS_PLATFORM) || defined(MAC_PLATFORM)
104 pthread_setname_np("OS_DebugThread");
105 #else
106 pthread_setname_np(pthread_self(), "OS_DebugThread");
107 #endif
108
109 static_cast<WsServer*>(server)->RunServer(args->isHybrid);
110 delete args;
111 return nullptr;
112 }
113
HandleNormalClient(void * const server)114 void* HandleNormalClient(void* const server)
115 {
116 LOGI("HandleClient");
117 if (server == nullptr) {
118 LOGE("HandleClient server nullptr");
119 return nullptr;
120 }
121
122 #if defined(IOS_PLATFORM) || defined(MAC_PLATFORM)
123 pthread_setname_np("OS_DebugThread");
124 #else
125 pthread_setname_np(pthread_self(), "OS_DebugThread");
126 #endif
127
128 static_cast<WsServer*>(server)->RunServer();
129 return nullptr;
130 }
131
132 #if !defined(IOS_PLATFORM)
LoadArkDebuggerLibrary()133 bool LoadArkDebuggerLibrary()
134 {
135 if (g_handle != nullptr) {
136 LOGI("LoadArkDebuggerLibrary, handle has already loaded");
137 return true;
138 }
139 g_handle = Load(ARK_DEBUGGER_SHARED_LIB);
140 if (g_handle == nullptr) {
141 LOGE("LoadArkDebuggerLibrary, handle load failed");
142 return false;
143 }
144 return true;
145 }
146
GetArkDynFunction(const char * symbol)147 void* GetArkDynFunction(const char* symbol)
148 {
149 return ResolveSymbol(g_handle, symbol);
150 }
151 #endif
152
SendReply(const void * vm,const std::string & message)153 void SendReply(const void* vm, const std::string& message)
154 {
155 std::shared_lock<std::shared_mutex> lock(g_mutex);
156 auto iter = g_inspectors.find(vm);
157 if (iter != g_inspectors.end() && iter->second != nullptr &&
158 iter->second->websocketServer_ != nullptr) {
159 iter->second->websocketServer_->SendReply(message);
160 }
161 }
162
ResetServiceLocked(void * vm,bool isCloseHandle)163 void ResetServiceLocked(void *vm, bool isCloseHandle)
164 {
165 auto iter = g_inspectors.find(vm);
166 if (iter != g_inspectors.end() && iter->second != nullptr &&
167 iter->second->websocketServer_ != nullptr) {
168 iter->second->websocketServer_->StopServer();
169 delete iter->second;
170 iter->second = nullptr;
171 g_inspectors.erase(iter);
172 }
173 #if !defined(IOS_PLATFORM)
174 if (g_handle != nullptr && isCloseHandle) {
175 CloseHandle(g_handle);
176 g_handle = nullptr;
177 }
178 #endif
179 }
180
InitializeInspector(void * vm,const DebuggerPostTask & debuggerPostTask,const DebugInfo & debugInfo,int tidForSocketPair=0,bool isHybrid=false)181 bool InitializeInspector(
182 void* vm, const DebuggerPostTask& debuggerPostTask,
183 const DebugInfo& debugInfo,
184 int tidForSocketPair = 0, bool isHybrid = false)
185 {
186 std::unique_lock<std::shared_mutex> lock(g_mutex);
187 auto iter = g_inspectors.find(vm);
188 if (iter != g_inspectors.end()) {
189 LOGW("Inspector already exist!");
190 return true;
191 }
192
193 Inspector *newInspector = new Inspector();
194 g_inspectors.emplace(vm, newInspector);
195
196 newInspector->tidForSocketPair_ = tidForSocketPair;
197 newInspector->tid_ = pthread_self();
198 newInspector->vm_ = vm;
199 newInspector->debuggerPostTask_ = debuggerPostTask;
200 newInspector->websocketServer_ = std::make_unique<WsServer>(debugInfo,
201 std::bind(&Inspector::OnMessage, newInspector, std::placeholders::_1, isHybrid));
202
203 pthread_t tid;
204
205 auto server = static_cast<void *>(newInspector->websocketServer_.get());
206 if (isHybrid) {
207 ClientArgs* args = new ClientArgs{server, isHybrid};
208 if (pthread_create(&tid, nullptr, &HandleHybridClient, args)) {
209 LOGE("Create inspector thread failed");
210 return false;
211 };
212 } else {
213 if (pthread_create(&tid, nullptr, &HandleNormalClient, server)) {
214 LOGE("Create inspector thread failed");
215 return false;
216 };
217 }
218
219 newInspector->websocketServer_->tid_ = tid;
220
221 return true;
222 }
223
224 #if !defined(IOS_PLATFORM)
InitializeArkFunctionsOthers()225 bool InitializeArkFunctionsOthers()
226 {
227 g_initializeDebugger = reinterpret_cast<InitializeDebugger>(
228 GetArkDynFunction("InitializeDebugger"));
229 if (g_initializeDebugger == nullptr) {
230 ResetServiceLocked(g_vm, true);
231 return false;
232 }
233 g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(
234 GetArkDynFunction("UninitializeDebugger"));
235 if (g_uninitializeDebugger == nullptr) {
236 ResetServiceLocked(g_vm, true);
237 return false;
238 }
239 g_waitForDebugger = reinterpret_cast<WaitForDebugger>(
240 GetArkDynFunction("WaitForDebugger"));
241 if (g_waitForDebugger == nullptr) {
242 ResetServiceLocked(g_vm, true);
243 return false;
244 }
245 g_onMessage = reinterpret_cast<OnMessage>(
246 GetArkDynFunction("OnMessage"));
247 if (g_onMessage == nullptr) {
248 ResetServiceLocked(g_vm, true);
249 return false;
250 }
251 g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(
252 GetArkDynFunction("GetDispatchStatus"));
253 if (g_getDispatchStatus == nullptr) {
254 ResetServiceLocked(g_vm, true);
255 return false;
256 }
257 g_processMessage = reinterpret_cast<ProcessMessage>(
258 GetArkDynFunction("ProcessMessage"));
259 if (g_processMessage == nullptr) {
260 ResetServiceLocked(g_vm, true);
261 return false;
262 }
263 g_getCallFrames = reinterpret_cast<GetCallFrames>(
264 GetArkDynFunction("GetCallFrames"));
265 if (g_getCallFrames == nullptr) {
266 ResetServiceLocked(g_vm, true);
267 return false;
268 }
269 g_operateDebugMessage = reinterpret_cast<OperateDebugMessage>(
270 GetArkDynFunction("OperateDebugMessage"));
271 if (g_operateDebugMessage == nullptr) {
272 ResetServiceLocked(g_vm, true);
273 return false;
274 }
275 return true;
276 }
277 #else
InitializeArkFunctionsIOS()278 bool InitializeArkFunctionsIOS()
279 {
280 using namespace panda::ecmascript;
281 g_initializeDebugger = reinterpret_cast<InitializeDebugger>(&tooling::InitializeDebugger);
282 g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(&tooling::UninitializeDebugger);
283 g_waitForDebugger = reinterpret_cast<WaitForDebugger>(&tooling::WaitForDebugger);
284 g_onMessage = reinterpret_cast<OnMessage>(&tooling::OnMessage);
285 g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(&tooling::GetDispatchStatus);
286 g_processMessage = reinterpret_cast<ProcessMessage>(&tooling::ProcessMessage);
287 g_getCallFrames = reinterpret_cast<GetCallFrames>(&tooling::GetCallFrames);
288 g_operateDebugMessage = reinterpret_cast<OperateDebugMessage>(&tooling::OperateDebugMessage);
289 return true;
290 }
291 #endif
292
InitializeArkFunctions()293 bool InitializeArkFunctions()
294 {
295 // no need to initialize again in case of multi-instance
296 if (g_hasArkFuncsInited) {
297 return true;
298 }
299
300 std::unique_lock<std::shared_mutex> lock(g_mutex);
301 if (g_hasArkFuncsInited) {
302 return true;
303 }
304 #if !defined(IOS_PLATFORM)
305 if (!InitializeArkFunctionsOthers()) {
306 return false;
307 }
308 #else
309 InitializeArkFunctionsIOS()
310 #endif
311
312 g_hasArkFuncsInited = true;
313 return true;
314 }
315
316 } // namespace
317
OnMessage(std::string && msg,bool isHybrid)318 void Inspector::OnMessage(std::string&& msg, bool isHybrid)
319 {
320 if (isHybrid) {
321 std::regex pattern("\"sessionId\":\\s*(\\d)");
322 std::smatch matches;
323 if (std::regex_search(msg, matches, pattern)) {
324 HandleMessage(std::move(msg));
325 } else {
326 HandleMessage(std::move(msg)); // IDE not adjusted
327 g_onMessage(vm_, std::move(msg));
328 }
329 } else {
330 g_onMessage(vm_, std::move(msg));
331 }
332
333 // message will be processed soon if the debugger thread is in running or waiting status
334 if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
335 return;
336 }
337 std::this_thread::sleep_for(std::chrono::microseconds(DELAY_CHECK_DISPATCH_STATUS));
338 if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
339 return;
340 }
341
342 // the debugger thread maybe in idle status, so try to post a task to wake it up
343 if (debuggerPostTask_ != nullptr) {
344 if (tidForSocketPair_ == 0) {
345 debuggerPostTask_([tid = tid_, vm = vm_] {
346 if (tid != pthread_self()) {
347 LOGE("Task not in debugger thread");
348 return;
349 }
350 g_processMessage(vm);
351 });
352 } else {
353 #if defined(OHOS_PLATFORM)
354 debuggerPostTask_([tid = tidForSocketPair_, vm = vm_] {
355 uint64_t threadOrTaskId = GetThreadOrTaskId();
356 if (tid != static_cast<pid_t>(threadOrTaskId)) {
357 LOGE("Task not in debugger thread for socketpair");
358 return;
359 }
360 g_processMessage(vm);
361 });
362 #endif // defined(OHOS_PLATFORM)
363 }
364 } else {
365 LOGW("No debuggerPostTask provided");
366 }
367 }
368
369 #if defined(OHOS_PLATFORM)
GetThreadOrTaskId()370 uint64_t Inspector::GetThreadOrTaskId()
371 {
372 #if defined(ENABLE_FFRT_INTERFACES)
373 uint64_t threadOrTaskId = ffrt_this_task_get_id();
374 if (threadOrTaskId != 0) {
375 return threadOrTaskId;
376 } else {
377 return static_cast<uint64_t>(getproctid());
378 }
379 #else
380 return static_cast<uint64_t>(getproctid());
381 #endif // defined(ENABLE_FFRT_INTERFACES)
382 }
383 #endif // defined(OHOS_PLATFORM)
384
GetDebuggerPostTask(int tid)385 const DebuggerPostTask &GetDebuggerPostTask(int tid)
386 {
387 std::shared_lock<std::shared_mutex> lock(g_mutex);
388 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
389 static DebuggerPostTask tempTask;
390 return tempTask;
391 }
392 return g_debuggerInfo[tid].second;
393 }
394
GetEcmaVM(int tid)395 void *GetEcmaVM(int tid)
396 {
397 std::shared_lock<std::shared_mutex> lock(g_mutex);
398 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
399 return nullptr;
400 }
401 return g_debuggerInfo[tid].first;
402 }
403
InitializeDebuggerForSocketpair(void * vm,bool isHybrid)404 bool InitializeDebuggerForSocketpair(void* vm, bool isHybrid)
405 {
406 #if !defined(IOS_PLATFORM)
407 if (!LoadArkDebuggerLibrary()) {
408 return false;
409 }
410 #endif
411 if (!InitializeArkFunctions()) {
412 LOGE("Initialize ark functions failed");
413 return false;
414 }
415 if (isHybrid) {
416 if (!InitializeArkFunctionsForStatic()) {
417 LOGE("Initialize ark functions failed");
418 return false;
419 };
420 }
421 g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2));
422 return true;
423 }
424
425 // for ohos platform.
StartDebugForSocketpair(int tid,int socketfd,bool isHybrid)426 bool StartDebugForSocketpair(int tid, int socketfd, bool isHybrid)
427 {
428 LOGI("StartDebugForSocketpair, tid = %{private}d, socketfd = %{private}d", tid, socketfd);
429 void* vm = GetEcmaVM(tid);
430 if (vm == nullptr) {
431 LOGD("VM has already been destroyed");
432 return false;
433 }
434 g_vm = vm;
435 if (!InitializeDebuggerForSocketpair(vm)) {
436 return false;
437 }
438 const DebuggerPostTask &debuggerPostTask = GetDebuggerPostTask(tid);
439 DebugInfo debugInfo = {socketfd};
440 if (!InitializeInspector(vm, debuggerPostTask, debugInfo, tid, isHybrid)) {
441 LOGE("Initialize inspector failed");
442 return false;
443 }
444
445 return true;
446 }
447
448 // 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)449 bool StartDebug(const std::string& componentName, void* vm, bool isDebugMode,
450 int32_t instanceId, const DebuggerPostTask& debuggerPostTask, int port)
451 {
452 LOGI("StartDebug, componentName = %{private}s, isDebugMode = %{private}d, instanceId = %{private}d",
453 componentName.c_str(), isDebugMode, instanceId);
454 g_vm = vm;
455 #if !defined(IOS_PLATFORM)
456 if (!LoadArkDebuggerLibrary()) {
457 return false;
458 }
459 #endif
460 if (!InitializeArkFunctions()) {
461 LOGE("Initialize ark functions failed");
462 return false;
463 }
464
465 g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2));
466
467 int startDebugInOldProcess = -2; // start debug in old process.
468 DebugInfo debugInfo = {startDebugInOldProcess, componentName, instanceId, port};
469 if (!InitializeInspector(vm, debuggerPostTask, debugInfo)) {
470 LOGE("Initialize inspector failed");
471 return false;
472 }
473
474 if (isDebugMode && port > 0) {
475 LOGI("Wait for debugger for previewer");
476 g_waitForDebugger(vm);
477 }
478 return true;
479 }
480
WaitForDebugger(void * vm)481 void WaitForDebugger(void* vm)
482 {
483 LOGI("WaitForDebugger");
484 g_waitForDebugger(vm);
485 }
486
487
StartDebugger(uint32_t port,bool breakOnStart)488 int StartDebugger(uint32_t port, bool breakOnStart)
489 {
490 return StartDebuggerInitForStatic(port, breakOnStart);
491 }
492
StopDebugger()493 int StopDebugger()
494 {
495 return StopDebuggerInitForStatic();
496 }
497
StopDebug(void * vm,bool isHybrid)498 void StopDebug(void* vm, bool isHybrid)
499 {
500 LOGI("StopDebug start, vm is %{private}p", vm);
501 std::unique_lock<std::shared_mutex> lock(g_mutex);
502 auto iter = g_inspectors.find(vm);
503 if (iter == g_inspectors.end() || iter->second == nullptr) {
504 return;
505 }
506 #ifdef PANDA_TARGET_MACOS
507 uint32_t tid = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(g_inspectors[vm]->tid_));
508 #else
509 uint32_t tid = g_inspectors[vm]->tid_;
510 #endif
511 auto debuggerInfo = g_debuggerInfo.find(tid);
512 if (debuggerInfo != g_debuggerInfo.end()) {
513 g_debuggerInfo.erase(debuggerInfo);
514 }
515 ResetServiceLocked(vm, false);
516 g_uninitializeDebugger(vm);
517 #if !defined(IOS_PLATFORM)
518 if (g_handle != nullptr) {
519 CloseHandle(g_handle);
520 g_handle = nullptr;
521 }
522 #endif
523 if (isHybrid) {
524 StopDebuggerForStatic();
525 }
526 LOGI("StopDebug end");
527 }
528
StopOldDebug(int tid,const std::string & componentName)529 void StopOldDebug(int tid, const std::string& componentName)
530 {
531 LOGI("StopDebug start, componentName = %{private}s, tid = %{private}d", componentName.c_str(), tid);
532 void* vm = GetEcmaVM(tid);
533 if (vm == nullptr) {
534 return;
535 }
536 std::unique_lock<std::shared_mutex> lock(g_mutex);
537 auto iter = g_inspectors.find(vm);
538 if (iter == g_inspectors.end() || iter->second == nullptr) {
539 return;
540 }
541
542 ResetServiceLocked(vm, false);
543 LOGI("StopDebug end");
544 }
545
546 // for socketpair process.
StoreDebuggerInfo(int tid,void * vm,const DebuggerPostTask & debuggerPostTask)547 void StoreDebuggerInfo(int tid, void* vm, const DebuggerPostTask& debuggerPostTask)
548 {
549 std::unique_lock<std::shared_mutex> lock(g_mutex);
550 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
551 g_debuggerInfo.emplace(tid, std::make_pair(vm, debuggerPostTask));
552 }
553 }
554
555 // The returned pointer must be released using free() after it is no longer needed.
556 // Failure to release the memory will result in memory leaks.
GetJsBacktrace()557 const char* GetJsBacktrace()
558 {
559 #if defined(OHOS_PLATFORM)
560 void* vm = GetEcmaVM(Inspector::GetThreadOrTaskId());
561 if (g_getCallFrames == nullptr) {
562 LOGE("GetCallFrames symbol resolve failed");
563 return "";
564 }
565 return g_getCallFrames(vm);
566 #else
567 return "";
568 #endif
569 }
570
OperateJsDebugMessage(const char * message)571 const char* OperateJsDebugMessage([[maybe_unused]] const char* message)
572 {
573 #if defined(OHOS_PLATFORM)
574 void* vm = GetEcmaVM(Inspector::GetThreadOrTaskId());
575 if (g_operateDebugMessage == nullptr) {
576 LOGE("OperateDebugMessage symbol resolve failed");
577 return "";
578 }
579 return g_operateDebugMessage(vm, message);
580 #else
581 return "";
582 #endif
583 }
584 } // namespace OHOS::ArkCompiler::Toolchain
585