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