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 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py @@ -126,6 +126,13 @@ "verify '%s' found in console output for '%s'" % ( cmd, flavor)) + def verify_contains_text(self, flavor, output, text): + self.assertTrue(output and len(output) > 0, "expect console output") + found = text in output + self.assertTrue(found, + "verify '%s' found in console output for '%s'" % ( + text, flavor)) + def get_dict_value(self, d, key_path): '''Verify each key in the key_path array is in contained in each dictionary within "d". Assert if any key isn't in the diff --git a/lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py b/lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py --- a/lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py +++ b/lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py @@ -373,6 +373,7 @@ ''' self.build_and_create_debug_adaptor() program = self.getBuildArtifact("a.out") + execSearchPaths = '/unique/path/to/symbols/launch/test' source = 'main.c' first_line = line_number(source, '// breakpoint 1') @@ -382,6 +383,7 @@ # also we can verify that "stopCommands" get run as the # breakpoints get hit launchCommands = [ + 'settings set target.exec-search-paths "%s"' % (execSearchPaths), 'target create "%s"' % (program), 'br s -f main.c -l %d' % first_line, 'br s -f main.c -l %d' % second_line, @@ -391,7 +393,7 @@ initCommands = ['target list', 'platform list'] preRunCommands = ['image list a.out', 'image dump sections a.out'] stopCommands = ['frame variable', 'bt'] - exitCommands = ['expr 2+3', 'expr 3+4'] + exitCommands = ['expr 2+3', 'expr 3+4', 'settings show target.exec-search-paths'] self.launch(program, initCommands=initCommands, preRunCommands=preRunCommands, @@ -428,6 +430,8 @@ # "exitCommands" that were run after the second breakpoint was hit output = self.get_console(timeout=1.0) self.verify_commands('exitCommands', output, exitCommands) + # confirm that output contains correct target.exec-search-paths value + self.verify_contains_text('exitCommands', output, execSearchPaths) @skipIfWindows @skipIfNetBSD # Hangs on NetBSD as well @@ -440,7 +444,7 @@ ''' self.build_and_create_debug_adaptor() program = self.getBuildArtifact("a.out") - + terminateCommands = ['expr 4+2'] self.launch(program=program, terminateCommands=terminateCommands) diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -578,28 +578,43 @@ // Run any initialize LLDB commands the user specified in the launch.json g_vsc.RunInitCommands(); - lldb::SBError status; - g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status)); - if (status.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", status.GetCString()); - g_vsc.SendJSON(llvm::json::Value(std::move(response))); - return; - } - - // Run any pre run LLDB commands the user specified in the launch.json - g_vsc.RunPreRunCommands(); + if (!attachCommands.empty()) { + // 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. - if (pid == LLDB_INVALID_PROCESS_ID && wait_for) { - char attach_msg[256]; - auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), - "Waiting to attach to \"%s\"...", - g_vsc.target.GetExecutable().GetFilename()); - g_vsc.SendOutput(OutputType::Console, - llvm::StringRef(attach_msg, attach_msg_len)); - } - if (attachCommands.empty()) { + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); + g_vsc.RunLLDBCommands("Running attachCommands:", attachCommands); + // The custom commands are expected to create a new target + if (g_vsc.debugger.GetNumTargets() > 0) { + g_vsc.SetTarget(g_vsc.debugger.GetSelectedTarget()); + } else { + error.SetErrorString("attachCommands failed to create target"); + } + } else { // No "attachCommands", just attach normally. + lldb::SBError status; + g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status)); + if (status.Fail()) { + response["success"] = llvm::json::Value(false); + EmplaceSafeString(response, "message", status.GetCString()); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; + } + + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); + + if (pid == LLDB_INVALID_PROCESS_ID && wait_for) { + char attach_msg[256]; + auto attach_msg_len = snprintf( + attach_msg, sizeof(attach_msg), "Waiting to attach to \"%s\"...", + g_vsc.target.GetExecutable().GetFilename()); + g_vsc.SendOutput(OutputType::Console, + llvm::StringRef(attach_msg, attach_msg_len)); + } + // Disable async events so the attach will be successful when we return from // the launch call and the launch will happen synchronously g_vsc.debugger.SetAsync(false); @@ -609,14 +624,6 @@ g_vsc.target.LoadCore(core_file.data(), error); // Reenable async events g_vsc.debugger.SetAsync(true); - } else { - // 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_vsc.RunLLDBCommands("Running attachCommands:", attachCommands); - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - g_vsc.target = g_vsc.debugger.GetSelectedTarget(); } SetSourceMapFromArguments(*arguments); @@ -1547,67 +1554,77 @@ SetSourceMapFromArguments(*arguments); - lldb::SBError status; - g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status)); - if (status.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", status.GetCString()); - g_vsc.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (!launchCommands.empty()) { + // if "launchCommands" are provided, then they are expected to make the + // launch happen for launch requests and they replace the normal logic that + // would implement the launch. Run any pre run LLDB commands the user + // specified in the launch.json + g_vsc.RunPreRunCommands(); + g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands); + // The custom commands are expected to create a new target + if (g_vsc.debugger.GetNumTargets() > 0) { + g_vsc.SetTarget(g_vsc.debugger.GetSelectedTarget()); + } else { + error.SetErrorString("launchCommands failed to create target"); + } + } else { + // the normal logic that would implement the launch + lldb::SBError status; + g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status)); + if (status.Fail()) { + response["success"] = llvm::json::Value(false); + EmplaceSafeString(response, "message", status.GetCString()); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; + } - if (GetBoolean(arguments, "runInTerminal", false)) { - request_runInTerminal(request, response); - g_vsc.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (GetBoolean(arguments, "runInTerminal", false)) { + request_runInTerminal(request, response); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; + } + + // Instantiate a launch info instance for the target. + auto launch_info = g_vsc.target.GetLaunchInfo(); + + // Grab the current working directory if there is one and set it in the + // launch info. + const auto cwd = GetString(arguments, "cwd"); + if (!cwd.empty()) + launch_info.SetWorkingDirectory(cwd.data()); + + // Extract any extra arguments and append them to our program arguments for + // when we launch + auto args = GetStrings(arguments, "args"); + if (!args.empty()) + launch_info.SetArguments(MakeArgv(args).data(), true); + + // Pass any environment variables along that the user specified. + auto envs = GetStrings(arguments, "env"); + if (!envs.empty()) + launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true); + + auto flags = launch_info.GetLaunchFlags(); + + if (GetBoolean(arguments, "disableASLR", true)) + flags |= lldb::eLaunchFlagDisableASLR; + if (GetBoolean(arguments, "disableSTDIO", false)) + flags |= lldb::eLaunchFlagDisableSTDIO; + if (GetBoolean(arguments, "shellExpandArguments", false)) + flags |= lldb::eLaunchFlagShellExpandArguments; + const bool detachOnError = GetBoolean(arguments, "detachOnError", false); + launch_info.SetDetachOnError(detachOnError); + launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | + lldb::eLaunchFlagStopAtEntry); + + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); - // Instantiate a launch info instance for the target. - auto launch_info = g_vsc.target.GetLaunchInfo(); - - // Grab the current working directory if there is one and set it in the - // launch info. - const auto cwd = GetString(arguments, "cwd"); - if (!cwd.empty()) - launch_info.SetWorkingDirectory(cwd.data()); - - // Extract any extra arguments and append them to our program arguments for - // when we launch - auto args = GetStrings(arguments, "args"); - if (!args.empty()) - launch_info.SetArguments(MakeArgv(args).data(), true); - - // Pass any environment variables along that the user specified. - auto envs = GetStrings(arguments, "env"); - if (!envs.empty()) - launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true); - - auto flags = launch_info.GetLaunchFlags(); - - if (GetBoolean(arguments, "disableASLR", true)) - flags |= lldb::eLaunchFlagDisableASLR; - if (GetBoolean(arguments, "disableSTDIO", false)) - flags |= lldb::eLaunchFlagDisableSTDIO; - if (GetBoolean(arguments, "shellExpandArguments", false)) - flags |= lldb::eLaunchFlagShellExpandArguments; - const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); - launch_info.SetDetachOnError(detatchOnError); - launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | - lldb::eLaunchFlagStopAtEntry); - - // Run any pre run LLDB commands the user specified in the launch.json - g_vsc.RunPreRunCommands(); - if (launchCommands.empty()) { // Disable async events so the launch will be successful when we return from // the launch call and the launch will happen synchronously g_vsc.debugger.SetAsync(false); g_vsc.target.Launch(launch_info, error); g_vsc.debugger.SetAsync(true); - } else { - g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands); - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - g_vsc.target = g_vsc.debugger.GetSelectedTarget(); } if (error.Fail()) { diff --git a/lldb/tools/lldb-vscode/package.json b/lldb/tools/lldb-vscode/package.json --- a/lldb/tools/lldb-vscode/package.json +++ b/lldb/tools/lldb-vscode/package.json @@ -85,7 +85,7 @@ "properties": { "program": { "type": "string", - "description": "Path to the program to debug." + "description": "Path to the program to debug. Ignored if \"launchCommands\" is provided." }, "args": { "type": [ @@ -163,7 +163,7 @@ }, "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.", + "description": "Custom commands that are executed instead of launching a process. The commands must create a target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. The following settings will be ignored if provided: \"program\", \"cwd\", \"args\", \"env\", \"disableASLR\", \"disableSTDIO\", \"shellExpandArguments\", \"detachOnError\", \"runInTerminal\", \"targetTriple\", \"platformName\"", "default": [] }, "stopCommands": { @@ -187,18 +187,18 @@ "properties": { "program": { "type": "string", - "description": "Path to the program to attach to." + "description": "Path to the program to attach to. Ignored if \"attachCommands\" is provided." }, "pid": { "type": [ "number", "string" ], - "description": "System process ID to attach to." + "description": "System process ID to attach to. Ignored if \"attachCommands\" is provided." }, "waitFor": { "type": "boolean", - "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.", + "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag. Ignored if \"attachCommands\" is provided.", "default": true }, "sourcePath": { @@ -224,7 +224,7 @@ }, "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 must create a target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail. The following settings will be ignored if provided: \"program\", \"pid\", \"waitFor\", \"coreFile\", \"targetTriple\", \"platformName\"", "default": [] }, "initCommands": {