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