[Lldb-commits] [lldb] [lldb] Expose language plugin commands based based on language of current frame (PR #136766)
Dave Lee via lldb-commits
lldb-commits at lists.llvm.org
Thu Apr 24 14:24:52 PDT 2025
https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/136766
>From daf394bf76b5fd627f77aee6e451e7d706d26916 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Tue, 22 Apr 2025 13:58:25 -0700
Subject: [PATCH 1/6] [lldb] Expose language plugin commands based based on
language of current frame
---
.../lldb/Interpreter/CommandInterpreter.h | 6 ++
.../source/Interpreter/CommandInterpreter.cpp | 55 ++++++++++++++++++-
2 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 724d88d65f6ac..26e0767951e7f 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -730,6 +730,12 @@ class CommandInterpreter : public Broadcaster,
bool EchoCommandNonInteractive(llvm::StringRef line,
const Flags &io_handler_flags) const;
+ /// Return the language specific command object for the current frame.
+ ///
+ /// For example, when stopped on a C++ frame, this returns the command object
+ /// for "language cplusplus" (`CommandObjectMultiwordItaniumABI`).
+ lldb::CommandObjectSP GetFrameLanguageCommand() const;
+
// A very simple state machine which models the command handling transitions
enum class CommandHandlingState {
eIdle,
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index eb4741feb0aa5..2ff02ae5086b4 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -1018,6 +1018,26 @@ CommandInterpreter::VerifyUserMultiwordCmdPath(Args &path, bool leaf_is_command,
return cur_as_multi;
}
+CommandObjectSP CommandInterpreter::GetFrameLanguageCommand() const {
+ if (auto frame_sp = GetExecutionContext().GetFrameSP()) {
+ auto frame_language = Language::GetPrimaryLanguage(
+ frame_sp->GuessLanguage().AsLanguageType());
+
+ auto it = m_command_dict.find("language");
+ if (it != m_command_dict.end()) {
+ // The root "language" command.
+ CommandObjectSP language_cmd_sp = it->second;
+
+ if (auto *plugin = Language::FindPlugin(frame_language)) {
+ // "cplusplus", "objc", etc.
+ auto lang_name = plugin->GetPluginName();
+ return language_cmd_sp->GetSubcommandSPExact(lang_name);
+ }
+ }
+ }
+ return {};
+}
+
CommandObjectSP
CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,
bool exact, StringList *matches,
@@ -1050,11 +1070,20 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,
command_sp = pos->second;
}
+ // The `language` subcommand ("language objc", "language cplusplus", etc).
+ CommandObjectMultiword *lang_subcmd = nullptr;
+ if (!command_sp) {
+ if (auto subcmd_sp = GetFrameLanguageCommand()) {
+ lang_subcmd = subcmd_sp->GetAsMultiwordCommand();
+ command_sp = subcmd_sp->GetSubcommandSPExact(cmd_str);
+ }
+ }
+
if (!exact && !command_sp) {
// We will only get into here if we didn't find any exact matches.
CommandObjectSP user_match_sp, user_mw_match_sp, alias_match_sp,
- real_match_sp;
+ real_match_sp, lang_match_sp;
StringList local_matches;
if (matches == nullptr)
@@ -1064,6 +1093,7 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,
unsigned int num_alias_matches = 0;
unsigned int num_user_matches = 0;
unsigned int num_user_mw_matches = 0;
+ unsigned int num_lang_matches = 0;
// Look through the command dictionaries one by one, and if we get only one
// match from any of them in toto, then return that, otherwise return an
@@ -1121,11 +1151,28 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,
user_mw_match_sp = pos->second;
}
+ if (lang_subcmd) {
+ num_lang_matches =
+ AddNamesMatchingPartialString(lang_subcmd->GetSubcommandDictionary(),
+ cmd_str, *matches, descriptions);
+ }
+
+ if (num_lang_matches == 1) {
+ cmd.assign(matches->GetStringAtIndex(num_cmd_matches + num_alias_matches +
+ num_user_matches +
+ num_user_mw_matches));
+
+ auto &lang_dict = lang_subcmd->GetSubcommandDictionary();
+ auto pos = lang_dict.find(cmd);
+ if (pos != lang_dict.end())
+ lang_match_sp = pos->second;
+ }
+
// If we got exactly one match, return that, otherwise return the match
// list.
if (num_user_matches + num_user_mw_matches + num_cmd_matches +
- num_alias_matches ==
+ num_alias_matches + num_lang_matches ==
1) {
if (num_cmd_matches)
return real_match_sp;
@@ -1133,8 +1180,10 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,
return alias_match_sp;
else if (num_user_mw_matches)
return user_mw_match_sp;
- else
+ else if (num_user_matches)
return user_match_sp;
+ else
+ return lang_match_sp;
}
} else if (matches && command_sp) {
matches->AppendString(cmd_str);
>From 1bbfd1276f9617f6df451700e0ed601c2bd0f341 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 23 Apr 2025 13:04:27 -0700
Subject: [PATCH 2/6] Add test
---
.../API/commands/command/language/Makefile | 3 ++
.../language/TestFrameLanguageCommands.py | 43 +++++++++++++++++++
.../API/commands/command/language/lib.cpp | 3 ++
.../API/commands/command/language/main.mm | 6 +++
4 files changed, 55 insertions(+)
create mode 100644 lldb/test/API/commands/command/language/Makefile
create mode 100644 lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
create mode 100644 lldb/test/API/commands/command/language/lib.cpp
create mode 100644 lldb/test/API/commands/command/language/main.mm
diff --git a/lldb/test/API/commands/command/language/Makefile b/lldb/test/API/commands/command/language/Makefile
new file mode 100644
index 0000000000000..ce845d59ac035
--- /dev/null
+++ b/lldb/test/API/commands/command/language/Makefile
@@ -0,0 +1,3 @@
+OBJCXX_SOURCES := main.mm
+CXX_SOURCES := lib.cpp
+include Makefile.rules
diff --git a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
new file mode 100644
index 0000000000000..89439856470a7
--- /dev/null
+++ b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
@@ -0,0 +1,43 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import lldbsuite.test.lldbutil as lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ _, _, thread, _ = lldbutil.run_to_source_breakpoint(
+ self, "break here", lldb.SBFileSpec("lib.cpp")
+ )
+
+ frame = thread.selected_frame
+ self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeC_plus_plus_11)
+ self.assertEqual(frame.name, "f()")
+ self.expect(
+ "help demangle",
+ substrs=[
+ "Demangle a C++ mangled name.",
+ "Syntax: language cplusplus demangle [<mangled-name> ...]",
+ ],
+ )
+ self.expect("demangle _Z1fv", startstr="_Z1fv ---> f()")
+
+ # Switch the objc caller.
+ self.runCmd("up")
+ frame = thread.selected_frame
+ self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeObjC_plus_plus)
+ self.assertEqual(frame.name, "main")
+ self.expect("help demangle", error=True)
+ self.expect(
+ "help tagged-pointer",
+ substrs=[
+ "Commands for operating on Objective-C tagged pointers.",
+ "Syntax: class-table <subcommand> [<subcommand-options>]",
+ ],
+ )
+ self.expect(
+ "tagged-pointer info 0",
+ error=True,
+ startstr="error: could not convert '0' to a valid address",
+ )
diff --git a/lldb/test/API/commands/command/language/lib.cpp b/lldb/test/API/commands/command/language/lib.cpp
new file mode 100644
index 0000000000000..225d2992d36d2
--- /dev/null
+++ b/lldb/test/API/commands/command/language/lib.cpp
@@ -0,0 +1,3 @@
+#include <stdio.h>
+extern void f();
+void f() { puts("break here"); }
diff --git a/lldb/test/API/commands/command/language/main.mm b/lldb/test/API/commands/command/language/main.mm
new file mode 100644
index 0000000000000..93b87eb4d3176
--- /dev/null
+++ b/lldb/test/API/commands/command/language/main.mm
@@ -0,0 +1,6 @@
+extern void f();
+
+int main() {
+ f();
+ return 0;
+}
>From 57173b13cacd2dc0c593420748731b47ebee30eb Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 23 Apr 2025 13:33:16 -0700
Subject: [PATCH 3/6] Check prefix matching in the test
---
.../language/TestFrameLanguageCommands.py | 20 ++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
index 89439856470a7..5a4c62c5721bb 100644
--- a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
+++ b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
@@ -14,6 +14,8 @@ def test(self):
frame = thread.selected_frame
self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeC_plus_plus_11)
self.assertEqual(frame.name, "f()")
+
+ # Test `help`.
self.expect(
"help demangle",
substrs=[
@@ -21,21 +23,21 @@ def test(self):
"Syntax: language cplusplus demangle [<mangled-name> ...]",
],
)
- self.expect("demangle _Z1fv", startstr="_Z1fv ---> f()")
- # Switch the objc caller.
+ # Run a `language cplusplus` command.
+ self.expect(f"demangle _Z1fv", startstr="_Z1fv ---> f()")
+ # Test prefix matching.
+ self.expect("dem _Z1fv", startstr="_Z1fv ---> f()")
+
+ # Select the objc caller.
self.runCmd("up")
frame = thread.selected_frame
self.assertEqual(frame.GuessLanguage(), lldb.eLanguageTypeObjC_plus_plus)
self.assertEqual(frame.name, "main")
+
+ # Ensure `demangle` doesn't resolve from the objc frame.
self.expect("help demangle", error=True)
- self.expect(
- "help tagged-pointer",
- substrs=[
- "Commands for operating on Objective-C tagged pointers.",
- "Syntax: class-table <subcommand> [<subcommand-options>]",
- ],
- )
+ # Run a `language objc` command.
self.expect(
"tagged-pointer info 0",
error=True,
>From e49a373d678d697b532fb3fbb76dda912e53ad46 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 23 Apr 2025 16:10:35 -0700
Subject: [PATCH 4/6] Change Makefile to (hopefully) load the objc runtime
---
lldb/test/API/commands/command/language/Makefile | 2 ++
.../API/commands/command/language/TestFrameLanguageCommands.py | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/lldb/test/API/commands/command/language/Makefile b/lldb/test/API/commands/command/language/Makefile
index ce845d59ac035..48d511771b0a6 100644
--- a/lldb/test/API/commands/command/language/Makefile
+++ b/lldb/test/API/commands/command/language/Makefile
@@ -1,3 +1,5 @@
OBJCXX_SOURCES := main.mm
+CFLAGS_EXTRAS := -fobjc-arc
CXX_SOURCES := lib.cpp
+LD_EXTRAS := -lobjc
include Makefile.rules
diff --git a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
index 5a4c62c5721bb..3936f06abeb80 100644
--- a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
+++ b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
@@ -25,7 +25,7 @@ def test(self):
)
# Run a `language cplusplus` command.
- self.expect(f"demangle _Z1fv", startstr="_Z1fv ---> f()")
+ self.expect("demangle _Z1fv", startstr="_Z1fv ---> f()")
# Test prefix matching.
self.expect("dem _Z1fv", startstr="_Z1fv ---> f()")
>From 0d267ec6eae56a5745d52338e0febbc33f32be9b Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Thu, 24 Apr 2025 10:11:24 -0700
Subject: [PATCH 5/6] Remove -fobjc-arc (not supported on linux)
---
lldb/test/API/commands/command/language/Makefile | 1 -
1 file changed, 1 deletion(-)
diff --git a/lldb/test/API/commands/command/language/Makefile b/lldb/test/API/commands/command/language/Makefile
index 48d511771b0a6..2d5049417ee70 100644
--- a/lldb/test/API/commands/command/language/Makefile
+++ b/lldb/test/API/commands/command/language/Makefile
@@ -1,5 +1,4 @@
OBJCXX_SOURCES := main.mm
-CFLAGS_EXTRAS := -fobjc-arc
CXX_SOURCES := lib.cpp
LD_EXTRAS := -lobjc
include Makefile.rules
>From f4f687fa0ca78e019311ba0e3f618a257fd3bd7b Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Thu, 24 Apr 2025 14:24:28 -0700
Subject: [PATCH 6/6] Fix test for linux
---
.../command/language/TestFrameLanguageCommands.py | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
index 3936f06abeb80..f11eb8a472939 100644
--- a/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
+++ b/lldb/test/API/commands/command/language/TestFrameLanguageCommands.py
@@ -37,9 +37,14 @@ def test(self):
# Ensure `demangle` doesn't resolve from the objc frame.
self.expect("help demangle", error=True)
+
# Run a `language objc` command.
self.expect(
- "tagged-pointer info 0",
- error=True,
- startstr="error: could not convert '0' to a valid address",
+ "tagged-pointer",
+ substrs=[
+ "Commands for operating on Objective-C tagged pointers.",
+ "Syntax: tagged-pointer <subcommand> [<subcommand-options>]",
+ "The following subcommands are supported:",
+ "info -- Dump information on a tagged pointer.",
+ ],
)
More information about the lldb-commits
mailing list