• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <thread>
21 #include <unordered_map>
22 
23 #include "log_wrapper.h"
24 #include "library_loader.h"
25 
26 #if defined(IOS_PLATFORM)
27 #include "tooling/debugger_service.h"
28 #endif
29 
30 namespace OHOS::ArkCompiler::Toolchain {
31 namespace {
32 enum DispatchStatus : int32_t {
33     UNKNOWN = 0,
34     DISPATCHING,
35     DISPATCHED
36 };
37 
38 using InitializeDebugger = void(*)(void*, const std::function<void(const void*, const std::string&)>&);
39 using UninitializeDebugger = void(*)(void*);
40 using WaitForDebugger = void(*)(void*);
41 using OnMessage = void(*)(void*, std::string&&);
42 using ProcessMessage = void(*)(void*);
43 using GetDispatchStatus = int32_t(*)(void*);
44 
45 OnMessage g_onMessage = nullptr;
46 InitializeDebugger g_initializeDebugger = nullptr;
47 UninitializeDebugger g_uninitializeDebugger = nullptr;
48 WaitForDebugger g_waitForDebugger = nullptr;
49 ProcessMessage g_processMessage = nullptr;
50 GetDispatchStatus g_getDispatchStatus = nullptr;
51 
52 std::atomic<bool> g_hasArkFuncsInited = false;
53 std::unordered_map<const void*, Inspector*> g_inspectors;
54 std::shared_mutex g_mutex;
55 
56 #if !defined(IOS_PLATFORM)
57 thread_local void* g_handle = nullptr;
58 #endif
59 thread_local void* g_vm = nullptr;
60 
61 #if !defined(IOS_PLATFORM)
62 #if defined(WINDOWS_PLATFORM)
63 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_ecma_debugger.dll";
64 #elif defined(MAC_PLATFORM)
65 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_ecma_debugger.dylib";
66 #else
67 constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_ecma_debugger.so";
68 #endif
69 #endif
70 
HandleClient(void * const server)71 void* HandleClient(void* const server)
72 {
73     LOGI("HandleClient");
74     if (server == nullptr) {
75         LOGE("HandleClient server nullptr");
76         return nullptr;
77     }
78     static_cast<WsServer*>(server)->RunServer();
79     return nullptr;
80 }
81 
82 #if !defined(IOS_PLATFORM)
LoadArkDebuggerLibrary()83 bool LoadArkDebuggerLibrary()
84 {
85     if (g_handle != nullptr) {
86         LOGE("Already opened");
87         return false;
88     }
89     g_handle = Load(ARK_DEBUGGER_SHARED_LIB);
90     if (g_handle == nullptr) {
91         return false;
92     }
93     return true;
94 }
95 
GetArkDynFunction(const char * symbol)96 void* GetArkDynFunction(const char* symbol)
97 {
98     return ResolveSymbol(g_handle, symbol);
99 }
100 #endif
101 
SendReply(const void * vm,const std::string & message)102 void SendReply(const void* vm, const std::string& message)
103 {
104     std::shared_lock<std::shared_mutex> lock(g_mutex);
105     auto iter = g_inspectors.find(vm);
106     if (iter != g_inspectors.end() && iter->second != nullptr &&
107         iter->second->websocketServer_ != nullptr) {
108         iter->second->websocketServer_->SendReply(message);
109     }
110 }
111 
ResetServiceLocked()112 void ResetServiceLocked()
113 {
114     auto iter = g_inspectors.find(g_vm);
115     if (iter != g_inspectors.end() && iter->second != nullptr &&
116         iter->second->websocketServer_ != nullptr) {
117         iter->second->websocketServer_->StopServer();
118         delete iter->second;
119         iter->second = nullptr;
120         g_inspectors.erase(iter);
121     }
122 #if !defined(IOS_PLATFORM)
123     if (g_handle != nullptr) {
124         CloseHandle(g_handle);
125         g_handle = nullptr;
126     }
127 #endif
128 }
129 
InitializeInspector(void * vm,const std::string & componentName,int32_t instanceId,const DebuggerPostTask & debuggerPostTask)130 bool InitializeInspector(void* vm, const std::string& componentName, int32_t instanceId,
131     const DebuggerPostTask& debuggerPostTask)
132 {
133     std::unique_lock<std::shared_mutex> lock(g_mutex);
134     auto iter = g_inspectors.find(vm);
135     if (iter != g_inspectors.end()) {
136         LOGE("Already have the same vm in the map");
137         return false;
138     }
139 
140     Inspector *newInspector = new Inspector();
141     if (!g_inspectors.emplace(vm, newInspector).second) {
142         delete newInspector;
143         return false;
144     }
145 
146     newInspector->tid_ = pthread_self();
147     newInspector->vm_ = vm;
148     newInspector->debuggerPostTask_ = debuggerPostTask;
149     newInspector->websocketServer_ = std::make_unique<WsServer>(componentName,
150         std::bind(&Inspector::OnMessage, newInspector, std::placeholders::_1), instanceId);
151 
152     pthread_t tid;
153     if (pthread_create(&tid, nullptr, &HandleClient, static_cast<void *>(
154         newInspector->websocketServer_.get())) != 0) {
155         LOGE("Create inspector thread failed");
156         return false;
157     }
158     newInspector->websocketServer_->tid_ = tid;
159 
160     return true;
161 }
162 
InitializeArkFunctions()163 bool InitializeArkFunctions()
164 {
165     // no need to initialize again in case of multi-instance
166     if (g_hasArkFuncsInited) {
167         return true;
168     }
169 
170     std::unique_lock<std::shared_mutex> lock(g_mutex);
171     if (g_hasArkFuncsInited) {
172         return true;
173     }
174 #if !defined(IOS_PLATFORM)
175     g_initializeDebugger = reinterpret_cast<InitializeDebugger>(
176         GetArkDynFunction("InitializeDebugger"));
177     if (g_initializeDebugger == nullptr) {
178         ResetServiceLocked();
179         return false;
180     }
181     g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(
182         GetArkDynFunction("UninitializeDebugger"));
183     if (g_uninitializeDebugger == nullptr) {
184         ResetServiceLocked();
185         return false;
186     }
187     g_waitForDebugger = reinterpret_cast<WaitForDebugger>(
188         GetArkDynFunction("WaitForDebugger"));
189     if (g_waitForDebugger == nullptr) {
190         ResetServiceLocked();
191         return false;
192     }
193     g_onMessage = reinterpret_cast<OnMessage>(
194         GetArkDynFunction("OnMessage"));
195     if (g_onMessage == nullptr) {
196         ResetServiceLocked();
197         return false;
198     }
199     g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(
200         GetArkDynFunction("GetDispatchStatus"));
201     if (g_getDispatchStatus == nullptr) {
202         ResetServiceLocked();
203         return false;
204     }
205     g_processMessage = reinterpret_cast<ProcessMessage>(
206         GetArkDynFunction("ProcessMessage"));
207     if (g_processMessage == nullptr) {
208         ResetServiceLocked();
209         return false;
210     }
211 #else
212     using namespace panda::ecmascript;
213     g_initializeDebugger = reinterpret_cast<InitializeDebugger>(&tooling::InitializeDebugger);
214     g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(&tooling::UninitializeDebugger);
215     g_waitForDebugger = reinterpret_cast<WaitForDebugger>(&tooling::WaitForDebugger);
216     g_onMessage = reinterpret_cast<OnMessage>(&tooling::OnMessage);
217     g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(&tooling::GetDispatchStatus);
218     g_processMessage = reinterpret_cast<ProcessMessage>(&tooling::ProcessMessage);
219 #endif
220 
221     g_hasArkFuncsInited = true;
222     return true;
223 }
224 } // namespace
225 
OnMessage(std::string && msg)226 void Inspector::OnMessage(std::string&& msg)
227 {
228     g_onMessage(vm_, std::move(msg));
229 
230     // message will be processed soon if the debugger thread is in running or waiting status
231     if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
232         return;
233     }
234     std::this_thread::sleep_for(std::chrono::microseconds(DELAY_CHECK_DISPATCH_STATUS));
235     if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
236         return;
237     }
238 
239     // the debugger thread maybe in idle status, so try to post a task to wake it up
240     if (debuggerPostTask_ != nullptr) {
241         debuggerPostTask_([tid = tid_, vm = vm_] {
242             if (tid != pthread_self()) {
243                 LOGE("Task not in debugger thread");
244                 return;
245             }
246             g_processMessage(vm);
247         });
248     } else {
249         LOGW("No debuggerPostTask provided");
250     }
251 }
252 
StartDebug(const std::string & componentName,void * vm,bool isDebugMode,int32_t instanceId,const DebuggerPostTask & debuggerPostTask)253 bool StartDebug(const std::string& componentName, void* vm, bool isDebugMode, int32_t instanceId,
254     const DebuggerPostTask& debuggerPostTask)
255 {
256     g_vm = vm;
257 #if !defined(IOS_PLATFORM)
258     if (!LoadArkDebuggerLibrary()) {
259         return false;
260     }
261 #endif
262     if (!InitializeArkFunctions()) {
263         LOGE("Initialize ark functions failed");
264         return false;
265     }
266 
267     g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2));
268 
269     if (!InitializeInspector(vm, componentName, instanceId, debuggerPostTask)) {
270         LOGE("Initialize inspector failed");
271         return false;
272     }
273 
274     if (isDebugMode) {
275         g_waitForDebugger(vm);
276     }
277     return true;
278 }
279 
StopDebug(const std::string & componentName)280 void StopDebug(const std::string& componentName)
281 {
282     LOGI("StopDebug: %{private}s", componentName.c_str());
283     std::unique_lock<std::shared_mutex> lock(g_mutex);
284     auto iter = g_inspectors.find(g_vm);
285     if (iter == g_inspectors.end() || iter->second == nullptr) {
286         return;
287     }
288 
289     g_uninitializeDebugger(g_vm);
290     ResetServiceLocked();
291     LOGI("StopDebug end");
292 }
293 } // namespace OHOS::ArkCompiler::Toolchain
294