Skip to content
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

[lldb-vscode] Allow specifying a custom escape prefix for LLDB commands #69238

Merged
merged 1 commit into from Oct 25, 2023

Conversation

walter-erquinigo
Copy link
Member

We've been using the backtick as our escape character, however that leads to a weird experience on VS Code, because on most hosts, as soon as you type the backtick on VS Code, the IDE will introduce another backtick. As changing the default escape character might be out of question because other plugins might rely on it, we can instead introduce an option to change this variable upon lldb-vscode initialization.
FWIW, my users will be using : instead ot the backtick.

@llvmbot
Copy link
Collaborator

llvmbot commented Oct 16, 2023

@llvm/pr-subscribers-lldb

Author: Walter Erquinigo (walter-erquinigo)

Changes

We've been using the backtick as our escape character, however that leads to a weird experience on VS Code, because on most hosts, as soon as you type the backtick on VS Code, the IDE will introduce another backtick. As changing the default escape character might be out of question because other plugins might rely on it, we can instead introduce an option to change this variable upon lldb-vscode initialization.
FWIW, my users will be using : instead ot the backtick.


Full diff: https://github.com/llvm/llvm-project/pull/69238.diff

7 Files Affected:

  • (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py (+6-4)
  • (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py (+2-2)
  • (modified) lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py (+17-4)
  • (modified) lldb/tools/lldb-vscode/Options.td (+6)
  • (modified) lldb/tools/lldb-vscode/VSCode.cpp (+5-3)
  • (modified) lldb/tools/lldb-vscode/VSCode.h (+1)
  • (modified) lldb/tools/lldb-vscode/lldb-vscode.cpp (+10)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
index 8cd4e8454c89099..1869a5579f0a12e 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
@@ -8,7 +8,7 @@
 class VSCodeTestCaseBase(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    def create_debug_adaptor(self, lldbVSCodeEnv=None):
+    def create_debug_adaptor(self, lldbVSCodeEnv=None, commandEscapeChar='`'):
         """Create the Visual Studio Code debug adaptor"""
         self.assertTrue(
             is_exe(self.lldbVSCodeExec), "lldb-vscode must exist and be executable"
@@ -16,14 +16,15 @@ def create_debug_adaptor(self, lldbVSCodeEnv=None):
         log_file_path = self.getBuildArtifact("vscode.txt")
         self.vscode = vscode.DebugAdaptor(
             executable=self.lldbVSCodeExec,
+            args=["--command-escape-character", commandEscapeChar],
             init_commands=self.setUpCommands(),
             log_file=log_file_path,
             env=lldbVSCodeEnv,
         )
 
-    def build_and_create_debug_adaptor(self, lldbVSCodeEnv=None):
+    def build_and_create_debug_adaptor(self, lldbVSCodeEnv=None, commandEscapeChar='`'):
         self.build()
-        self.create_debug_adaptor(lldbVSCodeEnv)
+        self.create_debug_adaptor(lldbVSCodeEnv, commandEscapeChar)
 
     def set_source_breakpoints(self, source_path, lines, data=None):
         """Sets source breakpoints and returns an array of strings containing
@@ -425,11 +426,12 @@ def build_and_launch(
         lldbVSCodeEnv=None,
         enableAutoVariableSummaries=False,
         enableSyntheticChildDebugging=False,
+        commandEscapeChar='`',
     ):
         """Build the default Makefile target, create the VSCode debug adaptor,
         and launch the process.
         """
-        self.build_and_create_debug_adaptor(lldbVSCodeEnv)
+        self.build_and_create_debug_adaptor(lldbVSCodeEnv, commandEscapeChar=commandEscapeChar)
         self.assertTrue(os.path.exists(program), "executable must exist")
 
         return self.launch(
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
index 5ee0800b27a5699..f4b393e91c10c6e 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
@@ -1015,7 +1015,7 @@ def terminate(self):
 
 class DebugAdaptor(DebugCommunication):
     def __init__(
-        self, executable=None, port=None, init_commands=[], log_file=None, env=None
+        self, executable=None, args=[], port=None, init_commands=[], log_file=None, env=None
     ):
         self.process = None
         if executable is not None:
@@ -1026,7 +1026,7 @@ def __init__(
             if log_file:
                 adaptor_env["LLDBVSCODE_LOG"] = log_file
             self.process = subprocess.Popen(
-                [executable],
+                [executable, *args],
                 stdin=subprocess.PIPE,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
diff --git a/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py b/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py
index d28e98b37c589dd..960b6c355fefc1d 100644
--- a/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py
+++ b/lldb/test/API/tools/lldb-vscode/console/TestVSCode_console.py
@@ -2,16 +2,16 @@
 Test lldb-vscode setBreakpoints request
 """
 
+import lldbvscode_testcase
 import vscode
+from lldbsuite.test import lldbutil
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-import lldbvscode_testcase
 
 
 class TestVSCode_console(lldbvscode_testcase.VSCodeTestCaseBase):
-    def check_lldb_command(self, lldb_command, contains_string, assert_msg):
-        response = self.vscode.request_evaluate("`%s" % (lldb_command), context="repl")
+    def check_lldb_command(self, lldb_command, contains_string, assert_msg, command_escape_char='`'):
+        response = self.vscode.request_evaluate(f"{command_escape_char}{lldb_command}", context="repl")
         output = response["body"]["result"]
         self.assertIn(
             contains_string,
@@ -66,3 +66,16 @@ def test_scopes_variables_setVariable_evaluate(self):
         # currently selected frame.
 
         self.check_lldb_command("frame select", "frame #1", "frame 1 is selected")
+
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_custom_escape_char(self):
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program, commandEscapeChar=":")
+        source = "main.cpp"
+        breakpoint1_line = line_number(source, "// breakpoint 1")
+        breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
+        self.continue_to_breakpoints(breakpoint_ids)
+
+        self.check_lldb_command("help", "For more information on any command", "Help can be invoked", command_escape_char=':')
diff --git a/lldb/tools/lldb-vscode/Options.td b/lldb/tools/lldb-vscode/Options.td
index a6ba0a318f388a1..7e885abeb615326 100644
--- a/lldb/tools/lldb-vscode/Options.td
+++ b/lldb/tools/lldb-vscode/Options.td
@@ -43,3 +43,9 @@ def debugger_pid: S<"debugger-pid">,
 def repl_mode: S<"repl-mode">,
   MetaVarName<"<mode>">,
   HelpText<"The mode for handling repl evaluation requests, supported modes: variable, command, auto.">;
+
+def command_escape_character: S<"command-escape-character">,
+  MetaVarName<"<character>">,
+  HelpText<"The escape prefix character to use for executing regular LLDB "
+           "commands in the Debug Console, instead of printing variables. "
+           "Defaults to a back-tick (`).">;
diff --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp
index 1384604c48371b7..7ce5932f8691e80 100644
--- a/lldb/tools/lldb-vscode/VSCode.cpp
+++ b/lldb/tools/lldb-vscode/VSCode.cpp
@@ -384,8 +384,8 @@ llvm::json::Value VSCode::CreateTopLevelScopes() {
 
 ExpressionContext VSCode::DetectExpressionContext(lldb::SBFrame &frame,
                                                   std::string &text) {
-  // Include ` as an escape hatch.
-  if (!text.empty() && text[0] == '`') {
+  // Include the escape hatch prefix.
+  if (!text.empty() && text[0] == g_vsc.command_escape_character) {
     text = text.substr(1);
     return ExpressionContext::Command;
   }
@@ -418,7 +418,9 @@ ExpressionContext VSCode::DetectExpressionContext(lldb::SBFrame &frame,
         if (!auto_repl_mode_collision_warning) {
           llvm::errs() << "Variable expression '" << text
                        << "' is hiding an lldb command, prefix an expression "
-                          "with ` to ensure it runs as a lldb command.\n";
+                          "with "
+                       << g_vsc.command_escape_character
+                       << " to ensure it runs as a lldb command.\n";
           auto_repl_mode_collision_warning = true;
         }
         return ExpressionContext::Variable;
diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h
index 59bb11c71e67203..1802eeb821fd30d 100644
--- a/lldb/tools/lldb-vscode/VSCode.h
+++ b/lldb/tools/lldb-vscode/VSCode.h
@@ -188,6 +188,7 @@ struct VSCode {
   ReplModeRequestHandler repl_mode_request_handler;
   ReplMode repl_mode;
   bool auto_repl_mode_collision_warning;
+  char command_escape_character = '`';
 
   VSCode();
   ~VSCode();
diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp
index 3904d430c49b4cd..06c7724fb66d25a 100644
--- a/lldb/tools/lldb-vscode/lldb-vscode.cpp
+++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp
@@ -3806,6 +3806,16 @@ int main(int argc, char *argv[]) {
     g_vsc.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
   }
 
+  if (llvm::opt::Arg *arg =
+          input_args.getLastArg(OPT_command_escape_character)) {
+    if (const char *escape_char = arg->getValue()) {
+      g_vsc.command_escape_character = *escape_char;
+    } else {
+      llvm::errs() << "Missing command escape character.\n";
+      return EXIT_FAILURE;
+    }
+  }
+
   bool CleanExit = true;
   if (auto Err = g_vsc.Loop()) {
     if (g_vsc.log)

@github-actions
Copy link

github-actions bot commented Oct 16, 2023

✅ With the latest revision this PR passed the Python code formatter.

@JDevlieghere
Copy link
Member

FWIW I like : because that's what the REPL uses and because ` already has a different meaning in LLDB.

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@JDevlieghere
Copy link
Member

(Not related to this PR but I put up an RFC on Friday to rename it to lldb-dap. Putting it up here for extra visibility.)

@walter-erquinigo
Copy link
Member Author

(Not related to this PR but I put up an RFC on Friday to rename it to lldb-dap. Putting it up here for extra visibility.)

Nice! I'll approve your PR if you make that change!

Copy link
Collaborator

@clayborg clayborg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With "lldb-vscode" (which might be "lldb-dap" soon) being a native DAP plug-in, I believe this means we can't set settings in the IDE itself if we are not a typescript DAP plug-in.

How would anyone specify this extra option when running lldb-vscode? Is there a way to specify extra options that "lldb-vscode" should be launched with from the VS Code GUI? If so, how does this work? Or are you using this from a different IDE other than VS Code that allows you to specify extra options?

The optimal solution for this in my mind would be to have some global settings that we can set in the VS Code GUI. I know there can be plug-in specific setting set as we allow a path to be specified in a custom DAP plug-in that points to the path to the "lldb-vscode" binary to actaully run instead of the one in the extensions folder. But I don't know if this can be done with a native only DAP plug-in (our custom solution has a typescript wrapper that eventually spawns "lldb-vscode" as we need to intercept and fix some packets, so we actaully have a typescript as our main DAP plug-in and we forward packets to lldb-vscode).

The other way to fix this would be to add this as a launch config option. @walter-erquinigo this is how you did a previous fix for auto variable summaries being enabled.

So we should really figure out our solution to having global settings and do them all consistently.

@bulbazord
Copy link
Member

With "lldb-vscode" (which might be "lldb-dap" soon) being a native DAP plug-in, I believe this means we can't set settings in the IDE itself if we are not a typescript DAP plug-in.

How would anyone specify this extra option when running lldb-vscode? Is there a way to specify extra options that "lldb-vscode" should be launched with from the VS Code GUI? If so, how does this work? Or are you using this from a different IDE other than VS Code that allows you to specify extra options?

The optimal solution for this in my mind would be to have some global settings that we can set in the VS Code GUI. I know there can be plug-in specific setting set as we allow a path to be specified in a custom DAP plug-in that points to the path to the "lldb-vscode" binary to actaully run instead of the one in the extensions folder. But I don't know if this can be done with a native only DAP plug-in (our custom solution has a typescript wrapper that eventually spawns "lldb-vscode" as we need to intercept and fix some packets, so we actaully have a typescript as our main DAP plug-in and we forward packets to lldb-vscode).

The other way to fix this would be to add this as a launch config option. @walter-erquinigo this is how you did a previous fix for auto variable summaries being enabled.

So we should really figure out our solution to having global settings and do them all consistently.

There doesn't seem to be an actual standard 'LLDB' plugin in the VSCode plugin marketplace. Since they all could work differently, it makes sense to me that we add customization at the lldb-dap layer so that the VSCode plugins can handle this however they want. Specifically, a plugin could add a setting to customize what the escape character is and then have it launch lldb-dap with that setting. Hopefully I understood your point? I'm not sure it makes sense for us to be able to add a global VSCode setting since this setting will only be used when interacting with lldb-dap.

@walter-erquinigo
Copy link
Member Author

@clayborg , good point, I'll move it to launch and attach settings.

@clayborg
Copy link
Collaborator

@clayborg , good point, I'll move it to launch and attach settings.

Can you check real quick if any native plug-ins can have and or access global settings from the VS Code GUI? That would be the best way for all of these. Maybe there is a combination mode where we have some typescript in our extension, but still have a native DAP plug-in?

@clayborg
Copy link
Collaborator

Mainly asking since you just started adding things that fall into the "we need global plug-in settings for lldb-vscode" recently and it will be good to make sure we are doing things right from a VS code perspective. It would be nice to not have all of our settings in our launch config if possible.

@walter-erquinigo
Copy link
Member Author

Can you check real quick if any native plug-ins can have and or access global settings from the VS Code GUI? That would be the best way for all of these. Maybe there is a combination mode where we have some typescript in our extension, but still have a native DAP plug-in?

There's no way to do it natively. Typescript code is always needed, and that means adding a typescript shim to the code in LLVM.

@walter-erquinigo
Copy link
Member Author

@clayborg , I've updated this patch using launch and attach settings, which might be fine following what've been doing so far. Even though I agree that it'd be nice to read the GUI global configs, I think that all settings should be also configurable via launch and attach settings, as an overwriting mechanism.

@JDevlieghere , @clayborg, what do you think of the idea of some of us in the LLDB community building a minimal typescript extension for lldb-vscode and upload it to the marketplace? Then companies building their custom debugging on top of lldb-vscode could just extend this minimal extension and contribute back to it.

@clayborg
Copy link
Collaborator

Can you check real quick if any native plug-ins can have and or access global settings from the VS Code GUI? That would be the best way for all of these. Maybe there is a combination mode where we have some typescript in our extension, but still have a native DAP plug-in?

There's no way to do it natively. Typescript code is always needed, and that means adding a typescript shim to the code in LLVM.

ok, then doing things as a launch config is fine for now.

@JDevlieghere
Copy link
Member

@JDevlieghere , @clayborg, what do you think of the idea of some of us in the LLDB community building a minimal typescript extension for lldb-vscode and upload it to the marketplace? Then companies building their custom debugging on top of lldb-vscode could just extend this minimal extension and contribute back to it.

Sounds reasonable to me, thought I'm not sure that actually solves Greg's concern? I think clangd has a community plugin in the marketplace, I think it would be helpful to have something similar for lldb-{vscode/dap}. We also can name the plugin whatever we like, the fact that the binary is called lldb-dap under the hood shouldn't matter to most end users.

@clayborg
Copy link
Collaborator

@clayborg , I've updated this patch using launch and attach settings, which might be fine following what've been doing so far. Even though I agree that it'd be nice to read the GUI global configs, I think that all settings should be also configurable via launch and attach settings, as an overwriting mechanism.

We should go with the flow of what VS Code designs. I am fine if we get global settings eventually and allow them to be overridden with launch config stuff.

@JDevlieghere , @clayborg, what do you think of the idea of some of us in the LLDB community building a minimal typescript extension for lldb-vscode and upload it to the marketplace? Then companies building their custom debugging on top of lldb-vscode could just extend this minimal extension and contribute back to it.

We should probably start shipping a real LLDB native marketplace solution at some point.

But having typescript seems like a good idea so we can get settings, add new VS code commands, etc. Not sure if there is a performance impact to going through typescript first and forwarding to a native plug-in.

If we do ship a native plug-in, it would be nice to have some settings:

  • path to a lldb-dap/lldb-vscode plug-in that can be use from another vendor
  • settings for auto summaries
  • settings for command prefix (this patch)
  • enabling lldb logging
  • sourcing extra command files

Can we modify this patch so we can specify an empty prefix? This would allow users to just type in lldb command all of the time. We have an internal setting for this in our custom lldb solution at Meta.

@walter-erquinigo
Copy link
Member Author

Sure, Greg, I'll update my patch with the empty prefix.
I'll probably write the very basic Typescript wrapper in two weeks. I would like to see it a point of contribution between different companies

@JDevlieghere
Copy link
Member

Okay, to answer my own question: the difference is the typescript vs native plugin. I had missed that nuance. For the uninitiated, what most of us are currently using is the native version because it's just a package.json + lldb-dap binary, right? I guess I personally care mostly about that as it supports more than just VSCode.

@walter-erquinigo walter-erquinigo changed the title [lldb-vscode] Allow specifying a custom escape character for LLDB commands [lldb-vscode] Allow specifying a custom escape prefix for LLDB commands Oct 17, 2023
@walter-erquinigo
Copy link
Member Author

@clayborg , friendly ping

lldb/tools/lldb-vscode/JSONUtils.h Outdated Show resolved Hide resolved
lldb/tools/lldb-vscode/VSCode.cpp Outdated Show resolved Hide resolved
@walter-erquinigo
Copy link
Member Author

@clayborg , friendly ping :)

…mands

We've been using the backtick as our escape character, however that leads to a weird experience on VS Code, because on most hosts, as soon as you type the backtick on VS Code, the IDE will introduce another backtick. As changing the default escape character might be out of question because other plugins might rely on it, we can instead introduce an option to change this variable upon lldb-vscode initialization.
FWIW, my users will be using : instead ot the backtick.
@walter-erquinigo walter-erquinigo merged commit 1066481 into llvm:main Oct 25, 2023
3 checks passed
@walter-erquinigo walter-erquinigo deleted the walter/escape branch October 25, 2023 04:05
jeffreytan81 added a commit that referenced this pull request Nov 20, 2023
#69238 caused breakage in
VSCode debug console usage -- the user's input is always treated as
commands instead of expressions (the same behavior as if empty command
escape prefix is specified).

The bug is in one overload of `GetString()` which did not respect the
default value of "\`". But more important, I am puzzled to find out why
the regression is not caught by lldb test (testdap_evaluate). Turns out
#69238 specifies
commandEscapePrefix default value in test framework to be "\`" while
VSCode will default not specify any commandEscapePrefix at all. Changing
it to None will fail `testdap_evaluate`. We should align the default
behavior between DAP client and testcase.

This patches fixes the bug in `GetString()` and changed the default
value of commandEscapePrefix in testcases to be None (be consistent with
IDE).

Co-authored-by: jeffreytan81 <jeffreytan@fb.com>
adrian-prantl pushed a commit to adrian-prantl/llvm-project that referenced this pull request Jan 18, 2024
…ds (llvm#69238)

We've been using the backtick as our escape character, however that
leads to a weird experience on VS Code, because on most hosts, as soon
as you type the backtick on VS Code, the IDE will introduce another
backtick. As changing the default escape character might be out of
question because other plugins might rely on it, we can instead
introduce an option to change this variable upon lldb-vscode
initialization.
FWIW, my users will be using : instead ot the backtick.

(cherry picked from commit 1066481)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants