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 index c1b33c220b4b..fa5a9c0db1eb 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py @@ -53,11 +53,12 @@ class VSCodeTestCaseBase(TestBase): breakpoint_ids.append('%i' % (breakpoint['id'])) return breakpoint_ids - def waitUntil(self, condition): - while True: - if condition(): - break + def waitUntil(self, condition_callback): + for _ in range(20): + if condition_callback(): + return True time.sleep(0.5) + return False def verify_breakpoint_hit(self, breakpoint_ids): '''Wait for the process we are debugging to stop, and verify we hit diff --git a/lldb/test/API/tools/lldb-vscode/module/Makefile b/lldb/test/API/tools/lldb-vscode/module/Makefile index 1fb944b13893..b30baf48b972 100644 --- a/lldb/test/API/tools/lldb-vscode/module/Makefile +++ b/lldb/test/API/tools/lldb-vscode/module/Makefile @@ -2,12 +2,16 @@ DYLIB_NAME := foo DYLIB_CXX_SOURCES := foo.cpp CXX_SOURCES := main.cpp -all: a.out.stripped +LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)" +USE_LIBDL :=1 include Makefile.rules -a.out.stripped: a.out.dSYM +all: a.out.stripped + +a.out.stripped: strip -o a.out.stripped a.out + ifneq "$(CODESIGN)" "" $(CODESIGN) -fs - a.out.stripped -endif +endif \ No newline at end of file diff --git a/lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py b/lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py index 40c4145b38e3..a16430fccae1 100644 --- a/lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py +++ b/lldb/test/API/tools/lldb-vscode/module/TestVSCode_module.py @@ -10,56 +10,93 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil import lldbvscode_testcase +import re class TestVSCode_module(lldbvscode_testcase.VSCodeTestCaseBase): mydir = TestBase.compute_mydir(__file__) - - @skipIfWindows - @skipUnlessDarwin - @skipIfRemote - def test_modules_event(self): + def run_test(self, symbol_basename, expect_debug_info_size): program_basename = "a.out.stripped" - program= self.getBuildArtifact(program_basename) + 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.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(program_basename, active_modules, '%s module is in 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))) + symbols_path = self.getBuildArtifact(symbol_basename) + self.vscode.request_evaluate('`%s' % ('target symbols add -s "%s" "%s"' % (program, symbols_path))) def checkSymbolsLoaded(): active_modules = self.vscode.get_active_modules() program_module = active_modules[program_basename] return 'Symbols loaded.' == program_module['symbolStatus'] - self.waitUntil(checkSymbolsLoaded) + def checkSymbolsLoadedWithSize(): + active_modules = self.vscode.get_active_modules() + program_module = active_modules[program_basename] + symbolsStatus = program_module['symbolStatus'] + symbol_regex = re.compile(r"Symbols loaded. \([0-9]+(\.[0-9]*)?[KMG]?B\)") + return symbol_regex.match(program_module['symbolStatus']) + + if expect_debug_info_size: + self.waitUntil(checkSymbolsLoadedWithSize) + else: + self.waitUntil(checkSymbolsLoaded) 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(symbols_path, program_module['symbolFilePath']) self.assertIn('addressRange', program_module) + @skipIfWindows + @skipUnlessDarwin + @skipIfRemote + #TODO: Update the Makefile so that this test runs on Linux + def test_module_event(self): + ''' + Mac or linux. + + On mac, if we load a.out as our symbol file, we will use DWARF with .o files and we will + have debug symbols, but we won't see any debug info size because all of the DWARF + sections are in .o files. + + On other platforms, we expect a.out to have debug info, so we will expect a size. + expect_debug_info_size = platform.system() != 'Darwin' + return self.run_test("a.out", expect_debug_info_size) + ''' + expect_debug_info_size = platform.system() != 'Darwin' + return self.run_test("a.out", expect_debug_info_size) + + @skipIfWindows + @skipUnlessDarwin + @skipIfRemote + def test_module_event_dsym(self): + ''' + Darwin only test with dSYM file. + + On mac, if we load a.out.dSYM as our symbol file, we will have debug symbols and we + will have DWARF sections added to the module, so we will expect a size. + return self.run_test("a.out.dSYM", True) + ''' + return self.run_test("a.out.dSYM", True) + @skipIfWindows @skipUnlessDarwin @skipIfRemote def test_compile_units(self): - program= self.getBuildArtifact("a.out") + program = self.getBuildArtifact("a.out") self.build_and_launch(program) source = "main.cpp" main_source_path = self.getSourcePath(source) diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp index 1ebaa5c37712..f6cdcf5a46cf 100644 --- a/lldb/tools/lldb-vscode/JSONUtils.cpp +++ b/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include <algorithm> +#include <iomanip> +#include <sstream> #include "llvm/ADT/Optional.h" #include "llvm/Support/FormatAdapters.h" @@ -327,6 +329,50 @@ llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp, return llvm::json::Value(std::move(object)); } +static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) { + uint64_t debug_info_size = 0; + llvm::StringRef section_name(section.GetName()); + if (section_name.startswith(".debug") || section_name.startswith("__debug") || + section_name.startswith(".apple") || section_name.startswith("__apple")) + debug_info_size += section.GetFileByteSize(); + size_t num_sub_sections = section.GetNumSubSections(); + for (size_t i = 0; i < num_sub_sections; i++) { + debug_info_size += + GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i)); + } + return debug_info_size; +} + +static uint64_t GetDebugInfoSize(lldb::SBModule module) { + uint64_t debug_info_size = 0; + size_t num_sections = module.GetNumSections(); + for (size_t i = 0; i < num_sections; i++) { + debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i)); + } + return debug_info_size; +} + +static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) { + std::ostringstream oss; + oss << " ("; + oss << std::fixed << std::setprecision(1); + + if (debug_info < 1024) { + oss << debug_info << "B"; + } else if (debug_info < 1024 * 1024) { + double kb = double(debug_info) / 1024.0; + oss << kb << "KB"; + } else if (debug_info < 1024 * 1024 * 1024) { + double mb = double(debug_info) / (1024.0 * 1024.0); + oss << mb << "MB"; + } else { + double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0); + oss << gb << "GB"; + ; + } + oss << ")"; + return oss.str(); +} llvm::json::Value CreateModule(lldb::SBModule &module) { llvm::json::Object object; if (!module.IsValid()) @@ -339,9 +385,15 @@ llvm::json::Value CreateModule(lldb::SBModule &module) { std::string module_path(module_path_arr); object.try_emplace("path", module_path); if (module.GetNumCompileUnits() > 0) { - object.try_emplace("symbolStatus", "Symbols loaded."); + std::string symbol_str = "Symbols loaded."; + uint64_t debug_info = GetDebugInfoSize(module); + if (debug_info > 0) { + symbol_str += ConvertDebugInfoSizeToString(debug_info); + } + object.try_emplace("symbolStatus", symbol_str); char symbol_path_arr[PATH_MAX]; - module.GetSymbolFileSpec().GetPath(symbol_path_arr, sizeof(symbol_path_arr)); + module.GetSymbolFileSpec().GetPath(symbol_path_arr, + sizeof(symbol_path_arr)); std::string symbol_path(symbol_path_arr); object.try_emplace("symbolFilePath", symbol_path); } else { @@ -352,8 +404,9 @@ llvm::json::Value CreateModule(lldb::SBModule &module) { 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) { + 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]);