[Lldb-commits] [lldb] b0b2b6b - [lldb][PlatformDarwin] Parse SDK path for module compilation from debug-info

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Wed Jul 26 10:26:41 PDT 2023


Author: Michael Buch
Date: 2023-07-26T18:26:08+01:00
New Revision: b0b2b6bab4d25122b2259dfac500fd83d60fa154

URL: https://github.com/llvm/llvm-project/commit/b0b2b6bab4d25122b2259dfac500fd83d60fa154
DIFF: https://github.com/llvm/llvm-project/commit/b0b2b6bab4d25122b2259dfac500fd83d60fa154.diff

LOG: [lldb][PlatformDarwin] Parse SDK path for module compilation from debug-info

When we build the Clang module compilation command (e.g., when
a user requests import of a module via `expression @import Foundation`),
LLDB will try to determine which SDK directory to use as the `sysroot`.
However, it currently does so by simply enumerating the `SDKs` directory
and picking the last one that's appropriate for module compilation
(see `PlatformDarwin::GetSDKDirectoryForModules`). That means if we have
multiple platform SDKs installed (e.g., a public and internal one), we
may pick the wrong one by chance.

On Darwin platforms we emit the SDK path that a object
file was compiled against into DWARF (using `DW_AT_LLVM_sysroot`
and `DW_AT_APPLE_sdk`). For Swift debugging, we already parse the SDK
path from debug-info if we can.

This patch mimicks the Swift behaviour for non-Swift languages. I.e.,
if we can get the SDK path from debug-info, do so. Otherwise, fall back
to the old heuristic.

rdar://110407148

Differential Revision: https://reviews.llvm.org/D156020

Added: 
    

Modified: 
    lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
    lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
    lldb/unittests/SymbolFile/DWARF/XcodeSDKModuleTests.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index 5fd666b8ce8b58..fb652d9f02e0ed 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -42,6 +42,7 @@
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/Timer.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/VersionTuple.h"
@@ -1095,8 +1096,21 @@ void PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(
   }
 
   FileSpec sysroot_spec;
-  // Scope for mutex locker below
-  {
+
+  if (target) {
+    if (ModuleSP exe_module_sp = target->GetExecutableModule()) {
+      auto path_or_err = ResolveSDKPathFromDebugInfo(*exe_module_sp);
+      if (path_or_err) {
+        sysroot_spec = FileSpec(*path_or_err);
+      } else {
+        LLDB_LOG_ERROR(GetLog(LLDBLog::Types | LLDBLog::Host),
+                       path_or_err.takeError(),
+                       "Failed to resolve SDK path: {0}");
+      }
+    }
+  }
+
+  if (!FileSystem::Instance().IsDirectory(sysroot_spec.GetPath())) {
     std::lock_guard<std::mutex> guard(m_mutex);
     sysroot_spec = GetSDKDirectoryForModules(sdk_type);
   }
@@ -1335,3 +1349,53 @@ llvm::Triple::OSType PlatformDarwin::GetHostOSType() {
 #endif
 #endif // __APPLE__
 }
+
+llvm::Expected<std::pair<XcodeSDK, bool>>
+PlatformDarwin::GetSDKPathFromDebugInfo(Module &module) {
+  SymbolFile *sym_file = module.GetSymbolFile();
+  if (!sym_file)
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        llvm::formatv("No symbol file available for module '{0}'",
+                      module.GetFileSpec().GetFilename().AsCString("")));
+
+  bool found_public_sdk = false;
+  bool found_internal_sdk = false;
+  XcodeSDK merged_sdk;
+  for (unsigned i = 0; i < sym_file->GetNumCompileUnits(); ++i) {
+    if (auto cu_sp = sym_file->GetCompileUnitAtIndex(i)) {
+      auto cu_sdk = sym_file->ParseXcodeSDK(*cu_sp);
+      bool is_internal_sdk = cu_sdk.IsAppleInternalSDK();
+      found_public_sdk |= !is_internal_sdk;
+      found_internal_sdk |= is_internal_sdk;
+
+      merged_sdk.Merge(cu_sdk);
+    }
+  }
+
+  const bool found_mismatch = found_internal_sdk && found_public_sdk;
+
+  return std::pair{std::move(merged_sdk), found_mismatch};
+}
+
+llvm::Expected<std::string>
+PlatformDarwin::ResolveSDKPathFromDebugInfo(Module &module) {
+  auto sdk_or_err = GetSDKPathFromDebugInfo(module);
+  if (!sdk_or_err)
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        llvm::formatv("Failed to parse SDK path from debug-info: {0}",
+                      llvm::toString(sdk_or_err.takeError())));
+
+  auto [sdk, _] = std::move(*sdk_or_err);
+
+  auto path_or_err = HostInfo::GetSDKRoot(HostInfo::SDKOptions{sdk});
+  if (!path_or_err)
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        llvm::formatv("Error while searching for SDK (XcodeSDK '{0}'): {1}",
+                      sdk.GetString(),
+                      llvm::toString(path_or_err.takeError())));
+
+  return path_or_err->str();
+}

