self.initialize_body = None
self.thread_stop_reasons = {}
self.breakpoint_events = []
+ self.module_events = {}
self.sequence = 1
self.threads = None
self.recv_thread.start()
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
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
}
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,
--- /dev/null
+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
--- /dev/null
+"""
+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')
+
--- /dev/null
+int foo() {
+ return 12;
+}
--- /dev/null
+int foo();
--- /dev/null
+#include "foo.h"
+
+int main(int argc, char const *argv[]) {
+ foo();
+ return 0; // breakpoint 1
+}
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<num_versions; ++i) {
+ if (!version_str.empty())
+ version_str += ".";
+ version_str += std::to_string(version_nums[i]);
+ }
+ if (!version_str.empty())
+ object.try_emplace("version", version_str);
+ return llvm::json::Value(std::move(object));
+}
+
void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
llvm::Optional<llvm::StringRef> request_path,
llvm::Optional<uint32_t> request_line) {
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
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include "VSCodeForward.h"
+#include "lldb/API/SBModule.h"
namespace lldb_vscode {
llvm::Optional<llvm::StringRef> request_path = llvm::None,
llvm::Optional<uint32_t> 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
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
lldb::SBTarget::eBroadcastBitBreakpointChanged);
listener.StartListeningForEvents(this->broadcaster,
eBroadcastBitStopEventThread);
+ listener.StartListeningForEvents(
+ this->target.GetBroadcaster(),
+ lldb::SBTarget::eBroadcastBitModulesLoaded |
+ lldb::SBTarget::eBroadcastBitModulesUnloaded |
+ lldb::SBTarget::eBroadcastBitSymbolsLoaded);
}
}
#include <set>
#include <sstream>
#include <thread>
+#include <vector>
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Option/Arg.h"
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;
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",
REQUEST_CALLBACK(disconnect),
REQUEST_CALLBACK(evaluate),
REQUEST_CALLBACK(exceptionInfo),
+ REQUEST_CALLBACK(getCompileUnits),
REQUEST_CALLBACK(initialize),
REQUEST_CALLBACK(launch),
REQUEST_CALLBACK(next),