Skip to content

Commit

Permalink
[lldb-vscode] Allow specifying a custom escape prefix for LLDB comman…
Browse files Browse the repository at this point in the history
…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)
  • Loading branch information
walter-erquinigo authored and adrian-prantl committed Jan 18, 2024
1 parent 5fc0017 commit 8ad5f6a
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ def request_launch(
postRunCommands=None,
enableAutoVariableSummaries=False,
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
):
args_dict = {"program": program}
if args:
Expand Down Expand Up @@ -772,6 +773,7 @@ def request_launch(
args_dict["postRunCommands"] = postRunCommands
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
args_dict["commandEscapePrefix"] = commandEscapePrefix
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
response = self.send_recv(command_dict)

Expand Down Expand Up @@ -1013,7 +1015,12 @@ def terminate(self):

class DebugAdaptorServer(DebugCommunication):
def __init__(
self, executable=None, port=None, init_commands=[], log_file=None, env=None
self,
executable=None,
port=None,
init_commands=[],
log_file=None,
env=None,
):
self.process = None
if executable is not None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ def launch(
postRunCommands=None,
enableAutoVariableSummaries=False,
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
):
"""Sending launch request to dap"""

Expand Down Expand Up @@ -389,6 +390,7 @@ def cleanup():
postRunCommands=postRunCommands,
enableAutoVariableSummaries=enableAutoVariableSummaries,
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
commandEscapePrefix=commandEscapePrefix,
)

if expectFailure:
Expand Down Expand Up @@ -425,6 +427,7 @@ def build_and_launch(
lldbDAPEnv=None,
enableAutoVariableSummaries=False,
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
):
"""Build the default Makefile target, create the DAP debug adaptor,
and launch the process.
Expand Down Expand Up @@ -455,4 +458,5 @@ def build_and_launch(
postRunCommands=postRunCommands,
enableAutoVariableSummaries=enableAutoVariableSummaries,
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
commandEscapePrefix=commandEscapePrefix,
)
44 changes: 40 additions & 4 deletions lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
"""

import dap_server
import lldbdap_testcase
from lldbsuite.test import lldbutil
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import lldbdap_testcase


class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
def check_lldb_command(self, lldb_command, contains_string, assert_msg):
def check_lldb_command(
self, lldb_command, contains_string, assert_msg, command_escape_prefix="`"
):
response = self.dap_server.request_evaluate(
"`%s" % (lldb_command), context="repl"
f"{command_escape_prefix}{lldb_command}", context="repl"
)
output = response["body"]["result"]
self.assertIn(
Expand Down Expand Up @@ -68,3 +70,37 @@ 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_prefix(self):
program = self.getBuildArtifact("a.out")
self.build_and_launch(program, commandEscapePrefix="::")
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_prefix="::",
)

@skipIfWindows
@skipIfRemote
def test_empty_escape_prefix(self):
program = self.getBuildArtifact("a.out")
self.build_and_launch(program, commandEscapePrefix="")
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_prefix="",
)
11 changes: 7 additions & 4 deletions lldb/tools/lldb-dap/DAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,10 @@ llvm::json::Value DAP::CreateTopLevelScopes() {

ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame &frame,
std::string &text) {
// Include ` as an escape hatch.
if (!text.empty() && text[0] == '`') {
text = text.substr(1);
// Include the escape hatch prefix.
if (!text.empty() &&
llvm::StringRef(text).starts_with(g_dap.command_escape_prefix)) {
text = text.substr(g_dap.command_escape_prefix.size());
return ExpressionContext::Command;
}

Expand Down Expand Up @@ -418,7 +419,9 @@ ExpressionContext DAP::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_dap.command_escape_prefix
<< "' to ensure it runs as a lldb command.\n";
auto_repl_mode_collision_warning = true;
}
return ExpressionContext::Variable;
Expand Down
1 change: 1 addition & 0 deletions lldb/tools/lldb-dap/DAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ struct DAP {
ReplModeRequestHandler repl_mode_request_handler;
ReplMode repl_mode;
bool auto_repl_mode_collision_warning;
std::string command_escape_prefix = "`";

DAP();
~DAP();
Expand Down
10 changes: 6 additions & 4 deletions lldb/tools/lldb-dap/JSONUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,17 @@ llvm::StringRef GetAsString(const llvm::json::Value &value) {
}

// Gets a string from a JSON object using the key, or returns an empty string.
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
llvm::StringRef defaultValue) {
if (std::optional<llvm::StringRef> value = obj.getString(key))
return *value;
return llvm::StringRef();
return defaultValue;
}

llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
llvm::StringRef defaultValue) {
if (obj == nullptr)
return llvm::StringRef();
return defaultValue;
return GetString(*obj, key);
}

Expand Down
11 changes: 8 additions & 3 deletions lldb/tools/lldb-dap/JSONUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,17 @@ llvm::StringRef GetAsString(const llvm::json::Value &value);
/// \param[in] key
/// The key to use when extracting the value
///
/// \param[in] defaultValue
/// The default value to return if the key is not present
///
/// \return
/// A llvm::StringRef that contains the string value for the
/// specified \a key, or an empty string if there is no key that
/// specified \a key, or the default value if there is no key that
/// matches or if the value is not a string.
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key);
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key);
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
llvm::StringRef defaultValue = {});
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
llvm::StringRef defaultValue = {});

/// Extract the unsigned integer value for the specified key from
/// the specified object.
Expand Down
4 changes: 4 additions & 0 deletions lldb/tools/lldb-dap/lldb-dap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,8 @@ void request_attach(const llvm::json::Object &request) {
GetBoolean(arguments, "enableAutoVariableSummaries", false);
g_dap.enable_synthetic_child_debugging =
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
g_dap.command_escape_prefix =
GetString(arguments, "commandEscapePrefix", "`");

// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which require
Expand Down Expand Up @@ -1799,6 +1801,8 @@ void request_launch(const llvm::json::Object &request) {
GetBoolean(arguments, "enableAutoVariableSummaries", false);
g_dap.enable_synthetic_child_debugging =
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
g_dap.command_escape_prefix =
GetString(arguments, "commandEscapePrefix", "`");

// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which require
Expand Down
10 changes: 10 additions & 0 deletions lldb/tools/lldb-dap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@
"type": "boolean",
"description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.",
"default": false
},
"commandEscapePrefix": {
"type": "string",
"description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.",
"default": "`"
}
}
},
Expand Down Expand Up @@ -335,6 +340,11 @@
"type": "boolean",
"description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.",
"default": false
},
"commandEscapePrefix": {
"type": "string",
"description": "The escape prefix character to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If empty, then all expression in the Debug Console are treated as regular LLDB commands.",
"default": "`"
}
}
}
Expand Down

0 comments on commit 8ad5f6a

Please sign in to comment.