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 Inspector *newInspector = nullptr;
154 auto iter = g_inspectors.find(vm);
155 if (iter != g_inspectors.end()) {
156 newInspector = g_inspectors[vm];
157 } else {
158 newInspector = new Inspector();
159 if (!g_inspectors.emplace(vm, newInspector).second) {
160 delete newInspector;
161 return false;
162 }
163 }
164
165 newInspector->tidForSocketPair_ = tidForSocketPair;
166 newInspector->tid_ = pthread_self();
167 newInspector->vm_ = vm;
168 newInspector->debuggerPostTask_ = debuggerPostTask;
169 newInspector->websocketServer_ = std::make_unique<WsServer>(debugInfo,
170 std::bind(&Inspector::OnMessage, newInspector, std::placeholders::_1));
171
172 pthread_t tid;
173 if (pthread_create(&tid, nullptr, &HandleClient, static_cast<void *>(
174 newInspector->websocketServer_.get())) != 0) {
175 LOGE("Create inspector thread failed");
176 return false;
177 }
178 newInspector->websocketServer_->tid_ = tid;
179
180 return true;
181 }
182
183 #if !defined(IOS_PLATFORM)
InitializeArkFunctionsOthers()184 bool InitializeArkFunctionsOthers()
185 {
186 g_initializeDebugger = reinterpret_cast<InitializeDebugger>(
187 GetArkDynFunction("InitializeDebugger"));
188 if (g_initializeDebugger == nullptr) {
189 ResetServiceLocked(g_vm, true);
190 return false;
191 }
192 g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(
193 GetArkDynFunction("UninitializeDebugger"));
194 if (g_uninitializeDebugger == nullptr) {
195 ResetServiceLocked(g_vm, true);
196 return false;
197 }
198 g_waitForDebugger = reinterpret_cast<WaitForDebugger>(
199 GetArkDynFunction("WaitForDebugger"));
200 if (g_waitForDebugger == nullptr) {
201 ResetServiceLocked(g_vm, true);
202 return false;
203 }
204 g_onMessage = reinterpret_cast<OnMessage>(
205 GetArkDynFunction("OnMessage"));
206 if (g_onMessage == nullptr) {
207 ResetServiceLocked(g_vm, true);
208 return false;
209 }
210 g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(
211 GetArkDynFunction("GetDispatchStatus"));
212 if (g_getDispatchStatus == nullptr) {
213 ResetServiceLocked(g_vm, true);
214 return false;
215 }
216 g_processMessage = reinterpret_cast<ProcessMessage>(
217 GetArkDynFunction("ProcessMessage"));
218 if (g_processMessage == nullptr) {
219 ResetServiceLocked(g_vm, true);
220 return false;
221 }
222 return true;
223 }
224 #else
InitializeArkFunctionsIOS()225 bool InitializeArkFunctionsIOS()
226 {
227 using namespace panda::ecmascript;
228 g_initializeDebugger = reinterpret_cast<InitializeDebugger>(&tooling::InitializeDebugger);
229 g_uninitializeDebugger = reinterpret_cast<UninitializeDebugger>(&tooling::UninitializeDebugger);
230 g_waitForDebugger = reinterpret_cast<WaitForDebugger>(&tooling::WaitForDebugger);
231 g_onMessage = reinterpret_cast<OnMessage>(&tooling::OnMessage);
232 g_getDispatchStatus = reinterpret_cast<GetDispatchStatus>(&tooling::GetDispatchStatus);
233 g_processMessage = reinterpret_cast<ProcessMessage>(&tooling::ProcessMessage);
234 return true;
235 }
236 #endif
237
InitializeArkFunctions()238 bool InitializeArkFunctions()
239 {
240 // no need to initialize again in case of multi-instance
241 if (g_hasArkFuncsInited) {
242 return true;
243 }
244
245 std::unique_lock<std::shared_mutex> lock(g_mutex);
246 if (g_hasArkFuncsInited) {
247 return true;
248 }
249 #if !defined(IOS_PLATFORM)
250 if (!InitializeArkFunctionsOthers()) {
251 return false;
252 }
253 #else
254 InitializeArkFunctionsIOS()
255 #endif
256
257 g_hasArkFuncsInited = true;
258 return true;
259 }
260
261 } // namespace
262
OnMessage(std::string && msg)263 void Inspector::OnMessage(std::string&& msg)
264 {
265 g_onMessage(vm_, std::move(msg));
266
267 // message will be processed soon if the debugger thread is in running or waiting status
268 if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
269 return;
270 }
271 std::this_thread::sleep_for(std::chrono::microseconds(DELAY_CHECK_DISPATCH_STATUS));
272 if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) {
273 return;
274 }
275
276 // the debugger thread maybe in idle status, so try to post a task to wake it up
277 if (debuggerPostTask_ != nullptr) {
278 if (tidForSocketPair_ == 0) {
279 debuggerPostTask_([tid = tid_, vm = vm_] {
280 if (tid != pthread_self()) {
281 LOGE("Task not in debugger thread");
282 return;
283 }
284 g_processMessage(vm);
285 });
286 } else {
287 #if defined(OHOS_PLATFORM)
288 debuggerPostTask_([tid = tidForSocketPair_, vm = vm_, this] {
289 uint64_t threadOrTaskId = GetThreadOrTaskId();
290 if (tid != static_cast<pid_t>(threadOrTaskId)) {
291 LOGE("Task not in debugger thread for socketpair");
292 return;
293 }
294 g_processMessage(vm);
295 });
296 #endif // defined(OHOS_PLATFORM)
297 }
298 } else {
299 LOGW("No debuggerPostTask provided");
300 }
301 }
302
303 #if defined(OHOS_PLATFORM)
GetThreadOrTaskId()304 uint64_t Inspector::GetThreadOrTaskId()
305 {
306 #if defined(ENABLE_FFRT_INTERFACES)
307 uint64_t threadOrTaskId = ffrt_this_task_get_id();
308 if (threadOrTaskId != 0) {
309 return threadOrTaskId;
310 } else {
311 return static_cast<uint64_t>(getproctid());
312 }
313 #else
314 return static_cast<uint64_t>(getproctid());
315 #endif // defined(ENABLE_FFRT_INTERFACES)
316 }
317 #endif // defined(OHOS_PLATFORM)
318
GetDebuggerPostTask(int tid)319 const DebuggerPostTask &GetDebuggerPostTask(int tid)
320 {
321 std::shared_lock<std::shared_mutex> lock(g_mutex);
322 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
323 static DebuggerPostTask tempTask;
324 return tempTask;
325 }
326 return g_debuggerInfo[tid].second;
327 }
328
GetEcmaVM(int tid)329 void *GetEcmaVM(int tid)
330 {
331 std::shared_lock<std::shared_mutex> lock(g_mutex);
332 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
333 return nullptr;
334 }
335 return g_debuggerInfo[tid].first;
336 }
337
InitializeDebuggerForSocketpair(void * vm)338 bool InitializeDebuggerForSocketpair(void* vm)
339 {
340 #if !defined(IOS_PLATFORM)
341 if (!LoadArkDebuggerLibrary()) {
342 return false;
343 }
344 #endif
345 if (!InitializeArkFunctions()) {
346 LOGE("Initialize ark functions failed");
347 return false;
348 }
349 g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2));
350 return true;
351 }
352
353 // for ohos platform.
StartDebugForSocketpair(int tid,int socketfd)354 bool StartDebugForSocketpair(int tid, int socketfd)
355 {
356 LOGI("StartDebugForSocketpair, tid = %{private}d, socketfd = %{private}d", tid, socketfd);
357 void* vm = GetEcmaVM(tid);
358 if (vm == nullptr) {
359 LOGD("VM has already been destroyed");
360 return false;
361 }
362 g_vm = vm;
363 if (!InitializeDebuggerForSocketpair(vm)) {
364 return false;
365 }
366 const DebuggerPostTask &debuggerPostTask = GetDebuggerPostTask(tid);
367 DebugInfo debugInfo = {socketfd};
368 if (!InitializeInspector(vm, debuggerPostTask, debugInfo, tid)) {
369 LOGE("Initialize inspector failed");
370 return false;
371 }
372
373 return true;
374 }
375
376 // 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)377 bool StartDebug(const std::string& componentName, void* vm, bool isDebugMode,
378 int32_t instanceId, const DebuggerPostTask& debuggerPostTask, int port)
379 {
380 LOGI("StartDebug, componentName = %{private}s, isDebugMode = %{private}d, instanceId = %{private}d",
381 componentName.c_str(), isDebugMode, instanceId);
382 g_vm = vm;
383 #if !defined(IOS_PLATFORM)
384 if (!LoadArkDebuggerLibrary()) {
385 return false;
386 }
387 #endif
388 if (!InitializeArkFunctions()) {
389 LOGE("Initialize ark functions failed");
390 return false;
391 }
392
393 g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2));
394
395 int startDebugInOldProcess = -2; // start debug in old process.
396 DebugInfo debugInfo = {startDebugInOldProcess, componentName, instanceId, port};
397 if (!InitializeInspector(vm, debuggerPostTask, debugInfo)) {
398 LOGE("Initialize inspector failed");
399 return false;
400 }
401
402 if (isDebugMode && port > 0) {
403 LOGI("Wait for debugger for previewer");
404 g_waitForDebugger(vm);
405 }
406 return true;
407 }
408
WaitForDebugger(void * vm)409 void WaitForDebugger(void* vm)
410 {
411 LOGI("WaitForDebugger");
412 g_waitForDebugger(vm);
413 }
414
StopDebug(void * vm)415 void StopDebug(void* vm)
416 {
417 LOGI("StopDebug start, vm is %{private}p", vm);
418 std::unique_lock<std::shared_mutex> lock(g_mutex);
419 auto iter = g_inspectors.find(vm);
420 if (iter == g_inspectors.end() || iter->second == nullptr) {
421 return;
422 }
423 #ifdef PANDA_TARGET_MACOS
424 uint32_t tid = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(g_inspectors[vm]->tid_));
425 #else
426 uint32_t tid = g_inspectors[vm]->tid_;
427 #endif
428 auto debuggerInfo = g_debuggerInfo.find(tid);
429 if (debuggerInfo != g_debuggerInfo.end()) {
430 g_debuggerInfo.erase(debuggerInfo);
431 }
432 g_uninitializeDebugger(vm);
433 ResetServiceLocked(vm, true);
434 LOGI("StopDebug end");
435 }
436
StopOldDebug(int tid,const std::string & componentName)437 void StopOldDebug(int tid, const std::string& componentName)
438 {
439 LOGI("StopDebug start, componentName = %{private}s, tid = %{private}d", componentName.c_str(), tid);
440 void* vm = GetEcmaVM(tid);
441 if (vm == nullptr) {
442 return;
443 }
444 std::unique_lock<std::shared_mutex> lock(g_mutex);
445 auto iter = g_inspectors.find(vm);
446 if (iter == g_inspectors.end() || iter->second == nullptr) {
447 return;
448 }
449
450 ResetServiceLocked(vm, false);
451 LOGI("StopDebug end");
452 }
453
454 // for socketpair process.
StoreDebuggerInfo(int tid,void * vm,const DebuggerPostTask & debuggerPostTask)455 void StoreDebuggerInfo(int tid, void* vm, const DebuggerPostTask& debuggerPostTask)
456 {
457 std::unique_lock<std::shared_mutex> lock(g_mutex);
458 if (g_debuggerInfo.find(tid) == g_debuggerInfo.end()) {
459 g_debuggerInfo.emplace(tid, std::make_pair(vm, debuggerPostTask));
460 }
461 }
462 } // namespace OHOS::ArkCompiler::Toolchain
463