-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[lldb] Introduce ScriptedFrameProvider for real threads #161870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-lldb Author: Med Ismail Bennani (medismailben) ChangesPatch is 27.35 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/161870.diff 19 Files Affected:
diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h
index e9fe5858d125e..7b91228528bc7 100644
--- a/lldb/include/lldb/API/SBThread.h
+++ b/lldb/include/lldb/API/SBThread.h
@@ -229,6 +229,8 @@ class LLDB_API SBThread {
SBValue GetSiginfo();
+ void RegisterFrameProvider(const char *class_name, SBStructuredData &args_data);
+
private:
friend class SBBreakpoint;
friend class SBBreakpointLocation;
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
new file mode 100644
index 0000000000000..7618d5e15d563
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
@@ -0,0 +1,27 @@
+//===-- ScriptedFrameProviderInterface.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_SCRIPTEDFRAMEPROVIDERINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEPROVIDERINTERFACE_H
+
+#include "lldb/lldb-private.h"
+
+#include "ScriptedInterface.h"
+
+namespace lldb_private {
+class ScriptedFrameProviderInterface : public ScriptedInterface {
+public:
+ virtual llvm::Expected<StructuredData::GenericSP>
+ CreatePluginObject(llvm::StringRef class_name, lldb::ThreadSP thread_sp,
+ StructuredData::DictionarySP args_sp) = 0;
+
+ virtual StructuredData::ArraySP GetStackFrames() { return {}; }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEPROVIDERINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 024bbc90a9a39..76ade002089bb 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -27,6 +27,7 @@
#include "lldb/Host/StreamFile.h"
#include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -536,6 +537,10 @@ class ScriptInterpreter : public PluginInterface {
return {};
}
+ virtual lldb::ScriptedFrameProviderInterfaceSP CreateScriptedFrameProviderInterface() {
+ return {};
+ }
+
virtual lldb::ScriptedThreadPlanInterfaceSP
CreateScriptedThreadPlanInterface() {
return {};
diff --git a/lldb/include/lldb/Interpreter/ScriptedFrameProvider.h b/lldb/include/lldb/Interpreter/ScriptedFrameProvider.h
new file mode 100644
index 0000000000000..6c4053f11eeb3
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/ScriptedFrameProvider.h
@@ -0,0 +1,51 @@
+//===-- ScriptedFrameProvider.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_SCRIPTEDFRAMEPROVIDER_H
+#define LLDB_INTERPRETER_SCRIPTEDFRAMEPROVIDER_H
+
+#include "lldb/Utility/ScriptedMetadata.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+
+class ScriptedFrameProvider {
+public:
+ /// Constructor that initializes the scripted frame provider.
+ ///
+ /// \param[in] thread_sp
+ /// The thread for which to provide scripted frames.
+ ///
+ /// \param[in] scripted_metadata
+ /// The metadata containing the class name and arguments for the
+ /// scripted frame provider.
+ ///
+ /// \param[out] error
+ /// Status object to report any errors during initialization.
+ ScriptedFrameProvider(lldb::ThreadSP thread_sp,
+ const ScriptedMetadata &scripted_metadata,
+ Status &error);
+ ~ScriptedFrameProvider();
+
+ /// Get the stack frames from the scripted frame provider.
+ ///
+ /// \return
+ /// An Expected containing the StackFrameListSP if successful,
+ /// otherwise an error describing what went wrong.
+ llvm::Expected<lldb::StackFrameListSP> GetStackFrames();
+
+private:
+ lldb::ThreadSP m_thread_sp;
+ lldb::ScriptedFrameProviderInterfaceSP m_interface_sp;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_SCRIPTEDFRAMEPROVIDER_H
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 688c056da2633..d0c33f557b12b 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -1294,6 +1294,10 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// The PC value before execution was resumed. May not be available;
/// an empty std::optional is returned in that case.
std::optional<lldb::addr_t> GetPreviousFrameZeroPC();
+
+ void SetScriptedFrameProvider(const ScriptedMetadata &scripted_metadata);
+
+ void ClearScriptedFrameProvider();
protected:
friend class ThreadPlan;
@@ -1338,6 +1342,8 @@ class Thread : public std::enable_shared_from_this<Thread>,
lldb::StackFrameListSP GetStackFrameList();
+ llvm::Expected<lldb::StackFrameListSP> GetScriptedFrameList();
+
void SetTemporaryResumeState(lldb::StateType new_state) {
m_temporary_resume_state = new_state;
}
@@ -1400,6 +1406,9 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// The Thread backed by this thread, if any.
lldb::ThreadWP m_backed_thread;
+ /// The Scripted Frame Provider, if any.
+ lldb::ScriptedFrameProviderSP m_frame_provider_sp;
+
private:
bool m_extended_info_fetched; // Have we tried to retrieve the m_extended_info
// for this thread?
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index af5656b3dcad1..85045a803b07a 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -188,6 +188,8 @@ class Scalar;
class ScriptInterpreter;
class ScriptInterpreterLocker;
class ScriptedFrameInterface;
+class ScriptedFrameProvider;
+class ScriptedFrameProviderInterface;
class ScriptedMetadata;
class ScriptedBreakpointInterface;
class ScriptedPlatformInterface;
@@ -411,6 +413,10 @@ typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
ScriptedFrameInterfaceSP;
+typedef std::shared_ptr<lldb_private::ScriptedFrameProvider>
+ ScriptedFrameProviderSP;
+typedef std::shared_ptr<lldb_private::ScriptedFrameProviderInterface>
+ ScriptedFrameProviderInterfaceSP;
typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
ScriptedPlatformInterfaceUP;
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 4e4aa48bc9a2e..a18d540f2a017 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -39,6 +39,7 @@
#include "lldb/Target/ThreadPlanStepOut.h"
#include "lldb/Target/ThreadPlanStepRange.h"
#include "lldb/Utility/Instrumentation.h"
+#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/Utility/State.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StructuredData.h"
@@ -1324,3 +1325,29 @@ SBValue SBThread::GetSiginfo() {
return SBValue();
return thread_sp->GetSiginfoValue();
}
+
+void SBThread::RegisterFrameProvider(const char *class_name,
+ SBStructuredData &dict) {
+ LLDB_INSTRUMENT_VA(this, class_name, args_data);
+
+ ThreadSP thread_sp = m_opaque_sp->GetThreadSP();
+ if (!thread_sp)
+ return;
+
+ if (!dict.IsValid() || !dict.m_impl_up)
+ return;
+
+ StructuredData::ObjectSP obj_sp = dict.m_impl_up->GetObjectSP();
+
+ if (!obj_sp)
+ return;
+
+ StructuredData::DictionarySP dict_sp =
+ std::make_shared<StructuredData::Dictionary>(obj_sp);
+ if (!dict_sp || dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
+ return;
+
+
+ ScriptedMetadata metadata(class_name, dict_sp);
+ thread_sp->SetScriptedFrameProvider(metadata);
+}
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 88a02dce35b9d..02d62aa9249d1 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -16,6 +16,7 @@
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionArgParser.h"
#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
#include "lldb/Interpreter/OptionGroupVariable.h"
#include "lldb/Interpreter/Options.h"
@@ -29,6 +30,7 @@
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/ValueObject/ValueObject.h"
#include <memory>
@@ -1223,6 +1225,97 @@ class CommandObjectFrameRecognizer : public CommandObjectMultiword {
~CommandObjectFrameRecognizer() override = default;
};
+#pragma mark CommandObjectFrameProvider
+
+#define LLDB_OPTIONS_frame_provider_register
+#include "CommandOptions.inc"
+
+class CommandObjectFrameProviderRegister : public CommandObjectParsed {
+public:
+ CommandObjectFrameProviderRegister(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "frame provider register",
+ "Register frame provider into current thread.",
+ nullptr, eCommandRequiresThread),
+
+ m_class_options("frame provider", true, 'C', 'k', 'v', 0) {
+ m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
+ LLDB_OPT_SET_ALL);
+ m_all_options.Finalize();
+
+ AddSimpleArgumentList(eArgTypeRunArgs, eArgRepeatOptional);
+ }
+
+ ~CommandObjectFrameProviderRegister() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+ std::optional<std::string> GetRepeatCommand(Args ¤t_command_args,
+ uint32_t index) override {
+ // No repeat for "process launch"...
+ return std::string("");
+ }
+
+protected:
+ void DoExecute(Args &launch_args, CommandReturnObject &result) override {
+ ScriptedMetadata metadata(m_class_options.GetName(), m_class_options.GetStructuredData());
+
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (!thread) {
+ result.AppendError("invalid thread");
+ return;
+ }
+
+ thread->SetScriptedFrameProvider(metadata);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ result.AppendMessageWithFormat(
+ "Successfully registered scripted frame provider '%s'\n",
+ m_class_options.GetName().c_str());
+ }
+
+ OptionGroupPythonClassWithDict m_class_options;
+ OptionGroupOptions m_all_options;
+};
+
+class CommandObjectFrameProviderClear : public CommandObjectParsed {
+public:
+ CommandObjectFrameProviderClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame provider clear",
+ "Delete registered frame provider.", nullptr) {}
+
+ ~CommandObjectFrameProviderClear() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (!thread) {
+ result.AppendError("invalid thread");
+ return;
+ }
+
+ thread->ClearScriptedFrameProvider();
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+class CommandObjectFrameProvider : public CommandObjectMultiword {
+public:
+ CommandObjectFrameProvider(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "frame provider",
+ "Commands for registering and viewing frame providers.",
+ "frame provider [<sub-command-options>] ") {
+ LoadSubCommand("register", CommandObjectSP(new CommandObjectFrameProviderRegister(
+ interpreter)));
+ LoadSubCommand(
+ "clear",
+ CommandObjectSP(new CommandObjectFrameProviderClear(interpreter)));
+ }
+
+ ~CommandObjectFrameProvider() override = default;
+};
+
#pragma mark CommandObjectMultiwordFrame
// CommandObjectMultiwordFrame
@@ -1243,6 +1336,8 @@ CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
LoadSubCommand("variable",
CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
#if LLDB_ENABLE_PYTHON
+ LoadSubCommand("provider", CommandObjectSP(new CommandObjectFrameProvider(
+ interpreter)));
LoadSubCommand("recognizer", CommandObjectSP(new CommandObjectFrameRecognizer(
interpreter)));
#endif
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index bbec714642ec9..0092151a13dd8 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -35,6 +35,7 @@
#include "lldb/Target/ThreadPlanStepInRange.h"
#include "lldb/Target/Trace.h"
#include "lldb/Target/TraceDumper.h"
+#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/Utility/State.h"
#include "lldb/ValueObject/ValueObject.h"
diff --git a/lldb/source/Interpreter/CMakeLists.txt b/lldb/source/Interpreter/CMakeLists.txt
index 8af7373702c38..ab877ddeecba8 100644
--- a/lldb/source/Interpreter/CMakeLists.txt
+++ b/lldb/source/Interpreter/CMakeLists.txt
@@ -53,6 +53,7 @@ add_lldb_library(lldbInterpreter NO_PLUGIN_DEPENDENCIES
OptionGroupWatchpoint.cpp
Options.cpp
Property.cpp
+ ScriptedFrameProvider.cpp
ScriptInterpreter.cpp
ADDITIONAL_HEADER_DIRS
diff --git a/lldb/source/Interpreter/ScriptedFrameProvider.cpp b/lldb/source/Interpreter/ScriptedFrameProvider.cpp
new file mode 100644
index 0000000000000..b35ed5d20f6ba
--- /dev/null
+++ b/lldb/source/Interpreter/ScriptedFrameProvider.cpp
@@ -0,0 +1,86 @@
+//===-- ScriptedFrameProvider.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/Interpreter/ScriptedFrameProvider.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ScriptedMetadata.h"
+#include "lldb/Utility/Status.h"
+#include "llvm/Support/Error.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ScriptedFrameProvider::ScriptedFrameProvider(
+ ThreadSP thread_sp, const ScriptedMetadata &scripted_metadata,
+ Status &error)
+ : m_thread_sp(thread_sp), m_interface_sp(nullptr) {
+ if (!m_thread_sp) {
+ error = Status::FromErrorString(
+ "cannot create scripted frame provider: Invalid thread");
+ return;
+ }
+
+ ProcessSP process_sp = m_thread_sp->GetProcess();
+ if (!process_sp) {
+ error = Status::FromErrorString(
+ "cannot create scripted frame provider: Invalid process");
+ return;
+ }
+
+ ScriptInterpreter *script_interp =
+ process_sp->GetTarget().GetDebugger().GetScriptInterpreter();
+ if (!script_interp) {
+ error = Status::FromErrorString("cannot create scripted frame provider: No "
+ "script interpreter installed");
+ return;
+ }
+
+ m_interface_sp = script_interp->CreateScriptedFrameProviderInterface();
+ if (!m_interface_sp) {
+ error = Status::FromErrorString(
+ "cannot create scripted frame provider: Script interpreter couldn't "
+ "create Scripted Frame Provider Interface");
+ return;
+ }
+
+ auto obj_or_err = m_interface_sp->CreatePluginObject(
+ scripted_metadata.GetClassName(), m_thread_sp,
+ scripted_metadata.GetArgsSP());
+ if (!obj_or_err) {
+ error = Status::FromError(obj_or_err.takeError());
+ return;
+ }
+
+ StructuredData::ObjectSP object_sp = *obj_or_err;
+ if (!object_sp || !object_sp->IsValid()) {
+ error = Status::FromErrorString(
+ "cannot create scripted frame provider: Failed to create valid script "
+ "object");
+ return;
+ }
+
+ error.Clear();
+}
+
+ScriptedFrameProvider::~ScriptedFrameProvider() = default;
+
+llvm::Expected<StackFrameListSP> ScriptedFrameProvider::GetStackFrames() {
+ if (!m_interface_sp)
+ return llvm::createStringError(
+ "cannot get stack frames: Scripted frame provider not initialized");
+
+ auto frames = m_interface_sp->GetStackFrames();
+
+ // TODO: Convert StructuredData::ArraySP to StackFrameListSP
+ // This is a placeholder for now
+ return nullptr;
+}
\ No newline at end of file
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
index 09103573b89c5..50569cdefaafa 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
@@ -23,6 +23,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
OperatingSystemPythonInterface.cpp
ScriptInterpreterPythonInterfaces.cpp
ScriptedFramePythonInterface.cpp
+ ScriptedFrameProviderPythonInterface.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 3814f46615078..0b9c7eb107bf5 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -18,6 +18,7 @@
#include "OperatingSystemPythonInterface.h"
#include "ScriptedBreakpointPythonInterface.h"
#include "ScriptedFramePythonInterface.h"
+#include "ScriptedFrameProviderPythonInterface.h"
#include "ScriptedPlatformPythonInterface.h"
#include "ScriptedProcessPythonInterface.h"
#include "ScriptedStopHookPythonInterface.h"
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
new file mode 100644
index 0000000000000..b9a659c44e6f1
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
@@ -0,0 +1,57 @@
+//===-- ScriptedFrameProviderPythonInterface.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/Thread.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 "ScriptedFrameProviderPythonInterface.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+ScriptedFrameProviderPythonInterface::Scripte...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
89e7ab6 to
9b9d08e
Compare
5fd3ebc to
70d72a2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Being able to modify the frame list from a script would indeed be amazing - thanks for looking into this! 🙂
As it happens, I am currently looking into gdb's "frame filter" API, which serves a very similar use case - and I am wondering if we should take some inspiration from the gdb APIs here. Gdb's frame filter API differs from the API in this PR in the following points:
- simpler API: gdb's API only has a single function
filter(frame_iter) -> frame_iterfunction. (This PR currently introduces two functions:get_merge_strategyandget_stack_frames) - lazy stack unwinding: afaict, your patch leads to eager materialization of all stack frames. Given that stacks can be pretty deep, it would be preferable to only create those stack frames lazily, as the users inspects the stack. gdb achieves this by only advancing the returned iterator as needed
- more flexible merging strategies: in gdb, I can use all of Python's iterator support (generator expressions,
yield, ...) - global registration: in gdb, frame filters are registered globally for all threads. In this PR, the frame providers are registered for each thread individually
- multiple frame filters: gdb supports registering multiple frame filters at the same time. I didn't use that mechanism, yet, but afaict they are simply chained
The relevant pieces of gdb's documentation:
- Writing a frame filter - high-level introduction
- Frame filter API - corresponding API reference
- Frame decorators API - close equivalent to lldb's
ScriptedFrame - Frame filter management - end users can change enable, disable and reorder frame filters
My motivation for using frame filters in gdb are C++ couroutines. I want to add frames for asynchronous operations. For more background see LLVM's documentatin on Async Stack Traces, in particular the coro bt example. At least for my use case, gdb's design decisions (global registration, flexible merging strategy, ...) are a pretty good fit. But not sure, maybe your current design is a better fit for your particular use case? Which use case are you envisioning?
By the way: would discourse be better-suited for discussing the best approach here? Happy to copy my reply over to discourse, if you would start a thread
|
Hello! Thanks for your interest in this PR.
A non-goal for this PR and for lldb in general is copy what gdb or other debuggers do.
That sounds pretty exciting. I think ScriptedFrameProvider would be a great fit to support coroutines as long as the ABI doesn't spawn new threads for that. My current use case for this is to translate CPython frames into something that's more understandable, to hopefully improve debugging python / C++ interoperability in lldb.
|
While compatibility is not generally a goal, there's also no reason to diverge for the sake of it. However, what I think Adrian is suggesting here, is that we can benefit from building on top of the learnings of an existing implementation. I think he raises some good points that are worth evaluating in the context of LLDB. |
70d72a2 to
ab4a9b8
Compare
yes. Thanks for summing it up! Use cases
I polished my existing script and now posted it in PR #162145. The most interesting piece is The two lines prefixed with
Could you provide an example of a I am currently slightly struggling with imagining the intended usage of the proposed APIs, and such an example might help me. (Of course only if possible and not blocked, e.g., due to intellectual property issues) |
ba84a60 to
9b2a82d
Compare
|
@vogelsgesang I've added a new I'm still waiting on @jimingham feedback on this before move forward but let me know if this newer approach fits better into your coroutines workflow. |
9b2a82d to
5b21aa8
Compare
|
Thanks for making the frame iteration lazy!
There are still two larger issues and one smaller:
Lazy stack unwinding Unfortunately, I don't think that Merging strategy None of the provided merging strategies (replace, prepend, append, replace-by-index) matches the need for coroutine stack traces. I need to splice in additional stack frames in the middle of real, physical stack frames. I still think that the best, most future proof solution here would be to delegate the merging strategy to the script. I.e. have the interface be Registration At least for my use case, I would like to globally register the frame filter for all threads. CPython Use CaseI think that at least "Lazy stack unwinding" and "Registration" would also be issues for your CPython use case. You probably don't want to eagerly unwind the full stack to identify which frames to replace (afaik, the CPython frames could be arbitrarily far up in the call stack and you wouldn't have a way to terminate the stack traversal in Furthermore, it would probably also be more convenient for your users if your script automatically becomes active for all relevant CPython frames on all threads, without the user having associate individual threads with your frame provider? But maybe I am still misunderstanding your use case? Can you share your CPython frame provider script? |
@vogelsgesang Have you considered writing a custom unwinder for this? If you want stack frames from which you can perform step operations, I think you need to go down language-plugin-unwinder route. This is what we do for swift async functions (which have a lot of similarities to C++ coroutines). |
I didn't know about that API, yet. Where can I learn more about it? Could those custom unwinders also solve @medismailben's CPython-frame-debugging use case?
That would be amazing. Is there any relevant documentation / examples where I could learn from? E.g., in Apple's open-source LLVM version? Maybe there are even publicly available design documents? |
|
There are two parts to this project. One is how to make synthetic stack frames that can be consed up and returned by some kind of scripted stack frame list entity. The other is how to structure the entity that uses synthetic stack frames to re-present the original StackFrameList of a thread. I'm mostly commenting on the second part of the design here. I agree with the other people commenting here that the latter part of the design is trying to help where it doesn't need to and ends up being confusing. To start with, the StackFrameList is better thought of as a generator of StackFrames that answers "give me stack frame at index N" than something that has a list of all the frames in it. So instead of thinking about this as a static process of "inserting or replacing frames in a stack frame list" it would be clearer if this design where we're re-presenting extant StackFramesLists from a thread - not creating them from whole cloth like the ScriptedThreads do - were modeled as a sequence of generators that uses some incoming StackFrameList generator as the source for it's reply to "give me stack frame at index N". So this would look something like:
As an aside, I don't think the notion of an unwinder is the right model. The unwinder's job is "Given Stack Frame N, produce THE frame one older than it." But for instance, way back in the day I did a C-Stack to Tcl stack transmogrifier (this was in gdbtk and some 30 years ago...) based on the fact that I could recognize the C stack frame sequence corresponding to one Tcl function invocation (the sequence was 3 or 4 frames long, I can't remember the details now). So in my transmogrifier, I ran through the incoming C stack and if I saw this pattern of C Frames, I elided them into a single frame that had the Tcl function source, and variables and presented that as a single frame, with all the indexes suitably adjusted for the elision. So the notion we want here is not an unwinder but a transmogrifier that takes a source stack frame list and produces another StackFrameList, or really "intercepts the request for 'give me frame at index N" and uses its stackframelist StackFrameList to answer the question. I don't think we need to be more specific about it than that, so I don't for instance see the various insertion modes as useful. The same transmogrifier might do any of those operations depending on what it sees in the StackFrameList it was given to work on. Instead, when you ask a thread for the "stack frame at index 5" from a thread, this would just ask the top-most transmogrifier to produce the stack up to frame 5. We don't need to care how it managed that. If there are no stack frame provider(s) attached to the thread (or the user did If there were a transmogrifier added then it would ask the ScriptedStackFrameList to use its transmogrifier and the StackFrameList it is based off for the requested frame. That would do whatever it wanted (I don't think we need to get involved with that) and produce frame 5. This would very naturally chain, since when you added a second ScriptedStackFrameList to the Thread, it would get the first Scripted StackFrameList as its source StackFrameList. |
|
BTW, I used the term |
|
As far as registration goes, I think we should probably have a AppliesToThread type API as part of the ScriptedStackFrameList API. Some of these providers might very well know "All the frames I am going to recognize are named with some recognizable pattern". Or they might want to have a look at all frames. However, there should also be a way to override this from the command line or with the SBThread API so if I say something like:
Since we don't want to force unwinding except when we really have to, the AppliesToThread should only check static data about the thread (like its name, etc.). If your provider doesn't know whether it would apply without backtracing, it's probably better to just always ask it when backtracking the threads. That's presuming that the task of "I don't have anything to add to the raw stack frame" is cheap, which it should be. If you don't implement applies_to_thread, then it will default to getting inserted on all new thread. |
|
And as far as stepping goes - which this PR doesn't really address - we will also need to have an API in the StackFrameList provider like
If there was a synthetic StackFrameList, it would decide what next meant to it in this frame, and return a thread plan that makes that happen using either "RunToBreakpoint" thread plans or any of the other primitive types. For instance, if I've done my Tcl transmogrification, and I can tell that the state of the Tcl C frames that it is stopped at line 4 of some Tcl source, then I can cons up a thread plan to put a breakpoint on where the implementation code fetches the next source line or break on some "new context" observer function if the language I'm emulating provides that, and then run till that shows a changed source line. I do think this job needs to be done in the Provider, and not hung off a particular SyntheticStackFrame. I might want "step out of one of my synthetic Tcl stack frames" to mean finding the Tcl frame that's next older on the C Stack (in Tcl the C & Tcl stacks mirror one another) and just run till we get back to that. I don't think the StackFrame I'm stepping out of can know how to do that on its own. That seems more like a job for the overall provider. And if we have a general mechanism we don't have to know about any of how this works, that's the provider's job. Then, since the scripted StackFrameList provider always ends at the "natural" stack frame list, when the oldest SyntheticStackFrameList requests PerformSteppingOperation, that will go to the "natural" StackFrameList, whose PerformSteppingOperation will just return the current StepOver, etc thread plans for that thread. I think this shouldn't be that difficult to implement, and we already have support for writing scripted thread plans so the providers can do pretty much anything execution-control-wise that lldb can do internally. So this should be pretty flexible. (*) This isn't the complete API since some of the stepping operations take parameters, so we'll need to come up with a way to express those as well. |
|
I left a bunch of individual comments. Beyond that I think a couple of UE improvements will really help. I think you should be able to enable and disable these - after all you might have some package that adds these and you want the other things in the package but don't want to turn on the frame providers now. If you clear them, you have to remember how you got them in the first place which might not be clear. |
d3e543f to
836bdae
Compare
|
I have three small quibbles and then this is good. There was one place where you missed provider-name -> provider-id. I think |
836bdae to
106f215
Compare
jimingham
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with the trivial provider -> providers swap.
106f215 to
d5516f6
Compare
This patch extends ScriptedFrame to work with real (non-scripted) threads, enabling frame providers to synthesize frames for native processes. Previously, ScriptedFrame only worked within ScriptedProcess/ScriptedThread contexts. This patch decouples ScriptedFrame from ScriptedThread, allowing users to augment or replace stack frames in real debugging sessions for use cases like custom calling conventions, reconstructing corrupted frames from core files, or adding diagnostic frames. Key changes: - ScriptedFrame::Create() now accepts ThreadSP instead of requiring ScriptedThread, extracting architecture from the target triple rather than ScriptedProcess.arch - Added SBTarget::RegisterScriptedFrameProvider() and ClearScriptedFrameProvider() APIs, with Target storing a SyntheticFrameProviderDescriptor template for new threads - Added "target frame-provider register/clear" commands for CLI access - Thread class gains LoadScriptedFrameProvider(), ClearScriptedFrameProvider(), and GetFrameProvider() methods for per-thread frame provider management - New SyntheticStackFrameList overrides FetchFramesUpTo() to lazily provide frames from either the frame provider or the real stack This enables practical use of the SyntheticFrameProvider infrastructure in real debugging workflows. rdar://161834688 Signed-off-by: Med Ismail Bennani <[email protected]>
d5516f6 to
88c30af
Compare
This patch extends ScriptedFrame to work with real (non-scripted) threads, enabling frame providers to synthesize frames for native processes. Previously, ScriptedFrame only worked within ScriptedProcess/ScriptedThread contexts. This patch decouples ScriptedFrame from ScriptedThread, allowing users to augment or replace stack frames in real debugging sessions for use cases like custom calling conventions, reconstructing corrupted frames from core files, or adding diagnostic frames. Key changes: - ScriptedFrame::Create() now accepts ThreadSP instead of requiring ScriptedThread, extracting architecture from the target triple rather than ScriptedProcess.arch - Added SBTarget::RegisterScriptedFrameProvider() and ClearScriptedFrameProvider() APIs, with Target storing a SyntheticFrameProviderDescriptor template for new threads - Added "target frame-provider register/clear" commands for CLI access - Thread class gains LoadScriptedFrameProvider(), ClearScriptedFrameProvider(), and GetFrameProvider() methods for per-thread frame provider management - New SyntheticStackFrameList overrides FetchFramesUpTo() to lazily provide frames from either the frame provider or the real stack This enables practical use of the SyntheticFrameProvider infrastructure in real debugging workflows. rdar://161834688 Signed-off-by: Med Ismail Bennani <[email protected]> Signed-off-by: Med Ismail Bennani <[email protected]> (cherry picked from commit 1e467e4)
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/18/builds/22598 Here is the relevant piece of the build log for the reference |
|
Looks like this is failing on the public macOS bots (both x86 and arm64): https://green.lab.llvm.org/job/llvm.org/view/LLDB/job/as-lldb-cmake/36221/execution/node/106/log/ Reverting for now |
)" This reverts commit 1e467e4.
|
I'm looking at the Arm failures. First part is probably a Thumb function, second part might be a codegen difference. |
…7662) The new test fails on x86 and arm64 public macOS bots: ``` 09:27:59 ====================================================================== 09:27:59 FAIL: test_append_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can add frames after real stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 122, in test_append_frames 09:27:59 self.assertEqual(new_frame_count, original_frame_count + 1) 09:27:59 AssertionError: 5 != 6 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_applies_to_thread (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that applies_to_thread filters which threads get the provider. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 218, in test_applies_to_thread 09:27:59 self.assertEqual( 09:27:59 AssertionError: 5 != 1 : Thread with ID 1 should have 1 synthetic frame 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_prepend_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can add frames before real stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 84, in test_prepend_frames 09:27:59 self.assertEqual(new_frame_count, original_frame_count + 2) 09:27:59 AssertionError: 5 != 7 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_remove_frame_provider_by_id (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that RemoveScriptedFrameProvider removes a specific provider by ID. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 272, in test_remove_frame_provider_by_id 09:27:59 self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") 09:27:59 AssertionError: 5 != 3 : Should have 3 synthetic frames 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_replace_all_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can replace the entire stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 41, in test_replace_all_frames 09:27:59 self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") 09:27:59 AssertionError: 5 != 3 : Should have 3 synthetic frames 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_scripted_frame_objects (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that provider can return ScriptedFrame objects. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 159, in test_scripted_frame_objects 09:27:59 self.assertEqual(frame0.GetFunctionName(), "custom_scripted_frame_0") 09:27:59 AssertionError: 'thread_func(int)' != 'custom_scripted_frame_0' 09:27:59 - thread_func(int) 09:27:59 + custom_scripted_frame_0 09:27:59 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ---------------------------------------------------------------------- 09:27:59 Ran 6 tests in 14.242s 09:27:59 09:27:59 FAILED (failures=6) ``` Reverts #161870
|
The PC value is because we know we're in Arm mode. Something is removing the bottom bit of the fake PC value to reflect that. Which is probably fine. The other failure is that we get no frames at all: Though the provider was registered successfully.
Since you've got MacOS things to fix I won't dig into this now. Fix those and if it still fails on Arm I'll look at it again. |
…reads" (#167662) The new test fails on x86 and arm64 public macOS bots: ``` 09:27:59 ====================================================================== 09:27:59 FAIL: test_append_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can add frames after real stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 122, in test_append_frames 09:27:59 self.assertEqual(new_frame_count, original_frame_count + 1) 09:27:59 AssertionError: 5 != 6 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_applies_to_thread (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that applies_to_thread filters which threads get the provider. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 218, in test_applies_to_thread 09:27:59 self.assertEqual( 09:27:59 AssertionError: 5 != 1 : Thread with ID 1 should have 1 synthetic frame 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_prepend_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can add frames before real stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 84, in test_prepend_frames 09:27:59 self.assertEqual(new_frame_count, original_frame_count + 2) 09:27:59 AssertionError: 5 != 7 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_remove_frame_provider_by_id (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that RemoveScriptedFrameProvider removes a specific provider by ID. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 272, in test_remove_frame_provider_by_id 09:27:59 self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") 09:27:59 AssertionError: 5 != 3 : Should have 3 synthetic frames 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_replace_all_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can replace the entire stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 41, in test_replace_all_frames 09:27:59 self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") 09:27:59 AssertionError: 5 != 3 : Should have 3 synthetic frames 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_scripted_frame_objects (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that provider can return ScriptedFrame objects. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 159, in test_scripted_frame_objects 09:27:59 self.assertEqual(frame0.GetFunctionName(), "custom_scripted_frame_0") 09:27:59 AssertionError: 'thread_func(int)' != 'custom_scripted_frame_0' 09:27:59 - thread_func(int) 09:27:59 + custom_scripted_frame_0 09:27:59 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ---------------------------------------------------------------------- 09:27:59 Ran 6 tests in 14.242s 09:27:59 09:27:59 FAILED (failures=6) ``` Reverts llvm/llvm-project#161870
|
Thanks @DavidSpickett and @Michael137 for taking care of this. I'll try to reproduce the issue locally. |
…m#167662) The new test fails on x86 and arm64 public macOS bots: ``` 09:27:59 ====================================================================== 09:27:59 FAIL: test_append_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can add frames after real stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 122, in test_append_frames 09:27:59 self.assertEqual(new_frame_count, original_frame_count + 1) 09:27:59 AssertionError: 5 != 6 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_applies_to_thread (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that applies_to_thread filters which threads get the provider. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 218, in test_applies_to_thread 09:27:59 self.assertEqual( 09:27:59 AssertionError: 5 != 1 : Thread with ID 1 should have 1 synthetic frame 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_prepend_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can add frames before real stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 84, in test_prepend_frames 09:27:59 self.assertEqual(new_frame_count, original_frame_count + 2) 09:27:59 AssertionError: 5 != 7 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_remove_frame_provider_by_id (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that RemoveScriptedFrameProvider removes a specific provider by ID. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 272, in test_remove_frame_provider_by_id 09:27:59 self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") 09:27:59 AssertionError: 5 != 3 : Should have 3 synthetic frames 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_replace_all_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that we can replace the entire stack. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 41, in test_replace_all_frames 09:27:59 self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") 09:27:59 AssertionError: 5 != 3 : Should have 3 synthetic frames 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ====================================================================== 09:27:59 FAIL: test_scripted_frame_objects (TestScriptedFrameProvider.ScriptedFrameProviderTestCase) 09:27:59 Test that provider can return ScriptedFrame objects. 09:27:59 ---------------------------------------------------------------------- 09:27:59 Traceback (most recent call last): 09:27:59 File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 159, in test_scripted_frame_objects 09:27:59 self.assertEqual(frame0.GetFunctionName(), "custom_scripted_frame_0") 09:27:59 AssertionError: 'thread_func(int)' != 'custom_scripted_frame_0' 09:27:59 - thread_func(int) 09:27:59 + custom_scripted_frame_0 09:27:59 09:27:59 Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang 09:27:59 ---------------------------------------------------------------------- 09:27:59 Ran 6 tests in 14.242s 09:27:59 09:27:59 FAILED (failures=6) ``` Reverts llvm#161870
This patch extends ScriptedFrame to work with real (non-scripted) threads, enabling frame providers to synthesize frames for native processes. Previously, ScriptedFrame only worked within ScriptedProcess/ScriptedThread contexts. This patch decouples ScriptedFrame from ScriptedThread, allowing users to augment or replace stack frames in real debugging sessions for use cases like custom calling conventions, reconstructing corrupted frames from core files, or adding diagnostic frames. Key changes: - ScriptedFrame::Create() now accepts ThreadSP instead of requiring ScriptedThread, extracting architecture from the target triple rather than ScriptedProcess.arch - Added SBTarget::RegisterScriptedFrameProvider() and ClearScriptedFrameProvider() APIs, with Target storing a SyntheticFrameProviderDescriptor template for new threads - Added "target frame-provider register/clear" commands for CLI access - Thread class gains LoadScriptedFrameProvider(), ClearScriptedFrameProvider(), and GetFrameProvider() methods for per-thread frame provider management - New SyntheticStackFrameList overrides FetchFramesUpTo() to lazily provide frames from either the frame provider or the real stack This enables practical use of the SyntheticFrameProvider infrastructure in real debugging workflows. rdar://161834688 Signed-off-by: Med Ismail Bennani <[email protected]> Signed-off-by: Med Ismail Bennani <[email protected]>
This patch extends ScriptedFrame to work with real (non-scripted) threads,
enabling frame providers to synthesize frames for native processes.
Previously, ScriptedFrame only worked within ScriptedProcess/ScriptedThread
contexts. This patch decouples ScriptedFrame from ScriptedThread, allowing
users to augment or replace stack frames in real debugging sessions for use
cases like custom calling conventions, reconstructing corrupted frames from
core files, or adding diagnostic frames.
Key changes:
ScriptedFrame::Create() now accepts ThreadSP instead of requiring
ScriptedThread, extracting architecture from the target triple rather
than ScriptedProcess.arch
Added SBTarget::RegisterScriptedFrameProvider() and
ClearScriptedFrameProvider() APIs, with Target storing a
SyntheticFrameProviderDescriptor template for new threads
Added "target frame-provider register/clear" commands for CLI access
Thread class gains LoadScriptedFrameProvider(), ClearScriptedFrameProvider(),
and GetFrameProvider() methods for per-thread frame provider management
New SyntheticStackFrameList overrides FetchFramesUpTo() to lazily provide
frames from either the frame provider or the real stack
This enables practical use of the SyntheticFrameProvider infrastructure in
real debugging workflows.
rdar://161834688
Signed-off-by: Med Ismail Bennani [email protected]