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 @@ -266,8 +266,8 @@ stopOnEntry=False, disableASLR=True, disableSTDIO=False, shellExpandArguments=False, trace=False, initCommands=None, preRunCommands=None, - stopCommands=None, exitCommands=None,sourcePath= None, - debuggerRoot=None, launchCommands=None): + stopCommands=None, exitCommands=None,sourcePath=None, + debuggerRoot=None, launchCommands=None, sourceMap=None): '''Sending launch request to vscode ''' @@ -298,7 +298,8 @@ exitCommands=exitCommands, sourcePath=sourcePath, debuggerRoot=debuggerRoot, - launchCommands=launchCommands) + launchCommands=launchCommands, + sourceMap=sourceMap) if not (response and response['success']): self.assertTrue(response['success'], 'launch failed (%s)' % (response['message'])) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -570,7 +570,7 @@ disableSTDIO=False, shellExpandArguments=False, trace=False, initCommands=None, preRunCommands=None, stopCommands=None, exitCommands=None, sourcePath=None, - debuggerRoot=None, launchCommands=None): + debuggerRoot=None, launchCommands=None, sourceMap=None): args_dict = { 'program': program } @@ -605,6 +605,8 @@ args_dict['debuggerRoot'] = debuggerRoot if launchCommands: args_dict['launchCommands'] = launchCommands + if sourceMap: + args_dict['sourceMap'] = sourceMap command_dict = { 'command': 'launch', 'type': 'request', diff --git a/lldb/test/API/tools/lldb-vscode/breakpoint/Makefile b/lldb/test/API/tools/lldb-vscode/breakpoint/Makefile --- a/lldb/test/API/tools/lldb-vscode/breakpoint/Makefile +++ b/lldb/test/API/tools/lldb-vscode/breakpoint/Makefile @@ -1,3 +1,7 @@ -CXX_SOURCES := main.cpp +CXX_SOURCES := main-copy.cpp include Makefile.rules + +# Copy file into the build folder to enable the test to modify it. +main-copy.cpp: main.cpp + cp -f $< $@ diff --git a/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py b/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py --- a/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py @@ -5,6 +5,7 @@ import unittest2 import vscode +import shutil from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil @@ -18,6 +19,46 @@ @skipIfWindows @skipIfRemote + def test_source_map(self): + self.build_and_create_debug_adaptor() + + source_basename = 'main-copy.cpp' + source_path = self.getBuildArtifact(source_basename) + source_folder = os.path.dirname(source_path) + + new_source_folder = os.path.join(os.path.dirname(source_folder), 'moved_location') + new_source_path = os.path.join(new_source_folder, source_basename) + + def cleanup(): + shutil.move(new_source_path, source_path) + os.rmdir(new_source_folder) + + self.addTearDownHook(cleanup) + + lines = [line_number('main.cpp', 'break 12')] + + os.mkdir(new_source_folder) + shutil.move(source_path, new_source_path) + + program = self.getBuildArtifact("a.out") + self.launch(program, sourceMap=[[source_folder, new_source_folder]]) + + response = self.vscode.request_setBreakpoints(new_source_path, lines) + if response: + breakpoints = response['body']['breakpoints'] + self.assertEquals(len(breakpoints), len(lines), + "expect %u source breakpoints" % (len(lines))) + for (breakpoint, index) in zip(breakpoints, range(len(lines))): + line = breakpoint['line'] + self.assertTrue(line, lines[index]) + self.assertTrue(line in lines, "line expected in lines array") + self.assertTrue(breakpoint['verified'], + "expect breakpoint verified") + self.assertEqual(source_basename, breakpoint['source']['name']) + self.assertEqual(new_source_path, breakpoint['source']['path']) + + @skipIfWindows + @skipIfRemote def test_set_and_clear(self): '''Tests setting and clearing source file and line breakpoints. This packet is a bit tricky on the debug adaptor side since there @@ -31,8 +72,8 @@ and makes sure things happen correctly. It doesn't test hitting breakpoints and the functionality of each breakpoint, like 'conditions' and 'hitCondition' settings.''' - source_basename = 'main.cpp' - source_path = os.path.join(os.getcwd(), source_basename) + source_basename = 'main-copy.cpp' + source_path = self.getBuildArtifact(source_basename) first_line = line_number('main.cpp', 'break 12') second_line = line_number('main.cpp', 'break 13') third_line = line_number('main.cpp', 'break 14') @@ -155,8 +196,8 @@ def test_functionality(self): '''Tests hitting breakpoints and the functionality of a single breakpoint, like 'conditions' and 'hitCondition' settings.''' - source_basename = 'main.cpp' - source_path = os.path.join(os.getcwd(), source_basename) + source_basename = 'main-copy.cpp' + source_path = self.getBuildArtifact(source_basename) loop_line = line_number('main.cpp', '// break loop') program = self.getBuildArtifact("a.out") diff --git a/lldb/tools/lldb-vscode/JSONUtils.h b/lldb/tools/lldb-vscode/JSONUtils.h --- a/lldb/tools/lldb-vscode/JSONUtils.h +++ b/lldb/tools/lldb-vscode/JSONUtils.h @@ -184,28 +184,42 @@ void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object, llvm::StringRef key); -/// Converts \a bp to a JSON value and appends all locations to the +/// Converts \a bp to a JSON value and appends the first valid location to the /// \a breakpoints array. /// /// \param[in] bp -/// A LLDB breakpoint object which will get all locations extracted -/// and converted into a JSON objects in the \a breakpoints array +/// A LLDB breakpoint object which will get the first valid location +/// extracted and converted into a JSON object in the \a breakpoints array /// /// \param[in] breakpoints /// A JSON array that will get a llvm::json::Value for \a bp /// appended to it. -void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints); +/// +/// \param[in] request_path +/// An optional source path to use when creating the "Source" object of this +/// breakpoint. If not specified, the "Source" object is created from the +/// breakpoint's address' LineEntry. It is useful to ensure the same source +/// paths provided by the setBreakpoints request are returned to the IDE. +void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints, + llvm::Optional request_path = {}); /// Converts breakpoint location to a Visual Studio Code "Breakpoint" -/// JSON object and appends it to the \a breakpoints array. /// /// \param[in] bp /// A LLDB breakpoint object to convert into a JSON value /// +/// \param[in] request_path +/// An optional source path to use when creating the "Source" object of this +/// breakpoint. If not specified, the "Source" object is created from the +/// breakpoint's address' LineEntry. It is useful to ensure the same source +/// paths provided by the setBreakpoints request are returned to the IDE. +/// /// \return /// A "Breakpoint" JSON object with that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp); +llvm::json::Value +CreateBreakpoint(lldb::SBBreakpoint &bp, + llvm::Optional request_path = {}); /// Create a "Event" JSON object using \a event_name as the event name /// @@ -263,6 +277,16 @@ /// definition outlined by Microsoft. llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry); +/// Create a "Source" object for a given source path. +/// +/// \param[in] source_path +/// The path to the source to use when creating the "Source" object. +/// +/// \return +/// A "Source" JSON object that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateSource(llvm::StringRef source_path); + /// Create a "Source" object for a given frame. /// /// When there is no source file information for a stack frame, we will diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp --- a/lldb/tools/lldb-vscode/JSONUtils.cpp +++ b/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -8,7 +8,10 @@ #include +#include "llvm/ADT/Optional.h" + #include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/Path.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBBreakpointLocation.h" @@ -281,7 +284,9 @@ // }, // "required": [ "verified" ] // } -llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp) { +llvm::json::Value +CreateBreakpoint(lldb::SBBreakpoint &bp, + llvm::Optional request_path) { // Each breakpoint location is treated as a separate breakpoint for VS code. // They don't have the notion of a single breakpoint with multiple locations. llvm::json::Object object; @@ -314,13 +319,15 @@ const auto line = line_entry.GetLine(); if (line != UINT32_MAX) object.try_emplace("line", line); - object.try_emplace("source", CreateSource(line_entry)); + object.try_emplace("source", request_path ? CreateSource(*request_path) + : CreateSource(line_entry)); } return llvm::json::Value(std::move(object)); } -void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints) { - breakpoints.emplace_back(CreateBreakpoint(bp)); +void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints, + llvm::Optional request_path) { + breakpoints.emplace_back(CreateBreakpoint(bp, request_path)); } // "Event": { @@ -481,6 +488,14 @@ return llvm::json::Value(std::move(object)); } +llvm::json::Value CreateSource(llvm::StringRef source_path) { + llvm::json::Object source; + llvm::StringRef name = llvm::sys::path::filename(source_path); + EmplaceSafeString(source, "name", name); + EmplaceSafeString(source, "path", source_path); + return llvm::json::Value(std::move(source)); +} + llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) { disasm_line = 0; auto line_entry = frame.GetLineEntry(); 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 @@ -1364,13 +1364,13 @@ llvm::sys::fs::set_current_path(debuggerRoot.data()); } - SetSourceMapFromArguments(*arguments); - // 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_vsc.RunInitCommands(); + SetSourceMapFromArguments(*arguments); + lldb::SBError status; g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status)); if (status.Fail()) { @@ -1748,13 +1748,13 @@ const auto &existing_bp = existing_source_bps->second.find(src_bp.line); if (existing_bp != existing_source_bps->second.end()) { existing_bp->second.UpdateBreakpoint(src_bp); - AppendBreakpoint(existing_bp->second.bp, response_breakpoints); + AppendBreakpoint(existing_bp->second.bp, response_breakpoints, path); continue; } } // At this point the breakpoint is new src_bp.SetBreakpoint(path.data()); - AppendBreakpoint(src_bp.bp, response_breakpoints); + AppendBreakpoint(src_bp.bp, response_breakpoints, path); g_vsc.source_breakpoints[path][src_bp.line] = std::move(src_bp); } }