https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/149622
>From 583a83e1001a824fcfbc9a0811af3fbaf446e5ca Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <ism...@bennani.ma> Date: Tue, 2 Sep 2025 17:36:27 -0700 Subject: [PATCH 1/2] [lldb] Mark scripted frames as synthetic instead of artificial This patch changes the way frames created from scripted affordances like Scripted Threads are displayed. Currently, they're marked artificial which is used usually for compiler generated frames. This patch changes that behaviour by introducing a new synthetic StackFrame kind and moves 'artificial' to be a distinct StackFrame attribut. On top of making these frames less confusing, this allows us to know when a frame was created from a scripted affordance. rdar://155949703 Signed-off-by: Med Ismail Bennani <ism...@bennani.ma> --- lldb/include/lldb/API/SBFrame.h | 2 ++ lldb/include/lldb/Core/FormatEntity.h | 1 + lldb/include/lldb/Target/StackFrame.h | 17 +++++++--- lldb/source/API/SBFrame.cpp | 16 ++++++++++ lldb/source/Core/CoreProperties.td | 4 +-- lldb/source/Core/FormatEntity.cpp | 14 ++++++++ .../Process/scripted/ScriptedThread.cpp | 4 ++- lldb/source/Target/StackFrame.cpp | 14 ++++---- lldb/source/Target/StackFrameList.cpp | 6 ++-- .../scripted_process/TestScriptedProcess.py | 2 +- .../app_specific_backtrace_crashlog.test | 32 +++++++++---------- .../interactive_crashlog_arm64_register.test | 30 ++++++++--------- .../Crashlog/interactive_crashlog_json.test | 30 ++++++++--------- .../Crashlog/interactive_crashlog_legacy.test | 30 ++++++++--------- .../last_exception_backtrace_crashlog.test | 32 +++++++++---------- .../skipped_status_interactive_crashlog.test | 30 ++++++++--------- lldb/unittests/Core/FormatEntityTest.cpp | 1 + 17 files changed, 156 insertions(+), 109 deletions(-) diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 08de0605b3240..e4bbcd5ddcd9c 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -104,6 +104,8 @@ class LLDB_API SBFrame { bool IsArtificial() const; + bool IsSynthetic() const; + /// Return whether a frame recognizer decided this frame should not /// be displayes in backtraces etc. bool IsHidden() const; diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index d602edffb4c88..40916dc48a70b 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -80,6 +80,7 @@ struct Entry { FrameRegisterFlags, FrameRegisterByName, FrameIsArtificial, + FrameKind, ScriptFrame, FunctionID, FunctionDidChange, diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index d4104bfe49d20..4ffbf97ce6e90 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -60,10 +60,9 @@ class StackFrame : public ExecutionContextScope, /// local variables. History, - /// An artificial stack frame (e.g. a synthesized result of inferring - /// missing tail call frames from a backtrace) with limited support for - /// local variables. - Artificial + /// An synthetic stack frame (e.g. a synthesized result from script + /// resource) possibly without support for local variables or register. + Synthetic }; /// Construct a StackFrame object without supplying a RegisterContextSP. @@ -109,7 +108,8 @@ class StackFrame : public ExecutionContextScope, StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx, lldb::user_id_t concrete_frame_idx, lldb::addr_t cfa, bool cfa_is_valid, lldb::addr_t pc, Kind frame_kind, - bool behaves_like_zeroth_frame, const SymbolContext *sc_ptr); + bool artificial, bool behaves_like_zeroth_frame, + const SymbolContext *sc_ptr); StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx, lldb::user_id_t concrete_frame_idx, @@ -400,6 +400,9 @@ class StackFrame : public ExecutionContextScope, /// true if this is an inlined frame. bool IsInlined(); + /// Query whether this frame is synthetic. + bool IsSynthetic() const; + /// Query whether this frame is part of a historical backtrace. bool IsHistorical() const; @@ -571,6 +574,10 @@ class StackFrame : public ExecutionContextScope, /// Does this frame have a CFA? Different from CFA == LLDB_INVALID_ADDRESS. bool m_cfa_is_valid; Kind m_stack_frame_kind; + /// Is this an artificial stack frame (e.g. a synthesized result of inferring + /// missing tail call frames from a backtrace) with limited support for + /// local variables. Orthogonal to `StackFrame::Kind`. + bool m_artificial; /// Whether this frame behaves like the zeroth frame, in the sense /// that its pc value might not immediately follow a call (and thus might diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index b12cfceacd75d..b6724bb0c4119 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -1118,6 +1118,22 @@ bool SBFrame::IsArtificial() const { return false; } +bool SBFrame::IsSynthetic() const { + LLDB_INSTRUMENT_VA(this); + + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->IsSynthetic(); + + return false; +} + bool SBFrame::IsHidden() const { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 53dd333f045c9..fda34a8ad2630 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -59,7 +59,7 @@ let Definition = "debugger" in { Desc<"The default disassembly format string to use when disassembling instruction sequences.">; def FrameFormat: Property<"frame-format", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads.">; def NotiftVoid: Property<"notify-void", "Boolean">, Global, @@ -233,7 +233,7 @@ let Definition = "debugger" in { Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">; def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">; def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">, Global, diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 5d3c8b421d5d1..2ff73979e4976 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -108,6 +108,7 @@ constexpr Definition g_frame_child_entries[] = { Entry::DefinitionWithChildren("reg", EntryType::FrameRegisterByName, g_string_entry), Definition("is-artificial", EntryType::FrameIsArtificial), + Definition("kind", EntryType::FrameKind), }; constexpr Definition g_function_child_entries[] = { @@ -380,6 +381,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) { ENUM_TO_CSTR(FrameRegisterFlags); ENUM_TO_CSTR(FrameRegisterByName); ENUM_TO_CSTR(FrameIsArtificial); + ENUM_TO_CSTR(FrameKind); ENUM_TO_CSTR(ScriptFrame); ENUM_TO_CSTR(FunctionID); ENUM_TO_CSTR(FunctionDidChange); @@ -1747,6 +1749,18 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return false; } + case Entry::Type::FrameKind: { + if (exe_ctx) + if (StackFrame *frame = exe_ctx->GetFramePtr()) { + if (frame->IsSynthetic()) + s.PutCString(" [synthetic]"); + else if (frame->IsHistorical()) + s.PutCString(" [history]"); + return true; + } + return false; + } + case Entry::Type::ScriptFrame: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp index d0d1508e85172..45ad83e4ed671 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -200,13 +200,15 @@ bool ScriptedThread::LoadArtificialStackFrames() { lldb::addr_t cfa = LLDB_INVALID_ADDRESS; bool cfa_is_valid = false; + const bool artificial = false; const bool behaves_like_zeroth_frame = false; SymbolContext sc; symbol_addr.CalculateSymbolContext(&sc); StackFrameSP synth_frame_sp = std::make_shared<StackFrame>( this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, - StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc); + StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame, + &sc); if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp)) return ScriptedInterface::ErrorWithMessage<bool>( diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index f27004c2d134c..2ed58c5331df4 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -57,14 +57,14 @@ using namespace lldb_private; StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, addr_t cfa, bool cfa_is_valid, addr_t pc, StackFrame::Kind kind, - bool behaves_like_zeroth_frame, + bool artificial, bool behaves_like_zeroth_frame, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(), m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), - m_stack_frame_kind(kind), + m_stack_frame_kind(kind), m_artificial(artificial), m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { @@ -92,7 +92,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), - m_stack_frame_kind(StackFrame::Kind::Regular), + m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false), m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { @@ -120,7 +120,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), - m_stack_frame_kind(StackFrame::Kind::Regular), + m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false), m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { @@ -1264,10 +1264,12 @@ bool StackFrame::IsHistorical() const { return m_stack_frame_kind == StackFrame::Kind::History; } -bool StackFrame::IsArtificial() const { - return m_stack_frame_kind == StackFrame::Kind::Artificial; +bool StackFrame::IsSynthetic() const { + return m_stack_frame_kind == StackFrame::Kind::Synthetic; } +bool StackFrame::IsArtificial() const { return m_artificial; } + bool StackFrame::IsHidden() { if (auto recognized_frame_sp = GetRecognizedFrame()) return recognized_frame_sp->ShouldHide(); diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index aedfc52cfb4cb..d83c90582ba9c 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -321,13 +321,14 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) { addr_t pc = calleeInfo.address; // If the callee address refers to the call instruction, we do not want to // subtract 1 from this value. + const bool artificial = true; const bool behaves_like_zeroth_frame = calleeInfo.address_type == CallEdge::AddrType::Call; SymbolContext sc; callee->CalculateSymbolContext(&sc); auto synth_frame = std::make_shared<StackFrame>( m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa, - cfa_is_valid, pc, StackFrame::Kind::Artificial, + cfa_is_valid, pc, StackFrame::Kind::Regular, artificial, behaves_like_zeroth_frame, &sc); m_frames.push_back(synth_frame); LLDB_LOG(log, "Pushed frame {0} at {1:x}", callee->GetDisplayName(), pc); @@ -470,7 +471,8 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx, const bool cfa_is_valid = true; unwind_frame_sp = std::make_shared<StackFrame>( m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid, - pc, StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr); + pc, StackFrame::Kind::Regular, false, behaves_like_zeroth_frame, + nullptr); // Create synthetic tail call frames between the previous frame and the // newly-found frame. The new frame's index may change after this call, diff --git a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py index 9519c576689d0..5916e62c44f2e 100644 --- a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py +++ b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py @@ -284,6 +284,6 @@ def cleanup(): break self.assertEqual(idx, int(reg.value, 16)) - self.assertTrue(frame.IsArtificial(), "Frame is not artificial") + self.assertTrue(frame.IsSynthetic(), "Frame is not synthetic") pc = frame.GetPCAddress().GetLoadAddress(target_0) self.assertEqual(pc, 0x0100001B00) diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test index 9c0510c34ccae..f680158fdf735 100644 --- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test +++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test @@ -11,7 +11,7 @@ # CHECK: (lldb) process status --verbose # CHECK-NEXT: Process 96535 stopped # CHECK-NEXT: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0) -# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[artificial] +# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[synthetic] # CHECK: Extended Crash Information: # CHECK: Application Specific Information: # CHECK-NEXT: CoreFoundation: *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** __boundsFail: index 10 beyond bounds [0 .. 3]' @@ -21,21 +21,21 @@ # CHECK: (lldb) thread backtrace --extended true # CHECK: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0) -# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[artificial] -# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[artificial] -# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[artificial] -# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[artificial] -# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[artificial] -# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[artificial] -# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[artificial] -# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[artificial] -# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[artificial] -# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[artificial] -# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[artificial] -# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[artificial] -# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[artificial] -# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[artificial] -# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[artificial] +# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[synthetic] +# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[synthetic] +# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[synthetic] +# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[synthetic] +# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[synthetic] +# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[synthetic] +# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[synthetic] +# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[synthetic] +# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[synthetic] +# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[synthetic] +# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[synthetic] +# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[synthetic] +# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[synthetic] +# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[synthetic] +# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[synthetic] # CHECK: thread #4294967295: tid = 0x0001, 0x00000001a0a58418{{.*}}, queue = 'Application Specific Backtrace' # CHECK-NEXT: frame #0: 0x00000001a0a58418{{.*}} diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test index 3f572c3300c0f..9da21d9aab78d 100644 --- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test +++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test @@ -15,9 +15,9 @@ # CHECK: (lldb) thread backtrace # CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] # CHECK: (lldb) thread list # CHECK-NEXT: Process 22511 stopped @@ -27,20 +27,20 @@ # CHECK: (lldb) bt all # CHECK: thread #1, queue = 'com.apple.main-thread' -# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic] # CHECK-NEXT: thread #2 -# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] # CHECK: (lldb) register read # CHECK: General Purpose Registers: diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test index 684be2846f78d..2509f2e88d9b2 100644 --- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test +++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test @@ -15,9 +15,9 @@ # CHECK: (lldb) thread backtrace # CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] # CHECK: (lldb) thread list # CHECK-NEXT: Process 22511 stopped @@ -27,17 +27,17 @@ # CHECK: (lldb) bt all # CHECK: thread #1, queue = 'com.apple.main-thread' -# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic] # CHECK-NEXT: thread #2 -# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test index 271a4c2aa90f4..74ced35fcfae0 100644 --- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test +++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test @@ -15,9 +15,9 @@ # CHECK: (lldb) thread backtrace # CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] # CHECK: (lldb) thread list # CHECK-NEXT: Process 22511 stopped @@ -27,17 +27,17 @@ # CHECK: (lldb) bt all # CHECK: thread #1 -# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic] # CHECK-NEXT: thread #2 -# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test index a17b7ac18a620..53c0732deb549 100644 --- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test +++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test @@ -11,7 +11,7 @@ # CHECK: (lldb) process status --verbose # CHECK-NEXT: Process 96535 stopped # CHECK-NEXT: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0) -# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[artificial] +# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[synthetic] # CHECK: Extended Crash Information: # CHECK: Application Specific Information: # CHECK-NEXT: CoreFoundation: *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** __boundsFail: index 10 beyond bounds [0 .. 3]' @@ -21,21 +21,21 @@ # CHECK: (lldb) thread backtrace --extended true # CHECK: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0) -# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[artificial] -# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[artificial] -# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[artificial] -# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[artificial] -# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[artificial] -# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[artificial] -# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[artificial] -# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[artificial] -# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[artificial] -# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[artificial] -# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[artificial] -# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[artificial] -# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[artificial] -# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[artificial] -# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[artificial] +# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[synthetic] +# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[synthetic] +# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[synthetic] +# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[synthetic] +# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[synthetic] +# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[synthetic] +# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[synthetic] +# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[synthetic] +# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[synthetic] +# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[synthetic] +# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[synthetic] +# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[synthetic] +# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[synthetic] +# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[synthetic] +# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[synthetic] # CHECK: thread #4294967295: tid = 0x0001, 0x00000001a0a5840c{{.*}}, queue = 'Application Specific Backtrace' # CHECK-NEXT: frame #0: 0x00000001a0a5840c{{.*}} diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test index 52a185b8cf760..138cd2bdffc44 100644 --- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test +++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test @@ -15,9 +15,9 @@ process status thread backtrace # CHECK: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] thread list # CHECK: Process 22511 stopped @@ -27,20 +27,20 @@ thread list bt all # CHECK: thread #1, queue = 'com.apple.main-thread' -# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic] # CHECK-NEXT: thread #2 -# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) -# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial] -# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial] -# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial] -# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial] +# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic] +# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic] +# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic] +# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic] image list # CHECK: 11111111-2222-3333-4444-555555555555 {{.*}}bogus.dylib diff --git a/lldb/unittests/Core/FormatEntityTest.cpp b/lldb/unittests/Core/FormatEntityTest.cpp index e056b6fe7de52..309294a501883 100644 --- a/lldb/unittests/Core/FormatEntityTest.cpp +++ b/lldb/unittests/Core/FormatEntityTest.cpp @@ -117,6 +117,7 @@ constexpr llvm::StringRef lookupStrings[] = { "${frame.no-debug}", "${frame.reg.*}", "${frame.is-artificial}", + "${frame.kind}", "${function.id}", "${function.name}", "${function.name-without-args}", >From 5b4e9ce93e78d18488e775b2a112c97fa55f4016 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <ism...@bennani.ma> Date: Tue, 2 Sep 2025 17:56:26 -0700 Subject: [PATCH 2/2] [lldb] Introduce ScriptedFrame affordance This patch introduces a new scripting affordance: `ScriptedFrame`. This allows user to produce mock stackframes in scripted threads, and scripted processes, from a python script. With this change, StackFrame can be synthetized from different sources: - Either from a dictionary containing a load address, and a frame index, which is the legacy way. - Or by creating a ScriptedFrame python object. One particularity of synthezising stackframes from the ScriptedFrame python object, is that these frame have an optional PC, meaning that they don't have a report a valid PC and they can act as shells that just contain static information, like the frame function name, the list of variables or registers, etc. It can also provide a symbol context. rdar://157260006 Signed-off-by: Med Ismail Bennani <ism...@bennani.ma> --- lldb/bindings/python/python-wrapper.swig | 1 + .../python/templates/scripted_process.py | 136 +++++++++++++ lldb/include/lldb/API/SBSymbolContext.h | 1 + .../Interfaces/ScriptedFrameInterface.h | 57 ++++++ .../Interfaces/ScriptedThreadInterface.h | 10 + .../lldb/Interpreter/ScriptInterpreter.h | 5 + lldb/include/lldb/Target/StackFrame.h | 34 ++-- lldb/include/lldb/lldb-forward.h | 3 + lldb/source/Core/FormatEntity.cpp | 2 +- .../Plugins/Process/scripted/CMakeLists.txt | 1 + .../Process/scripted/ScriptedFrame.cpp | 192 ++++++++++++++++++ .../Plugins/Process/scripted/ScriptedFrame.h | 66 ++++++ .../Process/scripted/ScriptedThread.cpp | 82 +++++++- .../Plugins/Process/scripted/ScriptedThread.h | 5 +- .../Python/Interfaces/CMakeLists.txt | 1 + .../ScriptInterpreterPythonInterfaces.h | 1 + .../ScriptedFramePythonInterface.cpp | 157 ++++++++++++++ .../Interfaces/ScriptedFramePythonInterface.h | 59 ++++++ .../Interfaces/ScriptedPythonInterface.cpp | 3 +- .../ScriptedThreadPythonInterface.cpp | 17 ++ .../ScriptedThreadPythonInterface.h | 5 + .../Python/ScriptInterpreterPython.cpp | 5 + .../Python/ScriptInterpreterPythonImpl.h | 2 + .../dummy_scripted_process.py | 61 +++++- 24 files changed, 874 insertions(+), 32 deletions(-) create mode 100644 lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h create mode 100644 lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp create mode 100644 lldb/source/Plugins/Process/scripted/ScriptedFrame.h create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index 2c30d536a753d..c0ad456bc12e8 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -519,6 +519,7 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyOb return sb_ptr; } + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyObject * data) { lldb::SBExecutionContext *sb_ptr = NULL; diff --git a/lldb/examples/python/templates/scripted_process.py b/lldb/examples/python/templates/scripted_process.py index b6360b8519077..49059d533f38a 100644 --- a/lldb/examples/python/templates/scripted_process.py +++ b/lldb/examples/python/templates/scripted_process.py @@ -383,6 +383,142 @@ def get_extended_info(self): """ return self.extended_info + def get_scripted_frame_plugin(self): + """Get scripted frame plugin name. + + Returns: + str: Name of the scripted frame plugin. + """ + return None + + +class ScriptedFrame(metaclass=ABCMeta): + """ + The base class for a scripted frame. + + Most of the base class methods are `@abstractmethod` that need to be + overwritten by the inheriting class. + """ + + @abstractmethod + def __init__(self, thread, args): + """Construct a scripted frame. + + Args: + thread (ScriptedThread): The thread owning this frame. + args (lldb.SBStructuredData): A Dictionary holding arbitrary + key/value pairs used by the scripted frame. + """ + self.target = None + self.originating_thread = None + self.thread = None + self.args = None + self.id = None + self.name = None + self.register_info = None + self.register_ctx = {} + self.variables = [] + + if ( + isinstance(thread, ScriptedThread) + or isinstance(thread, lldb.SBThread) + and thread.IsValid() + ): + self.target = thread.target + self.process = thread.process + self.originating_thread = thread + self.thread = self.process.GetThreadByIndexID(thread.tid) + self.get_register_info() + + @abstractmethod + def get_id(self): + """Get the scripted frame identifier. + + Returns: + int: The identifier of the scripted frame in the scripted thread. + """ + pass + + def get_pc(self): + """Get the scripted frame address. + + Returns: + int: The optional address of the scripted frame in the scripted thread. + """ + return None + + def get_symbol_context(self): + """Get the scripted frame symbol context. + + Returns: + lldb.SBSymbolContext: The symbol context of the scripted frame in the scripted thread. + """ + return None + + def is_inlined(self): + """Check if the scripted frame is inlined. + + Returns: + bool: True if scripted frame is inlined. False otherwise. + """ + return False + + def is_artificial(self): + """Check if the scripted frame is artificial. + + Returns: + bool: True if scripted frame is artificial. False otherwise. + """ + return True + + def is_hidden(self): + """Check if the scripted frame is hidden. + + Returns: + bool: True if scripted frame is hidden. False otherwise. + """ + return False + + def get_function_name(self): + """Get the scripted frame function name. + + Returns: + str: The function name of the scripted frame. + """ + return self.name + + def get_display_function_name(self): + """Get the scripted frame display function name. + + Returns: + str: The display function name of the scripted frame. + """ + return self.get_function_name() + + def get_variables(self, filters): + """Get the scripted thread state type. + + Args: + filter (lldb.SBVariablesOptions): The filter used to resolve the variables + Returns: + lldb.SBValueList: The SBValueList containing the SBValue for each resolved variable. + Returns None by default. + """ + return None + + def get_register_info(self): + if self.register_info is None: + self.register_info = self.originating_thread.get_register_info() + return self.register_info + + @abstractmethod + def get_register_context(self): + """Get the scripted thread register context + + Returns: + str: A byte representing all register's value. + """ + pass class PassthroughScriptedProcess(ScriptedProcess): driving_target = None diff --git a/lldb/include/lldb/API/SBSymbolContext.h b/lldb/include/lldb/API/SBSymbolContext.h index 128b0b65b7860..19f29c629d094 100644 --- a/lldb/include/lldb/API/SBSymbolContext.h +++ b/lldb/include/lldb/API/SBSymbolContext.h @@ -66,6 +66,7 @@ class LLDB_API SBSymbolContext { friend class SBTarget; friend class SBSymbolContextList; + friend class lldb_private::ScriptInterpreter; friend class lldb_private::python::SWIGBridge; SBSymbolContext(const lldb_private::SymbolContext &sc_ptr); diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h new file mode 100644 index 0000000000000..c70009abcfc3e --- /dev/null +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h @@ -0,0 +1,57 @@ +//===-- ScriptedFrameInterface.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H +#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H + +#include "ScriptedInterface.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Symbol/SymbolContext.h" + +#include "lldb/lldb-private.h" + +#include <optional> +#include <string> + +namespace lldb_private { +class ScriptedFrameInterface : virtual public ScriptedInterface { +public: + virtual llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) = 0; + + virtual lldb::user_id_t GetID() { return LLDB_INVALID_FRAME_ID; } + + virtual lldb::addr_t GetPC() { return LLDB_INVALID_ADDRESS; } + + virtual std::optional<SymbolContext> GetSymbolContext() { + return std::nullopt; + } + + virtual std::optional<std::string> GetFunctionName() { return std::nullopt; } + + virtual std::optional<std::string> GetDisplayFunctionName() { + return std::nullopt; + } + + virtual bool IsInlined() { return false; } + + virtual bool IsArtificial() { return true; } + + virtual bool IsHidden() { return false; } + + virtual StructuredData::DictionarySP GetRegisterInfo() { return {}; } + + virtual std::optional<std::string> GetRegisterContext() { + return std::nullopt; + } +}; +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h index a7cfc690b67dc..bc58f344d36f8 100644 --- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h @@ -44,6 +44,16 @@ class ScriptedThreadInterface : virtual public ScriptedInterface { } virtual StructuredData::ArraySP GetExtendedInfo() { return {}; } + + virtual std::optional<std::string> GetScriptedFramePluginName() { + return std::nullopt; + } + +protected: + friend class ScriptedFrame; + virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() { + return {}; + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index dffb9b82abf3d..024bbc90a9a39 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -26,6 +26,7 @@ #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/StreamFile.h" #include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h" @@ -531,6 +532,10 @@ class ScriptInterpreter : public PluginInterface { return {}; } + virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() { + return {}; + } + virtual lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() { return {}; diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index 4ffbf97ce6e90..cdbe8ae3c6779 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -398,7 +398,7 @@ class StackFrame : public ExecutionContextScope, /// /// \return /// true if this is an inlined frame. - bool IsInlined(); + virtual bool IsInlined(); /// Query whether this frame is synthetic. bool IsSynthetic() const; @@ -409,12 +409,12 @@ class StackFrame : public ExecutionContextScope, /// Query whether this frame is artificial (e.g a synthesized result of /// inferring missing tail call frames from a backtrace). Artificial frames /// may have limited support for inspecting variables. - bool IsArtificial() const; + virtual bool IsArtificial() const; /// Query whether this frame should be hidden from backtraces. Frame /// recognizers can customize this behavior and hide distracting /// system implementation details this way. - bool IsHidden(); + virtual bool IsHidden(); /// Language plugins can use this API to report language-specific /// runtime information about this compile unit, such as additional @@ -425,13 +425,13 @@ class StackFrame : public ExecutionContextScope, /// /// /// \return /// A C-String containing the function demangled name. Can be null. - const char *GetFunctionName(); + virtual const char *GetFunctionName(); /// Get the frame's demangled display name. /// /// /// \return /// A C-String containing the function demangled display name. Can be null. - const char *GetDisplayFunctionName(); + virtual const char *GetDisplayFunctionName(); /// Query this frame to find what frame it is in this Thread's /// StackFrameList. @@ -543,18 +543,7 @@ class StackFrame : public ExecutionContextScope, bool HasCachedData() const; -private: - /// Private methods, called from GetValueForVariableExpressionPath. - /// See that method for documentation of parameters and return value. - lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath( - llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, - uint32_t options, lldb::VariableSP &var_sp, Status &error); - - lldb::ValueObjectSP DILGetValueForVariableExpressionPath( - llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, - uint32_t options, lldb::VariableSP &var_sp, Status &error); - - /// For StackFrame only. + /// For StackFrame and derived classes only. /// \{ lldb::ThreadWP m_thread_wp; uint32_t m_frame_index; @@ -591,6 +580,17 @@ class StackFrame : public ExecutionContextScope, StreamString m_disassembly; std::recursive_mutex m_mutex; +private: + /// Private methods, called from GetValueForVariableExpressionPath. + /// See that method for documentation of parameters and return value. + lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath( + llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, + uint32_t options, lldb::VariableSP &var_sp, Status &error); + + lldb::ValueObjectSP DILGetValueForVariableExpressionPath( + llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, + uint32_t options, lldb::VariableSP &var_sp, Status &error); + StackFrame(const StackFrame &) = delete; const StackFrame &operator=(const StackFrame &) = delete; }; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 483dce98ea427..af5656b3dcad1 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -187,6 +187,7 @@ class SaveCoreOptions; class Scalar; class ScriptInterpreter; class ScriptInterpreterLocker; +class ScriptedFrameInterface; class ScriptedMetadata; class ScriptedBreakpointInterface; class ScriptedPlatformInterface; @@ -408,6 +409,8 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame> typedef std::shared_ptr<lldb_private::ScriptSummaryFormat> ScriptSummaryFormatSP; typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP; +typedef std::shared_ptr<lldb_private::ScriptedFrameInterface> + ScriptedFrameInterfaceSP; typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP; typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface> ScriptedPlatformInterfaceUP; diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 2ff73979e4976..491f5c6320d97 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1681,7 +1681,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const Address &pc_addr = frame->GetFrameCodeAddress(); - if (pc_addr.IsValid()) { + if (pc_addr.IsValid() || frame->IsSynthetic()) { if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false)) return true; } diff --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt b/lldb/source/Plugins/Process/scripted/CMakeLists.txt index 590166591a41e..1516ad3132e3b 100644 --- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt +++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_library(lldbPluginScriptedProcess PLUGIN ScriptedProcess.cpp ScriptedThread.cpp + ScriptedFrame.cpp LINK_COMPONENTS BinaryFormat diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp new file mode 100644 index 0000000000000..11dd50e1f9124 --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -0,0 +1,192 @@ +//===-- ScriptedFrame.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ScriptedFrame.h" + +#include "lldb/Utility/DataBufferHeap.h" + +using namespace lldb; +using namespace lldb_private; + +void ScriptedFrame::CheckInterpreterAndScriptObject() const { + lldbassert(m_script_object_sp && "Invalid Script Object."); + lldbassert(GetInterface() && "Invalid Scripted Frame Interface."); +} + +llvm::Expected<std::shared_ptr<ScriptedFrame>> +ScriptedFrame::Create(ScriptedThread &thread, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object) { + if (!thread.IsValid()) + return llvm::createStringError("Invalid scripted thread."); + + thread.CheckInterpreterAndScriptObject(); + + auto scripted_frame_interface = + thread.GetInterface()->CreateScriptedFrameInterface(); + if (!scripted_frame_interface) + return llvm::createStringError( + "Failed to create scripted frame interface."); + + llvm::StringRef frame_class_name; + if (!script_object) { + std::optional<std::string> class_name = + thread.GetInterface()->GetScriptedFramePluginName(); + if (!class_name || class_name->empty()) + return llvm::createStringError( + "Failed to get scripted thread class name."); + frame_class_name = *class_name; + } + + ExecutionContext exe_ctx(thread); + auto obj_or_err = scripted_frame_interface->CreatePluginObject( + frame_class_name, exe_ctx, args_sp, script_object); + + if (!obj_or_err) { + llvm::consumeError(obj_or_err.takeError()); + return llvm::createStringError("Failed to create script object."); + } + + StructuredData::GenericSP owned_script_object_sp = *obj_or_err; + + if (!owned_script_object_sp->IsValid()) + return llvm::createStringError("Created script object is invalid."); + + lldb::user_id_t frame_id = scripted_frame_interface->GetID(); + + lldb::addr_t pc = scripted_frame_interface->GetPC(); + SymbolContext sc; + Address symbol_addr; + if (pc != LLDB_INVALID_ADDRESS) { + symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget()); + symbol_addr.CalculateSymbolContext(&sc); + } + + std::optional<SymbolContext> maybe_sym_ctx = + scripted_frame_interface->GetSymbolContext(); + if (maybe_sym_ctx) { + sc = *maybe_sym_ctx; + } + + StructuredData::DictionarySP reg_info = + scripted_frame_interface->GetRegisterInfo(); + + if (!reg_info) + return llvm::createStringError( + "Failed to get scripted thread registers info."); + + std::shared_ptr<DynamicRegisterInfo> register_info_sp = + DynamicRegisterInfo::Create( + *reg_info, thread.GetProcess()->GetTarget().GetArchitecture()); + + lldb::RegisterContextSP reg_ctx_sp; + + std::optional<std::string> reg_data = + scripted_frame_interface->GetRegisterContext(); + if (reg_data) { + DataBufferSP data_sp( + std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) + return llvm::createStringError("Failed to copy raw registers data"); + + std::shared_ptr<RegisterContextMemory> reg_ctx_memory = + std::make_shared<RegisterContextMemory>( + thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS); + if (!reg_ctx_memory) + return llvm::createStringError("Failed to create a register context."); + + reg_ctx_memory->SetAllRegisterData(data_sp); + reg_ctx_sp = reg_ctx_memory; + } + + return std::make_shared<ScriptedFrame>( + thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp, + register_info_sp, owned_script_object_sp); +} + +ScriptedFrame::ScriptedFrame(ScriptedThread &thread, + ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t id, lldb::addr_t pc, + SymbolContext &sym_ctx, + lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr<DynamicRegisterInfo> reg_info_sp, + StructuredData::GenericSP script_object_sp) + : StackFrame(thread.shared_from_this(), /*frame_idx=*/id, + /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp, + /*cfa=*/0, /*pc=*/pc, + /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx), + m_scripted_thread(thread), m_scripted_frame_interface_sp(interface_sp), + m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {} + +ScriptedFrame::~ScriptedFrame() {} + +const char *ScriptedFrame::GetFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = GetInterface()->GetFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +const char *ScriptedFrame::GetDisplayFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = + GetInterface()->GetDisplayFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +bool ScriptedFrame::IsInlined() { return GetInterface()->IsInlined(); } + +bool ScriptedFrame::IsArtificial() const { + return GetInterface()->IsArtificial(); +} + +bool ScriptedFrame::IsHidden() { return GetInterface()->IsHidden(); } + +lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const { + return m_scripted_frame_interface_sp; +} + +std::shared_ptr<DynamicRegisterInfo> ScriptedFrame::GetDynamicRegisterInfo() { + CheckInterpreterAndScriptObject(); + + if (!m_register_info_sp) { + StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + + Status error; + if (!reg_info) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.", + error, LLDBLog::Thread); + + ThreadSP thread_sp = m_thread_wp.lock(); + if (!thread_sp || !thread_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid thread.", error, + LLDBLog::Thread); + + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp || !process_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid process.", + error, LLDBLog::Thread); + + m_register_info_sp = DynamicRegisterInfo::Create( + *reg_info, process_sp->GetTarget().GetArchitecture()); + } + + return m_register_info_sp; +} diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h new file mode 100644 index 0000000000000..639bf6964cb6c --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h @@ -0,0 +1,66 @@ +//===-- ScriptedFrame.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H + +#include <string> + +#include "ScriptedThread.h" + +#include "Plugins/Process/Utility/RegisterContextMemory.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Target/StackFrame.h" + +namespace lldb_private { +class ScriptedThread; +} + +namespace lldb_private { + +class ScriptedFrame : public lldb_private::StackFrame { + +public: + ScriptedFrame(ScriptedThread &thread, + lldb::ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t frame_idx, lldb::addr_t pc, + SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr<DynamicRegisterInfo> reg_info_sp, + StructuredData::GenericSP script_object_sp = nullptr); + + ~ScriptedFrame() override; + + static llvm::Expected<std::shared_ptr<ScriptedFrame>> + Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object = nullptr); + + bool IsInlined() override; + bool IsArtificial() const override; + bool IsHidden() override; + const char *GetFunctionName() override; + const char *GetDisplayFunctionName() override; + +private: + void CheckInterpreterAndScriptObject() const; + lldb::ScriptedFrameInterfaceSP GetInterface() const; + + ScriptedFrame(const ScriptedFrame &) = delete; + const ScriptedFrame &operator=(const ScriptedFrame &) = delete; + + std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo(); + + const ScriptedThread &m_scripted_thread; + lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp = nullptr; + lldb_private::StructuredData::GenericSP m_script_object_sp = nullptr; + std::shared_ptr<DynamicRegisterInfo> m_register_info_sp = nullptr; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp index 45ad83e4ed671..491efac5aadef 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ScriptedThread.h" +#include "ScriptedFrame.h" #include "Plugins/Process/Utility/RegisterContextThreadMemory.h" #include "Plugins/Process/Utility/StopInfoMachException.h" @@ -173,27 +174,31 @@ bool ScriptedThread::LoadArtificialStackFrames() { .str(), error, LLDBLog::Thread); - StackFrameListSP frames = GetStackFrameList(); - - for (size_t idx = 0; idx < arr_size; idx++) { + auto create_frame_from_dict = + [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> { + Status error; std::optional<StructuredData::Dictionary *> maybe_dict = arr_sp->GetItemAtIndexAsDictionary(idx); - if (!maybe_dict) - return ScriptedInterface::ErrorWithMessage<bool>( + if (!maybe_dict) { + ScriptedInterface::ErrorWithMessage<bool>( LLVM_PRETTY_FUNCTION, llvm::Twine( "Couldn't get artificial stackframe dictionary at index (" + llvm::Twine(idx) + llvm::Twine(") from stackframe array.")) .str(), error, LLDBLog::Thread); + return error.ToError(); + } StructuredData::Dictionary *dict = *maybe_dict; lldb::addr_t pc; - if (!dict->GetValueForKeyAsInteger("pc", pc)) - return ScriptedInterface::ErrorWithMessage<bool>( + if (!dict->GetValueForKeyAsInteger("pc", pc)) { + ScriptedInterface::ErrorWithMessage<bool>( LLVM_PRETTY_FUNCTION, "Couldn't find value for key 'pc' in stackframe dictionary.", error, LLDBLog::Thread); + return error.ToError(); + } Address symbol_addr; symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget()); @@ -205,10 +210,65 @@ bool ScriptedThread::LoadArtificialStackFrames() { SymbolContext sc; symbol_addr.CalculateSymbolContext(&sc); - StackFrameSP synth_frame_sp = std::make_shared<StackFrame>( - this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, - StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame, - &sc); + return std::make_shared<StackFrame>(this->shared_from_this(), idx, idx, cfa, + cfa_is_valid, pc, + StackFrame::Kind::Synthetic, artificial, + behaves_like_zeroth_frame, &sc); + }; + + auto create_frame_from_script_object = + [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> { + Status error; + StructuredData::ObjectSP object_sp = arr_sp->GetItemAtIndex(idx); + if (!object_sp || !object_sp->GetAsGeneric()) { + ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't get artificial stackframe object at index (" + + llvm::Twine(idx) + + llvm::Twine(") from stackframe array.")) + .str(), + error, LLDBLog::Thread); + return error.ToError(); + } + + auto frame_or_error = + ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric()); + + if (!frame_or_error) { + ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error); + return error.ToError(); + } + + StackFrameSP frame_sp = frame_or_error.get(); + lldbassert(frame_sp && "Couldn't initialize scripted frame."); + + return frame_sp; + }; + + StackFrameListSP frames = GetStackFrameList(); + + for (size_t idx = 0; idx < arr_size; idx++) { + StackFrameSP synth_frame_sp = nullptr; + + auto frame_from_dict_or_err = create_frame_from_dict(idx); + if (!frame_from_dict_or_err) { + auto frame_from_script_obj_or_err = create_frame_from_script_object(idx); + + if (!frame_from_script_obj_or_err) { + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't add artificial frame (" + llvm::Twine(idx) + + llvm::Twine(") to ScriptedThread StackFrameList.")) + .str(), + error, LLDBLog::Thread); + } else { + llvm::consumeError(frame_from_dict_or_err.takeError()); + synth_frame_sp = *frame_from_script_obj_or_err; + } + } else { + synth_frame_sp = *frame_from_dict_or_err; + } if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp)) return ScriptedInterface::ErrorWithMessage<bool>( diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h index cd224d60ceef8..ee5ace4059673 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -15,11 +15,12 @@ #include "Plugins/Process/Utility/RegisterContextMemory.h" #include "lldb/Interpreter/ScriptInterpreter.h" -#include "lldb/Target//DynamicRegisterInfo.h" +#include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/Thread.h" namespace lldb_private { class ScriptedProcess; +class ScriptedFrame; } namespace lldb_private { @@ -61,6 +62,8 @@ class ScriptedThread : public lldb_private::Thread { StructuredData::ObjectSP FetchThreadExtendedInfo() override; private: + friend class ScriptedFrame; + void CheckInterpreterAndScriptObject() const; lldb::ScriptedThreadInterfaceSP GetInterface() const; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt index 04370940423ae..09103573b89c5 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt @@ -22,6 +22,7 @@ endif() add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN OperatingSystemPythonInterface.cpp ScriptInterpreterPythonInterfaces.cpp + ScriptedFramePythonInterface.cpp ScriptedPlatformPythonInterface.cpp ScriptedProcessPythonInterface.cpp ScriptedPythonInterface.cpp diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 02dc06507caf2..3814f46615078 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h @@ -17,6 +17,7 @@ #include "OperatingSystemPythonInterface.h" #include "ScriptedBreakpointPythonInterface.h" +#include "ScriptedFramePythonInterface.h" #include "ScriptedPlatformPythonInterface.h" #include "ScriptedProcessPythonInterface.h" #include "ScriptedStopHookPythonInterface.h" diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp new file mode 100644 index 0000000000000..a6ae2d3206465 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp @@ -0,0 +1,157 @@ +//===-- ScriptedFramePythonInterface.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Config.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/Log.h" +#include "lldb/lldb-enumerations.h" + +#if LLDB_ENABLE_PYTHON + +// LLDB Python header must be included first +#include "../lldb-python.h" + +#include "../SWIGPythonBridge.h" +#include "../ScriptInterpreterPythonImpl.h" +#include "ScriptedFramePythonInterface.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; +using Locker = ScriptInterpreterPythonImpl::Locker; + +ScriptedFramePythonInterface::ScriptedFramePythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedFrameInterface(), ScriptedPythonInterface(interpreter) {} + +llvm::Expected<StructuredData::GenericSP> +ScriptedFramePythonInterface::CreatePluginObject( + const llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) { + ExecutionContextRefSP exe_ctx_ref_sp = + std::make_shared<ExecutionContextRef>(exe_ctx); + StructuredDataImpl sd_impl(args_sp); + return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj, + exe_ctx_ref_sp, sd_impl); +} + +lldb::user_id_t ScriptedFramePythonInterface::GetID() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_id", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return LLDB_INVALID_FRAME_ID; + + return obj->GetUnsignedIntegerValue(LLDB_INVALID_FRAME_ID); +} + +lldb::addr_t ScriptedFramePythonInterface::GetPC() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_pc", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return LLDB_INVALID_ADDRESS; + + return obj->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS); +} + +std::optional<SymbolContext> ScriptedFramePythonInterface::GetSymbolContext() { + Status error; + auto sym_ctx = Dispatch<SymbolContext>("get_symbol_context", error); + + if (error.Fail()) { + return ErrorWithMessage<SymbolContext>(LLVM_PRETTY_FUNCTION, + error.AsCString(), error); + } + + return sym_ctx; +} + +std::optional<std::string> ScriptedFramePythonInterface::GetFunctionName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_function_name", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +std::optional<std::string> +ScriptedFramePythonInterface::GetDisplayFunctionName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_display_function_name", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +bool ScriptedFramePythonInterface::IsInlined() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_inlined", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +bool ScriptedFramePythonInterface::IsArtificial() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_artificial", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +bool ScriptedFramePythonInterface::IsHidden() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_hidden", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +StructuredData::DictionarySP ScriptedFramePythonInterface::GetRegisterInfo() { + Status error; + StructuredData::DictionarySP dict = + Dispatch<StructuredData::DictionarySP>("get_register_info", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) + return {}; + + return dict; +} + +std::optional<std::string> ScriptedFramePythonInterface::GetRegisterContext() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_register_context", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetAsString()->GetValue().str(); +} + +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h new file mode 100644 index 0000000000000..6c15b97307989 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h @@ -0,0 +1,59 @@ +//===-- ScriptedFramePythonInterface.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "ScriptedPythonInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" +#include <optional> + +namespace lldb_private { +class ScriptedFramePythonInterface : public ScriptedFrameInterface, + public ScriptedPythonInterface { +public: + ScriptedFramePythonInterface(ScriptInterpreterPythonImpl &interpreter); + + llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override; + + llvm::SmallVector<AbstractMethodRequirement> + GetAbstractMethodRequirements() const override { + return llvm::SmallVector<AbstractMethodRequirement>({{"get_id"}}); + } + + lldb::user_id_t GetID() override; + + lldb::addr_t GetPC() override; + + std::optional<SymbolContext> GetSymbolContext() override; + + std::optional<std::string> GetFunctionName() override; + + std::optional<std::string> GetDisplayFunctionName() override; + + bool IsInlined() override; + + bool IsArtificial() override; + + bool IsHidden() override; + + StructuredData::DictionarySP GetRegisterInfo() override; + + std::optional<std::string> GetRegisterContext() override; +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index b49d1d82fe535..8083ccae04026 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -167,7 +167,8 @@ ScriptedPythonInterface::ExtractValueFromPythonObject< if (!sb_mem_reg_info) { error = Status::FromErrorStringWithFormat( - "Couldn't cast lldb::SBMemoryRegionInfo to lldb::MemoryRegionInfoSP."); + "Couldn't cast lldb::SBMemoryRegionInfo to " + "lldb_private::MemoryRegionInfo."); return {}; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp index 8af89d761764b..fd4d231a747fe 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp @@ -144,4 +144,21 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetExtendedInfo() { return arr; } +std::optional<std::string> +ScriptedThreadPythonInterface::GetScriptedFramePluginName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_scripted_frame_plugin", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +lldb::ScriptedFrameInterfaceSP +ScriptedThreadPythonInterface::CreateScriptedFrameInterface() { + return m_interpreter.CreateScriptedFrameInterface(); +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h index 1fb23b39c7076..043557a827461 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h @@ -51,6 +51,11 @@ class ScriptedThreadPythonInterface : public ScriptedThreadInterface, std::optional<std::string> GetRegisterContext() override; StructuredData::ArraySP GetExtendedInfo() override; + + std::optional<std::string> GetScriptedFramePluginName() override; + +protected: + lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 9330a634489a2..73c5c72932ff1 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1521,6 +1521,11 @@ ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() { return std::make_shared<ScriptedThreadPythonInterface>(*this); } +ScriptedFrameInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() { + return std::make_shared<ScriptedFramePythonInterface>(*this); +} + ScriptedThreadPlanInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() { return std::make_shared<ScriptedThreadPlanPythonInterface>(*this); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 83b64b85faebd..dedac280788f4 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -99,6 +99,8 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; + lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; + lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() override; diff --git a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py index 2721d961bcb9d..2f23179c20893 100644 --- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -5,6 +5,7 @@ import lldb from lldb.plugins.scripted_process import ScriptedProcess from lldb.plugins.scripted_process import ScriptedThread +from lldb.plugins.scripted_process import ScriptedFrame class DummyStopHook: @@ -22,7 +23,7 @@ class DummyScriptedProcess(ScriptedProcess): def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData): super().__init__(exe_ctx, args) - self.threads[0] = DummyScriptedThread(self, None) + self.threads[0] = DummyScriptedThread(self, args) self.memory = {} addr = 0x500000000 debugger = self.target.GetDebugger() @@ -69,6 +70,9 @@ class DummyScriptedThread(ScriptedThread): def __init__(self, process, args): super().__init__(process, args) self.frames.append({"pc": 0x0100001B00}) + self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "baz123")) + self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "bar")) + self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "foo")) def get_thread_id(self) -> int: return 0x19 @@ -109,6 +113,61 @@ def get_register_context(self) -> str: ) +class DummyScriptedFrame(ScriptedFrame): + def __init__(self, thread, args, id, name): + super().__init__(thread, args) + self.id = id + self.name = name + + def get_id(self): + return self.id + + def get_function_name(self): + return self.name + + def get_register_context(self) -> str: + return struct.pack( + "21Q", + 0x10001, + 0x10002, + 0x10003, + 0x10004, + 0x10005, + 0x10006, + 0x10007, + 0x10008, + 0x10009, + 0x100010, + 0x100011, + 0x100012, + 0x100013, + 0x100014, + 0x100015, + 0x100016, + 0x100017, + 0x100018, + 0x100019, + 0x100020, + 0x100021, + ) + + def get_symbol_context(self): + def get_symbol_context_for_function(func_name): + module = self.target.FindModule(self.target.GetExecutable()) + if not module.IsValid(): + return None + + sym_ctx_list = module.FindFunctions(func_name) + if not sym_ctx_list.IsValid() or sym_ctx_list.GetSize() == 0: + return None + + return sym_ctx_list.GetContextAtIndex(0) + + return get_symbol_context_for_function(self.name) + + def get_scripted_frame_plugin(self): + return DummyScriptedFrame.__module__ + "." + DummyScriptedFrame.__name__ + def __lldb_init_module(debugger, dict): # This is used when loading the script in an interactive debug session to # automatically, register the stop-hook and launch the scripted process. _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits