Author: Walter Erquinigo Date: 2023-12-14T15:04:35-05:00 New Revision: aa207674f9e6caf5bc29c1b4925183a398382d6f
URL: https://github.com/llvm/llvm-project/commit/aa207674f9e6caf5bc29c1b4925183a398382d6f DIFF: https://github.com/llvm/llvm-project/commit/aa207674f9e6caf5bc29c1b4925183a398382d6f.diff LOG: [lldb-dap] Implement command directives (#74808) This adds support for optionally prefixing any command with `?` and/or `!`. - `?` prevents the output of a commands to be printed to the console unless it fails. - `!` aborts the dap if the command fails. They come in handy when programmatically running commands on behalf of the user without wanting them to know unless they fail, or when a critical setup is required as part of launchCommands and it's better to abort on failures than to silently skip. Added: lldb/test/API/tools/lldb-dap/commands/Makefile lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py lldb/test/API/tools/lldb-dap/commands/main.cpp Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/LLDBUtils.cpp lldb/tools/lldb-dap/LLDBUtils.h lldb/tools/lldb-dap/lldb-dap.cpp lldb/tools/lldb-dap/package.json Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 4ccd6014e54be6..7436b9900e98b0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -291,6 +291,7 @@ def attach( postRunCommands=None, sourceMap=None, sourceInitFile=False, + expectFailure=False, ): """Build the default Makefile target, create the DAP debug adaptor, and attach to the process. @@ -322,6 +323,8 @@ def cleanup(): postRunCommands=postRunCommands, sourceMap=sourceMap, ) + if expectFailure: + return response if not (response and response["success"]): self.assertTrue( response["success"], "attach failed (%s)" % (response["message"]) @@ -437,6 +440,8 @@ def build_and_launch( commandEscapePrefix=None, customFrameFormat=None, customThreadFormat=None, + launchCommands=None, + expectFailure=False, ): """Build the default Makefile target, create the DAP debug adaptor, and launch the process. @@ -470,4 +475,6 @@ def build_and_launch( commandEscapePrefix=commandEscapePrefix, customFrameFormat=customFrameFormat, customThreadFormat=customThreadFormat, + launchCommands=launchCommands, + expectFailure=expectFailure, ) diff --git a/lldb/test/API/tools/lldb-dap/commands/Makefile b/lldb/test/API/tools/lldb-dap/commands/Makefile new file mode 100644 index 00000000000000..99998b20bcb050 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/commands/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py new file mode 100644 index 00000000000000..226b9385fe719a --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py @@ -0,0 +1,80 @@ +import os + +import dap_server +import lldbdap_testcase +from lldbsuite.test import lldbtest, lldbutil +from lldbsuite.test.decorators import * + + +class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase): + def test_command_directive_quiet_on_success(self): + program = self.getBuildArtifact("a.out") + command_quiet = ( + "settings set target.show-hex-variable-values-with-leading-zeroes false" + ) + command_not_quiet = ( + "settings set target.show-hex-variable-values-with-leading-zeroes true" + ) + self.build_and_launch( + program, + initCommands=["?" + command_quiet, command_not_quiet], + terminateCommands=["?" + command_quiet, command_not_quiet], + stopCommands=["?" + command_quiet, command_not_quiet], + exitCommands=["?" + command_quiet, command_not_quiet], + ) + full_output = self.collect_console(duration=1.0) + self.assertNotIn(command_quiet, full_output) + self.assertIn(command_not_quiet, full_output) + + def do_test_abort_on_error( + self, + use_init_commands=False, + use_launch_commands=False, + use_pre_run_commands=False, + use_post_run_commands=False, + ): + program = self.getBuildArtifact("a.out") + command_quiet = ( + "settings set target.show-hex-variable-values-with-leading-zeroes false" + ) + command_abort_on_error = "settings set foo bar" + commands = ["?!" + command_quiet, "!" + command_abort_on_error] + self.build_and_launch( + program, + initCommands=commands if use_init_commands else None, + launchCommands=commands if use_launch_commands else None, + preRunCommands=commands if use_pre_run_commands else None, + postRunCommands=commands if use_post_run_commands else None, + expectFailure=True, + ) + full_output = self.collect_console(duration=1.0) + self.assertNotIn(command_quiet, full_output) + self.assertIn(command_abort_on_error, full_output) + + def test_command_directive_abort_on_error_init_commands(self): + self.do_test_abort_on_error(use_init_commands=True) + + def test_command_directive_abort_on_error_launch_commands(self): + self.do_test_abort_on_error(use_launch_commands=True) + + def test_command_directive_abort_on_error_pre_run_commands(self): + self.do_test_abort_on_error(use_pre_run_commands=True) + + def test_command_directive_abort_on_error_post_run_commands(self): + self.do_test_abort_on_error(use_post_run_commands=True) + + def test_command_directive_abort_on_error_attach_commands(self): + program = self.getBuildArtifact("a.out") + command_quiet = ( + "settings set target.show-hex-variable-values-with-leading-zeroes false" + ) + command_abort_on_error = "settings set foo bar" + self.build_and_create_debug_adaptor() + self.attach( + program, + attachCommands=["?!" + command_quiet, "!" + command_abort_on_error], + expectFailure=True, + ) + full_output = self.collect_console(duration=1.0) + self.assertNotIn(command_quiet, full_output) + self.assertIn(command_abort_on_error, full_output) diff --git a/lldb/test/API/tools/lldb-dap/commands/main.cpp b/lldb/test/API/tools/lldb-dap/commands/main.cpp new file mode 100644 index 00000000000000..76e8197013aabc --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/commands/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 49266209739765..4b72c13f9215a8 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -434,20 +434,54 @@ ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame &frame, return ExpressionContext::Variable; } -void DAP::RunLLDBCommands(llvm::StringRef prefix, - const std::vector<std::string> &commands) { - SendOutput(OutputType::Console, - llvm::StringRef(::RunLLDBCommands(prefix, commands))); +bool DAP::RunLLDBCommands(llvm::StringRef prefix, + llvm::ArrayRef<std::string> commands) { + bool required_command_failed = false; + std::string output = + ::RunLLDBCommands(prefix, commands, required_command_failed); + SendOutput(OutputType::Console, output); + return !required_command_failed; +} + +static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + llvm::formatv( + "Failed to run {0} commands. See the Debug Console for more details.", + category) + .str() + .c_str()); +} + +llvm::Error +DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) { + if (!RunLLDBCommands("Running attachCommands:", attach_commands)) + return createRunLLDBCommandsErrorMessage("attach"); + return llvm::Error::success(); } -void DAP::RunInitCommands() { - RunLLDBCommands("Running initCommands:", init_commands); +llvm::Error +DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) { + if (!RunLLDBCommands("Running launchCommands:", launch_commands)) + return createRunLLDBCommandsErrorMessage("launch"); + return llvm::Error::success(); } -void DAP::RunPreRunCommands() { - RunLLDBCommands("Running preRunCommands:", pre_run_commands); +llvm::Error DAP::RunInitCommands() { + if (!RunLLDBCommands("Running initCommands:", init_commands)) + return createRunLLDBCommandsErrorMessage("initCommands"); + return llvm::Error::success(); } +llvm::Error DAP::RunPreRunCommands() { + if (!RunLLDBCommands("Running preRunCommands:", pre_run_commands)) + return createRunLLDBCommandsErrorMessage("preRunCommands"); + return llvm::Error::success(); +} + +void DAP::RunPostRunCommands() { + RunLLDBCommands("Running postRunCommands:", post_run_commands); +} void DAP::RunStopCommands() { RunLLDBCommands("Running stopCommands:", stop_commands); } diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index c7d56a06bfa1fd..20817194de2d8d 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -158,6 +158,7 @@ struct DAP { std::vector<ExceptionBreakpoint> exception_breakpoints; std::vector<std::string> init_commands; std::vector<std::string> pre_run_commands; + std::vector<std::string> post_run_commands; std::vector<std::string> exit_commands; std::vector<std::string> stop_commands; std::vector<std::string> terminate_commands; @@ -227,11 +228,17 @@ struct DAP { ExpressionContext DetectExpressionContext(lldb::SBFrame &frame, std::string &text); - void RunLLDBCommands(llvm::StringRef prefix, - const std::vector<std::string> &commands); - - void RunInitCommands(); - void RunPreRunCommands(); + /// \return + /// \b false if a fatal error was found while executing these commands, + /// according to the rules of \a LLDBUtils::RunLLDBCommands. + bool RunLLDBCommands(llvm::StringRef prefix, + llvm::ArrayRef<std::string> commands); + + llvm::Error RunAttachCommands(llvm::ArrayRef<std::string> attach_commands); + llvm::Error RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands); + llvm::Error RunInitCommands(); + llvm::Error RunPreRunCommands(); + void RunPostRunCommands(); void RunStopCommands(); void RunExitCommands(); void RunTerminateCommands(); diff --git a/lldb/tools/lldb-dap/LLDBUtils.cpp b/lldb/tools/lldb-dap/LLDBUtils.cpp index 955c11f636895b..35b7a986a8964b 100644 --- a/lldb/tools/lldb-dap/LLDBUtils.cpp +++ b/lldb/tools/lldb-dap/LLDBUtils.cpp @@ -11,40 +11,81 @@ namespace lldb_dap { -void RunLLDBCommands(llvm::StringRef prefix, +bool RunLLDBCommands(llvm::StringRef prefix, const llvm::ArrayRef<std::string> &commands, - llvm::raw_ostream &strm) { + llvm::raw_ostream &strm, bool parse_command_directives) { if (commands.empty()) - return; + return true; + + bool did_print_prefix = false; + lldb::SBCommandInterpreter interp = g_dap.debugger.GetCommandInterpreter(); - if (!prefix.empty()) - strm << prefix << "\n"; - for (const auto &command : commands) { + for (llvm::StringRef command : commands) { lldb::SBCommandReturnObject result; - strm << "(lldb) " << command << "\n"; - interp.HandleCommand(command.c_str(), result); - auto output_len = result.GetOutputSize(); - if (output_len) { - const char *output = result.GetOutput(); - strm << output; + bool quiet_on_success = false; + bool check_error = false; + + while (parse_command_directives) { + if (command.starts_with("?")) { + command = command.drop_front(); + quiet_on_success = true; + } else if (command.starts_with("!")) { + command = command.drop_front(); + check_error = true; + } else { + break; + } } - auto error_len = result.GetErrorSize(); - if (error_len) { - const char *error = result.GetError(); - strm << error; + + interp.HandleCommand(command.str().c_str(), result); + const bool got_error = !result.Succeeded(); + // The if statement below is assuming we always print out `!` prefixed + // lines. The only time we don't print is when we have `quiet_on_success == + // true` and we don't have an error. + if (quiet_on_success ? got_error : true) { + if (!did_print_prefix && !prefix.empty()) { + strm << prefix << "\n"; + did_print_prefix = true; + } + strm << "(lldb) " << command << "\n"; + auto output_len = result.GetOutputSize(); + if (output_len) { + const char *output = result.GetOutput(); + strm << output; + } + auto error_len = result.GetErrorSize(); + if (error_len) { + const char *error = result.GetError(); + strm << error; + } } + if (check_error && got_error) + return false; // Stop running commands. } + return true; } std::string RunLLDBCommands(llvm::StringRef prefix, - const llvm::ArrayRef<std::string> &commands) { + const llvm::ArrayRef<std::string> &commands, + bool &required_command_failed, + bool parse_command_directives) { + required_command_failed = false; std::string s; llvm::raw_string_ostream strm(s); - RunLLDBCommands(prefix, commands, strm); + required_command_failed = + !RunLLDBCommands(prefix, commands, strm, parse_command_directives); strm.flush(); return s; } +std::string +RunLLDBCommandsVerbatim(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands) { + bool required_command_failed = false; + return RunLLDBCommands(prefix, commands, required_command_failed, + /*parse_command_directives=*/false); +} + bool ThreadHasStopReason(lldb::SBThread &thread) { switch (thread.GetStopReason()) { case lldb::eStopReasonTrace: diff --git a/lldb/tools/lldb-dap/LLDBUtils.h b/lldb/tools/lldb-dap/LLDBUtils.h index a99f798835370d..ee701da2230fe0 100644 --- a/lldb/tools/lldb-dap/LLDBUtils.h +++ b/lldb/tools/lldb-dap/LLDBUtils.h @@ -23,6 +23,12 @@ namespace lldb_dap { /// All output from every command, including the prompt + the command /// is placed into the "strm" argument. /// +/// Each individual command can be prefixed with \b ! and/or \b ? in no +/// particular order. If \b ? is provided, then the output of that command is +/// only emitted if it fails, and if \b ! is provided, then the output is +/// emitted regardless, and \b false is returned without executing the +/// remaining commands. +/// /// \param[in] prefix /// A string that will be printed into \a strm prior to emitting /// the prompt + command and command output. Can be NULL. @@ -33,9 +39,17 @@ namespace lldb_dap { /// \param[in] strm /// The stream that will receive the prefix, prompt + command and /// all command output. -void RunLLDBCommands(llvm::StringRef prefix, +/// +/// \param[in] parse_command_directives +/// If \b false, then command prefixes like \b ! or \b ? are not parsed and +/// each command is executed verbatim. +/// +/// \return +/// \b true, unless a command prefixed with \b ! fails and parsing of +/// command directives is enabled. +bool RunLLDBCommands(llvm::StringRef prefix, const llvm::ArrayRef<std::string> &commands, - llvm::raw_ostream &strm); + llvm::raw_ostream &strm, bool parse_command_directives); /// Run a list of LLDB commands in the LLDB command interpreter. /// @@ -49,11 +63,26 @@ void RunLLDBCommands(llvm::StringRef prefix, /// \param[in] commands /// An array of LLDB commands to execute. /// +/// \param[out] required_command_failed +/// If parsing of command directives is enabled, this variable is set to +/// \b true if one of the commands prefixed with \b ! fails. +/// +/// \param[in] parse_command_directives +/// If \b false, then command prefixes like \b ! or \b ? are not parsed and +/// each command is executed verbatim. +/// /// \return /// A std::string that contains the prefix and all commands and -/// command output +/// command output. std::string RunLLDBCommands(llvm::StringRef prefix, - const llvm::ArrayRef<std::string> &commands); + const llvm::ArrayRef<std::string> &commands, + bool &required_command_failed, + bool parse_command_directives = true); + +/// Similar to the method above, but without parsing command directives. +std::string +RunLLDBCommandsVerbatim(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands); /// Check if a thread has a stop reason. /// diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index d6b593eba93eca..d36e9b4d1b0982 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -644,8 +644,7 @@ void request_attach(const llvm::json::Object &request) { const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30); g_dap.stop_at_entry = core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true; - std::vector<std::string> postRunCommands = - GetStrings(arguments, "postRunCommands"); + g_dap.post_run_commands = GetStrings(arguments, "postRunCommands"); const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot"); g_dap.enable_auto_variable_summaries = GetBoolean(arguments, "enableAutoVariableSummaries", false); @@ -664,7 +663,12 @@ void request_attach(const llvm::json::Object &request) { llvm::sys::fs::set_current_path(debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json - g_dap.RunInitCommands(); + if (llvm::Error err = g_dap.RunInitCommands()) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + g_dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } SetSourceMapFromArguments(*arguments); @@ -678,7 +682,12 @@ void request_attach(const llvm::json::Object &request) { } // Run any pre run LLDB commands the user specified in the launch.json - g_dap.RunPreRunCommands(); + if (llvm::Error err = g_dap.RunPreRunCommands()) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + g_dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } if (pid == LLDB_INVALID_PROCESS_ID && wait_for) { char attach_msg[256]; @@ -703,7 +712,12 @@ void request_attach(const llvm::json::Object &request) { // We have "attachCommands" that are a set of commands that are expected // to execute the commands after which a process should be created. If there // is no valid process after running these commands, we have failed. - g_dap.RunLLDBCommands("Running attachCommands:", attachCommands); + if (llvm::Error err = g_dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + g_dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } // The custom commands might have created a new target so we should use the // selected target after these commands are run. g_dap.target = g_dap.debugger.GetSelectedTarget(); @@ -727,7 +741,7 @@ void request_attach(const llvm::json::Object &request) { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", std::string(error.GetCString())); } else { - g_dap.RunLLDBCommands("Running postRunCommands:", postRunCommands); + g_dap.RunPostRunCommands(); } g_dap.SendJSON(llvm::json::Value(std::move(response))); @@ -1270,7 +1284,8 @@ void request_evaluate(const llvm::json::Object &request) { if (frame.IsValid()) { g_dap.focus_tid = frame.GetThread().GetThreadID(); } - auto result = RunLLDBCommands(llvm::StringRef(), {std::string(expression)}); + auto result = + RunLLDBCommandsVerbatim(llvm::StringRef(), {std::string(expression)}); EmplaceSafeString(body, "result", result); body.try_emplace("variablesReference", (int64_t)0); } else { @@ -1740,7 +1755,8 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) { // Set the launch info so that run commands can access the configured // launch details. g_dap.target.SetLaunchInfo(launch_info); - g_dap.RunLLDBCommands("Running launchCommands:", launchCommands); + if (llvm::Error err = g_dap.RunLaunchCommands(launchCommands)) + error.SetErrorString(llvm::toString(std::move(err)).c_str()); // The custom commands might have created a new target so we should use the // selected target after these commands are run. g_dap.target = g_dap.debugger.GetSelectedTarget(); @@ -1797,8 +1813,7 @@ void request_launch(const llvm::json::Object &request) { g_dap.stop_commands = GetStrings(arguments, "stopCommands"); g_dap.exit_commands = GetStrings(arguments, "exitCommands"); g_dap.terminate_commands = GetStrings(arguments, "terminateCommands"); - std::vector<std::string> postRunCommands = - GetStrings(arguments, "postRunCommands"); + g_dap.post_run_commands = GetStrings(arguments, "postRunCommands"); g_dap.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot"); g_dap.enable_auto_variable_summaries = @@ -1820,7 +1835,12 @@ void request_launch(const llvm::json::Object &request) { // Run any initialize LLDB commands the user specified in the launch.json. // This is run before target is created, so commands can't do anything with // the targets - preRunCommands are run with the target. - g_dap.RunInitCommands(); + if (llvm::Error err = g_dap.RunInitCommands()) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + g_dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } SetSourceMapFromArguments(*arguments); @@ -1834,7 +1854,12 @@ void request_launch(const llvm::json::Object &request) { } // Run any pre run LLDB commands the user specified in the launch.json - g_dap.RunPreRunCommands(); + if (llvm::Error err = g_dap.RunPreRunCommands()) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + g_dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } status = LaunchProcess(request); @@ -1842,7 +1867,7 @@ void request_launch(const llvm::json::Object &request) { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", std::string(status.GetCString())); } else { - g_dap.RunLLDBCommands("Running postRunCommands:", postRunCommands); + g_dap.RunPostRunCommands(); } g_dap.SendJSON(llvm::json::Value(std::move(response))); diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index ebb1103d695e17..68cdade4439924 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -204,32 +204,32 @@ }, "initCommands": { "type": "array", - "description": "Initialization commands executed upon debugger startup.", + "description": "Initialization commands executed upon debugger startup. Each command can be prefixed with `!` and/or `?` in no particular order. If `?` is provided, then the output of the command is only emitted if it fails, and if `!` is provided, the debug session terminates if the command fails, in which case the output of the command is emitted regardless.", "default": [] }, "preRunCommands": { "type": "array", - "description": "Commands executed just before the program is launched.", + "description": "Commands executed just before the program is launched. Each command can be prefixed with `!` and/or `?` in no particular order. If `?` is provided, then the output of the command is only emitted if it fails, and if `!` is provided, the debug session terminates if the command fails, in which case the output of the command is emitted regardless.", "default": [] }, "postRunCommands": { "type": "array", - "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.", + "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation. If a command is prefixed with `?`, then its output is only emitted if it fails. Unlike `initCommands` or `launchCommands`, the `!` prefix is ignored.", "default": [] }, "launchCommands": { "type": "array", - "description": "Custom commands that are executed instead of launching a process. A target will be created with the launch arguments prior to executing these commands. The commands may optionally create a new target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. Launch the process with \"process launch -s\" to make the process to at the entry point since lldb-dap will auto resume if necessary.", + "description": "Custom commands that are executed instead of launching a process. A target will be created with the launch arguments prior to executing these commands. The commands may optionally create a new target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. Launch the process with \"process launch -s\" to make the process to at the entry point since lldb-dap will auto resume if necessary. Each command can be prefixed with `!` and/or `?` in no particular order. If `?` is provided, then the output of the command is only emitted if it fails, and if `!` is provided, the debug session terminates if the command fails, in which case the output of the command is emitted regardless.", "default": [] }, "stopCommands": { "type": "array", - "description": "Commands executed each time the program stops.", + "description": "Commands executed each time the program stops. If a command is prefixed with `?`, then its output is only emitted if it fails. Unlike `initCommands` or `launchCommands`, the `!` prefix is ignored.", "default": [] }, "exitCommands": { "type": "array", - "description": "Commands executed at the end of debugging session.", + "description": "Commands executed at the end of debugging session. If a command is prefixed with `?`, then its output is only emitted if it fails. Unlike `initCommands` or `launchCommands`, the `!` prefix is ignored.", "default": [] }, "runInTerminal": { @@ -309,32 +309,32 @@ }, "attachCommands": { "type": "array", - "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.", + "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail. Each command can be prefixed with `!` and/or `?` in no particular order. If `?` is provided, then the output of the command is only emitted if it fails, and if `!` is provided, the debug session terminates if the command fails, in which case the output of the command is emitted regardless.", "default": [] }, "initCommands": { "type": "array", - "description": "Initialization commands executed upon debugger startup.", + "description": "Initialization commands executed upon debugger startup. Each command can be prefixed with `!` and/or `?` in no particular order. If `?` is provided, then the output of the command is only emitted if it fails, and if `!` is provided, the debug session terminates if the command fails, in which case the output of the command is emitted regardless.", "default": [] }, "preRunCommands": { "type": "array", - "description": "Commands executed just before the program is attached to.", + "description": "Commands executed just before the program is attached to. Each command can be prefixed with `!` and/or `?` in no particular order. If `?` is provided, then the output of the command is only emitted if it fails, and if `!` is provided, the debug session terminates if the command fails, in which case the output of the command is emitted regardless.", "default": [] }, "postRunCommands": { "type": "array", - "description": "Commands executed just as soon as the program is successfully attached when it's in a stopped state prior to any automatic continuation.", + "description": "Commands executed just as soon as the program is successfully attached when it's in a stopped state prior to any automatic continuation. Each command can be prefixed with `!` and/or `?` in no particular order. If `?` is provided, then the output of the command is only emitted if it fails, and if `!` is provided, the debug session terminates if the command fails, in which case the output of the command is emitted regardless.", "default": [] }, "stopCommands": { "type": "array", - "description": "Commands executed each time the program stops.", + "description": "Commands executed each time the program stops. If a command is prefixed with `?`, then its output is only emitted if it fails. Unlike `initCommands` or `attachCommands`, the `!` prefix is ignored.", "default": [] }, "exitCommands": { "type": "array", - "description": "Commands executed at the end of debugging session.", + "description": "Commands executed at the end of debugging session. If a command is prefixed with `?`, then its output is only emitted if it fails. Unlike `initCommands` or `attachCommands`, the `!` prefix is ignored.", "default": [] }, "coreFile": { _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits