diff --git a/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py b/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py index e4cb103010fde..9b9195561606b 100644 --- a/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py +++ b/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py @@ -7,7 +7,7 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil import lldbvscode_testcase - +import os def make_buffer_verify_dict(start_idx, count, offset=0): verify_dict = {} @@ -38,6 +38,14 @@ def verify_values(self, verify_dict, actual, varref_dict=None, expression=None): ' "%s")') % ( key, actual_value, verify_value)) + if 'contains' in verify_dict: + verify = verify_dict['contains'] + for key in verify: + contains_array = verify[key] + actual_value = actual[key] + self.assertTrue(isinstance(contains_array, list)) + for verify_value in contains_array: + self.assertIn(verify_value, actual_value) if 'missing' in verify_dict: missing = verify_dict['missing'] for key in missing: @@ -508,3 +516,46 @@ def test_registers(self): self.assertTrue(value.startswith('0x')) self.assertTrue('a.out`main + ' in value) self.assertTrue('at main.cpp:' in value) + + @no_debug_info_test + @skipUnlessDarwin + def test_darwin_dwarf_missing_obj(self): + ''' + Test that if we build a binary with DWARF in .o files and we remove + the .o file for main.cpp, that we get a variable named "" + whose value matches the appriopriate error. Errors when getting + variables are returned in the LLDB API when the user should be + notified of issues that can easily be solved by rebuilding or + changing compiler options and are designed to give better feedback + to the user. + ''' + self.build(debug_info="dwarf") + program = self.getBuildArtifact("a.out") + main_obj = self.getBuildArtifact("main.o") + self.assertTrue(os.path.exists(main_obj)) + # Delete the main.o file that contains the debug info so we force an + # error when we run to main and try to get variables + os.unlink(main_obj) + + self.create_debug_adaptor() + self.assertTrue(os.path.exists(program), 'executable must exist') + self.launch(program) + + functions = ['main'] + breakpoint_ids = self.set_function_breakpoints(functions) + self.assertEquals(len(breakpoint_ids), len(functions), "expect one breakpoint") + self.continue_to_breakpoints(breakpoint_ids) + + locals = self.vscode.get_local_variables() + + verify_locals = { + '': { + 'equals': {'type': 'const char *'}, + 'contains': { 'value': [ + 'debug map object file ', + 'main.o" containing debug info does not exist, debug info will not be loaded'] + } + }, + } + varref_dict = {} + self.verify_variables(verify_locals, locals, varref_dict) diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp index 22ff84f42921b..b7285915f8542 100644 --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -2953,6 +2953,31 @@ void request_variables(const llvm::json::Object &request) { } num_children = top_scope->GetSize(); + if (num_children == 0 && variablesReference == VARREF_LOCALS) { + // Check for an error in the SBValueList that might explain why we don't + // have locals. If we have an error display it as the sole value in the + // the locals. + + // "error" owns the error string so we must keep it alive as long as we + // want to use the returns "const char *" + lldb::SBError error = top_scope->GetError(); + const char *var_err = error.GetCString(); + if (var_err) { + // Create a fake variable named "error" to explain why variables were + // not available. This new error will help let users know when there was + // a problem that kept variables from being available for display and + // allow users to fix this issue instead of seeing no variables. The + // errors are only set when there is a problem that the user could + // fix, so no error will show up when you have no debug info, only when + // we do have debug info and something that is fixable can be done. + llvm::json::Object object; + EmplaceSafeString(object, "name", ""); + EmplaceSafeString(object, "type", "const char *"); + EmplaceSafeString(object, "value", var_err); + object.try_emplace("variablesReference", (int64_t)0); + variables.emplace_back(std::move(object)); + } + } const int64_t end_idx = start_idx + ((count == 0) ? num_children : count); // We first find out which variable names are duplicated