diff  --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
index 005dea5190604d..097e149f0f0f92 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
@@ -124,6 +124,33 @@ class PlatformDarwin : public PlatformPOSIX {
   /// located in.
   static FileSpec GetCurrentCommandLineToolsDirectory();
 
+  /// Search each CU associated with the specified 'module' for
+  /// the SDK paths the CUs were compiled against. In the presence
+  /// of 
diff erent SDKs, we try to pick the most appropriate one
+  /// using \ref XcodeSDK::Merge.
+  ///
+  /// \param[in] module Module whose debug-info CUs to parse for
+  ///                   which SDK they were compiled against.
+  ///
+  /// \returns If successful, returns a pair of a parsed XcodeSDK
+  ///          object and a boolean that is 'true' if we encountered
+  ///          a conflicting combination of SDKs when parsing the CUs
+  ///          (e.g., a public and internal SDK).
+  static llvm::Expected<std::pair<XcodeSDK, bool>>
+  GetSDKPathFromDebugInfo(Module &module);
+
+  /// Returns the full path of the most appropriate SDK for the
+  /// specified 'module'. This function gets this path by parsing
+  /// debug-info (see \ref `GetSDKPathFromDebugInfo`).
+  ///
+  /// \param[in] module Module whose debug-info to parse for
+  ///                   which SDK it was compiled against.
+  ///
+  /// \returns If successful, returns the full path to an
+  ///          Xcode SDK.
+  static llvm::Expected<std::string>
+  ResolveSDKPathFromDebugInfo(Module &module);
+
 protected:
   static const char *GetCompatibleArch(ArchSpec::Core core, size_t idx);
 

diff  --git a/lldb/unittests/SymbolFile/DWARF/XcodeSDKModuleTests.cpp b/lldb/unittests/SymbolFile/DWARF/XcodeSDKModuleTests.cpp
index 9fb4602a41e3da..ca4c29ebb9148f 100644
--- a/lldb/unittests/SymbolFile/DWARF/XcodeSDKModuleTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/XcodeSDKModuleTests.cpp
@@ -12,6 +12,8 @@
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
 #include "TestingSupport/Symbol/YAMLModuleTester.h"
 #include "lldb/Core/PluginManager.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -23,8 +25,55 @@ namespace {
 class XcodeSDKModuleTests : public testing::Test {
   SubsystemRAII<HostInfoBase, PlatformMacOSX> subsystems;
 };
-} // namespace
 
