[Lldb-commits] [lldb] aa20767 - [lldb-dap] Implement command directives (#74808)

via lldb-commits lldb-commits at lists.llvm.org
Thu Dec 14 12:04:40 PST 2023


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": {


        


More information about the lldb-commits mailing list