From 77c9aafc5d85a816c20d1f1fb176024bc0b8d0fe Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Mon, 13 Jul 2020 12:10:49 -0700 Subject: [PATCH] Retry ""[lldb-vscode] Fix TestVSCode_module"" Original commit c60216db15132401ff60c08ccef899321f63b6b6. The test can only run on Darwin because of how it was setup, so I'm enforcing that. Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: --- .../test/tools/lldb-vscode/vscode.py | 23 +++++ .../API/tools/lldb-vscode/module/Makefile | 13 +++ .../lldb-vscode/module/TestVSCode_module.py | 72 +++++++++++++++ .../test/API/tools/lldb-vscode/module/foo.cpp | 3 + lldb/test/API/tools/lldb-vscode/module/foo.h | 1 + .../API/tools/lldb-vscode/module/main.cpp | 6 ++ lldb/tools/lldb-vscode/JSONUtils.cpp | 44 +++++++++ lldb/tools/lldb-vscode/JSONUtils.h | 13 +++ lldb/tools/lldb-vscode/VSCode.cpp | 5 + lldb/tools/lldb-vscode/lldb-vscode.cpp | 92 +++++++++++++++++++ 10 files changed, 272 insertions(+) create mode 100644 lldb/test/API/tools/lldb-vscode/module/Makefile create mode 100644 lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py create mode 100644 lldb/test/API/tools/lldb-vscode/module/foo.cpp create mode 100644 lldb/test/API/tools/lldb-vscode/module/foo.h create mode 100644 lldb/test/API/tools/lldb-vscode/module/main.cpp diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py index 1ad168e794cff..6b1c1c961b545 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -113,6 +113,7 @@ def __init__(self, recv, send, init_commands): self.initialize_body = None self.thread_stop_reasons = {} self.breakpoint_events = [] + self.module_events = {} self.sequence = 1 self.threads = None self.recv_thread.start() @@ -133,6 +134,9 @@ def validate_response(cls, command, response): if command['seq'] != response['request_seq']: raise ValueError('seq mismatch in response') + def get_active_modules(self): + return self.module_events + def get_output(self, category, timeout=0.0, clear=True): self.output_condition.acquire() output = None @@ -218,6 +222,15 @@ def handle_recv_packet(self, packet): self.breakpoint_events.append(packet) # no need to add 'breakpoint' event packets to our packets list return keepGoing + elif event == 'module': + reason = body['reason'] + if (reason == 'new' or reason == 'changed'): + self.module_events[body['module']['name']] = body['module'] + elif reason == 'removed': + if body['module']['name'] in self.module_events: + self.module_events.pop(body['module']['name']) + return keepGoing + elif packet_type == 'response': if packet['command'] == 'disconnect': keepGoing = False @@ -747,6 +760,16 @@ def request_setFunctionBreakpoints(self, names, condition=None, } return self.send_recv(command_dict) + def request_getCompileUnits(self, moduleId): + args_dict = {'moduleId': moduleId} + command_dict = { + 'command': 'getCompileUnits', + 'type': 'request', + 'arguments': args_dict + } + response = self.send_recv(command_dict) + return response + def request_completions(self, text): args_dict = { 'text': text, diff --git a/lldb/test/API/tools/lldb-vscode/module/Makefile b/lldb/test/API/tools/lldb-vscode/module/Makefile new file mode 100644 index 0000000000000..1fb944b138937 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/module/Makefile @@ -0,0 +1,13 @@ +DYLIB_NAME := foo +DYLIB_CXX_SOURCES := foo.cpp +CXX_SOURCES := main.cpp + +all: a.out.stripped + +include Makefile.rules + +a.out.stripped: a.out.dSYM + strip -o a.out.stripped a.out +ifneq "$(CODESIGN)" "" + $(CODESIGN) -fs - a.out.stripped +endif diff --git a/lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py b/lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py new file mode 100644 index 0000000000000..461ac201a73f4 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py @@ -0,0 +1,72 @@ +""" +Test lldb-vscode setBreakpoints request +""" + +from __future__ import print_function + +import unittest2 +import vscode +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import lldbvscode_testcase + + +class TestVSCode_module(lldbvscode_testcase.VSCodeTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + + @skipIfWindows + @skipUnlessDarwin + @skipIfRemote + def test_modules_event(self): + program_basename = "a.out.stripped" + program= self.getBuildArtifact(program_basename) + self.build_and_launch(program) + functions = ['foo'] + breakpoint_ids = self.set_function_breakpoints(functions) + self.assertEquals(len(breakpoint_ids), len(functions), + 'expect one breakpoint') + self.continue_to_breakpoints(breakpoint_ids) + active_modules = self.vscode.get_active_modules() + self.assertIn(program_basename, active_modules, '%s module is in active modules' % (program_basename)) + program_module = active_modules[program_basename] + self.assertIn('name', program_module, 'make sure name is in module') + self.assertEqual(program_basename, program_module['name']) + self.assertIn('path', program_module, 'make sure path is in module') + self.assertEqual(program, program_module['path']) + self.assertTrue('symbolFilePath' not in program_module, 'Make sure a.out.stripped has no debug info') + self.assertEqual('Symbols not found.', program_module['symbolStatus']) + symbol_path = self.getBuildArtifact("a.out") + self.vscode.request_evaluate('`%s' % ('target symbols add -s "%s" "%s"' % (program, symbol_path))) + active_modules = self.vscode.get_active_modules() + program_module = active_modules[program_basename] + self.assertEqual(program_basename, program_module['name']) + self.assertEqual(program, program_module['path']) + self.assertEqual('Symbols loaded.', program_module['symbolStatus']) + self.assertIn('symbolFilePath', program_module) + self.assertEqual(symbol_path, program_module['symbolFilePath']) + self.assertIn('addressRange', program_module) + + @skipIfWindows + @skipUnlessDarwin + @skipIfRemote + def test_compile_units(self): + program= self.getBuildArtifact("a.out") + self.build_and_launch(program) + source = "main.cpp" + main_source_path = self.getSourcePath(source) + breakpoint1_line = line_number(source, '// breakpoint 1') + lines = [breakpoint1_line] + breakpoint_ids = self.set_source_breakpoints(source, lines) + self.continue_to_breakpoints(breakpoint_ids) + moduleId = self.vscode.get_active_modules()['a.out']['id'] + response = self.vscode.request_getCompileUnits(moduleId) + print(response['body']) + self.assertTrue(response['body']) + self.assertTrue(len(response['body']['compileUnits']) == 1, + 'Only one source file should exist') + self.assertTrue(response['body']['compileUnits'][0]['compileUnitPath'] == main_source_path, + 'Real path to main.cpp matches') + diff --git a/lldb/test/API/tools/lldb-vscode/module/foo.cpp b/lldb/test/API/tools/lldb-vscode/module/foo.cpp new file mode 100644 index 0000000000000..9dba85a9cccab --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/module/foo.cpp @@ -0,0 +1,3 @@ +int foo() { + return 12; +} diff --git a/lldb/test/API/tools/lldb-vscode/module/foo.h b/lldb/test/API/tools/lldb-vscode/module/foo.h new file mode 100644 index 0000000000000..5d5f8f0c9e786 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/module/foo.h @@ -0,0 +1 @@ +int foo(); diff --git a/lldb/test/API/tools/lldb-vscode/module/main.cpp b/lldb/test/API/tools/lldb-vscode/module/main.cpp new file mode 100644 index 0000000000000..4ff2b2360eb97 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/module/main.cpp @@ -0,0 +1,6 @@ +#include "foo.h" + +int main(int argc, char const *argv[]) { + foo(); + return 0; // breakpoint 1 +} diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp index 8fcf179b29aad..86c29fb238112 100644 --- a/lldb/tools/lldb-vscode/JSONUtils.cpp +++ b/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -327,6 +327,41 @@ llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp, return llvm::json::Value(std::move(object)); } +llvm::json::Value CreateModule(lldb::SBModule &module) { + llvm::json::Object object; + if (!module.IsValid()) + return llvm::json::Value(std::move(object)); + object.try_emplace("id", std::string(module.GetUUIDString())); + object.try_emplace("name", std::string(module.GetFileSpec().GetFilename())); + char module_path_arr[PATH_MAX]; + module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr)); + std::string module_path(module_path_arr); + object.try_emplace("path", module_path); + if (module.GetNumCompileUnits() > 0) { + object.try_emplace("symbolStatus", "Symbols loaded."); + char symbol_path_arr[PATH_MAX]; + module.GetSymbolFileSpec().GetPath(symbol_path_arr, sizeof(symbol_path_arr)); + std::string symbol_path(symbol_path_arr); + object.try_emplace("symbolFilePath", symbol_path); + } else { + object.try_emplace("symbolStatus", "Symbols not found."); + } + std::string loaded_addr = std::to_string( + module.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc.target)); + object.try_emplace("addressRange", loaded_addr); + std::string version_str; + uint32_t version_nums[3]; + uint32_t num_versions = module.GetVersion(version_nums, sizeof(version_nums)/sizeof(uint32_t)); + for (uint32_t i=0; i request_path, llvm::Optional request_line) { @@ -902,4 +937,13 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, return llvm::json::Value(std::move(object)); } +llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) { + llvm::json::Object object; + char unit_path_arr[PATH_MAX]; + unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr)); + std::string unit_path(unit_path_arr); + object.try_emplace("compileUnitPath", unit_path); + return llvm::json::Value(std::move(object)); +} + } // namespace lldb_vscode diff --git a/lldb/tools/lldb-vscode/JSONUtils.h b/lldb/tools/lldb-vscode/JSONUtils.h index af76683d11cc8..e2ccfdb1fb2b6 100644 --- a/lldb/tools/lldb-vscode/JSONUtils.h +++ b/lldb/tools/lldb-vscode/JSONUtils.h @@ -13,6 +13,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/JSON.h" #include "VSCodeForward.h" +#include "lldb/API/SBModule.h" namespace lldb_vscode { @@ -237,6 +238,16 @@ CreateBreakpoint(lldb::SBBreakpoint &bp, llvm::Optional request_path = llvm::None, llvm::Optional request_line = llvm::None); +/// Converts a LLDB module to a VS Code DAP module for use in "modules" events. +/// +/// \param[in] module +/// A LLDB module object to convert into a JSON value +/// +/// \return +/// A "Module" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateModule(lldb::SBModule &module); + /// Create a "Event" JSON object using \a event_name as the event name /// /// \param[in] event_name @@ -430,6 +441,8 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, uint32_t stop_id); llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, int64_t varID, bool format_hex); +llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit); + } // namespace lldb_vscode #endif diff --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp index b2d16f96d1f2b..4a30aef3a6db4 100644 --- a/lldb/tools/lldb-vscode/VSCode.cpp +++ b/lldb/tools/lldb-vscode/VSCode.cpp @@ -358,6 +358,11 @@ void VSCode::SetTarget(const lldb::SBTarget target) { lldb::SBTarget::eBroadcastBitBreakpointChanged); listener.StartListeningForEvents(this->broadcaster, eBroadcastBitStopEventThread); + listener.StartListeningForEvents( + this->target.GetBroadcaster(), + lldb::SBTarget::eBroadcastBitModulesLoaded | + lldb::SBTarget::eBroadcastBitModulesUnloaded | + lldb::SBTarget::eBroadcastBitSymbolsLoaded); } } diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp index 168873f827527..27ee832677d72 100644 --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "llvm/ADT/ArrayRef.h" #include "llvm/Option/Arg.h" @@ -434,6 +435,30 @@ void EventThreadFunction() { g_vsc.SendJSON(llvm::json::Value(std::move(bp_event))); } } + } else if (lldb::SBTarget::EventIsTargetEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || + event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded) { + int num_modules = lldb::SBTarget::GetNumModulesFromEvent(event); + for (int i = 0; i < num_modules; i++) { + auto module = lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); + auto module_event = CreateEventObject("module"); + llvm::json::Value module_value = CreateModule(module); + llvm::json::Object body; + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) { + body.try_emplace("reason", "new"); + } else if (event_mask & + lldb::SBTarget::eBroadcastBitModulesUnloaded) { + body.try_emplace("reason", "removed"); + } else if (event_mask & + lldb::SBTarget::eBroadcastBitSymbolsLoaded) { + body.try_emplace("reason", "changed"); + } + body.try_emplace("module", module_value); + module_event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(module_event))); + } + } } else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) { if (event_mask & eBroadcastBitStopEventThread) { done = true; @@ -1149,6 +1174,72 @@ void request_evaluate(const llvm::json::Object &request) { g_vsc.SendJSON(llvm::json::Value(std::move(response))); } +// "getCompileUnitsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Compile Unit request; value of command field is +// 'getCompileUnits'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "getCompileUnits" ] +// }, +// "arguments": { +// "$ref": "#/definitions/getCompileUnitRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "getCompileUnitsRequestArguments": { +// "type": "object", +// "description": "Arguments for 'getCompileUnits' request.", +// "properties": { +// "moduleId": { +// "type": "string", +// "description": "The ID of the module." +// } +// }, +// "required": [ "moduleId" ] +// }, +// "getCompileUnitsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'getCompileUnits' request.", +// "properties": { +// "body": { +// "description": "Response to 'getCompileUnits' request. Array of +// paths of compile units." +// } +// } +// }] +// } + +void request_getCompileUnits(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + lldb::SBProcess process = g_vsc.target.GetProcess(); + llvm::json::Object body; + llvm::json::Array units; + auto arguments = request.getObject("arguments"); + std::string module_id = std::string(GetString(arguments, "moduleId")); + int num_modules = g_vsc.target.GetNumModules(); + for (int i = 0; i < num_modules; i++) { + auto curr_module = g_vsc.target.GetModuleAtIndex(i); + if (module_id == curr_module.GetUUIDString()) { + int num_units = curr_module.GetNumCompileUnits(); + for (int j = 0; j < num_units; j++) { + auto curr_unit = curr_module.GetCompileUnitAtIndex(j);\ + units.emplace_back(CreateCompileUnit(curr_unit));\ + } + body.try_emplace("compileUnits", std::move(units)); + break; + } + } + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + // "InitializeRequest": { // "allOf": [ { "$ref": "#/definitions/Request" }, { // "type": "object", @@ -2734,6 +2825,7 @@ const std::map &GetRequestHandlers() { REQUEST_CALLBACK(disconnect), REQUEST_CALLBACK(evaluate), REQUEST_CALLBACK(exceptionInfo), + REQUEST_CALLBACK(getCompileUnits), REQUEST_CALLBACK(initialize), REQUEST_CALLBACK(launch), REQUEST_CALLBACK(next),