+struct SDKPathParsingTestData {
+  /// Each path will be put into a new CU's
+  /// DW_AT_LLVM_sysroot.
+  std::vector<llvm::StringRef> input_sdk_paths;
+
+  /// 'true' if we expect \ref GetSDKPathFromDebugInfo
+  /// to notify us about an SDK mismatch.
+  bool expect_mismatch;
+
+  /// 'true if the test expects the parsed SDK to
+  /// be an internal one.
+  bool expect_internal_sdk;
+
+  /// A substring that the final parsed sdk
+  /// is expected to contain.
+  llvm::StringRef expect_sdk_path_pattern;
+};
+
+struct SDKPathParsingMultiparamTests
+    : public XcodeSDKModuleTests,
+      public testing::WithParamInterface<SDKPathParsingTestData> {
+  std::vector<std::string>
+  createCompileUnits(std::vector<llvm::StringRef> const &sdk_paths) {
+    std::vector<std::string> compile_units;
+
+    for (auto sdk_path : sdk_paths) {
+      compile_units.emplace_back(llvm::formatv(
+          R"(
+        - Version:         2
+          AddrSize:        8
+          AbbrevTableID:   0
+          AbbrOffset:      0x0
+          Entries:
+            - AbbrCode:        0x00000001
+              Values:
+                - Value:       0x000000000000000C
+                - CStr:        {0}
+                - CStr:        {1}
+            - AbbrCode:        0x00000000
+            )",
+          llvm::sys::path::filename(sdk_path, llvm::sys::path::Style::posix),
+          sdk_path));
+    }
+
+    return compile_units;
+  }
+};
+} // namespace
 
 TEST_F(XcodeSDKModuleTests, TestModuleGetXcodeSDK) {
   const char *yamldata = R"(
@@ -72,4 +121,190 @@ TEST_F(XcodeSDKModuleTests, TestModuleGetXcodeSDK) {
   ASSERT_EQ(sdk.GetType(), XcodeSDK::Type::MacOSX);
   ASSERT_EQ(module->GetSourceMappingList().GetSize(), 1u);
 }
+
+TEST_F(XcodeSDKModuleTests, TestSDKPathFromDebugInfo_InvalidSDKPath) {
+  // Tests that parsing a CU with an invalid SDK directory name fails.
+
+  const char *yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_386
+DWARF:
+  debug_abbrev:
+    - Table:
+        - Code:            0x00000001
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+            - Attribute:       DW_AT_APPLE_sdk
+              Form:            DW_FORM_string
+  debug_info:
+    - Version:         2
+      AddrSize:        8
+      AbbrevTableID:   0
+      AbbrOffset:      0x0
+      Entries:
+        - AbbrCode:        0x00000001
+          Values:
+            - Value:       0x000000000000000C
+            - CStr:        "1abc at defgh2"
+        - AbbrCode:        0x00000000
+...
+)";
+
+  YAMLModuleTester t(yamldata);
+  ModuleSP module = t.GetModule();
+  ASSERT_NE(module, nullptr);
+
+  auto path_or_err = PlatformDarwin::ResolveSDKPathFromDebugInfo(*module);
+  EXPECT_FALSE(static_cast<bool>(path_or_err));
+  llvm::consumeError(path_or_err.takeError());
+}
+
+TEST_F(XcodeSDKModuleTests, TestSDKPathFromDebugInfo_No_DW_AT_APPLE_sdk) {
+  // Tests that parsing a CU without a DW_AT_APPLE_sdk fails.
+
+  const char *yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_386
+DWARF:
+  debug_abbrev:
+    - Table:
+        - Code:            0x00000001
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+            - Attribute:       DW_AT_LLVM_sysroot
+              Form:            DW_FORM_string
+  debug_info:
+    - Version:         2
+      AddrSize:        8
+      AbbrevTableID:   0
+      AbbrOffset:      0x0
+      Entries:
+        - AbbrCode:        0x00000001
+          Values:
+            - Value:       0x000000000000000C
+            - CStr:        "/Library/Developer/CommandLineTools/SDKs/iPhoneOS14.0.Internal.sdk"
+        - AbbrCode:        0x00000000
+...
+)";
+
+  YAMLModuleTester t(yamldata);
+  ModuleSP module = t.GetModule();
+  ASSERT_NE(module, nullptr);
+
+  auto path_or_err = PlatformDarwin::ResolveSDKPathFromDebugInfo(*module);
+  EXPECT_FALSE(static_cast<bool>(path_or_err));
+  llvm::consumeError(path_or_err.takeError());
+}
+
+TEST_P(SDKPathParsingMultiparamTests, TestSDKPathFromDebugInfo) {
+  // Tests that we can parse the SDK path from debug-info.
+  // In the presence of multiple compile units, one of which
+  // points to an internal SDK, we should pick the internal SDK.
+
+  std::string yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_386
+DWARF:
+  debug_abbrev:
+    - Table:
+        - Code:            0x00000001
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+            - Attribute:       DW_AT_APPLE_sdk
+              Form:            DW_FORM_string
+            - Attribute:       DW_AT_LLVM_sysroot
+              Form:            DW_FORM_string
+  debug_info:
+)";
+
+  auto [input_sdk_paths, expect_mismatch, expect_internal_sdk,
+        expect_sdk_path_pattern] = GetParam();
+
+  for (auto &&sdk : createCompileUnits(input_sdk_paths))
+    yamldata += std::move(sdk);
+
+  YAMLModuleTester t(yamldata);
+  DWARFUnit *dwarf_unit = t.GetDwarfUnit();
+  auto *dwarf_cu = llvm::cast<DWARFCompileUnit>(dwarf_unit);
+  ASSERT_TRUE(static_cast<bool>(dwarf_cu));
+  SymbolFileDWARF &sym_file = dwarf_cu->GetSymbolFileDWARF();
+  ASSERT_EQ(sym_file.GetNumCompileUnits(), input_sdk_paths.size());
+  ModuleSP module = t.GetModule();
+  ASSERT_NE(module, nullptr);
+
+  auto sdk_or_err = PlatformDarwin::GetSDKPathFromDebugInfo(*module);
+  ASSERT_TRUE(static_cast<bool>(sdk_or_err));
+
+  auto [sdk, found_mismatch] = *sdk_or_err;
+
+  EXPECT_EQ(found_mismatch, expect_mismatch);
+  EXPECT_EQ(sdk.IsAppleInternalSDK(), expect_internal_sdk);
+  EXPECT_NE(sdk.GetString().find(expect_sdk_path_pattern), std::string::npos);
+}
+
+SDKPathParsingTestData sdkPathParsingTestCases[] = {
+    /// Multiple CUs with a mix of internal and public SDKs
+    {.input_sdk_paths =
+         {"/Library/Developer/CommandLineTools/SDKs/MacOSX10.9.sdk",
+          "/invalid/path/to/something.invalid.sdk",
+          "/Library/Developer/CommandLineTools/SDKs/iPhoneOS14.0.Internal.sdk",
+          "/Library/Developer/CommandLineTools/SDKs/MacOSX10.9.sdk"},
+     .expect_mismatch = true,
+     .expect_internal_sdk = true,
+     .expect_sdk_path_pattern = "Internal.sdk"},
+
+    /// Single CU with a public SDK
+    {.input_sdk_paths =
+         {"/Library/Developer/CommandLineTools/SDKs/MacOSX10.9.sdk"},
+     .expect_mismatch = false,
+     .expect_internal_sdk = false,
+     .expect_sdk_path_pattern = "MacOSX10.9.sdk"},
+
+    /// Single CU with an internal SDK
+    {.input_sdk_paths =
+         {"/Library/Developer/CommandLineTools/SDKs/iPhoneOS14.0.Internal.sdk"},
+     .expect_mismatch = false,
+     .expect_internal_sdk = true,
+     .expect_sdk_path_pattern = "Internal.sdk"},
+
+    /// Two CUs with an internal SDK each
+    {.input_sdk_paths =
+         {"/Library/Developer/CommandLineTools/SDKs/iPhoneOS14.0.Internal.sdk",
+          "/Library/Developer/CommandLineTools/SDKs/iPhoneOS12.9.Internal.sdk"},
+     .expect_mismatch = false,
+     .expect_internal_sdk = true,
+     .expect_sdk_path_pattern = "Internal.sdk"},
+
+    /// Two CUs with an internal SDK each
+    {.input_sdk_paths =
+         {"/Library/Developer/CommandLineTools/SDKs/iPhoneOS14.1.sdk",
+          "/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk"},
+     .expect_mismatch = false,
+     .expect_internal_sdk = false,
+     .expect_sdk_path_pattern = "iPhoneOS14.1.sdk"},
+};
+
+INSTANTIATE_TEST_CASE_P(SDKPathParsingTests, SDKPathParsingMultiparamTests,
+                        ::testing::ValuesIn(sdkPathParsingTestCases));
 #endif


        


More information about the lldb-commits mailing list