https://github.com/medismailben created 
https://github.com/llvm/llvm-project/pull/208232

`CommandObjectTargetFrameProviderRegister::DoExecute` never called 
`CommandReturnObject::SetStatus()` on its success path. `CommandObject.cpp` has 
a `DoExecuteStatusCheck` RAII guard that resets the result's status to 
`eReturnStatusInvalid` before `DoExecute` runs, and asserts on exit that 
`DoExecute` changed it. `AppendMessage()`/`AppendMessageWithFormatv()` don't 
touch status (unlike AppendError()/SetError(), which call 
`SetStatus(eReturnStatusFailed)`), so on the success path the status stayed 
eReturnStatusInvalid, tripping the assert.

This went unnoticed because every existing `scripted_frame_provider` test uses 
`SBTarget::RegisterScriptedFrameProvider` directly, bypassing the `target 
frame-provider register` command entirely. Add a regression test that exercises 
the command instead.

>From 9808fd70ab893fb1cbe32c58325095ecc5674a87 Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <[email protected]>
Date: Wed, 8 Jul 2026 06:51:48 -0700
Subject: [PATCH] [lldb] Fix assert when `target frame-provider register`
 succeeds

CommandObjectTargetFrameProviderRegister::DoExecute never called
CommandReturnObject::SetStatus() on its success path. CommandObject.cpp
has a DoExecuteStatusCheck RAII guard that resets the result's status to
eReturnStatusInvalid before DoExecute runs, and asserts on exit that
DoExecute changed it. AppendMessage()/AppendMessageWithFormatv() don't
touch status (unlike AppendError()/SetError(), which call
SetStatus(eReturnStatusFailed)), so on the success path the status stayed
eReturnStatusInvalid, tripping the assert.

This went unnoticed because every existing scripted_frame_provider test
uses SBTarget::RegisterScriptedFrameProvider directly, bypassing the
`target frame-provider register` command entirely. Add a regression test
that exercises the command instead.
---
 lldb/source/Commands/CommandObjectTarget.cpp  |  1 +
 .../register_command_status/Makefile          |  3 ++
 .../TestFrameProviderRegisterCommandStatus.py | 41 +++++++++++++++++++
 .../register_command_status/frame_provider.py | 16 ++++++++
 .../register_command_status/main.c            |  7 ++++
 5 files changed, 68 insertions(+)
 create mode 100644 
lldb/test/API/functionalities/scripted_frame_provider/register_command_status/Makefile
 create mode 100644 
lldb/test/API/functionalities/scripted_frame_provider/register_command_status/TestFrameProviderRegisterCommandStatus.py
 create mode 100644 
lldb/test/API/functionalities/scripted_frame_provider/register_command_status/frame_provider.py
 create mode 100644 
lldb/test/API/functionalities/scripted_frame_provider/register_command_status/main.c

diff --git a/lldb/source/Commands/CommandObjectTarget.cpp 
b/lldb/source/Commands/CommandObjectTarget.cpp
index 28065a47fd413..4ef3a6fe82115 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -6175,6 +6175,7 @@ class CommandObjectTargetFrameProviderRegister : public 
CommandObjectParsed {
     result.AppendMessageWithFormatv(
         "successfully registered scripted frame provider '{0}' for target",
         m_class_options.GetName().c_str());
+    result.SetStatus(eReturnStatusSuccessFinishResult);
   }
 
   OptionGroupPythonClassWithDict m_class_options;
diff --git 
a/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/Makefile
 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/Makefile
new file mode 100644
index 0000000000000..0b710c6e298ae
--- /dev/null
+++ 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -std=c99
+include Makefile.rules
diff --git 
a/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/TestFrameProviderRegisterCommandStatus.py
 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/TestFrameProviderRegisterCommandStatus.py
new file mode 100644
index 0000000000000..06287cadcf182
--- /dev/null
+++ 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/TestFrameProviderRegisterCommandStatus.py
@@ -0,0 +1,41 @@
+"""
+Test that `target frame-provider register` succeeds without asserting.
+
+CommandObjectTargetFrameProviderRegister::DoExecute never called
+CommandReturnObject::SetStatus() on its success path. CommandObject.cpp
+has a DoExecuteStatusCheck RAII guard that asserts DoExecute always sets a
+status; since AppendMessage()/AppendMessageWithFormatv() don't touch
+status (unlike AppendError()/SetError(), which do), the status stayed
+eReturnStatusInvalid on success, tripping the assert.
+
+This regression went unnoticed because every other scripted_frame_provider
+test calls SBTarget::RegisterScriptedFrameProvider directly, bypassing
+this command entirely.
+"""
+
+import os
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestFrameProviderRegisterCommandStatus(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_register_command_succeeds(self):
+        """
+        `target frame-provider register` should complete successfully
+        (and not assert) when given a valid scripted frame provider class.
+        """
+        self.build()
+
+        lldbutil.run_to_name_breakpoint(self, "frame3")
+
+        provider_path = os.path.join(self.getSourceDir(), "frame_provider.py")
+        self.runCmd("command script import " + provider_path)
+
+        self.expect(
+            "target frame-provider register -C frame_provider.MinimalProvider",
+            substrs=["successfully registered scripted frame provider"],
+        )
diff --git 
a/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/frame_provider.py
 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/frame_provider.py
new file mode 100644
index 0000000000000..b66060bc8a77f
--- /dev/null
+++ 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/frame_provider.py
@@ -0,0 +1,16 @@
+"""
+Minimal scripted frame provider used only to exercise `target
+frame-provider register`. get_frame_at_index is never invoked by this
+test: registration alone is enough to trigger the bug under test.
+"""
+
+from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider
+
+
+class MinimalProvider(ScriptedFrameProvider):
+    @staticmethod
+    def get_description():
+        return "minimal provider"
+
+    def get_frame_at_index(self, index):
+        return None
diff --git 
a/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/main.c
 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/main.c
new file mode 100644
index 0000000000000..1aa56e3eddf7a
--- /dev/null
+++ 
b/lldb/test/API/functionalities/scripted_frame_provider/register_command_status/main.c
@@ -0,0 +1,7 @@
+int frame3() { return 3; }
+
+int frame2() { return frame3(); }
+
+int frame1() { return frame2(); }
+
+int main() { return frame1(); }

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to