Index: packages/Python/lldbsuite/test/dotest.py =================================================================== --- packages/Python/lldbsuite/test/dotest.py +++ packages/Python/lldbsuite/test/dotest.py @@ -1120,6 +1120,33 @@ if skipped: print("Skipping following debug info categories:", skipped) +def canRunDWZTests(): + from lldbsuite.test import lldbplatformutil + + platform = lldbplatformutil.getPlatform() + + if platform == "linux": + import distutils.spawn + + if not os.access("/usr/lib/rpm/sepdebugcrcfix", os.X_OK): + return False, "Unable to find /usr/lib/rpm/sepdebugcrcfix" + if distutils.spawn.find_executable("eu-strip") is None: + return False, "Unable to find executable eu-strip" + if distutils.spawn.find_executable("dwz") is None: + return False, "Unable to find executable dwz" + return True, "/usr/lib/rpm/sepdebugcrcfix, eu-strip and dwz found" + + return False, "Don't know how to build with DWZ on %s" % platform + +def checkDWZSupport(): + result, reason = canRunDWZTests() + if result: + return # dwz supported + if "dwz" in configuration.categoriesList: + return # dwz category explicitly requested, let it run. + print("dwz tests will not be run because: " + reason) + configuration.skipCategories.append("dwz") + def run_suite(): # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults # does not exist before proceeding to running the test suite. @@ -1229,6 +1256,7 @@ checkLibcxxSupport() checkDebugInfoSupport() + checkDWZSupport() # Don't do debugserver tests on anything except OS X. configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform Index: packages/Python/lldbsuite/test/lldbinline.py =================================================================== --- packages/Python/lldbsuite/test/lldbinline.py +++ packages/Python/lldbsuite/test/lldbinline.py @@ -128,6 +128,14 @@ self.build() self.do_test() + @add_test_categories(["dwz"]) + def __test_with_dwz(self): + self.using_dsym = False + self.BuildMakefile() + self.build() + self.do_test() + __test_with_dwz.debug_info = "dwz" + def execute_user_command(self, __command): exec(__command, globals(), locals()) Index: packages/Python/lldbsuite/test/lldbtest.py =================================================================== --- packages/Python/lldbsuite/test/lldbtest.py +++ packages/Python/lldbsuite/test/lldbtest.py @@ -1581,6 +1581,23 @@ dictionary, testdir, testname): raise Exception("Don't know how to build binary with gmodules") + def buildDWZ( + self, + architecture=None, + compiler=None, + dictionary=None): + """Platform specific way to build binaries with dwz optimizer.""" + testdir = self.mydir + testname = self.getBuildDirBasename() + if self.getDebugInfo() != "dwz": + raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") + + module = builder_module() + dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) + if not module.buildDWZ(self, architecture, compiler, + dictionary, testdir, testname): + raise Exception("Don't know how to build binary with dwz") + def buildGo(self): """Build the default go binary. """ @@ -2282,6 +2299,8 @@ return self.buildDwo(architecture, compiler, dictionary) elif self.getDebugInfo() == "gmodules": return self.buildGModules(architecture, compiler, dictionary) + elif self.getDebugInfo() == "dwz": + return self.buildDWZ(architecture, compiler, dictionary) else: self.fail("Can't build for debug info: %s" % self.getDebugInfo()) Index: packages/Python/lldbsuite/test/make/Makefile.rules =================================================================== --- packages/Python/lldbsuite/test/make/Makefile.rules +++ packages/Python/lldbsuite/test/make/Makefile.rules @@ -19,6 +19,7 @@ # LD_EXTRAS := # SPLIT_DEBUG_SYMBOLS := YES # CROSS_COMPILE := +# DWZ := YES # # And test/functionalities/archives/Makefile: # MAKE_DSYM := NO @@ -318,6 +319,27 @@ LDFLAGS += -pie endif +#---------------------------------------------------------------------- +# Make the DWZ symbol file from the executable if $(DWZ) = "YES" +# The dwz retry without -m is there because: +# dwz -m still succeeds even for no debug info present (missing -g): +# dwz: $(1).debug: .debug_info section not present +# But dwz -m fails if there is some debug info but none if it is big enough +# to be shared from the the common file $(1).debug.dwz: +# dwz: $(1).debug.dwz: .debug_info section not present +#---------------------------------------------------------------------- +ifeq "$(DWZ)" "YES" + dwz_strip = \ + rel=$$(realpath --relative-to=$$PWD "$(1)") \ + && eu-strip --remove-comment -f "$$rel.debug" "$$rel" \ + && cp "$$rel.debug" "$$rel.debug.dup" \ + && (dwz -m "$$rel.debug.dwz" "$$rel.debug" "$$rel.debug.dup" \ + || dwz "$$rel.debug") \ + && /usr/lib/rpm/sepdebugcrcfix . "$$rel" +else + dwz_strip = +endif + #---------------------------------------------------------------------- # Windows specific options #---------------------------------------------------------------------- @@ -502,12 +524,14 @@ ifeq "$(DYLIB_ONLY)" "" $(EXE) : $(OBJECTS) $(ARCHIVE_NAME) $(DYLIB_FILENAME) $(LD) $(OBJECTS) $(ARCHIVE_NAME) -L. -l$(DYLIB_NAME) $(LDFLAGS) -o "$(EXE)" + $(call dwz_strip,$(EXE)) else EXE = $(DYLIB_FILENAME) endif else $(EXE) : $(OBJECTS) $(ARCHIVE_NAME) $(LD) $(OBJECTS) $(LDFLAGS) $(ARCHIVE_NAME) -o "$(EXE)" + $(call dwz_strip,$(EXE)) endif #---------------------------------------------------------------------- @@ -563,6 +587,7 @@ $(OBJCOPY) --only-keep-debug "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME).debug" $(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DYLIB_FILENAME).debug" "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME)" endif + $(call dwz_strip,$(DYLIB_FILENAME)) endif #---------------------------------------------------------------------- @@ -648,6 +673,7 @@ ifneq "$(DYLIB_NAME)" "" $(RM) -r $(DYLIB_FILENAME).dSYM $(RM) $(DYLIB_OBJECTS) $(DYLIB_PREREQS) $(DYLIB_PREREQS:.d=.d.tmp) $(DYLIB_DWOS) $(DYLIB_FILENAME) $(DYLIB_FILENAME).debug + $(RM) $(DYLIB_FILENAME).debug.dup $(DYLIB_FILENAME).debug.dwz endif ifneq "$(PCH_OUTPUT)" "" $(RM) $(PCH_OUTPUT) @@ -663,7 +689,7 @@ $(RM) $(DYLIB_NAME).lib $(DYLIB_NAME).exp endif else - $(RM) "$(EXE)" + $(RM) "$(EXE)" "$(EXE).debug" "$(EXE).debug.dup" "$(EXE).debug.dwz" endif #---------------------------------------------------------------------- Index: packages/Python/lldbsuite/test/plugins/builder_base.py =================================================================== --- packages/Python/lldbsuite/test/plugins/builder_base.py +++ packages/Python/lldbsuite/test/plugins/builder_base.py @@ -212,6 +212,28 @@ return True +def buildDWZ( + sender=None, + architecture=None, + compiler=None, + dictionary=None, + testdir=None, + testname=None): + """Build the binaries with DWZ - a DWARF optimization tool.""" + commands = [] + # dwz has a bug being unable to process non-separated debug info. + commands.append(getMake(testdir, testname) + + ["MAKE_DSYM=NO", + "DWZ=YES", + getArchSpec(architecture), + getCCSpec(compiler), + getCmdLine(dictionary)]) + + lldbtest.system(commands, sender=sender) + # True signifies that we can handle building dwz. + return True + + def cleanup(sender=None, dictionary=None): """Perform a platform-specific cleanup after the test.""" return True Index: packages/Python/lldbsuite/test/test_categories.py =================================================================== --- packages/Python/lldbsuite/test/test_categories.py +++ packages/Python/lldbsuite/test/test_categories.py @@ -15,7 +15,7 @@ debug_info_categories = [ - 'dwarf', 'dwo', 'dsym', 'gmodules' + 'dwarf', 'dwo', 'dsym', 'gmodules', 'dwz' ] all_categories = { @@ -24,6 +24,7 @@ 'dwo': 'Tests that can be run with DWO debug information', 'dsym': 'Tests that can be run with DSYM debug information', 'gmodules': 'Tests that can be run with -gmodules debug information', + 'dwz': 'Tests using DWZ and its DWARF partial units', 'expression': 'Tests related to the expression parser', 'libc++': 'Test for libc++ data formatters', 'objc': 'Tests related to the Objective-C programming language support', @@ -61,6 +62,8 @@ if platform not in ["freebsd", "darwin", "macosx", "ios", "watchos", "tvos", "bridgeos"]: return False return gmodules.is_compiler_clang_with_gmodules(compiler_path) + elif category == "dwz": + return platform in ["linux"] return True Index: unittests/SymbolFile/CMakeLists.txt =================================================================== --- unittests/SymbolFile/CMakeLists.txt +++ unittests/SymbolFile/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(DWARF) +add_subdirectory(DWZ) if (LLVM_ENABLE_DIA_SDK) add_subdirectory(PDB) endif() Index: unittests/SymbolFile/DWZ/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/SymbolFile/DWZ/CMakeLists.txt @@ -0,0 +1,21 @@ +add_lldb_unittest(SymbolFileDWZTests + SymbolFileDWZTests.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbPluginSymbolFileDWARF + lldbUtilityHelpers + lldbPluginObjectFileELF + lldbPluginSymbolVendorELF + LINK_COMPONENTS + Support + ) + +set(test_inputs + dwztest.out + dwztest.debug + dwztest.debug.dwz) + +add_unittest_inputs(SymbolFileDWZTests "${test_inputs}") Index: unittests/SymbolFile/DWZ/Inputs/dwztest.c =================================================================== --- /dev/null +++ unittests/SymbolFile/DWZ/Inputs/dwztest.c @@ -0,0 +1,9 @@ +// gcc -Wall -g -o dwztest.out dwztest.c; eu-strip --remove-comment -f dwztest.debug dwztest.out; cp -p dwztest.debug dwztest.debug.dup; dwz -m dwztest.debug.dwz dwztest.debug dwztest.debug.dup;rm dwztest.debug.dup; /usr/lib/rpm/sepdebugcrcfix . dwztest.out + +struct StructMovedToDWZCommonFile { + int i1, i2, i3, i4, i5, i6, i7, i8, i9; +} VarWithStructMovedToDWZCommonFile; +static const int sizeof_StructMovedToDWZCommonFile = sizeof(struct StructMovedToDWZCommonFile); +int main() { + return sizeof_StructMovedToDWZCommonFile; +} Index: unittests/SymbolFile/DWZ/SymbolFileDWZTests.cpp =================================================================== --- /dev/null +++ unittests/SymbolFile/DWZ/SymbolFileDWZTests.cpp @@ -0,0 +1,89 @@ +//===-- SymbolFileDWZTests.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "TestingSupport/TestUtilities.h" + +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" + +#if defined(_MSC_VER) +#include "lldb/Host/windows/windows.h" +#include +#endif + +#include + +using namespace lldb_private; + +class SymbolFileDWZTests : public testing::Test { +public: + void SetUp() override { +// Initialize and TearDown the plugin every time, so we get a brand new +// AST every time so that modifications to the AST from each test don't +// leak into the next test. +#if defined(_MSC_VER) + ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); +#endif + + HostInfo::Initialize(); + SymbolFileDWARF::Initialize(); + ClangASTContext::Initialize(); + ObjectFileELF::Initialize(); + SymbolVendorELF::Initialize(); + + m_dwztest_out = GetInputFilePath("dwztest.out"); + } + + void TearDown() override { + SymbolVendorELF::Terminate(); + ObjectFileELF::Terminate(); + ClangASTContext::Terminate(); + SymbolFileDWARF::Terminate(); + HostInfo::Terminate(); + +#if defined(_MSC_VER) + ::CoUninitialize(); +#endif + } + +protected: + std::string m_dwztest_out; +}; + +TEST_F(SymbolFileDWZTests, TestSimpleClassTypes) { + FileSpec fspec(m_dwztest_out.c_str(), false); + ArchSpec aspec("x86_64-pc-linux"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + EXPECT_NE(nullptr, symfile); + EXPECT_EQ(symfile->GetPluginName(), SymbolFileDWARF::GetPluginNameStatic()); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + EXPECT_EQ(1u, + symfile->FindTypes(sc, ConstString("StructMovedToDWZCommonFile"), nullptr, + false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("StructMovedToDWZCommonFile"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); +}