From 3239f3d368f85f921021efdd50039581da6a33de Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Thu, 24 Nov 2022 03:32:28 +0300 Subject: [PATCH 1/6] [PT][Inspector] Add support for pause-on-exception states This adds support for 3 pause-on-exceptions states, set by `Runtime.setPauseOnExceptions` call, in which the debugger pauses if an exception occurs, is caught, or becomes uncaught, respectively. In all of these cases the pause reason is augmented with the exception object, which is converted to RemoteObject with help of the new LanguageContext hook. Signed-off-by: Eugene Sharygin --- runtime/tooling/inspector/inspector.cpp | 202 +++++++++++------- runtime/tooling/inspector/inspector.h | 9 + .../tooling/inspector/inspector_server.cpp | 36 +++- runtime/tooling/inspector/inspector_server.h | 4 +- .../inspector/tests/inspector_test.cpp | 4 +- runtime/tooling/inspector/thread_state.cpp | 91 +++++++- runtime/tooling/inspector/thread_state.h | 38 +++- 7 files changed, 291 insertions(+), 93 deletions(-) diff --git a/runtime/tooling/inspector/inspector.cpp b/runtime/tooling/inspector/inspector.cpp index af9c49888..c0d7f9390 100644 --- a/runtime/tooling/inspector/inspector.cpp +++ b/runtime/tooling/inspector/inspector.cpp @@ -16,6 +16,8 @@ #include "inspector.h" #include "error.h" +#include "thread_state.h" +#include "types/numeric_id.h" #include "macros.h" #include "plugins.h" @@ -31,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +86,7 @@ Inspector::Inspector(Server &server, DebugInterface &debugger, std::unique_ptrGetClass()->GetSourceLang(); + auto extension = GetExtension(source_lang); + if (extension == nullptr) { + LOG(WARNING, DEBUGGER) << "Could not convert exception object, please define InspectorExtension for " + << plugins::LangToRuntimeType(source_lang); + return; + } + + FindThreadState( + thread, // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(&ThreadState::OnException, _1, std::ref(location), + extension->GetRemoteObject(thread.GetManagedThread(), object_repository_, exception_object)), + true); +} + +void Inspector::ExceptionCatch(PtThread thread, Method * /* catch_method */, const PtLocation &location, + ObjectHeader * /* exception_object */) { os::memory::ReadLockHolder lock(debugger_events_lock_); - auto it = states_.find(thread); - ASSERT(it != states_.end()); - it->second.OnFramePop(); + // NOLINTNEXTLINE(modernize-avoid-bind) + FindThreadState(thread, std::bind(&ThreadState::OnExceptionCatch, _1, std::ref(location)), true); +} + +void Inspector::FramePop(PtThread thread, Method * /* method */, bool /* was_popped_by_exception */) +{ + os::memory::ReadLockHolder lock(debugger_events_lock_); + FindThreadState(thread, &ThreadState::OnFramePop, true); } void Inspector::MethodEntry(PtThread thread, Method * /* method */) { os::memory::ReadLockHolder lock(debugger_events_lock_); - auto it = states_.find(thread); - ASSERT(it != states_.end()); - if (it->second.OnMethodEntry()) { - HandleError(debugger_.NotifyFramePop(thread, 0)); - } + FindThreadState( + thread, + [&](auto &state) { + if (state.OnMethodEntry()) { + HandleError(debugger_.NotifyFramePop(thread, 0)); + } + }, + true); } void Inspector::LoadModule(std::string_view file_name) @@ -159,9 +192,8 @@ void Inspector::SingleStep(PtThread thread, Method * /* method */, const PtLocat { os::memory::ReadLockHolder lock(debugger_events_lock_); - auto it = states_.find(thread); - ASSERT(it != states_.end()); - it->second.OnSingleStep(location); + // NOLINTNEXTLINE(modernize-avoid-bind) + FindThreadState(thread, std::bind(&ThreadState::OnSingleStep, _1, std::ref(location)), true); } void Inspector::ThreadStart(PtThread thread) @@ -175,31 +207,33 @@ void Inspector::ThreadStart(PtThread thread) auto [it, inserted] = states_.emplace( std::piecewise_construct, std::forward_as_tuple(thread), std::forward_as_tuple( - [this, thread](auto &hit_breakpoints) { + [this, thread](auto &hit_breakpoints, auto exception) { Suspend(thread); - inspector_server_.CallDebuggerPaused(thread, hit_breakpoints, [this, thread](auto &handler) { - std::deque scope_chain; - - auto res = HandleError(debugger_.EnumerateFrames(thread, [&](auto &frame) { - scope_chain.push_back(GetFrameObject(thread, frame)); - return true; - })); - if (!res) { - return; - } - - HandleError(debugger_.EnumerateFrames(thread, [this, &handler, &scope_chain](const PtFrame &frame) { - std::string_view source_file; - std::string_view method_name; - size_t line_number; - debug_info_cache_.GetSourceLocation(frame, source_file, method_name, line_number); - - handler(frame.GetFrameId(), method_name, source_file, line_number, scope_chain); - - scope_chain.pop_front(); - return true; - })); - }); + inspector_server_.CallDebuggerPaused( + thread, hit_breakpoints, std::move(exception), [this, thread](auto &handler) { + std::deque scope_chain; + + auto res = HandleError(debugger_.EnumerateFrames(thread, [&](auto &frame) { + scope_chain.push_back(GetFrameObject(thread, frame)); + return true; + })); + if (!res) { + return; + } + + HandleError( + debugger_.EnumerateFrames(thread, [this, &handler, &scope_chain](const PtFrame &frame) { + std::string_view source_file; + std::string_view method_name; + size_t line_number; + debug_info_cache_.GetSourceLocation(frame, source_file, method_name, line_number); + + handler(frame.GetFrameId(), method_name, source_file, line_number, scope_chain); + + scope_chain.pop_front(); + return true; + })); + }); }, [this, thread]() NO_THREAD_SAFETY_ANALYSIS { debugger_events_lock_.Unlock(); @@ -222,6 +256,8 @@ void Inspector::ThreadEnd(PtThread thread) { os::memory::ReadLockHolder lock(debugger_events_lock_); + FindThreadState(thread, &ThreadState::OnThreadEnd); + if (thread != PtThread::NONE) { inspector_server_.CallTargetDetachedFromTarget(thread); } @@ -229,6 +265,25 @@ void Inspector::ThreadEnd(PtThread thread) states_.erase(thread); } +void Inspector::VmDeath() +{ + os::memory::ReadLockHolder lock(debugger_events_lock_); + + for (auto &state : states_) { + state.second.OnThreadEnd(); + } + states_.clear(); +} + +void Inspector::FindThreadState(PtThread thread, const std::function &handler, bool assert_found) +{ + if (auto it = states_.find(thread); it != states_.end()) { + handler(it->second); + } else if (assert_found) { + UNREACHABLE(); + } +} + void Inspector::RuntimeEnable(PtThread thread) { inspector_server_.CallRuntimeExecutionContextCreated(thread); @@ -236,34 +291,23 @@ void Inspector::RuntimeEnable(PtThread thread) void Inspector::RunIfWaitingForDebugger(PtThread thread) { - auto it = states_.find(thread); - if (it != states_.end()) { - it->second.Touch(); - } + FindThreadState(thread, &ThreadState::Touch); } void Inspector::Pause(PtThread thread) { - auto it = states_.find(thread); - if (it != states_.end()) { - it->second.Pause(); - } + FindThreadState(thread, &ThreadState::Pause); } void Inspector::Continue(PtThread thread) { - auto it = states_.find(thread); - if (it != states_.end()) { - it->second.Continue(); - } + FindThreadState(thread, &ThreadState::Continue); } void Inspector::SetBreakpointsActive(PtThread thread, bool active) { - auto it = states_.find(thread); - if (it != states_.end()) { - it->second.SetBreakpointsActive(active); - } + // NOLINTNEXTLINE(modernize-avoid-bind) + FindThreadState(thread, std::bind(&ThreadState::SetBreakpointsActive, _1, active)); } std::set Inspector::GetPossibleBreakpoints(std::string_view source_file, size_t start_line, size_t end_line, @@ -276,20 +320,27 @@ std::optional Inspector::SetBreakpoint(PtThread thread, const std::function &source_files_filter, size_t line_number, std::set &source_files) { - if (auto it = states_.find(thread); it != states_.end()) { + std::optional breakpoint_id; + + FindThreadState(thread, [&](auto &state) { auto locations = debug_info_cache_.GetBreakpointLocations(source_files_filter, line_number, source_files); - return it->second.SetBreakpoint(locations); - } + breakpoint_id = state.SetBreakpoint(locations); + }); - return {}; + + return breakpoint_id; } void Inspector::RemoveBreakpoint(PtThread thread, BreakpointId id) { - auto it = states_.find(thread); - if (it != states_.end()) { - it->second.RemoveBreakpoint(id); - } + // NOLINTNEXTLINE(modernize-avoid-bind) + FindThreadState(thread, std::bind(&ThreadState::RemoveBreakpoint, _1, id)); +} + +void Inspector::SetPauseOnExceptions(PtThread thread, ThreadState::PauseOnExceptions state) +{ + // NOLINTNEXTLINE(modernize-avoid-bind) + FindThreadState(thread, std::bind(&ThreadState::SetPauseOnExceptions, _1, state)); } void Inspector::StepIntoOver(PtThread thread, ThreadState::StepKind step_kind) @@ -297,33 +348,30 @@ void Inspector::StepIntoOver(PtThread thread, ThreadState::StepKind step_kind) // NOLINTNEXTLINE(readability-simplify-boolean-expr) ASSERT(step_kind == ThreadState::StepKind::STEP_INTO || step_kind == ThreadState::StepKind::STEP_OVER); - auto it = states_.find(thread); - if (it != states_.end()) { - auto frame = debugger_.GetCurrentFrame(thread); - if (!frame) { - HandleError(frame.Error()); - return; - } - - it->second.StepIntoOver(step_kind, debug_info_cache_.GetCurrentLineLocations(*frame.Value())); + auto frame = debugger_.GetCurrentFrame(thread); + if (!frame) { + HandleError(frame.Error()); + return; } + + FindThreadState(thread, [&](auto &state) { + state.StepIntoOver(step_kind, debug_info_cache_.GetCurrentLineLocations(**frame)); + }); } void Inspector::StepOut(PtThread thread) { - auto it = states_.find(thread); - if (it != states_.end()) { + FindThreadState(thread, [&](auto &state) { HandleError(debugger_.NotifyFramePop(thread, 0)); - it->second.StepOut(); - } + state.StepOut(); + }); } void Inspector::ContinueToLocation(PtThread thread, std::string_view source_file, size_t line_number) { - auto it = states_.find(thread); - if (it != states_.end()) { - it->second.ContinueTo(debug_info_cache_.GetContinueToLocations(source_file, line_number)); - } + FindThreadState(thread, [&](auto &state) { + state.ContinueTo(debug_info_cache_.GetContinueToLocations(source_file, line_number)); + }); } InspectorExtension *Inspector::GetExtension(panda_file::SourceLang source_lang) diff --git a/runtime/tooling/inspector/inspector.h b/runtime/tooling/inspector/inspector.h index cf41a2932..e6fad6c81 100644 --- a/runtime/tooling/inspector/inspector.h +++ b/runtime/tooling/inspector/inspector.h @@ -58,14 +58,21 @@ public: void ConsoleCall(PtThread thread, ConsoleCallType type, uint64_t timestamp, const PandaVector &arguments) override; + void Exception(PtThread thread, Method *method, const PtLocation &location, ObjectHeader *exception_object, + Method *catch_method, const PtLocation &catch_location) override; + void ExceptionCatch(PtThread thread, Method *catch_method, const PtLocation &location, + ObjectHeader *exception_object) override; void FramePop(PtThread thread, Method *method, bool was_popped_by_exception) override; void MethodEntry(PtThread thread, Method *method) override; void LoadModule(std::string_view file_name) override; void SingleStep(PtThread thread, Method *method, const PtLocation &location) override; void ThreadStart(PtThread thread) override; void ThreadEnd(PtThread thread) override; + void VmDeath() override; private: + void FindThreadState(PtThread thread, const std::function &handler, bool assert_found = false); + void RuntimeEnable(PtThread thread); void RunIfWaitingForDebugger(PtThread thread); @@ -81,6 +88,8 @@ private: size_t line_number, std::set &source_files); void RemoveBreakpoint(PtThread thread, BreakpointId breakpoint_id); + void SetPauseOnExceptions(PtThread thread, ThreadState::PauseOnExceptions state); + void StepIntoOver(PtThread thread, ThreadState::StepKind step_kind); void StepOut(PtThread thread); void ContinueToLocation(PtThread thread, std::string_view source_file, size_t line_number); diff --git a/runtime/tooling/inspector/inspector_server.cpp b/runtime/tooling/inspector/inspector_server.cpp index bde6aa4ce..d9666d876 100644 --- a/runtime/tooling/inspector/inspector_server.cpp +++ b/runtime/tooling/inspector/inspector_server.cpp @@ -16,6 +16,7 @@ #include "inspector_server.h" #include "server.h" +#include "thread_state.h" #include "types/location.h" #include "types/numeric_id.h" @@ -86,7 +87,7 @@ void InspectorServer::OnFail(std::function &&handler) } void InspectorServer::CallDebuggerPaused( - PtThread thread, const std::vector &hit_breakpoints, + PtThread thread, const std::vector &hit_breakpoints, std::optional exception, const std::function &)> &)> &enumerate_frames) { @@ -126,7 +127,11 @@ void InspectorServer::CallDebuggerPaused( } }); - params.AddProperty("reason", "other"); + if (exception) { + params.AddProperty("data", exception->ToJson()); + } + + params.AddProperty("reason", exception ? "exception" : "other"); }); } @@ -429,6 +434,33 @@ void InspectorServer::OnCallDebuggerSetBreakpointsActive(std::function &&handler) +{ + server_.OnCall("Debugger.setPauseOnExceptions", + [this, handler = std::move(handler)](auto &session_id, auto &, const JsonObject ¶ms) { + auto state = params.GetValue("state"); + if (state == nullptr) { + LOG(INFO, DEBUGGER) << "No 'state' property"; + return; + } + + auto thread = session_manager_->GetThreadBySessionId(session_id); + + if (*state == "none") { + handler(thread, ThreadState::PauseOnExceptions::NONE); + } else if (*state == "caught") { + handler(thread, ThreadState::PauseOnExceptions::CAUGHT); + } else if (*state == "uncaught") { + handler(thread, ThreadState::PauseOnExceptions::UNCAUGHT); + } else if (*state == "all") { + handler(thread, ThreadState::PauseOnExceptions::ALL); + } else { + LOG(INFO, DEBUGGER) << "Invalid 'state' value: " << *state; + } + }); +} + void InspectorServer::OnCallDebuggerStepInto(std::function &&handler) { server_.OnCall("Debugger.stepInto", [this, handler = std::move(handler)](auto &session_id, auto &, auto &) { diff --git a/runtime/tooling/inspector/inspector_server.h b/runtime/tooling/inspector/inspector_server.h index 9d4a8cc8e..e9dc49a08 100644 --- a/runtime/tooling/inspector/inspector_server.h +++ b/runtime/tooling/inspector/inspector_server.h @@ -18,6 +18,7 @@ #include "session_manager.h" #include "source_manager.h" +#include "thread_state.h" #include "types/numeric_id.h" #include "console_call_type.h" @@ -54,7 +55,7 @@ public: void OnFail(std::function &&handler); void CallDebuggerPaused( - PtThread thread, const std::vector &hit_breakpoints, + PtThread thread, const std::vector &hit_breakpoints, std::optional exception, const std::function &)> &)> &enumerate_frames); void CallDebuggerResumed(PtThread thread); @@ -79,6 +80,7 @@ public: std::function(PtThread, const std::function &, size_t, std::set &)> &&handler); void OnCallDebuggerSetBreakpointsActive(std::function &&handler); + void OnCallDebuggerSetPauseOnExceptions(std::function &&handler); void OnCallDebuggerStepInto(std::function &&handler); void OnCallDebuggerStepOut(std::function &&handler); void OnCallDebuggerStepOver(std::function &&handler); diff --git a/runtime/tooling/inspector/tests/inspector_test.cpp b/runtime/tooling/inspector/tests/inspector_test.cpp index a56f77ab0..643bd638a 100644 --- a/runtime/tooling/inspector/tests/inspector_test.cpp +++ b/runtime/tooling/inspector/tests/inspector_test.cpp @@ -102,8 +102,8 @@ private: TEST_F(InspectorTest, InitialSequence) { - ExpectUnsupportedMethods("Profiler.enable", "Debugger.setPauseOnExceptions", "Debugger.setAsyncCallStackDepth", - "Runtime.getIsolateId", "Debugger.setBlackboxPatterns"); + ExpectUnsupportedMethods("Profiler.enable", "Debugger.setAsyncCallStackDepth", "Runtime.getIsolateId", + "Debugger.setBlackboxPatterns"); // NOLINTNEXTLINE(readability-isolate-declaration) MockFunction execution_context_created, runtime_enabled, debugger_enabled, debugger_paused, diff --git a/runtime/tooling/inspector/thread_state.cpp b/runtime/tooling/inspector/thread_state.cpp index 15c7047b4..a95aeb570 100644 --- a/runtime/tooling/inspector/thread_state.cpp +++ b/runtime/tooling/inspector/thread_state.cpp @@ -15,9 +15,18 @@ #include "thread_state.h" +#include "types/numeric_id.h" + +#include "macros.h" +#include "os/mutex.h" + +#include +#include +#include +#include + namespace panda::tooling::inspector { -ThreadState::ThreadState(std::function &)> &&suspend, - std::function &&wait_suspension, std::function &&resume) +ThreadState::ThreadState(SuspendFunction &&suspend, WaitSuspensionFunction &&wait_suspension, ResumeFunction &&resume) : suspend_(std::move(suspend)), wait_suspension_(std::move(wait_suspension)), resume_(std::move(resume)) { } @@ -33,6 +42,8 @@ void ThreadState::Reset() breakpoints_active_ = true; next_breakpoint_id_ = 0; breakpoint_locations_.clear(); + pause_on_exceptions_ = PauseOnExceptions::NONE; + exception_.reset(); } void ThreadState::BreakOnStart() @@ -137,6 +148,55 @@ void ThreadState::RemoveBreakpoint(BreakpointId id) } } +void ThreadState::SetPauseOnExceptions(PauseOnExceptions state) +{ + os::memory::LockHolder lock(mutex_); + pause_on_exceptions_ = state; +} + +void ThreadState::OnException(const PtLocation &location, RemoteObject object) +{ + os::memory::LockHolder lock(mutex_); + + if (pause_on_exceptions_ == PauseOnExceptions::NONE) { + return; + } + + // If there is an active exception, it has just become uncaught + if (exception_ && pause_on_exceptions_ == PauseOnExceptions::UNCAUGHT) { + paused_ = true; + WaitForUnpause(&location); + } + + exception_ = std::move(object); + + if (pause_on_exceptions_ == PauseOnExceptions::ALL) { + paused_ = true; + WaitForUnpause(&location); + } +} + +void ThreadState::OnExceptionCatch(const PtLocation &location) +{ + os::memory::LockHolder lock(mutex_); + + switch (pause_on_exceptions_) { + case PauseOnExceptions::NONE: + case PauseOnExceptions::ALL: + return; + + case PauseOnExceptions::CAUGHT: + ASSERT(exception_); + paused_ = true; + WaitForUnpause(&location); + break; + + case PauseOnExceptions::UNCAUGHT: + exception_.reset(); + break; + } +} + void ThreadState::OnFramePop() { os::memory::LockHolder lock(mutex_); @@ -225,13 +285,32 @@ void ThreadState::OnSingleStep(const PtLocation &location) paused_ = true; } + WaitForUnpause(&location); +} + +void ThreadState::OnThreadEnd() +{ + os::memory::LockHolder lock(mutex_); + + if (exception_ && pause_on_exceptions_ == PauseOnExceptions::UNCAUGHT) { + paused_ = true; + WaitForUnpause(nullptr); + } +} + +void ThreadState::WaitForUnpause(const PtLocation *location) +{ while (paused_) { std::vector hit_breakpoints; - auto range = breakpoint_locations_.equal_range(location); - std::transform(range.first, range.second, std::back_inserter(hit_breakpoints), - [](auto &p) { return p.second; }); - suspend_(hit_breakpoints); + if (location != nullptr) { + auto range = breakpoint_locations_.equal_range(*location); + std::transform(range.first, range.second, std::back_inserter(hit_breakpoints), + [](auto &p) { return p.second; }); + } + + suspend_(hit_breakpoints, std::move(exception_)); + exception_.reset(); mutex_.Unlock(); wait_suspension_(); diff --git a/runtime/tooling/inspector/thread_state.h b/runtime/tooling/inspector/thread_state.h index 78c8c8dac..0997000cc 100644 --- a/runtime/tooling/inspector/thread_state.h +++ b/runtime/tooling/inspector/thread_state.h @@ -18,8 +18,13 @@ #include "types/numeric_id.h" +#include "os/mutex.h" #include "tooling/debugger.h" +#include "tooling/inspector/remote_object.h" +#include "tooling/pt_location.h" +#include +#include #include #include @@ -51,8 +56,13 @@ public: STEP_OVER }; - ThreadState(std::function &)> &&suspend, - std::function &&wait_suspension, std::function &&resume); + enum class PauseOnExceptions { NONE, CAUGHT, UNCAUGHT, ALL }; + + using SuspendFunction = std::function &, std::optional)>; + using WaitSuspensionFunction = std::function; + using ResumeFunction = std::function; + + ThreadState(SuspendFunction &&suspend, WaitSuspensionFunction &&wait_suspension, ResumeFunction &&resume); ~ThreadState() = default; NO_COPY_SEMANTIC(ThreadState); @@ -98,12 +108,21 @@ public: // Removes the breakpoint by ID void RemoveBreakpoint(BreakpointId id); + // Sets the pause-on-exceptions state + void SetPauseOnExceptions(PauseOnExceptions state); + //////////////////////////////////////////////////////////////////////////// // // The following methods should be called on an application thread // //////////////////////////////////////////////////////////////////////////// + // Notification that an exception was created + void OnException(const PtLocation &location, RemoteObject object); + + // Notification that an exception was caught + void OnExceptionCatch(const PtLocation &location); + // Notification that an "interesting" frame was popped void OnFramePop(); @@ -114,17 +133,23 @@ public: // Notification that a next step will be performed. Pauses the thread if necessary void OnSingleStep(const PtLocation &location); + // Notification that the thread ended + void OnThreadEnd(); + private: + // Suspends the thread until it is unpaused + void WaitForUnpause(const PtLocation *location); + os::memory::Mutex mutex_; // Marks a paused thread as suspended. Should be called on an application thread - std::function &)> suspend_ GUARDED_BY(mutex_); + SuspendFunction suspend_ GUARDED_BY(mutex_); // Waits until the suspension of a paused thread ends. Should be called on an application thread - std::function wait_suspension_; + WaitSuspensionFunction wait_suspension_; // Marks a paused thread as not suspended. Should be called on the server thread - std::function resume_ GUARDED_BY(mutex_); + ResumeFunction resume_ GUARDED_BY(mutex_); StepKind step_kind_ GUARDED_BY(mutex_) {StepKind::NONE}; @@ -140,6 +165,9 @@ private: BreakpointId next_breakpoint_id_ GUARDED_BY(mutex_) {0}; std::unordered_multimap breakpoint_locations_ GUARDED_BY(mutex_); + PauseOnExceptions pause_on_exceptions_ GUARDED_BY(mutex_) {PauseOnExceptions::NONE}; + std::optional exception_ GUARDED_BY(mutex_); + bool paused_ GUARDED_BY(mutex_) {false}; }; } // namespace panda::tooling::inspector -- Gitee From 093eab583e130c5a94e1afc82fc57f0205666aa8 Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Tue, 29 Nov 2022 05:54:24 +0300 Subject: [PATCH 2/6] [PT][Inspector] Refactor - Factor out CallFrame and Scope protocol types into separate classes, and call stack construction into an Inspector member function. - Move SourceManager to Inspector. - Simplify interfaces of `DebugInfoCache::GetBreakpointLocations` and `Inspector::SetBreakpoint` by factoring out part of functionality into `DebugInfoCache::GetSourceFile`. Signed-off-by: Eugene Sharygin --- runtime/tooling/inspector/BUILD.gn | 2 + runtime/tooling/inspector/CMakeLists.txt | 2 + .../tooling/inspector/debug_info_cache.cpp | 45 +++-- runtime/tooling/inspector/debug_info_cache.h | 5 +- runtime/tooling/inspector/inspector.cpp | 157 ++++++++++------ runtime/tooling/inspector/inspector.h | 21 ++- .../tooling/inspector/inspector_server.cpp | 172 +++++++----------- runtime/tooling/inspector/inspector_server.h | 28 ++- .../tooling/inspector/types/call_frame.cpp | 38 ++++ runtime/tooling/inspector/types/call_frame.h | 62 +++++++ runtime/tooling/inspector/types/numeric_id.h | 1 + runtime/tooling/inspector/types/scope.cpp | 28 +++ runtime/tooling/inspector/types/scope.h | 49 +++++ 13 files changed, 409 insertions(+), 201 deletions(-) create mode 100644 runtime/tooling/inspector/types/call_frame.cpp create mode 100644 runtime/tooling/inspector/types/call_frame.h create mode 100644 runtime/tooling/inspector/types/scope.cpp create mode 100644 runtime/tooling/inspector/types/scope.h diff --git a/runtime/tooling/inspector/BUILD.gn b/runtime/tooling/inspector/BUILD.gn index f03a90b21..414ad20ae 100644 --- a/runtime/tooling/inspector/BUILD.gn +++ b/runtime/tooling/inspector/BUILD.gn @@ -25,8 +25,10 @@ libarkinspector_sources = [ "session_manager.cpp", "source_manager.cpp", "thread_state.cpp", + "types/call_frame.cpp", "types/location.cpp", "types/remote_object.cpp", + "types/scope.cpp", "ws_logger.cpp", ] diff --git a/runtime/tooling/inspector/CMakeLists.txt b/runtime/tooling/inspector/CMakeLists.txt index 7c812e097..3ee1c3b22 100644 --- a/runtime/tooling/inspector/CMakeLists.txt +++ b/runtime/tooling/inspector/CMakeLists.txt @@ -31,8 +31,10 @@ add_library(arkinspector SHARED session_manager.cpp source_manager.cpp thread_state.cpp + types/call_frame.cpp types/location.cpp types/remote_object.cpp + types/scope.cpp ws_logger.cpp ) diff --git a/runtime/tooling/inspector/debug_info_cache.cpp b/runtime/tooling/inspector/debug_info_cache.cpp index 4178e5ada..b0040e824 100644 --- a/runtime/tooling/inspector/debug_info_cache.cpp +++ b/runtime/tooling/inspector/debug_info_cache.cpp @@ -17,11 +17,14 @@ #include "debug_info_extractor.h" #include "disassembler/disasm_backed_debug_info_extractor.h" +#include "macros.h" #include "optimizer/ir_builder/inst_builder.h" #include "tooling/pt_location.h" #include "utils/logger.h" #include "utils/utf.h" +#include + namespace panda::tooling::inspector { DebugInfoCache::DebugInfoCache(bool back_debug_info_with_disasm) : back_debug_info_with_disasm_(back_debug_info_with_disasm) @@ -43,6 +46,20 @@ void DebugInfoCache::AddPandaFile(const panda_file::File &file) : std::make_unique(&file)); } +std::string_view DebugInfoCache::GetSourceFile(const PtLocation &location) +{ + std::string_view source_file; + EnumerateLineEntries([&](auto file, auto &) { return file->GetFilename() == location.GetPandaFile(); }, + [&](auto, auto &debug_info, auto method_id) { + if (method_id == location.GetMethodId()) { + source_file = debug_info->GetSourceFile(method_id); + } + return false; + }, + [](auto &&...) { return false; }); + return source_file; +} + void DebugInfoCache::GetSourceLocation(const PtFrame &frame, std::string_view &source_file, std::string_view &method_name, size_t &line_number) { @@ -127,24 +144,20 @@ std::unordered_set DebugInfoCache::GetContinueToLocati } std::vector DebugInfoCache::GetBreakpointLocations( - const std::function &source_file_filter, size_t line_number, - std::set &source_files) + const std::function &source_file_filter, size_t line_number) { std::vector locations; - source_files.clear(); - EnumerateLineEntries([](auto, auto &) { return true; }, - [&source_file_filter](auto, auto &debug_info, auto method_id) { - return source_file_filter(debug_info->GetSourceFile(method_id)); - }, - [line_number, &source_files, &locations](auto panda_file, auto &debug_info, auto method_id, - auto &entry, auto /* next */) { - if (entry.line == line_number) { - source_files.insert(debug_info->GetSourceFile(method_id)); - locations.emplace_back(panda_file->GetFilename().data(), method_id, entry.offset); - } - - return true; - }); + EnumerateLineEntries( + [](auto, auto &) { return true; }, + [&source_file_filter](auto, auto &debug_info, auto method_id) { + return source_file_filter(debug_info->GetSourceFile(method_id)); + }, + [line_number, &locations](auto panda_file, auto &, auto method_id, auto &entry, auto /* next */) { + if (entry.line == line_number) { + locations.emplace_back(panda_file->GetFilename().data(), method_id, entry.offset); + } + return true; + }); return locations; } diff --git a/runtime/tooling/inspector/debug_info_cache.h b/runtime/tooling/inspector/debug_info_cache.h index 0df02febe..f3e483b39 100644 --- a/runtime/tooling/inspector/debug_info_cache.h +++ b/runtime/tooling/inspector/debug_info_cache.h @@ -20,9 +20,11 @@ #include "method.h" #include "tooling/debugger.h" +#include "tooling/pt_location.h" #include #include +#include #include #include #include @@ -37,13 +39,14 @@ public: NO_MOVE_SEMANTIC(DebugInfoCache); void AddPandaFile(const panda_file::File &file); + std::string_view GetSourceFile(const PtLocation &location); void GetSourceLocation(const PtFrame &frame, std::string_view &source_file, std::string_view &method_name, size_t &line_number); std::unordered_set GetCurrentLineLocations(const PtFrame &frame); std::unordered_set GetContinueToLocations(std::string_view source_file, size_t line_number); std::vector GetBreakpointLocations(const std::function &source_file_filter, - size_t line_number, std::set &source_files); + size_t line_number); std::set GetValidLineNumbers(std::string_view source_file, size_t start_line, size_t end_line, bool restrict_to_function); diff --git a/runtime/tooling/inspector/inspector.cpp b/runtime/tooling/inspector/inspector.cpp index c0d7f9390..9baec419f 100644 --- a/runtime/tooling/inspector/inspector.cpp +++ b/runtime/tooling/inspector/inspector.cpp @@ -17,7 +17,10 @@ #include "error.h" #include "thread_state.h" +#include "types/call_frame.h" +#include "types/location.h" #include "types/numeric_id.h" +#include "types/scope.h" #include "macros.h" #include "plugins.h" @@ -30,11 +33,13 @@ #include #include -#include #include #include +#include #include #include +#include +#include #include #include @@ -61,7 +66,7 @@ Inspector::Inspector(Server &server, DebugInterface &debugger, std::unique_ptr scope_chain; - - auto res = HandleError(debugger_.EnumerateFrames(thread, [&](auto &frame) { - scope_chain.push_back(GetFrameObject(thread, frame)); - return true; - })); - if (!res) { - return; - } - - HandleError( - debugger_.EnumerateFrames(thread, [this, &handler, &scope_chain](const PtFrame &frame) { - std::string_view source_file; - std::string_view method_name; - size_t line_number; - debug_info_cache_.GetSourceLocation(frame, source_file, method_name, line_number); - - handler(frame.GetFrameId(), method_name, source_file, line_number, scope_chain); - - scope_chain.pop_front(); - return true; - })); - }); - }, - [this, thread]() NO_THREAD_SAFETY_ANALYSIS { - debugger_events_lock_.Unlock(); - WaitSuspension(thread); - debugger_events_lock_.ReadLock(); - }, - [this, thread]() { - Resume(thread); - inspector_server_.CallDebuggerResumed(thread); - })); + auto [it, inserted] = states_.try_emplace( + thread, + [this, thread](auto &hit_breakpoints, auto exception) { + Suspend(thread); + inspector_server_.CallDebuggerPaused(thread, hit_breakpoints, std::move(exception), GetCallStack(thread)); + }, + [this, thread]() NO_THREAD_SAFETY_ANALYSIS { + debugger_events_lock_.Unlock(); + WaitSuspension(thread); + debugger_events_lock_.ReadLock(); + }, + [this, thread]() { + Resume(thread); + inspector_server_.CallDebuggerResumed(thread); + }); (void)inserted; ASSERT(inserted); @@ -260,6 +240,7 @@ void Inspector::ThreadEnd(PtThread thread) if (thread != PtThread::NONE) { inspector_server_.CallTargetDetachedFromTarget(thread); + source_manager_.RemoveThread(thread); } states_.erase(thread); @@ -310,25 +291,35 @@ void Inspector::SetBreakpointsActive(PtThread thread, bool active) FindThreadState(thread, std::bind(&ThreadState::SetBreakpointsActive, _1, active)); } -std::set Inspector::GetPossibleBreakpoints(std::string_view source_file, size_t start_line, size_t end_line, +std::set Inspector::GetPossibleBreakpoints(ScriptId script_id, size_t start_line, size_t end_line, bool restrict_to_function) { + auto source_file = source_manager_.GetSourceFileName(script_id); return debug_info_cache_.GetValidLineNumbers(source_file, start_line, end_line, restrict_to_function); } -std::optional Inspector::SetBreakpoint(PtThread thread, - const std::function &source_files_filter, - size_t line_number, std::set &source_files) +std::optional>> Inspector::SetBreakpoint( + PtThread thread, const std::function &source_file_filter, size_t line_number) { - std::optional breakpoint_id; + std::optional>> result; FindThreadState(thread, [&](auto &state) { - auto locations = debug_info_cache_.GetBreakpointLocations(source_files_filter, line_number, source_files); - breakpoint_id = state.SetBreakpoint(locations); - }); + auto pt_locations = debug_info_cache_.GetBreakpointLocations( + [&](auto file_name) { return source_file_filter(GetScriptId(thread, file_name), file_name); }, line_number); + + result.emplace(); + result->first = state.SetBreakpoint(pt_locations); + + std::unordered_set script_ids; + for (auto &pt_location : pt_locations) { + script_ids.insert(GetScriptId(thread, debug_info_cache_.GetSourceFile(pt_location))); + } + std::transform(script_ids.begin(), script_ids.end(), std::back_inserter(result->second), + [&](auto script_id) { return Location(script_id, line_number); }); + }); - return breakpoint_id; + return result; } void Inspector::RemoveBreakpoint(PtThread thread, BreakpointId id) @@ -367,13 +358,52 @@ void Inspector::StepOut(PtThread thread) }); } -void Inspector::ContinueToLocation(PtThread thread, std::string_view source_file, size_t line_number) +void Inspector::ContinueToLocation(PtThread thread, const Location &location) { FindThreadState(thread, [&](auto &state) { - state.ContinueTo(debug_info_cache_.GetContinueToLocations(source_file, line_number)); + auto source_file = source_manager_.GetSourceFileName(location.GetScriptId()); + auto locations = debug_info_cache_.GetContinueToLocations(source_file, location.GetLineNumber()); + state.ContinueTo(std::move(locations)); }); } +std::shared_ptr Inspector::GetCallStack(PtThread thread) +{ + std::function(std::shared_ptr)> build_scope_chain = [](auto s) { return s; }; + if (!HandleError(debugger_.EnumerateFrames(thread, [&](auto &frame) { + auto object = GetFrameObject(thread, frame); + build_scope_chain = [build = std::move(build_scope_chain), object = std::move(object)](auto next) mutable { + return build(std::make_shared(std::move(object), std::move(next))); + }; + return true; + }))) { + return {}; + } + + auto scope_chain = build_scope_chain(nullptr); + + std::function(std::shared_ptr)> build_call_stack = [](auto s) { return s; }; + HandleError(debugger_.EnumerateFrames(thread, [&](auto &frame) { + std::string_view source_file; + std::string_view method_name; + size_t line_number; + debug_info_cache_.GetSourceLocation(frame, source_file, method_name, line_number); + + Location location(GetScriptId(thread, source_file), line_number); + + build_call_stack = [build = std::move(build_call_stack), id = frame.GetFrameId(), method_name, location, + source_file, scope_chain](auto next) mutable { + return build(std::make_shared(id, method_name, location, source_file, std::move(scope_chain), + std::move(next))); + }; + + scope_chain = scope_chain->GetNext(); + return true; + })); + + return build_call_stack(nullptr); +} + InspectorExtension *Inspector::GetExtension(panda_file::SourceLang source_lang) { auto &extension = extensions_[panda_file::GetLangArrIndex(source_lang)]; @@ -452,9 +482,20 @@ std::vector Inspector::GetProperties(RemoteObjectId object_i return *properties; } -std::string Inspector::GetSourceCode(std::string_view source_file) +ScriptId Inspector::GetScriptId(PtThread thread, std::string_view file_name) +{ + auto [script_id, is_new] = source_manager_.GetScriptId(thread, file_name); + + if (is_new) { + inspector_server_.CallDebuggerScriptParsed(thread, script_id, file_name); + } + + return script_id; +} + +std::string Inspector::GetSourceCode(ScriptId script_id) { - return debug_info_cache_.GetSourceCode(source_file); + return debug_info_cache_.GetSourceCode(source_manager_.GetSourceFileName(script_id)); } InspectorST::InspectorST(Server &server, DebugInterface &debugger) diff --git a/runtime/tooling/inspector/inspector.h b/runtime/tooling/inspector/inspector.h index e6fad6c81..27efbebf8 100644 --- a/runtime/tooling/inspector/inspector.h +++ b/runtime/tooling/inspector/inspector.h @@ -18,7 +18,9 @@ #include "debug_info_cache.h" #include "inspector_server.h" +#include "source_manager.h" #include "thread_state.h" +#include "types/location.h" #include "types/numeric_id.h" #include "source_lang_enum.h" @@ -38,14 +40,15 @@ #include #include #include +#include #include namespace panda::tooling { class DebugInterface; namespace inspector { -// NOLINTNEXTLINE(fuchsia-virtual-inheritance) -class Server; +class CallFrame; +class Server; // NOLINT(fuchsia-virtual-inheritance) class Inspector : public PtHooks { public: @@ -81,23 +84,24 @@ private: void Continue(PtThread thread); void SetBreakpointsActive(PtThread thread, bool active); - std::set GetPossibleBreakpoints(std::string_view source_file, size_t start_line, size_t end_line, + std::set GetPossibleBreakpoints(ScriptId script_id, size_t start_line, size_t end_line, bool restrict_to_function); - std::optional SetBreakpoint(PtThread thread, - const std::function &source_files_filter, - size_t line_number, std::set &source_files); + std::optional>> SetBreakpoint( + PtThread thread, const std::function &source_file_filter, size_t line_number); void RemoveBreakpoint(PtThread thread, BreakpointId breakpoint_id); void SetPauseOnExceptions(PtThread thread, ThreadState::PauseOnExceptions state); void StepIntoOver(PtThread thread, ThreadState::StepKind step_kind); void StepOut(PtThread thread); - void ContinueToLocation(PtThread thread, std::string_view source_file, size_t line_number); + void ContinueToLocation(PtThread thread, const Location &location); + std::shared_ptr GetCallStack(PtThread thread); InspectorExtension *GetExtension(panda_file::SourceLang source_lang); RemoteObject GetFrameObject(PtThread thread, const PtFrame &frame); std::vector GetProperties(RemoteObjectId object_id, bool generate_preview); - std::string GetSourceCode(std::string_view source_file); + ScriptId GetScriptId(PtThread thread, std::string_view file_name); + std::string GetSourceCode(ScriptId script_id); virtual void Suspend(PtThread thread) = 0; virtual void WaitSuspension(PtThread thread) = 0; @@ -115,6 +119,7 @@ private: DebugInterface &debugger_; DebugInfoCache debug_info_cache_; std::map states_; + SourceManager source_manager_; ObjectRepository object_repository_; std::array>, panda_file::LANG_COUNT> extensions_; diff --git a/runtime/tooling/inspector/inspector_server.cpp b/runtime/tooling/inspector/inspector_server.cpp index d9666d876..97bebcd1b 100644 --- a/runtime/tooling/inspector/inspector_server.cpp +++ b/runtime/tooling/inspector/inspector_server.cpp @@ -17,6 +17,7 @@ #include "server.h" #include "thread_state.h" +#include "types/call_frame.h" #include "types/location.h" #include "types/numeric_id.h" @@ -28,9 +29,11 @@ #include "utils/json_parser.h" #include "utils/logger.h" +#include #include #include #include +#include #include namespace panda::tooling::inspector { @@ -66,8 +69,7 @@ void InspectorServer::OnOpen(std::function &&handler) { server_.OnOpen([this, handler = std::move(handler)]() { // A new connection is open, reinitialize the state - session_manager_->EnumerateSessions([this](auto &id, auto thread) { - source_manager_.RemoveThread(thread); + session_manager_->EnumerateSessions([this](auto &id, auto) { if (!id.empty()) { SendTargetAttachedToTarget(id); } @@ -86,39 +88,17 @@ void InspectorServer::OnFail(std::function &&handler) }); } -void InspectorServer::CallDebuggerPaused( - PtThread thread, const std::vector &hit_breakpoints, std::optional exception, - const std::function &)> &)> &enumerate_frames) +void InspectorServer::CallDebuggerPaused(PtThread thread, const std::vector &hit_breakpoints, + std::optional exception, + const std::shared_ptr &call_stack) { auto session_id = session_manager_->GetSessionIdByThread(thread); server_.Call(session_id, "Debugger.paused", [&](auto ¶ms) { - params.AddProperty("callFrames", [this, thread, &enumerate_frames](JsonArrayBuilder &call_frames) { - enumerate_frames([this, thread, &call_frames](auto frame_id, auto method_name, auto source_file, - auto line_number, auto &scope_chain) { - call_frames.Add([&](JsonObjectBuilder &call_frame) { - auto [script_id, is_new] = source_manager_.GetScriptId(thread, source_file); - - if (is_new) { - CallDebuggerScriptParsed(thread, script_id, source_file); - } - - call_frame.AddProperty("callFrameId", std::to_string(frame_id)); - call_frame.AddProperty("functionName", method_name.data()); - call_frame.AddProperty("location", Location(script_id, line_number).ToJson()); - call_frame.AddProperty("url", source_file.data()); - - call_frame.AddProperty("scopeChain", [&](JsonArrayBuilder &scope_chain_builder) { - for (auto &scope : scope_chain) { - scope_chain_builder.Add([&](JsonObjectBuilder &scope_builder) { - scope_builder.AddProperty("type", "local"); - scope_builder.AddProperty("object", scope.ToJson()); - }); - } - }); - }); - }); + params.AddProperty("callFrames", [&](JsonArrayBuilder &call_stack_builder) { + for (auto call_frame = call_stack; call_frame; call_frame = call_frame->GetNext()) { + call_stack_builder.Add(call_frame->ToJson()); + } }); params.AddProperty("hitBreakpoints", [&hit_breakpoints](JsonArrayBuilder &hit_breakpoints_builder) { @@ -226,7 +206,6 @@ void InspectorServer::CallTargetDetachedFromTarget(PtThread thread) server_.Pause(); session_manager_->RemoveSession(session_id); - source_manager_.RemoveThread(thread); // Now no one will retrieve the detached thread from the sessions manager server_.Continue(); @@ -237,28 +216,25 @@ void InspectorServer::CallTargetDetachedFromTarget(PtThread thread) } } -void InspectorServer::OnCallDebuggerContinueToLocation( - std::function &&handler) +void InspectorServer::OnCallDebuggerContinueToLocation(std::function &&handler) { - server_.OnCall( - "Debugger.continueToLocation", [this, handler = std::move(handler)](auto &session_id, auto &, auto ¶ms) { - auto location = Location::FromJsonProperty(params, "location"); - if (!location) { - LOG(INFO, DEBUGGER) << location.Error(); - return; - } - - auto thread = session_manager_->GetThreadBySessionId(session_id); + server_.OnCall("Debugger.continueToLocation", + [this, handler = std::move(handler)](auto &session_id, auto &, auto ¶ms) { + auto location = Location::FromJsonProperty(params, "location"); + if (!location) { + LOG(INFO, DEBUGGER) << location.Error(); + return; + } - handler(thread, source_manager_.GetSourceFileName(location->GetScriptId()), location->GetLineNumber()); - }); + handler(session_manager_->GetThreadBySessionId(session_id), *location); + }); } void InspectorServer::OnCallDebuggerGetPossibleBreakpoints( - std::function(std::string_view, size_t, size_t, bool)> &&handler) + std::function(ScriptId, size_t, size_t, bool)> &&handler) { server_.OnCall("Debugger.getPossibleBreakpoints", - [this, handler = std::move(handler)](auto &, auto &result, const JsonObject ¶ms) { + [handler = std::move(handler)](auto &, auto &result, const JsonObject ¶ms) { auto start = Location::FromJsonProperty(params, "start"); if (!start) { LOG(INFO, DEBUGGER) << start.Error(); @@ -282,8 +258,7 @@ void InspectorServer::OnCallDebuggerGetPossibleBreakpoints( restrict_to_function = *prop; } - auto line_numbers = handler(source_manager_.GetSourceFileName(script_id), start->GetLineNumber(), - end_line, restrict_to_function); + auto line_numbers = handler(script_id, start->GetLineNumber(), end_line, restrict_to_function); result.AddProperty("locations", [script_id, &line_numbers](JsonArrayBuilder &array) { for (auto line_number : line_numbers) { @@ -293,17 +268,15 @@ void InspectorServer::OnCallDebuggerGetPossibleBreakpoints( }); } -void InspectorServer::OnCallDebuggerGetScriptSource(std::function &&handler) +void InspectorServer::OnCallDebuggerGetScriptSource(std::function &&handler) { - server_.OnCall("Debugger.getScriptSource", - [this, handler = std::move(handler)](auto &, auto &result, auto ¶ms) { - if (auto script_id = ParseNumericId(params, "scriptId")) { - auto source_file = source_manager_.GetSourceFileName(*script_id); - result.AddProperty("scriptSource", handler(source_file)); - } else { - LOG(INFO, DEBUGGER) << script_id.Error(); - } - }); + server_.OnCall("Debugger.getScriptSource", [handler = std::move(handler)](auto &, auto &result, auto ¶ms) { + if (auto script_id = ParseNumericId(params, "scriptId")) { + result.AddProperty("scriptSource", handler(*script_id)); + } else { + LOG(INFO, DEBUGGER) << script_id.Error(); + } + }); } void InspectorServer::OnCallDebuggerPause(std::function &&handler) @@ -335,38 +308,36 @@ void InspectorServer::OnCallDebuggerResume(std::function &&handl } void InspectorServer::OnCallDebuggerSetBreakpoint( - std::function(PtThread, const std::function &, size_t, - std::set &)> &&handler) + std::function>>( + PtThread, const std::function &, size_t)> &&handler) { - server_.OnCall("Debugger.setBreakpoint", - [this, handler = std::move(handler)](auto &session_id, auto &result, auto ¶ms) { - auto location = Location::FromJsonProperty(params, "location"); - if (!location) { - LOG(INFO, DEBUGGER) << location.Error(); - return; - } - - auto thread = session_manager_->GetThreadBySessionId(session_id); + server_.OnCall( + "Debugger.setBreakpoint", [this, handler = std::move(handler)](auto &session_id, auto &result, auto ¶ms) { + auto location = Location::FromJsonProperty(params, "location"); + if (!location) { + LOG(INFO, DEBUGGER) << location.Error(); + return; + } - auto source_file = source_manager_.GetSourceFileName(location->GetScriptId()); - std::set source_files; + auto thread = session_manager_->GetThreadBySessionId(session_id); + auto source_file_filter = [&](auto script_id, auto) { return script_id == location->GetScriptId(); }; - auto id = handler( - thread, [source_file](auto file_name) { return file_name == source_file; }, - location->GetLineNumber(), source_files); - if (!id) { - LOG(INFO, DEBUGGER) << "Failed to set breakpoint"; - return; - } + BreakpointId id; + if (auto breakpoint = handler(thread, source_file_filter, location->GetLineNumber())) { + id = breakpoint->first; + } else { + LOG(INFO, DEBUGGER) << "Failed to set breakpoint"; + return; + } - result.AddProperty("breakpointId", std::to_string(*id)); - result.AddProperty("actualLocation", location->ToJson()); - }); + result.AddProperty("breakpointId", std::to_string(id)); + result.AddProperty("actualLocation", location->ToJson()); + }); } void InspectorServer::OnCallDebuggerSetBreakpointByUrl( - std::function(PtThread, const std::function &, size_t, - std::set &)> &&handler) + std::function>>( + PtThread, const std::function &, size_t)> &&handler) { server_.OnCall("Debugger.setBreakpointByUrl", [this, handler = std::move(handler)](auto &session_id, auto &result, const JsonObject ¶ms) { @@ -378,12 +349,16 @@ void InspectorServer::OnCallDebuggerSetBreakpointByUrl( return; } - std::function source_file_filter; + std::function source_file_filter; if (auto url = params.GetValue("url")) { - source_file_filter = [source_file = url->find("file://") == 0 ? url->substr(std::strlen("file://")) : *url]( - auto file_name) { return file_name == source_file; }; + std::string_view source_file(*url); + if (url->find("file://") == 0) { + source_file = source_file.substr(std::strlen("file://")); + } + + source_file_filter = [source_file](auto, auto file_name) { return file_name == source_file; }; } else if (auto url_regex = params.GetValue("urlRegex")) { - source_file_filter = [regex = std::regex(*url_regex)](auto file_name) { + source_file_filter = [regex = std::regex(*url_regex)](auto, auto file_name) { return std::regex_match(file_name.data(), regex); }; } else { @@ -391,27 +366,18 @@ void InspectorServer::OnCallDebuggerSetBreakpointByUrl( return; } - std::set source_files; auto thread = session_manager_->GetThreadBySessionId(session_id); - auto id = handler(thread, source_file_filter, line_number, source_files); - if (!id) { + auto breakpoint = handler(thread, source_file_filter, line_number); + if (!breakpoint) { LOG(INFO, DEBUGGER) << "Failed to set breakpoint"; return; } - result.AddProperty("breakpointId", std::to_string(*id)); - result.AddProperty("locations", [this, line_number, &source_files, thread](JsonArrayBuilder &locations) { - for (auto source_file : source_files) { - locations.Add([this, line_number, thread, source_file](JsonObjectBuilder &location) { - auto [script_id, is_new] = source_manager_.GetScriptId(thread, source_file); - - if (is_new) { - CallDebuggerScriptParsed(thread, script_id, source_file); - } - - Location(script_id, line_number).ToJson()(location); - }); + result.AddProperty("breakpointId", std::to_string(breakpoint->first)); + result.AddProperty("locations", [&](JsonArrayBuilder &locations) { + for (auto &location : breakpoint->second) { + locations.Add(location.ToJson()); } }); }); diff --git a/runtime/tooling/inspector/inspector_server.h b/runtime/tooling/inspector/inspector_server.h index e9dc49a08..7932ff5e1 100644 --- a/runtime/tooling/inspector/inspector_server.h +++ b/runtime/tooling/inspector/inspector_server.h @@ -17,8 +17,8 @@ #define PANDA_TOOLING_INSPECTOR_INSPECTOR_SERVER_H #include "session_manager.h" -#include "source_manager.h" #include "thread_state.h" +#include "types/location.h" #include "types/numeric_id.h" #include "console_call_type.h" @@ -28,15 +28,16 @@ #include "tooling/pt_thread.h" #include -#include -#include #include +#include #include #include #include +#include #include namespace panda::tooling::inspector { +class CallFrame; class Server; // NOLINT(fuchsia-virtual-inheritance) class InspectorServer final { @@ -54,10 +55,8 @@ public: void OnOpen(std::function &&handler); void OnFail(std::function &&handler); - void CallDebuggerPaused( - PtThread thread, const std::vector &hit_breakpoints, std::optional exception, - const std::function &)> &)> &enumerate_frames); + void CallDebuggerPaused(PtThread thread, const std::vector &hit_breakpoints, + std::optional exception, const std::shared_ptr &call_stack); void CallDebuggerResumed(PtThread thread); void CallDebuggerScriptParsed(PtThread thread, ScriptId id, std::string_view source_file); void CallRuntimeConsoleApiCalled(PtThread thread, ConsoleCallType type, uint64_t timestamp, @@ -66,19 +65,19 @@ public: void CallTargetAttachedToTarget(PtThread thread); void CallTargetDetachedFromTarget(PtThread thread); - void OnCallDebuggerContinueToLocation(std::function &&handler); + void OnCallDebuggerContinueToLocation(std::function &&handler); void OnCallDebuggerGetPossibleBreakpoints( - std::function(std::string_view, size_t, size_t, bool)> &&handler); - void OnCallDebuggerGetScriptSource(std::function &&handler); + std::function(ScriptId, size_t, size_t, bool)> &&handler); + void OnCallDebuggerGetScriptSource(std::function &&handler); void OnCallDebuggerPause(std::function &&handler); void OnCallDebuggerRemoveBreakpoint(std::function &&handler); void OnCallDebuggerResume(std::function &&handler); void OnCallDebuggerSetBreakpoint( - std::function(PtThread, const std::function &, size_t, - std::set &)> &&handler); + std::function>>( + PtThread, const std::function &, size_t)> &&handler); void OnCallDebuggerSetBreakpointByUrl( - std::function(PtThread, const std::function &, size_t, - std::set &)> &&handler); + std::function>>( + PtThread, const std::function &, size_t)> &&handler); void OnCallDebuggerSetBreakpointsActive(std::function &&handler); void OnCallDebuggerSetPauseOnExceptions(std::function &&handler); void OnCallDebuggerStepInto(std::function &&handler); @@ -94,7 +93,6 @@ private: Server &server_; std::unique_ptr session_manager_; - SourceManager source_manager_; }; } // namespace panda::tooling::inspector diff --git a/runtime/tooling/inspector/types/call_frame.cpp b/runtime/tooling/inspector/types/call_frame.cpp new file mode 100644 index 000000000..6ef0a7abc --- /dev/null +++ b/runtime/tooling/inspector/types/call_frame.cpp @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "call_frame.h" + +#include "utils/json_builder.h" + +#include + +namespace panda::tooling::inspector { +std::function CallFrame::ToJson() const +{ + return [this](auto &json_builder) { + json_builder.AddProperty("callFrameId", std::to_string(id_)); + json_builder.AddProperty("functionName", function_name_); + json_builder.AddProperty("location", location_.ToJson()); + json_builder.AddProperty("url", url_); + + json_builder.AddProperty("scopeChain", [&](JsonArrayBuilder &scope_chain_builder) { + for (auto scope = scope_chain_; scope; scope = scope->GetNext()) { + scope_chain_builder.Add(scope->ToJson()); + } + }); + }; +} +} // namespace panda::tooling::inspector diff --git a/runtime/tooling/inspector/types/call_frame.h b/runtime/tooling/inspector/types/call_frame.h new file mode 100644 index 000000000..36a1e2d74 --- /dev/null +++ b/runtime/tooling/inspector/types/call_frame.h @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PANDA_TOOLING_INSPECTOR_TYPES_CALL_FRAME_H +#define PANDA_TOOLING_INSPECTOR_TYPES_CALL_FRAME_H + +#include "location.h" +#include "numeric_id.h" +#include "scope.h" + +#include +#include +#include +#include + +namespace panda { +class JsonObjectBuilder; +} // namespace panda + +namespace panda::tooling::inspector { +class CallFrame { +public: + CallFrame(CallFrameId id, std::string_view function_name, Location location, std::string_view url, + std::shared_ptr scope_chain, std::shared_ptr next) + : id_(id), + function_name_(function_name), + location_(location), + url_(url), + scope_chain_(std::move(scope_chain)), + next_(std::move(next)) + { + } + + std::shared_ptr GetNext() const + { + return next_; + } + + std::function ToJson() const; + +private: + CallFrameId id_; + std::string_view function_name_; + Location location_; + std::string_view url_; + std::shared_ptr scope_chain_; + std::shared_ptr next_; +}; +} // namespace panda::tooling::inspector + +#endif // PANDA_TOOLING_INSPECTOR_TYPES_CALL_FRAME_H diff --git a/runtime/tooling/inspector/types/numeric_id.h b/runtime/tooling/inspector/types/numeric_id.h index 6dc4826e7..74eb3352e 100644 --- a/runtime/tooling/inspector/types/numeric_id.h +++ b/runtime/tooling/inspector/types/numeric_id.h @@ -28,6 +28,7 @@ namespace panda::tooling::inspector { using BreakpointId = size_t; +using CallFrameId = uint32_t; using FrameId = uint32_t; using ScriptId = size_t; diff --git a/runtime/tooling/inspector/types/scope.cpp b/runtime/tooling/inspector/types/scope.cpp new file mode 100644 index 000000000..4afcb9cd9 --- /dev/null +++ b/runtime/tooling/inspector/types/scope.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "scope.h" + +#include "utils/json_builder.h" + +namespace panda::tooling::inspector { +std::function Scope::ToJson() const +{ + return [this](auto &json_builder) { + json_builder.AddProperty("type", "local"); + json_builder.AddProperty("object", object_.ToJson()); + }; +} +} // namespace panda::tooling::inspector diff --git a/runtime/tooling/inspector/types/scope.h b/runtime/tooling/inspector/types/scope.h new file mode 100644 index 000000000..3cc2ac638 --- /dev/null +++ b/runtime/tooling/inspector/types/scope.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PANDA_TOOLING_INSPECTOR_TYPES_SCOPE_H +#define PANDA_TOOLING_INSPECTOR_TYPES_SCOPE_H + +#include "tooling/inspector/remote_object.h" + +#include +#include +#include + +namespace panda { +class JsonObjectBuilder; +} // namespace panda + +namespace panda::tooling::inspector { +class Scope { +public: + explicit Scope(RemoteObject object, std::shared_ptr next = nullptr) + : object_(std::move(object)), next_(std::move(next)) + { + } + + std::shared_ptr GetNext() const + { + return next_; + } + + std::function ToJson() const; + +private: + RemoteObject object_; + std::shared_ptr next_; +}; +} // namespace panda::tooling::inspector + +#endif // PANDA_TOOLING_INSPECTOR_TYPES_SCOPE_H -- Gitee From 2756297366aa4c8468ae7f9ca0d08e6cd1d9ed65 Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Tue, 29 Nov 2022 06:27:16 +0300 Subject: [PATCH 3/6] [PT][Inspector] Eliminate spurious "Sending" log messages Signed-off-by: Eugene Sharygin --- runtime/tooling/inspector/endpoint.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/runtime/tooling/inspector/endpoint.h b/runtime/tooling/inspector/endpoint.h index 15cacc169..c1c064f30 100644 --- a/runtime/tooling/inspector/endpoint.h +++ b/runtime/tooling/inspector/endpoint.h @@ -128,13 +128,15 @@ private: template void Send(BuildFunction &&build) { + if (!connection_) { + return; + } + JsonObjectBuilder builder; build(builder); auto message = std::move(builder).Build(); LOG(DEBUG, DEBUGGER) << "Sending " << message; - if (auto connection = GetPinnedConnection()) { - connection->send(message, websocketpp::frame::opcode::text); - } + connection_->send(message, websocketpp::frame::opcode::text); } typename WsEndpoint::connection_ptr connection_; -- Gitee From a6b391085c4e4dcd37311781a886aa73c5f27922 Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Tue, 29 Nov 2022 09:47:09 +0300 Subject: [PATCH 4/6] [PT][Inspector] Send call stacks when paused on uncaught exceptions This makes the inspector record the call stack with each created exception and send it on pause when the exception becomes uncaught. This may be necessary to display the pause and the exception object on the client. Signed-off-by: Eugene Sharygin --- runtime/tooling/inspector/BUILD.gn | 1 + runtime/tooling/inspector/CMakeLists.txt | 1 + runtime/tooling/inspector/inspector.cpp | 48 +++++++++++++++---- runtime/tooling/inspector/inspector.h | 3 ++ .../tooling/inspector/inspector_server.cpp | 26 +++++----- runtime/tooling/inspector/inspector_server.h | 4 +- .../inspector/tests/inspector_test.cpp | 2 + .../inspector/tests/inspector_test_base.cpp | 1 + runtime/tooling/inspector/thread_state.cpp | 15 ++++-- runtime/tooling/inspector/thread_state.h | 14 +++++- .../types/execution_context_description.cpp | 39 +++++++++++++++ .../types/execution_context_description.h | 47 ++++++++++++++++++ runtime/tooling/inspector/types/numeric_id.h | 1 + 13 files changed, 173 insertions(+), 29 deletions(-) create mode 100644 runtime/tooling/inspector/types/execution_context_description.cpp create mode 100644 runtime/tooling/inspector/types/execution_context_description.h diff --git a/runtime/tooling/inspector/BUILD.gn b/runtime/tooling/inspector/BUILD.gn index 414ad20ae..602d58773 100644 --- a/runtime/tooling/inspector/BUILD.gn +++ b/runtime/tooling/inspector/BUILD.gn @@ -26,6 +26,7 @@ libarkinspector_sources = [ "source_manager.cpp", "thread_state.cpp", "types/call_frame.cpp", + "types/execution_context_description.cpp", "types/location.cpp", "types/remote_object.cpp", "types/scope.cpp", diff --git a/runtime/tooling/inspector/CMakeLists.txt b/runtime/tooling/inspector/CMakeLists.txt index 3ee1c3b22..b26c7a957 100644 --- a/runtime/tooling/inspector/CMakeLists.txt +++ b/runtime/tooling/inspector/CMakeLists.txt @@ -32,6 +32,7 @@ add_library(arkinspector SHARED source_manager.cpp thread_state.cpp types/call_frame.cpp + types/execution_context_description.cpp types/location.cpp types/remote_object.cpp types/scope.cpp diff --git a/runtime/tooling/inspector/inspector.cpp b/runtime/tooling/inspector/inspector.cpp index 9baec419f..39ac314f2 100644 --- a/runtime/tooling/inspector/inspector.cpp +++ b/runtime/tooling/inspector/inspector.cpp @@ -23,11 +23,13 @@ #include "types/scope.h" #include "macros.h" +#include "managed_thread.h" #include "plugins.h" #include "runtime.h" #include "source_lang_enum.h" #include "thread.h" #include "tooling/inspector/remote_object.h" +#include "tooling/pt_thread.h" #include "tooling/vreg_value.h" #include "utils/logger.h" @@ -210,10 +212,10 @@ void Inspector::ThreadStart(PtThread thread) } auto [it, inserted] = states_.try_emplace( - thread, - [this, thread](auto &hit_breakpoints, auto exception) { + thread, std::bind(&Inspector::GetCallStack, this, thread), // NOLINT(modernize-avoid-bind) + [this, thread](auto &call_stack, auto &hit_breakpoints, auto exception) { Suspend(thread); - inspector_server_.CallDebuggerPaused(thread, hit_breakpoints, std::move(exception), GetCallStack(thread)); + inspector_server_.CallDebuggerPaused(thread, hit_breakpoints, std::move(exception), call_stack); }, [this, thread]() NO_THREAD_SAFETY_ANALYSIS { debugger_events_lock_.Unlock(); @@ -246,10 +248,20 @@ void Inspector::ThreadEnd(PtThread thread) states_.erase(thread); } +void Inspector::VmInitialization(PtThread thread) +{ + ASSERT(!execution_context_); + execution_context_.emplace(thread); +} + void Inspector::VmDeath() { os::memory::ReadLockHolder lock(debugger_events_lock_); + ASSERT(execution_context_); + inspector_server_.CallRuntimeExecutionContextDestroyed(PtThread(ManagedThread::GetCurrent()), *execution_context_); + execution_context_.reset(); + for (auto &state : states_) { state.second.OnThreadEnd(); } @@ -267,7 +279,8 @@ void Inspector::FindThreadState(PtThread thread, const std::function states_; SourceManager source_manager_; ObjectRepository object_repository_; + std::optional execution_context_; std::array>, panda_file::LANG_COUNT> extensions_; }; diff --git a/runtime/tooling/inspector/inspector_server.cpp b/runtime/tooling/inspector/inspector_server.cpp index 97bebcd1b..f1a703b4f 100644 --- a/runtime/tooling/inspector/inspector_server.cpp +++ b/runtime/tooling/inspector/inspector_server.cpp @@ -18,13 +18,13 @@ #include "server.h" #include "thread_state.h" #include "types/call_frame.h" +#include "types/execution_context_description.h" #include "types/location.h" #include "types/numeric_id.h" #include "console_call_type.h" #include "macros.h" #include "tooling/inspector/remote_object_id.h" -#include "tooling/pt_thread.h" #include "utils/json_builder.h" #include "utils/json_parser.h" #include "utils/logger.h" @@ -172,22 +172,22 @@ void InspectorServer::CallRuntimeConsoleApiCalled(PtThread thread, ConsoleCallTy }); } -void InspectorServer::CallRuntimeExecutionContextCreated(PtThread thread) +void InspectorServer::CallRuntimeExecutionContextCreated(PtThread thread, + const ExecutionContextDescription &execution_context) { auto session_id = session_manager_->GetSessionIdByThread(thread); - std::string name; - if (thread != PtThread::NONE) { - name = "Thread #" + std::to_string(thread.GetId()); - } + server_.Call(session_id, "Runtime.executionContextCreated", + [&](auto ¶ms) { params.AddProperty("context", execution_context.ToJson()); }); +} - server_.Call(session_id, "Runtime.executionContextCreated", [&](auto ¶ms) { - params.AddProperty("context", [&](JsonObjectBuilder &context) { - context.AddProperty("id", thread.GetId()); - context.AddProperty("origin", ""); - context.AddProperty("name", name); - }); - }); +void InspectorServer::CallRuntimeExecutionContextDestroyed(PtThread thread, + const ExecutionContextDescription &execution_context) +{ + auto session_id = session_manager_->GetSessionIdByThread(thread); + + server_.Call(session_id, "Runtime.executionContextDestroyed", + [&](auto ¶ms) { params.AddProperty("executionContextId", execution_context.GetId()); }); } void InspectorServer::CallTargetAttachedToTarget(PtThread thread) diff --git a/runtime/tooling/inspector/inspector_server.h b/runtime/tooling/inspector/inspector_server.h index 7932ff5e1..4673d61d6 100644 --- a/runtime/tooling/inspector/inspector_server.h +++ b/runtime/tooling/inspector/inspector_server.h @@ -38,6 +38,7 @@ namespace panda::tooling::inspector { class CallFrame; +class ExecutionContextDescription; class Server; // NOLINT(fuchsia-virtual-inheritance) class InspectorServer final { @@ -61,7 +62,8 @@ public: void CallDebuggerScriptParsed(PtThread thread, ScriptId id, std::string_view source_file); void CallRuntimeConsoleApiCalled(PtThread thread, ConsoleCallType type, uint64_t timestamp, const std::vector &arguments); - void CallRuntimeExecutionContextCreated(PtThread thread); + void CallRuntimeExecutionContextCreated(PtThread thread, const ExecutionContextDescription &execution_context); + void CallRuntimeExecutionContextDestroyed(PtThread thread, const ExecutionContextDescription &execution_context); void CallTargetAttachedToTarget(PtThread thread); void CallTargetDetachedFromTarget(PtThread thread); diff --git a/runtime/tooling/inspector/tests/inspector_test.cpp b/runtime/tooling/inspector/tests/inspector_test.cpp index 643bd638a..1e46a7371 100644 --- a/runtime/tooling/inspector/tests/inspector_test.cpp +++ b/runtime/tooling/inspector/tests/inspector_test.cpp @@ -116,6 +116,7 @@ TEST_F(InspectorTest, InitialSequence) EXPECT_CALL(debugger_paused, Call); EXPECT_CALL(debugger_resumed, Call); + debugger_.GetHooks().VmInitialization(PtThread::NONE); debugger_.GetHooks().ThreadStart(PtThread::NONE); Call("Profiler.enable", Callback()); @@ -159,6 +160,7 @@ TEST_F(InspectorTest, BreaksOnStart) EXPECT_CALL(debugger_resumed, Call); EXPECT_CALL(debugger_resume_replied_to, Call); + debugger_.GetHooks().VmInitialization(PtThread::NONE); debugger_.GetHooks().ThreadStart(PtThread::NONE); Call("Runtime.runIfWaitingForDebugger"); (server_ + client_).Poll(); diff --git a/runtime/tooling/inspector/tests/inspector_test_base.cpp b/runtime/tooling/inspector/tests/inspector_test_base.cpp index e971f973c..d5e8338d9 100644 --- a/runtime/tooling/inspector/tests/inspector_test_base.cpp +++ b/runtime/tooling/inspector/tests/inspector_test_base.cpp @@ -85,6 +85,7 @@ void InspectorTestBase::SetUp() SetUpSourceFiles(); if (AttachDebugger()) { + debugger_.GetHooks().VmInitialization(PtThread::NONE); debugger_.GetHooks().ThreadStart(PtThread::NONE); client_.Call("Runtime.runIfWaitingForDebugger"); (server_ + client_).Poll(); diff --git a/runtime/tooling/inspector/thread_state.cpp b/runtime/tooling/inspector/thread_state.cpp index a95aeb570..c62e79cd6 100644 --- a/runtime/tooling/inspector/thread_state.cpp +++ b/runtime/tooling/inspector/thread_state.cpp @@ -26,8 +26,12 @@ #include namespace panda::tooling::inspector { -ThreadState::ThreadState(SuspendFunction &&suspend, WaitSuspensionFunction &&wait_suspension, ResumeFunction &&resume) - : suspend_(std::move(suspend)), wait_suspension_(std::move(wait_suspension)), resume_(std::move(resume)) +ThreadState::ThreadState(GetCallStackFunction &&get_call_stack, SuspendFunction &&suspend, + WaitSuspensionFunction &&wait_suspension, ResumeFunction &&resume) + : get_call_stack_(std::move(get_call_stack)), + suspend_(std::move(suspend)), + wait_suspension_(std::move(wait_suspension)), + resume_(std::move(resume)) { } @@ -44,6 +48,7 @@ void ThreadState::Reset() breakpoint_locations_.clear(); pause_on_exceptions_ = PauseOnExceptions::NONE; exception_.reset(); + exception_call_stack_.reset(); } void ThreadState::BreakOnStart() @@ -169,6 +174,7 @@ void ThreadState::OnException(const PtLocation &location, RemoteObject object) } exception_ = std::move(object); + exception_call_stack_ = get_call_stack_(); if (pause_on_exceptions_ == PauseOnExceptions::ALL) { paused_ = true; @@ -193,6 +199,7 @@ void ThreadState::OnExceptionCatch(const PtLocation &location) case PauseOnExceptions::UNCAUGHT: exception_.reset(); + exception_call_stack_.reset(); break; } } @@ -301,6 +308,8 @@ void ThreadState::OnThreadEnd() void ThreadState::WaitForUnpause(const PtLocation *location) { while (paused_) { + auto call_stack = exception_ ? std::move(exception_call_stack_) : get_call_stack_(); + std::vector hit_breakpoints; if (location != nullptr) { @@ -309,7 +318,7 @@ void ThreadState::WaitForUnpause(const PtLocation *location) [](auto &p) { return p.second; }); } - suspend_(hit_breakpoints, std::move(exception_)); + suspend_(call_stack, hit_breakpoints, std::move(exception_)); exception_.reset(); mutex_.Unlock(); diff --git a/runtime/tooling/inspector/thread_state.h b/runtime/tooling/inspector/thread_state.h index 0997000cc..00f927af4 100644 --- a/runtime/tooling/inspector/thread_state.h +++ b/runtime/tooling/inspector/thread_state.h @@ -24,11 +24,14 @@ #include "tooling/pt_location.h" #include +#include #include #include #include namespace panda::tooling::inspector { +class CallFrame; + class ThreadState final { public: enum class StepKind { @@ -58,11 +61,14 @@ public: enum class PauseOnExceptions { NONE, CAUGHT, UNCAUGHT, ALL }; - using SuspendFunction = std::function &, std::optional)>; + using GetCallStackFunction = std::function()>; + using SuspendFunction = std::function &, const std::vector &, + std::optional)>; using WaitSuspensionFunction = std::function; using ResumeFunction = std::function; - ThreadState(SuspendFunction &&suspend, WaitSuspensionFunction &&wait_suspension, ResumeFunction &&resume); + ThreadState(GetCallStackFunction &&get_call_stack, SuspendFunction &&suspend, + WaitSuspensionFunction &&wait_suspension, ResumeFunction &&resume); ~ThreadState() = default; NO_COPY_SEMANTIC(ThreadState); @@ -142,6 +148,9 @@ private: os::memory::Mutex mutex_; + // Gets the current call stack. Should be called on an application thread + GetCallStackFunction get_call_stack_ GUARDED_BY(mutex_); + // Marks a paused thread as suspended. Should be called on an application thread SuspendFunction suspend_ GUARDED_BY(mutex_); @@ -167,6 +176,7 @@ private: PauseOnExceptions pause_on_exceptions_ GUARDED_BY(mutex_) {PauseOnExceptions::NONE}; std::optional exception_ GUARDED_BY(mutex_); + std::shared_ptr exception_call_stack_ GUARDED_BY(mutex_); bool paused_ GUARDED_BY(mutex_) {false}; }; diff --git a/runtime/tooling/inspector/types/execution_context_description.cpp b/runtime/tooling/inspector/types/execution_context_description.cpp new file mode 100644 index 000000000..52397dec2 --- /dev/null +++ b/runtime/tooling/inspector/types/execution_context_description.cpp @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "execution_context_description.h" + +#include "tooling/pt_thread.h" +#include "utils/json_builder.h" + +#include + +namespace panda::tooling::inspector { +ExecutionContextDescription::ExecutionContextDescription(PtThread thread) : thread_(thread) +{ + if (thread != PtThread::NONE) { + name_ = "Thread #" + std::to_string(thread.GetId()); + } +} + +std::function ExecutionContextDescription::ToJson() const +{ + return [this](JsonObjectBuilder &json_builder) { + json_builder.AddProperty("id", GetId()); + json_builder.AddProperty("origin", ""); + json_builder.AddProperty("name", name_); + }; +} +} // namespace panda::tooling::inspector diff --git a/runtime/tooling/inspector/types/execution_context_description.h b/runtime/tooling/inspector/types/execution_context_description.h new file mode 100644 index 000000000..34da47555 --- /dev/null +++ b/runtime/tooling/inspector/types/execution_context_description.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PANDA_TOOLING_INSPECTOR_TYPES_EXECUTION_CONTEXT_DESCRIPTION_H +#define PANDA_TOOLING_INSPECTOR_TYPES_EXECUTION_CONTEXT_DESCRIPTION_H + +#include "numeric_id.h" + +#include "tooling/pt_thread.h" + +#include +#include + +namespace panda { +class JsonObjectBuilder; +} // namespace panda + +namespace panda::tooling::inspector { +class ExecutionContextDescription { +public: + explicit ExecutionContextDescription(PtThread thread); + + ExecutionContextId GetId() const + { + return thread_.GetId(); + } + + std::function ToJson() const; + +private: + PtThread thread_; + std::string name_; +}; +} // namespace panda::tooling::inspector + +#endif // PANDA_TOOLING_INSPECTOR_TYPES_EXECUTION_CONTEXT_DESCRIPTION_H diff --git a/runtime/tooling/inspector/types/numeric_id.h b/runtime/tooling/inspector/types/numeric_id.h index 74eb3352e..0f6e8f4c3 100644 --- a/runtime/tooling/inspector/types/numeric_id.h +++ b/runtime/tooling/inspector/types/numeric_id.h @@ -29,6 +29,7 @@ namespace panda::tooling::inspector { using BreakpointId = size_t; using CallFrameId = uint32_t; +using ExecutionContextId = uint32_t; using FrameId = uint32_t; using ScriptId = size_t; -- Gitee From 4ff0c7c2e35a5b99f07c5358c76eb7ede1ae4d0c Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Tue, 6 Dec 2022 18:01:37 +0300 Subject: [PATCH 5/6] [PT][Inspector] Stop Asio endpoint when the server is killed This prevents the Inspector server from hanging in the listening state at the end of a debugging session, by stopping the endpoint when the server is killed. Signed-off-by: Eugene Sharygin --- runtime/tooling/inspector/asio_server.h | 6 ++++++ runtime/tooling/inspector/event_loop.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/runtime/tooling/inspector/asio_server.h b/runtime/tooling/inspector/asio_server.h index f23e05548..ebfe98f28 100644 --- a/runtime/tooling/inspector/asio_server.h +++ b/runtime/tooling/inspector/asio_server.h @@ -27,6 +27,12 @@ namespace panda::tooling::inspector { // NOLINTNEXTLINE(fuchsia-multiple-inheritance) class AsioServer final : public ServerEndpoint { public: + bool Kill() override + { + endpoint_.stop(); + return ServerEndpoint::Kill(); + } + bool Poll() override { return endpoint_.poll() != 0; diff --git a/runtime/tooling/inspector/event_loop.h b/runtime/tooling/inspector/event_loop.h index 95d1b7430..436a09aec 100644 --- a/runtime/tooling/inspector/event_loop.h +++ b/runtime/tooling/inspector/event_loop.h @@ -24,7 +24,7 @@ namespace panda::tooling::inspector { class EventLoop { public: // Notify the running event loop to stop. - bool Kill(); + virtual bool Kill(); // Execute ready event handlers without blocking. virtual bool Poll() = 0; -- Gitee From 8fc2e29550b40aef2874308e6feb11ce7b047b1a Mon Sep 17 00:00:00 2001 From: Eugene Sharygin Date: Fri, 9 Dec 2022 15:19:37 +0300 Subject: [PATCH 6/6] [PT][Inspector] Fix assertion in GetFrameObject Signed-off-by: Eugene Sharygin --- runtime/tooling/inspector/inspector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tooling/inspector/inspector.cpp b/runtime/tooling/inspector/inspector.cpp index 39ac314f2..7f743152d 100644 --- a/runtime/tooling/inspector/inspector.cpp +++ b/runtime/tooling/inspector/inspector.cpp @@ -464,7 +464,7 @@ RemoteObject Inspector::GetFrameObject(PtThread thread, const PtFrame &frame) } } else if (auto extension = GetExtension(source_lang)) { auto parameters = debug_info_cache_.GetParameterInfo(method); - ASSERT(parameters.empty() || parameters.size() == frame.GetArgumentNum()); + ASSERT(parameters.size() <= frame.GetArgumentNum()); for (auto parameter_num = 0U; parameter_num < parameters.size(); ++parameter_num) { auto parameter = parameters[parameter_num]; -- Gitee