• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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