From 86188d8a404093c37b7b63515aaf223d5aa4d113 Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Mon, 21 May 2018 14:14:36 +0000 Subject: [PATCH] Fix PathMappingList for relative and empty paths after recent FileSpec normalization changes PathMappingList was broken for relative and empty paths after normalization changes in FileSpec. There were also no tests for PathMappingList so I added those. Changes include: Change PathMappingList::ReverseRemapPath() to take FileSpec objects instead of ConstString. The only client of this was doing work to convert to and from ConstString objects for no reason. Normalize all paths prefix and replacements that are added to the PathMappingList vector so they match the paths that have been already normalized in the debug info Unify code in the two forms of PathMappingList::RemapPath() so only one contains the actual functionality. Prior to this, there were two versions of this code. Use FileSpec::AppendPathComponent() and remove a long standing TODO so paths are correctly appended to each other. Added tests for absolute, relative and empty paths. Differential Revision: https://reviews.llvm.org/D47021 llvm-svn: 332842 --- lldb/include/lldb/Target/PathMappingList.h | 2 +- lldb/lldb.xcodeproj/project.pbxproj | 4 + lldb/source/Target/PathMappingList.cpp | 98 +++++++++------- lldb/source/Target/Target.cpp | 6 +- lldb/source/Utility/FileSpec.cpp | 54 +++++---- lldb/unittests/Target/CMakeLists.txt | 1 + lldb/unittests/Target/PathMappingListTest.cpp | 109 ++++++++++++++++++ lldb/unittests/Utility/FileSpecTest.cpp | 47 ++++++++ 8 files changed, 248 insertions(+), 73 deletions(-) create mode 100644 lldb/unittests/Target/PathMappingListTest.cpp diff --git a/lldb/include/lldb/Target/PathMappingList.h b/lldb/include/lldb/Target/PathMappingList.h index b1b551f2d24d..a5ee3265a223 100644 --- a/lldb/include/lldb/Target/PathMappingList.h +++ b/lldb/include/lldb/Target/PathMappingList.h @@ -90,7 +90,7 @@ public: bool RemapPath(llvm::StringRef path, std::string &new_path) const; bool RemapPath(const char *, std::string &) const = delete; - bool ReverseRemapPath(const ConstString &path, ConstString &new_path) const; + bool ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const; //------------------------------------------------------------------ /// Finds a source file given a file spec using the path remappings. diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index 382ddf5c66aa..070b24712ecc 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -267,6 +267,7 @@ 26680336116005EF008E1FE4 /* SBBreakpointLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AF16CC7114086A1007A7B3F /* SBBreakpointLocation.cpp */; }; 26680337116005F1008E1FE4 /* SBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AF16A9C11402D5B007A7B3F /* SBBreakpoint.cpp */; }; 2668035C11601108008E1FE4 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26680207115FD0ED008E1FE4 /* LLDB.framework */; }; + 2668A2EE20AF417D00D94111 /* PathMappingListTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2668A2ED20AF417D00D94111 /* PathMappingListTest.cpp */; }; 266942001A6DC2AC0063BE93 /* MICmdArgContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941601A6DC2AB0063BE93 /* MICmdArgContext.cpp */; }; 266942011A6DC2AC0063BE93 /* MICmdArgSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941621A6DC2AB0063BE93 /* MICmdArgSet.cpp */; }; 266942021A6DC2AC0063BE93 /* MICmdArgValBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941641A6DC2AB0063BE93 /* MICmdArgValBase.cpp */; }; @@ -1755,6 +1756,7 @@ 2666ADC31B3CB675001FAFD3 /* HexagonDYLDRendezvous.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HexagonDYLDRendezvous.cpp; sourceTree = ""; }; 2666ADC41B3CB675001FAFD3 /* HexagonDYLDRendezvous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HexagonDYLDRendezvous.h; sourceTree = ""; }; 26680207115FD0ED008E1FE4 /* LLDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LLDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2668A2ED20AF417D00D94111 /* PathMappingListTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathMappingListTest.cpp; path = Target/PathMappingListTest.cpp; sourceTree = ""; }; 2669415B1A6DC2AB0063BE93 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = "tools/lldb-mi/CMakeLists.txt"; sourceTree = SOURCE_ROOT; }; 2669415E1A6DC2AB0063BE93 /* lldb-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "lldb-Info.plist"; path = "tools/lldb-mi/lldb-Info.plist"; sourceTree = SOURCE_ROOT; }; 2669415F1A6DC2AB0063BE93 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = "tools/lldb-mi/Makefile"; sourceTree = SOURCE_ROOT; }; @@ -6709,6 +6711,7 @@ children = ( 9A2057061F3B818600F6C293 /* MemoryRegionInfoTest.cpp */, AFAFD8091E57E1B90017A14F /* ModuleCacheTest.cpp */, + 2668A2ED20AF417D00D94111 /* PathMappingListTest.cpp */, ); name = Target; sourceTree = ""; @@ -7351,6 +7354,7 @@ 23CB15341D66DA9300EDDDE1 /* CPlusPlusLanguageTest.cpp in Sources */, 9A2057381F3B8E7E00F6C293 /* FileSystemTest.cpp in Sources */, 9A2057201F3B8D2500F6C293 /* UnixSignalsTest.cpp in Sources */, + 2668A2EE20AF417D00D94111 /* PathMappingListTest.cpp in Sources */, AFAFD80A1E57E1B90017A14F /* ModuleCacheTest.cpp in Sources */, 9A3D43DA1F3151C400EB767C /* StructuredDataTest.cpp in Sources */, 9A2057121F3B824B00F6C293 /* SymbolFileDWARFTests.cpp in Sources */, diff --git a/lldb/source/Target/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp index 782c6e49623c..9b8250e7ee65 100644 --- a/lldb/source/Target/PathMappingList.cpp +++ b/lldb/source/Target/PathMappingList.cpp @@ -14,6 +14,7 @@ // Other libraries and framework includes // Project includes +#include "lldb/lldb-private-enumerations.h" #include "lldb/Host/PosixApi.h" #include "lldb/Target/PathMappingList.h" #include "lldb/Utility/FileSpec.h" @@ -23,6 +24,22 @@ using namespace lldb; using namespace lldb_private; +namespace { + // We must normalize our path pairs that we store because if we don't then + // things won't always work. We found a case where if we did: + // (lldb) settings set target.source-map . /tmp + // We would store a path pairs of "." and "/tmp" as raw strings. If the debug + // info contains "./foo/bar.c", the path will get normalized to "foo/bar.c". + // When PathMappingList::RemapPath() is called, it expects the path to start + // with the raw path pair, which doesn't work anymore because the paths have + // been normalized when the debug info was loaded. So we need to store + // nomalized path pairs to ensure things match up. + ConstString NormalizePath(const ConstString &path) { + // If we use "path" to construct a FileSpec, it will normalize the path for + // us. We then grab the string and turn it back into a ConstString. + return ConstString(FileSpec(path.GetStringRef(), false).GetPath()); + } +} //---------------------------------------------------------------------- // PathMappingList constructor //---------------------------------------------------------------------- @@ -52,7 +69,7 @@ PathMappingList::~PathMappingList() = default; void PathMappingList::Append(const ConstString &path, const ConstString &replacement, bool notify) { ++m_mod_id; - m_pairs.push_back(pair(path, replacement)); + m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement))); if (notify && m_callback) m_callback(*this, m_callback_baton); } @@ -77,7 +94,8 @@ void PathMappingList::Insert(const ConstString &path, insert_iter = m_pairs.end(); else insert_iter = m_pairs.begin() + index; - m_pairs.insert(insert_iter, pair(path, replacement)); + m_pairs.emplace(insert_iter, pair(NormalizePath(path), + NormalizePath(replacement))); if (notify && m_callback) m_callback(*this, m_callback_baton); } @@ -88,7 +106,7 @@ bool PathMappingList::Replace(const ConstString &path, if (index >= m_pairs.size()) return false; ++m_mod_id; - m_pairs[index] = pair(path, replacement); + m_pairs[index] = pair(NormalizePath(path), NormalizePath(replacement)); if (notify && m_callback) m_callback(*this, m_callback_baton); return true; @@ -134,22 +152,10 @@ void PathMappingList::Clear(bool notify) { bool PathMappingList::RemapPath(const ConstString &path, ConstString &new_path) const { - const char *path_cstr = path.GetCString(); - // CLEANUP: Convert this function to use StringRefs internally instead - // of raw c-strings. - if (!path_cstr) - return false; - - const_iterator pos, end = m_pairs.end(); - for (pos = m_pairs.begin(); pos != end; ++pos) { - const size_t prefixLen = pos->first.GetLength(); - - if (::strncmp(pos->first.GetCString(), path_cstr, prefixLen) == 0) { - std::string new_path_str(pos->second.GetCString()); - new_path_str.append(path.GetCString() + prefixLen); - new_path.SetCString(new_path_str.c_str()); - return true; - } + std::string remapped; + if (RemapPath(path.GetStringRef(), remapped)) { + new_path.SetString(remapped); + return true; } return false; } @@ -158,34 +164,41 @@ bool PathMappingList::RemapPath(llvm::StringRef path, std::string &new_path) const { if (m_pairs.empty() || path.empty()) return false; - - const_iterator pos, end = m_pairs.end(); - for (pos = m_pairs.begin(); pos != end; ++pos) { - if (!path.consume_front(pos->first.GetStringRef())) - continue; - - new_path = pos->second.GetStringRef(); - new_path.append(path); + LazyBool path_is_relative = eLazyBoolCalculate; + for (const auto &it : m_pairs) { + auto prefix = it.first.GetStringRef(); + if (!path.consume_front(prefix)) { + // Relative paths won't have a leading "./" in them unless "." is the + // only thing in the relative path so we need to work around "." + // carefully. + if (prefix != ".") + continue; + // We need to figure out if the "path" argument is relative. If it is, + // then we should remap, else skip this entry. + if (path_is_relative == eLazyBoolCalculate) { + path_is_relative = FileSpec(path, false).IsRelative() ? eLazyBoolYes : + eLazyBoolNo; + } + if (!path_is_relative) + continue; + } + FileSpec remapped(it.second.GetStringRef(), false); + remapped.AppendPathComponent(path); + new_path = remapped.GetPath(); return true; } return false; } -bool PathMappingList::ReverseRemapPath(const ConstString &path, - ConstString &new_path) const { - const char *path_cstr = path.GetCString(); - if (!path_cstr) - return false; - +bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const { + std::string path = file.GetPath(); + llvm::StringRef path_ref(path); for (const auto &it : m_pairs) { - // FIXME: This should be using FileSpec API's to do the path appending. - const size_t prefixLen = it.second.GetLength(); - if (::strncmp(it.second.GetCString(), path_cstr, prefixLen) == 0) { - std::string new_path_str(it.first.GetCString()); - new_path_str.append(path.GetCString() + prefixLen); - new_path.SetCString(new_path_str.c_str()); - return true; - } + if (!path_ref.consume_front(it.second.GetStringRef())) + continue; + fixed.SetFile(it.first.GetStringRef(), false); + fixed.AppendPathComponent(path_ref); + return true; } return false; } @@ -277,7 +290,8 @@ bool PathMappingList::GetPathsAtIndex(uint32_t idx, ConstString &path, return false; } -uint32_t PathMappingList::FindIndexForPath(const ConstString &path) const { +uint32_t PathMappingList::FindIndexForPath(const ConstString &orig_path) const { + const ConstString path = NormalizePath(orig_path); const_iterator pos; const_iterator begin = m_pairs.begin(); const_iterator end = m_pairs.end(); diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 6db30f7c8f62..83a4b35f070d 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -328,11 +328,7 @@ BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, bool hardware, LazyBool move_to_nearest_code) { FileSpec remapped_file; - ConstString remapped_path; - if (GetSourcePathMap().ReverseRemapPath(ConstString(file.GetPath().c_str()), - remapped_path)) - remapped_file.SetFile(remapped_path.AsCString(), false); - else + if (!GetSourcePathMap().ReverseRemapPath(file, remapped_file)) remapped_file = file; if (check_inlines == eLazyBoolCalculate) { diff --git a/lldb/source/Utility/FileSpec.cpp b/lldb/source/Utility/FileSpec.cpp index 944f01d90001..ac404cea435b 100644 --- a/lldb/source/Utility/FileSpec.cpp +++ b/lldb/source/Utility/FileSpec.cpp @@ -67,6 +67,31 @@ void Denormalize(llvm::SmallVectorImpl &path, FileSpec::Style style) { std::replace(path.begin(), path.end(), '/', '\\'); } + +bool PathIsRelative(llvm::StringRef path, FileSpec::Style style) { + + if (path.empty()) + return false; + + if (PathStyleIsPosix(style)) { + // If the path doesn't start with '/' or '~', return true + switch (path[0]) { + case '/': + case '~': + return false; + default: + return true; + } + } else { + if (path.size() >= 2 && path[1] == ':') + return false; + if (path[0] == '/') + return false; + return true; + } + return false; +} + } // end anonymous namespace void FileSpec::Resolve(llvm::SmallVectorImpl &path) { @@ -814,31 +839,10 @@ bool FileSpec::IsSourceImplementationFile() const { } bool FileSpec::IsRelative() const { - const char *dir = m_directory.GetCString(); - llvm::StringRef directory(dir ? dir : ""); - - if (directory.size() > 0) { - if (PathStyleIsPosix(m_style)) { - // If the path doesn't start with '/' or '~', return true - switch (directory[0]) { - case '/': - case '~': - return false; - default: - return true; - } - } else { - if (directory.size() >= 2 && directory[1] == ':') - return false; - if (directory[0] == '/') - return false; - return true; - } - } else if (m_filename) { - // No directory, just a basename, return true - return true; - } - return false; + if (m_directory) + return PathIsRelative(m_directory.GetStringRef(), m_style); + else + return PathIsRelative(m_filename.GetStringRef(), m_style); } bool FileSpec::IsAbsolute() const { return !FileSpec::IsRelative(); } diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt index ec8f2db2c39f..bd73d708cb8b 100644 --- a/lldb/unittests/Target/CMakeLists.txt +++ b/lldb/unittests/Target/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_unittest(TargetTests MemoryRegionInfoTest.cpp ModuleCacheTest.cpp + PathMappingListTest.cpp LINK_LIBS lldbCore diff --git a/lldb/unittests/Target/PathMappingListTest.cpp b/lldb/unittests/Target/PathMappingListTest.cpp new file mode 100644 index 000000000000..b86ffbb0ca61 --- /dev/null +++ b/lldb/unittests/Target/PathMappingListTest.cpp @@ -0,0 +1,109 @@ +//===-- PathMappingListTest.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "lldb/Target/PathMappingList.h" +#include "lldb/Utility/FileSpec.h" +#include "gtest/gtest.h" +#include + +using namespace lldb_private; + +namespace { + struct Matches { + ConstString original; + ConstString remapped; + Matches(const char *o, const char *r) : original(o), + remapped(r) {} + }; + + void TestPathMappings(const PathMappingList &map, + llvm::ArrayRef matches, + llvm::ArrayRef fails) { + ConstString actual_remapped; + for (const auto &fail: fails) { + EXPECT_FALSE(map.RemapPath(fail, actual_remapped)); + } + for (const auto &match: matches) { + FileSpec orig_spec(match.original.GetStringRef(), false); + std::string orig_normalized = orig_spec.GetPath(); + EXPECT_TRUE(map.RemapPath(match.original, actual_remapped)); + EXPECT_EQ(actual_remapped, match.remapped); + FileSpec remapped_spec(match.remapped.GetStringRef(), false); + FileSpec unmapped_spec; + EXPECT_TRUE(map.ReverseRemapPath(remapped_spec, unmapped_spec)); + std::string unmapped_path = unmapped_spec.GetPath(); + EXPECT_EQ(unmapped_path, orig_normalized); + } + } +} + +TEST(PathMappingListTest, RelativeTests) { + Matches matches[] = { + {".", "/tmp"}, + {"./", "/tmp"}, + {"./////", "/tmp"}, + {"./foo.c", "/tmp/foo.c"}, + {"foo.c", "/tmp/foo.c"}, + {"./bar/foo.c", "/tmp/bar/foo.c"}, + {"bar/foo.c", "/tmp/bar/foo.c"}, + }; + ConstString fails[] = { + ConstString("/a"), + ConstString("/"), + }; + PathMappingList map; + map.Append(ConstString("."), ConstString("/tmp"), false); + TestPathMappings(map, matches, fails); + PathMappingList map2; + map2.Append(ConstString(""), ConstString("/tmp"), false); + TestPathMappings(map, matches, fails); +} + +TEST(PathMappingListTest, AbsoluteTests) { + PathMappingList map; + map.Append(ConstString("/old"), ConstString("/new"), false); + Matches matches[] = { + {"/old", "/new"}, + {"/old/", "/new"}, + {"/old/foo/.", "/new/foo"}, + {"/old/foo.c", "/new/foo.c"}, + {"/old/foo.c/.", "/new/foo.c"}, + {"/old/./foo.c", "/new/foo.c"}, + }; + ConstString fails[] = { + ConstString("/foo"), + ConstString("/"), + ConstString("foo.c"), + ConstString("./foo.c"), + ConstString("../foo.c"), + ConstString("../bar/foo.c"), + }; + TestPathMappings(map, matches, fails); +} + +TEST(PathMappingListTest, RemapRoot) { + PathMappingList map; + map.Append(ConstString("/"), ConstString("/new"), false); + Matches matches[] = { + {"/old", "/new/old"}, + {"/old/", "/new/old"}, + {"/old/foo/.", "/new/old/foo"}, + {"/old/foo.c", "/new/old/foo.c"}, + {"/old/foo.c/.", "/new/old/foo.c"}, + {"/old/./foo.c", "/new/old/foo.c"}, + }; + ConstString fails[] = { + ConstString("foo.c"), + ConstString("./foo.c"), + ConstString("../foo.c"), + ConstString("../bar/foo.c"), + }; + TestPathMappings(map, matches, fails); +} diff --git a/lldb/unittests/Utility/FileSpecTest.cpp b/lldb/unittests/Utility/FileSpecTest.cpp index d535ad713ae1..03c9793fc15b 100644 --- a/lldb/unittests/Utility/FileSpecTest.cpp +++ b/lldb/unittests/Utility/FileSpecTest.cpp @@ -273,3 +273,50 @@ TEST(FileSpecTest, FormatFileSpec) { EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str()); } +TEST(FileSpecTest, IsRelative) { + llvm::StringRef not_relative[] = { + "/", + "/a", + "/a/", + "/a/b", + "/a/b/", + "//", + "//a", + "//a/", + "//a/b", + "//a/b/", + "~", + "~/", + "~/a", + "~/a/", + "~/a/b" + "~/a/b/", + "/foo/.", + "/foo/..", + "/foo/../", + "/foo/../.", + }; + for (const auto &path: not_relative) { + FileSpec spec(path, false, FileSpec::Style::posix); + EXPECT_FALSE(spec.IsRelative()); + } + llvm::StringRef is_relative[] = { + ".", + "./", + ".///", + "a", + "./a", + "./a/", + "./a/", + "./a/b", + "./a/b/", + "../foo", + "foo/bar.c", + "./foo/bar.c" + }; + for (const auto &path: is_relative) { + FileSpec spec(path, false, FileSpec::Style::posix); + EXPECT_TRUE(spec.IsRelative()); + } +} +