Skip to content
This repository has been archived by the owner on Apr 2, 2020. It is now read-only.

Commit

Permalink
Fix PathMappingList for relative and empty paths after recent FileSpe…
Browse files Browse the repository at this point in the history
…c 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



git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@332842 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
Greg Clayton committed May 21, 2018
1 parent 8a615ff commit 327df73
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 73 deletions.
2 changes: 1 addition & 1 deletion include/lldb/Target/PathMappingList.h
Expand Up @@ -90,7 +90,7 @@ class PathMappingList {
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.
Expand Down
4 changes: 4 additions & 0 deletions lldb.xcodeproj/project.pbxproj
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -1755,6 +1756,7 @@
2666ADC31B3CB675001FAFD3 /* HexagonDYLDRendezvous.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HexagonDYLDRendezvous.cpp; sourceTree = "<group>"; };
2666ADC41B3CB675001FAFD3 /* HexagonDYLDRendezvous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HexagonDYLDRendezvous.h; sourceTree = "<group>"; };
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 = "<group>"; };
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; };
Expand Down Expand Up @@ -6709,6 +6711,7 @@
children = (
9A2057061F3B818600F6C293 /* MemoryRegionInfoTest.cpp */,
AFAFD8091E57E1B90017A14F /* ModuleCacheTest.cpp */,
2668A2ED20AF417D00D94111 /* PathMappingListTest.cpp */,
);
name = Target;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
Expand Down
98 changes: 56 additions & 42 deletions source/Target/PathMappingList.cpp
Expand Up @@ -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"
Expand All @@ -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
//----------------------------------------------------------------------
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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();
Expand Down
6 changes: 1 addition & 5 deletions source/Target/Target.cpp
Expand Up @@ -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) {
Expand Down
54 changes: 29 additions & 25 deletions source/Utility/FileSpec.cpp
Expand Up @@ -67,6 +67,31 @@ void Denormalize(llvm::SmallVectorImpl<char> &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<char> &path) {
Expand Down Expand Up @@ -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(); }
Expand Down
1 change: 1 addition & 0 deletions unittests/Target/CMakeLists.txt
@@ -1,6 +1,7 @@
add_lldb_unittest(TargetTests
MemoryRegionInfoTest.cpp
ModuleCacheTest.cpp
PathMappingListTest.cpp

LINK_LIBS
lldbCore
Expand Down
109 changes: 109 additions & 0 deletions 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 <utility>

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> matches,
llvm::ArrayRef<ConstString> 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);
}

0 comments on commit 327df73

Please sign in to comment.