• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022-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 
18 #include <functional>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "debugger/breakpoint.h"
24 #include "macros.h"
25 #include "os/mutex.h"
26 #include "include/runtime.h"
27 #include "utils/logger.h"
28 
29 #include "error.h"
30 #include "evaluation/base64.h"
31 #include "tooling/sampler/sampling_profiler.h"
32 #include "types/remote_object.h"
33 #include "types/scope.h"
34 
35 using namespace std::placeholders;  // NOLINT(google-build-using-namespace)
36 
37 namespace ark::tooling::inspector {
LogDebuggerNotPaused(std::string_view methodName)38 static void LogDebuggerNotPaused(std::string_view methodName)
39 {
40     LOG(WARNING, DEBUGGER) << "Inspector method '" << methodName << "' must be called on pause";
41 }
42 
Inspector(Server & server,DebugInterface & debugger,bool breakOnStart)43 Inspector::Inspector(Server &server, DebugInterface &debugger, bool breakOnStart)
44     : breakOnStart_(breakOnStart), inspectorServer_(server), debugger_(debugger)
45 {
46     if (!HandleError(debugger_.RegisterHooks(this))) {
47         return;
48     }
49 
50     // acquire lock to later release it either in `OnOpen` or `OnFail` callbacks
51     inspectorServer_.OnValidate([this]() NO_THREAD_SAFETY_ANALYSIS {
52         ASSERT(!connecting_);  // NOLINT(bugprone-lambda-function-name)
53         debuggerEventsLock_.WriteLock();
54         connecting_ = true;
55     });
56     inspectorServer_.OnOpen([this]() NO_THREAD_SAFETY_ANALYSIS {
57         ASSERT(connecting_);  // NOLINT(bugprone-lambda-function-name)
58         connecting_ = false;
59         debuggerEventsLock_.Unlock();
60     });
61     inspectorServer_.OnFail([this]() NO_THREAD_SAFETY_ANALYSIS {
62         if (connecting_) {
63             connecting_ = false;
64             debuggerEventsLock_.Unlock();
65         }
66     });
67 
68     RegisterMethodHandlers();
69 }
70 
~Inspector()71 Inspector::~Inspector()
72 {
73     // Current implementation destroys `Inspector` after server connection is closed,
74     // hence no need to notify client
75     inspectorServer_.Kill();
76     HandleError(debugger_.UnregisterHooks());
77 }
78 
CollectModules()79 void Inspector::CollectModules()
80 {
81     os::memory::ReadLockHolder lock(debuggerEventsLock_);
82     Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles([this](auto &file) {
83         debugInfoCache_.AddPandaFile(file);
84         // Do not call server, cause no connection at this stage
85         return true;
86     });
87 }
88 
Run(const std::string & msg)89 void Inspector::Run(const std::string& msg)
90 {
91     inspectorServer_.Run(msg);
92 }
93 
Stop()94 void Inspector::Stop()
95 {
96     serverThread_.join();
97 }
98 
ConsoleCall(PtThread thread,ConsoleCallType type,uint64_t timestamp,const PandaVector<TypedValue> & arguments)99 void Inspector::ConsoleCall(PtThread thread, ConsoleCallType type, uint64_t timestamp,
100                             const PandaVector<TypedValue> &arguments)
101 {
102     os::memory::ReadLockHolder lock(debuggerEventsLock_);
103 
104     auto *debuggableThread = GetDebuggableThread(thread);
105     if (debuggableThread != nullptr) {
106         inspectorServer_.CallRuntimeConsoleApiCalled(
107             thread,
108             type,
109             timestamp,
110             debuggableThread->OnConsoleCall(arguments)
111         );
112     }
113 }
114 
115 // CC-OFFNXT(G.FUN.01-CPP) Decreasing the number of arguments will decrease the clarity of the code.
Exception(PtThread thread,Method *,const PtLocation &,ObjectHeader *,Method *,const PtLocation & catchLocation)116 void Inspector::Exception(PtThread thread, Method * /* method */, const PtLocation & /* location */,
117                           ObjectHeader * /* exception */, Method * /* catch_method */, const PtLocation &catchLocation)
118 {
119     os::memory::ReadLockHolder lock(debuggerEventsLock_);
120 
121     auto *debuggableThread = GetDebuggableThread(thread);
122     if (debuggableThread != nullptr) {
123         debuggableThread->OnException(catchLocation.GetBytecodeOffset() == panda_file::INVALID_OFFSET);
124     }
125 }
126 
FramePop(PtThread thread,Method *,bool)127 void Inspector::FramePop(PtThread thread, Method * /* method */, bool /* was_popped_by_exception */)
128 {
129     os::memory::ReadLockHolder lock(debuggerEventsLock_);
130 
131     auto *debuggableThread = GetDebuggableThread(thread);
132     if (debuggableThread != nullptr) {
133         debuggableThread->OnFramePop();
134     }
135 }
136 
MethodEntry(PtThread thread,Method *)137 void Inspector::MethodEntry(PtThread thread, Method * /* method */)
138 {
139     os::memory::ReadLockHolder lock(debuggerEventsLock_);
140 
141     auto *debuggableThread = GetDebuggableThread(thread);
142     auto stack = StackWalker::Create(thread.GetManagedThread());
143     if (stack.IsCFrame()) {
144         return;
145     }
146     if (debuggableThread == nullptr) {
147         return;
148     }
149     if (debuggableThread != nullptr) {
150         if (debuggableThread->OnMethodEntry()) {
151             HandleError(debugger_.NotifyFramePop(thread, 0));
152         }
153     }
154 }
155 
SourceNameInsert(const panda_file::DebugInfoExtractor * extractor)156 void Inspector::SourceNameInsert(const panda_file::DebugInfoExtractor *extractor)
157 {
158     const auto &methodList = extractor->GetMethodIdList();
159     std::unordered_set<std::string> sourceNames;
160     for (const auto &method : methodList) {
161         sourceNames.insert(extractor->GetSourceFile(method));
162     }
163     for (const auto &sourceName : sourceNames) {
164         // Get src file name
165         auto [scriptId, isNew] = inspectorServer_.GetSourceManager().GetScriptId(sourceName);
166         inspectorServer_.CallDebuggerScriptParsed(scriptId, sourceName);
167     }
168 }
169 
LoadModule(std::string_view fileName)170 void Inspector::LoadModule(std::string_view fileName)
171 {
172     os::memory::ReadLockHolder lock(debuggerEventsLock_);
173 
174     Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles(
175         [this, fileName](auto &file) {
176             if (file.GetFilename() == fileName) {
177                 debugInfoCache_.AddPandaFile(file, true);
178             }
179 
180             return true;
181         },
182         !fileName.empty());
183 }
184 
ResolveBreakpoints(const panda_file::File & file,const panda_file::DebugInfoExtractor * debugInfo)185 void Inspector::ResolveBreakpoints(const panda_file::File &file, const panda_file::DebugInfoExtractor *debugInfo)
186 {
187     breakpointStorage_.ResolveBreakpoints(file, debugInfo);
188 }
189 
SingleStep(PtThread thread,Method * method,const PtLocation & location)190 void Inspector::SingleStep(PtThread thread, Method *method, const PtLocation &location)
191 {
192     os::memory::ReadLockHolder lock(debuggerEventsLock_);
193 
194     auto sourceFile = debugInfoCache_.GetUserSourceFile(method);
195     // NOTE(fangting, #IC98Z2): etsstdlib.ets should not call loadModule in pytest.
196     if ((sourceFile == nullptr) || (strcmp(sourceFile, "etsstdlib.ets") == 0)) {
197         return;
198     }
199 
200     auto *debuggableThread = GetDebuggableThread(thread);
201     if (debuggableThread != nullptr) {
202         debuggableThread->OnSingleStep(location, sourceFile);
203     }
204 }
205 
ThreadStart(PtThread thread)206 void Inspector::ThreadStart(PtThread thread)
207 {
208     os::memory::ReadLockHolder lock(debuggerEventsLock_);
209 
210     if (thread != PtThread::NONE) {
211         inspectorServer_.CallTargetAttachedToTarget(thread);
212     }
213 
214     // NOLINTBEGIN(modernize-avoid-bind)
215     auto callbacks = DebuggableThread::SuspensionCallbacks {
216         [](auto &, auto &, auto) {},
217         std::bind(&Inspector::DebuggableThreadPostSuspend, this, thread, _1, _2, _3, _4),
218         [this]() NO_THREAD_SAFETY_ANALYSIS { debuggerEventsLock_.Unlock(); },
219         [this]() NO_THREAD_SAFETY_ANALYSIS { debuggerEventsLock_.ReadLock(); },
220         []() {},
221         [this, thread]() { inspectorServer_.CallDebuggerResumed(thread); }};
222     // NOLINTEND(modernize-avoid-bind)
223     auto [it, inserted] = threads_.emplace(
224         std::piecewise_construct, std::forward_as_tuple(thread),
225         std::forward_as_tuple(thread.GetManagedThread(), &debugger_, std::move(callbacks), breakpointStorage_));
226     (void)inserted;
227     ASSERT(inserted);
228 
229     if (breakOnStart_) {
230         it->second.BreakOnStart();
231     }
232 }
233 
ThreadEnd(PtThread thread)234 void Inspector::ThreadEnd(PtThread thread)
235 {
236     os::memory::ReadLockHolder lock(debuggerEventsLock_);
237 
238     if (thread != PtThread::NONE) {
239         inspectorServer_.CallTargetDetachedFromTarget(thread);
240     }
241 
242     [[maybe_unused]] auto erased = threads_.erase(thread);
243     ASSERT(erased == 1);
244 }
245 
VmDeath()246 void Inspector::VmDeath()
247 {
248     os::memory::WriteLockHolder lock(vmDeathLock_);
249 
250     ASSERT(!isVmDead_);
251     isVmDead_ = true;
252 
253     NotifyExecutionEnded();
254 }
255 
RuntimeEnable(PtThread thread)256 void Inspector::RuntimeEnable(PtThread thread)
257 {
258     os::memory::ReadLockHolder lock(vmDeathLock_);
259     if (UNLIKELY(CheckVmDead())) {
260         return;
261     }
262 
263     inspectorServer_.CallRuntimeExecutionContextCreated(thread);
264 }
265 
RunIfWaitingForDebugger(PtThread thread)266 void Inspector::RunIfWaitingForDebugger(PtThread thread)
267 {
268     os::memory::ReadLockHolder lock(vmDeathLock_);
269     if (UNLIKELY(CheckVmDead())) {
270         return;
271     }
272 
273     auto *debuggableThread = GetDebuggableThread(thread);
274     if (debuggableThread != nullptr) {
275         debuggableThread->Touch();
276     }
277 
278     os::memory::LockHolder<os::memory::Mutex> lockHolder(waitDebuggerMutex_);
279     waitDebuggerCond_.Signal();
280 }
281 
282 //For Hybrid it was not used, instead it use 1.0 waitForDebugger
WaitForDebugger()283 void Inspector::WaitForDebugger()
284 {
285     os::memory::LockHolder<os::memory::Mutex> lock(waitDebuggerMutex_);
286     waitDebuggerCond_.Wait(&waitDebuggerMutex_);
287 }
288 
Pause(PtThread thread)289 void Inspector::Pause(PtThread thread)
290 {
291     os::memory::ReadLockHolder lock(vmDeathLock_);
292     if (UNLIKELY(CheckVmDead())) {
293         return;
294     }
295 
296     auto *debuggableThread = GetDebuggableThread(thread);
297     if (debuggableThread != nullptr) {
298         debuggableThread->Pause();
299     }
300 }
301 
Continue(PtThread thread)302 void Inspector::Continue(PtThread thread)
303 {
304     os::memory::ReadLockHolder lock(vmDeathLock_);
305     if (UNLIKELY(CheckVmDead())) {
306         return;
307     }
308 
309     auto *debuggableThread = GetDebuggableThread(thread);
310     if (debuggableThread != nullptr) {
311         debuggableThread->Continue();
312     }
313 }
314 
SetBreakpointsActive(PtThread thread,bool active)315 void Inspector::SetBreakpointsActive([[maybe_unused]] PtThread thread, bool active)
316 {
317     os::memory::ReadLockHolder lock(vmDeathLock_);
318     if (UNLIKELY(CheckVmDead())) {
319         return;
320     }
321 
322     breakpointStorage_.SetBreakpointsActive(active);
323 }
324 
SetSkipAllPauses(PtThread thread,bool skip)325 void Inspector::SetSkipAllPauses(PtThread thread, bool skip)
326 {
327     os::memory::ReadLockHolder lock(vmDeathLock_);
328     if (UNLIKELY(CheckVmDead())) {
329         return;
330     }
331 
332     auto *debuggableThread = GetDebuggableThread(thread);
333     if (debuggableThread != nullptr) {
334         debuggableThread->SetSkipAllPauses(skip);
335     }
336 }
337 
SetMixedDebugEnabled(PtThread thread,bool mixedDebugEnabled)338 void Inspector::SetMixedDebugEnabled(PtThread thread, bool mixedDebugEnabled)
339 {
340     os::memory::ReadLockHolder lock(vmDeathLock_);
341     if (UNLIKELY(CheckVmDead())) {
342         return;
343     }
344 
345     auto *debuggableThread = GetDebuggableThread(thread);
346     if (debuggableThread != nullptr) {
347         debuggableThread->SetMixedDebugEnabled(mixedDebugEnabled);
348     }
349 }
350 
GetPossibleBreakpoints(std::string_view sourceFile,size_t startLine,size_t endLine,bool restrictToFunction)351 std::set<size_t> Inspector::GetPossibleBreakpoints(std::string_view sourceFile, size_t startLine, size_t endLine,
352                                                    bool restrictToFunction)
353 {
354     os::memory::ReadLockHolder lock(vmDeathLock_);
355     if (UNLIKELY(CheckVmDead())) {
356         return {};
357     }
358 
359     return debugInfoCache_.GetValidLineNumbers(sourceFile, startLine, endLine, restrictToFunction);
360 }
361 
SetBreakpoint(PtThread thread,SourceFileFilter && sourceFilesFilter,size_t lineNumber,std::set<std::string_view> & sourceFiles,const std::string * condition)362 std::optional<BreakpointId> Inspector::SetBreakpoint([[maybe_unused]] PtThread thread,
363                                                      SourceFileFilter &&sourceFilesFilter, size_t lineNumber,
364                                                      std::set<std::string_view> &sourceFiles,
365                                                      const std::string *condition)
366 {
367     os::memory::ReadLockHolder lock(vmDeathLock_);
368     if (UNLIKELY(CheckVmDead())) {
369         return {};
370     }
371 
372     std::string optBytecode;
373     if (condition != nullptr) {
374         if (condition->empty()) {
375             // Some debugger clients send empty condition by default
376             condition = nullptr;
377         } else {
378             Base64Decoder::Decode(*condition, optBytecode);
379             condition = &optBytecode;
380         }
381     }
382 
383     return breakpointStorage_.SetBreakpoint(std::move(sourceFilesFilter), lineNumber, sourceFiles, condition,
384                                             debugInfoCache_);
385 }
386 
RemoveBreakpoint(PtThread thread,BreakpointId id)387 void Inspector::RemoveBreakpoint([[maybe_unused]] PtThread thread, BreakpointId id)
388 {
389     os::memory::ReadLockHolder lock(vmDeathLock_);
390     if (UNLIKELY(CheckVmDead())) {
391         return;
392     }
393 
394     breakpointStorage_.RemoveBreakpoint(id);
395 }
396 
RemoveBreakpoints(PtThread thread,const SourceFileFilter & sourceFilesFilter)397 void Inspector::RemoveBreakpoints(PtThread thread, const SourceFileFilter &sourceFilesFilter)
398 {
399     os::memory::ReadLockHolder lock(vmDeathLock_);
400     if (UNLIKELY(CheckVmDead())) {
401         return;
402     }
403 
404     auto *debuggableThread = GetDebuggableThread(thread);
405     if (debuggableThread == nullptr) {
406         return;
407     }
408     auto pandaFilesPaths = debugInfoCache_.GetPandaFiles(sourceFilesFilter);
409     if (pandaFilesPaths.empty()) {
410         return;
411     }
412 
413     breakpointStorage_.RemoveBreakpoints([pfs = std::as_const(pandaFilesPaths)](const auto &loc) {
414         for (const auto &pf : pfs) {
415             if (pf == loc.GetPandaFile()) {
416                 return true;
417             }
418         }
419         return false;
420     });
421 }
422 
SetPauseOnExceptions(PtThread thread,PauseOnExceptionsState state)423 void Inspector::SetPauseOnExceptions(PtThread thread, PauseOnExceptionsState state)
424 {
425     os::memory::ReadLockHolder lock(vmDeathLock_);
426     if (UNLIKELY(CheckVmDead())) {
427         return;
428     }
429 
430     auto *debuggableThread = GetDebuggableThread(thread);
431     if (debuggableThread != nullptr) {
432         debuggableThread->SetPauseOnExceptions(state);
433     }
434 }
435 
StepInto(PtThread thread)436 void Inspector::StepInto(PtThread thread)
437 {
438     os::memory::ReadLockHolder lock(vmDeathLock_);
439     if (UNLIKELY(CheckVmDead())) {
440         return;
441     }
442 
443     auto *debuggableThread = GetDebuggableThread(thread);
444     if (debuggableThread != nullptr) {
445         if (UNLIKELY(!debuggableThread->IsPaused())) {
446             LogDebuggerNotPaused("stepInto");
447             return;
448         }
449 
450         auto frame = debugger_.GetCurrentFrame(thread);
451         if (!frame) {
452             HandleError(frame.Error());
453             return;
454         }
455 
456         debuggableThread->StepInto(debugInfoCache_.GetCurrentLineLocations(*frame.Value()));
457     }
458 }
459 
StepOver(PtThread thread)460 void Inspector::StepOver(PtThread thread)
461 {
462     os::memory::ReadLockHolder lock(vmDeathLock_);
463     if (UNLIKELY(CheckVmDead())) {
464         return;
465     }
466 
467     auto *debuggableThread = GetDebuggableThread(thread);
468     if (debuggableThread != nullptr) {
469         if (UNLIKELY(!debuggableThread->IsPaused())) {
470             LogDebuggerNotPaused("stepOver");
471             return;
472         }
473 
474         auto frame = debugger_.GetCurrentFrame(thread);
475         if (!frame) {
476             HandleError(frame.Error());
477             return;
478         }
479 
480         debuggableThread->StepOver(debugInfoCache_.GetCurrentLineLocations(*frame.Value()));
481     }
482 }
483 
StepOut(PtThread thread)484 void Inspector::StepOut(PtThread thread)
485 {
486     os::memory::ReadLockHolder lock(vmDeathLock_);
487     if (UNLIKELY(CheckVmDead())) {
488         return;
489     }
490 
491     auto *debuggableThread = GetDebuggableThread(thread);
492     if (debuggableThread != nullptr) {
493         if (UNLIKELY(!debuggableThread->IsPaused())) {
494             LogDebuggerNotPaused("stepOut");
495             return;
496         }
497 
498         HandleError(debugger_.NotifyFramePop(thread, 0));
499         debuggableThread->StepOut();
500     }
501 }
502 
ContinueToLocation(PtThread thread,std::string_view sourceFile,size_t lineNumber)503 void Inspector::ContinueToLocation(PtThread thread, std::string_view sourceFile, size_t lineNumber)
504 {
505     os::memory::ReadLockHolder lock(vmDeathLock_);
506     if (UNLIKELY(CheckVmDead())) {
507         return;
508     }
509 
510     auto *debuggableThread = GetDebuggableThread(thread);
511     if (debuggableThread != nullptr) {
512         if (UNLIKELY(!debuggableThread->IsPaused())) {
513             LogDebuggerNotPaused("continueToLocation");
514             return;
515         }
516 
517         debuggableThread->ContinueTo(debugInfoCache_.GetContinueToLocations(sourceFile, lineNumber));
518     }
519 }
520 
RestartFrame(PtThread thread,FrameId frameId)521 void Inspector::RestartFrame(PtThread thread, FrameId frameId)
522 {
523     os::memory::ReadLockHolder lock(vmDeathLock_);
524     if (UNLIKELY(CheckVmDead())) {
525         return;
526     }
527 
528     auto *debuggableThread = GetDebuggableThread(thread);
529     if (debuggableThread != nullptr) {
530         if (UNLIKELY(!debuggableThread->IsPaused())) {
531             LogDebuggerNotPaused("restartFrame");
532             return;
533         }
534 
535         if (auto error = debugger_.RestartFrame(thread, frameId)) {
536             HandleError(*error);
537             return;
538         }
539 
540         debuggableThread->StepInto({});
541     }
542 }
543 
GetProperties(PtThread thread,RemoteObjectId objectId,bool generatePreview)544 std::vector<PropertyDescriptor> Inspector::GetProperties(PtThread thread, RemoteObjectId objectId, bool generatePreview)
545 {
546     os::memory::ReadLockHolder lock(vmDeathLock_);
547     if (UNLIKELY(CheckVmDead())) {
548         return {};
549     }
550 
551     std::optional<std::vector<PropertyDescriptor>> properties;
552 
553     auto *debuggableThread = GetDebuggableThread(thread);
554     if (debuggableThread != nullptr) {
555         debuggableThread->RequestToObjectRepository([objectId, generatePreview, &properties](auto &objectRepository) {
556             properties = objectRepository.GetProperties(objectId, generatePreview);
557         });
558     }
559 
560     if (!properties) {
561         LOG(INFO, DEBUGGER) << "Failed to resolve object id: " << objectId;
562         return {};
563     }
564 
565     return *properties;
566 }
567 
GetSourceCode(std::string_view sourceFile)568 std::string Inspector::GetSourceCode(std::string_view sourceFile)
569 {
570     os::memory::ReadLockHolder lock(vmDeathLock_);
571     if (UNLIKELY(CheckVmDead())) {
572         return {};
573     }
574 
575     return debugInfoCache_.GetSourceCode(sourceFile);
576 }
577 
DebuggableThreadPostSuspend(PtThread thread,ObjectRepository & objectRepository,const std::vector<BreakpointId> & hitBreakpoints,ObjectHeader * exception,PauseReason pauseReason)578 void Inspector::DebuggableThreadPostSuspend(PtThread thread, ObjectRepository &objectRepository,
579                                             const std::vector<BreakpointId> &hitBreakpoints, ObjectHeader *exception,
580                                             PauseReason pauseReason)
581 {
582     auto exceptionRemoteObject = exception != nullptr ? objectRepository.CreateObject(TypedValue::Reference(exception))
583                                                       : std::optional<RemoteObject>();
584 
585     inspectorServer_.CallDebuggerPaused(
586         thread, hitBreakpoints, exceptionRemoteObject, pauseReason, [this, thread, &objectRepository](auto &handler) {
587             FrameId frameId = 0;
588             HandleError(debugger_.EnumerateFrames(thread, [this, &objectRepository, &handler,
589                                                            &frameId](const PtFrame &frame) {
590                 std::string_view sourceFile;
591                 std::string_view methodName;
592                 size_t lineNumber;
593                 debugInfoCache_.GetSourceLocation(frame, sourceFile, methodName, lineNumber);
594                 if (sourceFile.empty()) {
595                     return false;
596                 }
597 
598                 std::optional<RemoteObject> objThis;
599                 auto frameObject = objectRepository.CreateFrameObject(frame, debugInfoCache_.GetLocals(frame), objThis);
600                 auto scopeChain = std::vector {Scope(Scope::Type::LOCAL, std::move(frameObject)),
601                                                Scope(Scope::Type::GLOBAL, objectRepository.CreateGlobalObject())};
602 
603                 handler(frameId++, methodName, sourceFile, lineNumber, scopeChain, objThis);
604 
605                 return true;
606             }));
607         });
608 }
609 
NotifyExecutionEnded()610 void Inspector::NotifyExecutionEnded()
611 {
612     inspectorServer_.CallRuntimeExecutionContextsCleared();
613 }
614 
Evaluate(PtThread thread,const std::string & bytecodeBase64,size_t frameNumber)615 Expected<EvaluationResult, std::string> Inspector::Evaluate(PtThread thread, const std::string &bytecodeBase64,
616                                                             size_t frameNumber)
617 {
618     os::memory::ReadLockHolder lock(vmDeathLock_);
619     if (UNLIKELY(CheckVmDead())) {
620         return Unexpected(std::string("Fatal, VM is dead"));
621     }
622 
623     auto *debuggableThread = GetDebuggableThread(thread);
624     if (debuggableThread == nullptr) {
625         return Unexpected(std::string("No thread found"));
626     }
627 
628     if (UNLIKELY(!debuggableThread->IsPaused())) {
629         LogDebuggerNotPaused("evaluate");
630         return Unexpected(std::string("Expression evaluation can be done only on pause"));
631     }
632 
633     std::string bytecode;
634     Base64Decoder::Decode(bytecodeBase64, bytecode);
635     auto optResult = debuggableThread->EvaluateExpression(frameNumber, bytecode);
636     if (!optResult) {
637         return Unexpected(std::move(optResult.Error()));
638     }
639     auto optExceptionDetails = (optResult->second) ? CreateExceptionDetails(thread, std::move(*optResult->second))
640                                                    : std::optional<ExceptionDetails>();
641     return EvaluationResult(std::move(optResult->first), std::move(optExceptionDetails));
642 }
643 
CreateExceptionDetails(PtThread thread,RemoteObject && exception)644 std::optional<ExceptionDetails> Inspector::CreateExceptionDetails(PtThread thread, RemoteObject &&exception)
645 {
646     auto frame = debugger_.GetCurrentFrame(thread);
647     if (!frame) {
648         HandleError(frame.Error());
649         return {};
650     }
651 
652     std::string_view sourceFile;
653     std::string_view methodName;
654     size_t lineNumber;
655     debugInfoCache_.GetSourceLocation(*frame.Value(), sourceFile, methodName, lineNumber);
656 
657     ExceptionDetails exceptionDetails(GetNewExceptionId(), "", lineNumber, 0);
658     return exceptionDetails.SetUrl(sourceFile).SetExceptionObject(std::move(exception));
659 }
660 
GetNewExceptionId()661 size_t Inspector::GetNewExceptionId()
662 {
663     // Atomic with relaxed order reason: data race on concurrent exceptions happening in conditional breakpoints.
664     return currentExceptionId_.fetch_add(1, std::memory_order_relaxed);
665 }
666 
GetDebuggableThread(PtThread thread)667 DebuggableThread *Inspector::GetDebuggableThread(PtThread thread)
668 {
669     auto it = threads_.find(thread);
670     return it != threads_.end() ? &it->second : nullptr;
671 }
672 
Disable(PtThread thread)673 void Inspector::Disable(PtThread thread)
674 {
675     os::memory::ReadLockHolder lock(vmDeathLock_);
676     if (UNLIKELY(CheckVmDead())) {
677         return;
678     }
679 
680     auto *debuggableThread = GetDebuggableThread(thread);
681     if (debuggableThread == nullptr) {
682         return;
683     }
684     debuggableThread->Reset();
685     debuggableThread->Continue();
686 }
687 
ClientDisconnect(PtThread thread)688 void Inspector::ClientDisconnect(PtThread thread)
689 {
690     (void)thread;
691 }
692 
SetAsyncCallStackDepth(PtThread thread)693 void Inspector::SetAsyncCallStackDepth(PtThread thread)
694 {
695     (void)thread;
696 }
697 
SetBlackboxPatterns(PtThread thread)698 void Inspector::SetBlackboxPatterns(PtThread thread)
699 {
700     (void)thread;
701 }
702 
SmartStepInto(PtThread thread)703 void Inspector::SmartStepInto(PtThread thread)
704 {
705     (void)thread;
706 }
707 
DropFrame(PtThread thread)708 void Inspector::DropFrame(PtThread thread)
709 {
710     (void)thread;
711 }
712 
SetNativeRange(PtThread thread)713 void Inspector::SetNativeRange(PtThread thread)
714 {
715     (void)thread;
716 }
717 
ReplyNativeCalling(PtThread thread)718 void Inspector::ReplyNativeCalling(PtThread thread)
719 {
720     Continue(thread);
721 }
722 
ProfilerSetSamplingInterval(int32_t interval)723 void Inspector::ProfilerSetSamplingInterval(int32_t interval)
724 {
725     os::memory::ReadLockHolder lock(vmDeathLock_);
726     if (UNLIKELY(CheckVmDead() || interval < 0)) {
727         return;
728     }
729     samplingInterval_ = static_cast<uint32_t>(interval);
730 }
731 
ProfilerStart()732 Expected<bool, std::string> Inspector::ProfilerStart()
733 {
734     os::memory::ReadLockHolder lock(vmDeathLock_);
735     if (UNLIKELY(CheckVmDead())) {
736         return Unexpected(std::string("Fatal, VM is dead"));
737     }
738     if (cpuProfilerStarted_) {
739         return Unexpected(std::string("Fatal, profiling operation is already running."));
740     }
741     cpuProfilerStarted_ = true;
742     profileInfoBuffer_ = std::make_shared<sampler::SamplesRecord>();
743     profileInfoBuffer_->SetThreadStartTime(sampler::Sampler::GetMicrosecondsTimeStamp());
744     Runtime::GetCurrent()->GetTools().StartSamplingProfiler(
745         std::make_unique<sampler::InspectorStreamWriter>(profileInfoBuffer_), samplingInterval_);
746     return true;
747 }
748 
ProfilerStop()749 Expected<Profile, std::string> Inspector::ProfilerStop()
750 {
751     os::memory::ReadLockHolder lock(vmDeathLock_);
752     if (UNLIKELY(CheckVmDead())) {
753         return Unexpected(std::string("Fatal, VM is dead"));
754     }
755 
756     if (!cpuProfilerStarted_) {
757         return Unexpected(std::string("Fatal, profiler inactive"));
758     }
759 
760     Runtime::GetCurrent()->GetTools().StopSamplingProfiler();
761     auto profileInfoPtr = profileInfoBuffer_->GetAllThreadsProfileInfos();
762     if (!profileInfoPtr) {
763         return Unexpected(std::string("Fatal, profiler info is empty"));
764     }
765     profileInfoBuffer_.reset();
766     cpuProfilerStarted_ = false;
767     return Profile(std::move(profileInfoPtr));
768 }
769 
DebuggerEnable()770 void Inspector::DebuggerEnable()
771 {
772     os::memory::WriteLockHolder lock(debuggerEventsLock_);
773     for (auto &[_, dbgThread] : threads_) {
774         (void)_;
775         dbgThread.Reset();
776     }
777     breakpointStorage_.Reset();
778 }
779 
RegisterMethodHandlers()780 void Inspector::RegisterMethodHandlers()
781 {
782     // NOLINTBEGIN(modernize-avoid-bind)
783     inspectorServer_.OnCallDebuggerContinueToLocation(std::bind(&Inspector::ContinueToLocation, this, _1, _2, _3));
784     inspectorServer_.OnCallDebuggerEnable(std::bind(&Inspector::DebuggerEnable, this));
785     inspectorServer_.OnCallDebuggerGetPossibleBreakpoints(
786         std::bind(&Inspector::GetPossibleBreakpoints, this, _1, _2, _3, _4));
787     inspectorServer_.OnCallDebuggerGetScriptSource(std::bind(&Inspector::GetSourceCode, this, _1));
788     inspectorServer_.OnCallDebuggerPause(std::bind(&Inspector::Pause, this, _1));
789     inspectorServer_.OnCallDebuggerRemoveBreakpoint(std::bind(&Inspector::RemoveBreakpoint, this, _1, _2));
790     inspectorServer_.OnCallDebuggerRemoveBreakpointsByUrl(std::bind(&Inspector::RemoveBreakpoints, this, _1, _2));
791     inspectorServer_.OnCallDebuggerRestartFrame(std::bind(&Inspector::RestartFrame, this, _1, _2));
792     inspectorServer_.OnCallDebuggerResume(std::bind(&Inspector::Continue, this, _1));
793     inspectorServer_.OnCallDebuggerSetAsyncCallStackDepth(std::bind(&Inspector::SetAsyncCallStackDepth, this, _1));
794     inspectorServer_.OnCallDebuggerSetBlackboxPatterns(std::bind(&Inspector::SetBlackboxPatterns, this, _1));
795     inspectorServer_.OnCallDebuggerSmartStepInto(std::bind(&Inspector::SmartStepInto, this, _1));
796     inspectorServer_.OnCallDebuggerSetBreakpoint(std::bind(&Inspector::SetBreakpoint, this, _1, _2, _3, _4, _5));
797     inspectorServer_.OnCallDebuggerSetBreakpointByUrl(std::bind(&Inspector::SetBreakpoint, this, _1, _2, _3, _4, _5));
798     inspectorServer_.OnCallDebuggerGetPossibleAndSetBreakpointByUrl(
799         std::bind(&Inspector::SetBreakpoint, this, _1, _2, _3, _4, _5));
800     inspectorServer_.OnCallDebuggerSetBreakpointsActive(std::bind(&Inspector::SetBreakpointsActive, this, _1, _2));
801     inspectorServer_.OnCallDebuggerSetSkipAllPauses(std::bind(&Inspector::SetSkipAllPauses, this, _1, _2));
802     inspectorServer_.OnCallDebuggerSetPauseOnExceptions(std::bind(&Inspector::SetPauseOnExceptions, this, _1, _2));
803     inspectorServer_.OnCallDebuggerStepInto(std::bind(&Inspector::StepInto, this, _1));
804     inspectorServer_.OnCallDebuggerStepOut(std::bind(&Inspector::StepOut, this, _1));
805     inspectorServer_.OnCallDebuggerStepOver(std::bind(&Inspector::StepOver, this, _1));
806     inspectorServer_.OnCallDebuggerEvaluateOnCallFrame(std::bind(&Inspector::Evaluate, this, _1, _2, _3));
807     inspectorServer_.OnCallDebuggerDisable(std::bind(&Inspector::Disable, this, _1));
808     inspectorServer_.OnCallDebuggerClientDisconnect(std::bind(&Inspector::ClientDisconnect, this, _1));
809     inspectorServer_.OnCallDebuggerDropFrame(std::bind(&Inspector::DropFrame, this, _1));
810     inspectorServer_.OnCallDebuggerSetNativeRange(std::bind(&Inspector::SetNativeRange, this, _1));
811     inspectorServer_.OnCallDebuggerReplyNativeCalling(std::bind(&Inspector::ReplyNativeCalling, this, _1));
812     inspectorServer_.OnCallDebuggerCallFunctionOn(std::bind(&Inspector::Evaluate, this, _1, _2, _3));
813     inspectorServer_.OnCallDebuggerSetMixedDebugEnabled(std::bind(&Inspector::SetMixedDebugEnabled, this, _1, _2));
814     inspectorServer_.OnCallRuntimeEnable(std::bind(&Inspector::RuntimeEnable, this, _1));
815     inspectorServer_.OnCallRuntimeGetProperties(std::bind(&Inspector::GetProperties, this, _1, _2, _3));
816     inspectorServer_.OnCallRuntimeRunIfWaitingForDebugger(std::bind(&Inspector::RunIfWaitingForDebugger, this, _1));
817     inspectorServer_.OnCallRuntimeEvaluate(std::bind(&Inspector::Evaluate, this, _1, _2, 0));
818     inspectorServer_.OnCallProfilerEnable();
819     inspectorServer_.OnCallProfilerDisable();
820     inspectorServer_.OnCallProfilerSetSamplingInterval(std::bind(&Inspector::ProfilerSetSamplingInterval, this, _1));
821     inspectorServer_.OnCallProfilerStart(std::bind(&Inspector::ProfilerStart, this));
822     inspectorServer_.OnCallProfilerStop(std::bind(&Inspector::ProfilerStop, this));
823     // NOLINTEND(modernize-avoid-bind)
824 }
825 }  // namespace ark::tooling::inspector
826