1 //===-- PathMappingList.cpp -----------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include <climits> 10 #include <cstring> 11 12 #include "lldb/Host/FileSystem.h" 13 #include "lldb/Host/PosixApi.h" 14 #include "lldb/Target/PathMappingList.h" 15 #include "lldb/Utility/FileSpec.h" 16 #include "lldb/Utility/Status.h" 17 #include "lldb/Utility/Stream.h" 18 #include "lldb/lldb-private-enumerations.h" 19 20 using namespace lldb; 21 using namespace lldb_private; 22 23 namespace { 24 // We must normalize our path pairs that we store because if we don't then 25 // things won't always work. We found a case where if we did: 26 // (lldb) settings set target.source-map . /tmp 27 // We would store a path pairs of "." and "/tmp" as raw strings. If the debug 28 // info contains "./foo/bar.c", the path will get normalized to "foo/bar.c". 29 // When PathMappingList::RemapPath() is called, it expects the path to start 30 // with the raw path pair, which doesn't work anymore because the paths have 31 // been normalized when the debug info was loaded. So we need to store 32 // nomalized path pairs to ensure things match up. 33 std::string NormalizePath(llvm::StringRef path) { 34 // If we use "path" to construct a FileSpec, it will normalize the path for 35 // us. We then grab the string. 36 return FileSpec(path).GetPath(); 37 } 38 } 39 // PathMappingList constructor 40 PathMappingList::PathMappingList() : m_pairs() {} 41 42 PathMappingList::PathMappingList(ChangedCallback callback, void *callback_baton) 43 : m_pairs(), m_callback(callback), m_callback_baton(callback_baton) {} 44 45 PathMappingList::PathMappingList(const PathMappingList &rhs) 46 : m_pairs(rhs.m_pairs) {} 47 48 const PathMappingList &PathMappingList::operator=(const PathMappingList &rhs) { 49 if (this != &rhs) { 50 m_pairs = rhs.m_pairs; 51 m_callback = nullptr; 52 m_callback_baton = nullptr; 53 m_mod_id = rhs.m_mod_id; 54 } 55 return *this; 56 } 57 58 PathMappingList::~PathMappingList() = default; 59 60 void PathMappingList::Append(llvm::StringRef path, llvm::StringRef replacement, 61 bool notify) { 62 ++m_mod_id; 63 m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement))); 64 if (notify && m_callback) 65 m_callback(*this, m_callback_baton); 66 } 67 68 void PathMappingList::Append(const PathMappingList &rhs, bool notify) { 69 ++m_mod_id; 70 if (!rhs.m_pairs.empty()) { 71 const_iterator pos, end = rhs.m_pairs.end(); 72 for (pos = rhs.m_pairs.begin(); pos != end; ++pos) 73 m_pairs.push_back(*pos); 74 if (notify && m_callback) 75 m_callback(*this, m_callback_baton); 76 } 77 } 78 79 void PathMappingList::Insert(llvm::StringRef path, llvm::StringRef replacement, 80 uint32_t index, bool notify) { 81 ++m_mod_id; 82 iterator insert_iter; 83 if (index >= m_pairs.size()) 84 insert_iter = m_pairs.end(); 85 else 86 insert_iter = m_pairs.begin() + index; 87 m_pairs.emplace(insert_iter, pair(NormalizePath(path), 88 NormalizePath(replacement))); 89 if (notify && m_callback) 90 m_callback(*this, m_callback_baton); 91 } 92 93 bool PathMappingList::Replace(llvm::StringRef path, llvm::StringRef replacement, 94 uint32_t index, bool notify) { 95 if (index >= m_pairs.size()) 96 return false; 97 ++m_mod_id; 98 m_pairs[index] = pair(NormalizePath(path), NormalizePath(replacement)); 99 if (notify && m_callback) 100 m_callback(*this, m_callback_baton); 101 return true; 102 } 103 104 bool PathMappingList::Remove(size_t index, bool notify) { 105 if (index >= m_pairs.size()) 106 return false; 107 108 ++m_mod_id; 109 iterator iter = m_pairs.begin() + index; 110 m_pairs.erase(iter); 111 if (notify && m_callback) 112 m_callback(*this, m_callback_baton); 113 return true; 114 } 115 116 // For clients which do not need the pair index dumped, pass a pair_index >= 0 117 // to only dump the indicated pair. 118 void PathMappingList::Dump(Stream *s, int pair_index) { 119 unsigned int numPairs = m_pairs.size(); 120 121 if (pair_index < 0) { 122 unsigned int index; 123 for (index = 0; index < numPairs; ++index) 124 s->Printf("[%d] \"%s\" -> \"%s\"\n", index, 125 m_pairs[index].first.GetCString(), 126 m_pairs[index].second.GetCString()); 127 } else { 128 if (static_cast<unsigned int>(pair_index) < numPairs) 129 s->Printf("%s -> %s", m_pairs[pair_index].first.GetCString(), 130 m_pairs[pair_index].second.GetCString()); 131 } 132 } 133 134 void PathMappingList::Clear(bool notify) { 135 if (!m_pairs.empty()) 136 ++m_mod_id; 137 m_pairs.clear(); 138 if (notify && m_callback) 139 m_callback(*this, m_callback_baton); 140 } 141 142 bool PathMappingList::RemapPath(ConstString path, 143 ConstString &new_path) const { 144 if (llvm::Optional<FileSpec> remapped = RemapPath(path.GetStringRef())) { 145 new_path.SetString(remapped->GetPath()); 146 return true; 147 } 148 return false; 149 } 150 151 /// Append components to path, applying style. 152 static void AppendPathComponents(FileSpec &path, llvm::StringRef components, 153 llvm::sys::path::Style style) { 154 auto component = llvm::sys::path::begin(components, style); 155 auto e = llvm::sys::path::end(components); 156 while (component != e && 157 llvm::sys::path::is_separator(*component->data(), style)) 158 ++component; 159 for (; component != e; ++component) 160 path.AppendPathComponent(*component); 161 } 162 163 llvm::Optional<FileSpec> 164 PathMappingList::RemapPath(llvm::StringRef mapping_path, 165 bool only_if_exists) const { 166 if (m_pairs.empty() || mapping_path.empty()) 167 return {}; 168 LazyBool path_is_relative = eLazyBoolCalculate; 169 170 for (const auto &it : m_pairs) { 171 llvm::StringRef prefix = it.first.GetStringRef(); 172 // We create a copy of mapping_path because StringRef::consume_from 173 // effectively modifies the instance itself. 174 llvm::StringRef path = mapping_path; 175 if (!path.consume_front(prefix)) { 176 // Relative paths won't have a leading "./" in them unless "." is the 177 // only thing in the relative path so we need to work around "." 178 // carefully. 179 if (prefix != ".") 180 continue; 181 // We need to figure out if the "path" argument is relative. If it is, 182 // then we should remap, else skip this entry. 183 if (path_is_relative == eLazyBoolCalculate) { 184 path_is_relative = 185 FileSpec(path).IsRelative() ? eLazyBoolYes : eLazyBoolNo; 186 } 187 if (!path_is_relative) 188 continue; 189 } 190 FileSpec remapped(it.second.GetStringRef()); 191 auto orig_style = FileSpec::GuessPathStyle(prefix).value_or( 192 llvm::sys::path::Style::native); 193 AppendPathComponents(remapped, path, orig_style); 194 if (!only_if_exists || FileSystem::Instance().Exists(remapped)) 195 return remapped; 196 } 197 return {}; 198 } 199 200 bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const { 201 std::string path = file.GetPath(); 202 llvm::StringRef path_ref(path); 203 for (const auto &it : m_pairs) { 204 if (!path_ref.consume_front(it.second.GetStringRef())) 205 continue; 206 auto orig_file = it.first.GetStringRef(); 207 auto orig_style = FileSpec::GuessPathStyle(orig_file).value_or( 208 llvm::sys::path::Style::native); 209 fixed.SetFile(orig_file, orig_style); 210 AppendPathComponents(fixed, path_ref, orig_style); 211 return true; 212 } 213 return false; 214 } 215 216 llvm::Optional<FileSpec> PathMappingList::FindFile(const FileSpec &orig_spec) const { 217 // We must normalize the orig_spec again using the host's path style, 218 // otherwise there will be mismatch between the host and remote platform 219 // if they use different path styles. 220 if (auto remapped = RemapPath(NormalizePath(orig_spec.GetPath()), 221 /*only_if_exists=*/true)) 222 return remapped; 223 224 return {}; 225 } 226 227 bool PathMappingList::Replace(llvm::StringRef path, llvm::StringRef new_path, 228 bool notify) { 229 uint32_t idx = FindIndexForPath(path); 230 if (idx < m_pairs.size()) { 231 ++m_mod_id; 232 m_pairs[idx].second = ConstString(new_path); 233 if (notify && m_callback) 234 m_callback(*this, m_callback_baton); 235 return true; 236 } 237 return false; 238 } 239 240 bool PathMappingList::Remove(ConstString path, bool notify) { 241 iterator pos = FindIteratorForPath(path); 242 if (pos != m_pairs.end()) { 243 ++m_mod_id; 244 m_pairs.erase(pos); 245 if (notify && m_callback) 246 m_callback(*this, m_callback_baton); 247 return true; 248 } 249 return false; 250 } 251 252 PathMappingList::const_iterator 253 PathMappingList::FindIteratorForPath(ConstString path) const { 254 const_iterator pos; 255 const_iterator begin = m_pairs.begin(); 256 const_iterator end = m_pairs.end(); 257 258 for (pos = begin; pos != end; ++pos) { 259 if (pos->first == path) 260 break; 261 } 262 return pos; 263 } 264 265 PathMappingList::iterator 266 PathMappingList::FindIteratorForPath(ConstString path) { 267 iterator pos; 268 iterator begin = m_pairs.begin(); 269 iterator end = m_pairs.end(); 270 271 for (pos = begin; pos != end; ++pos) { 272 if (pos->first == path) 273 break; 274 } 275 return pos; 276 } 277 278 bool PathMappingList::GetPathsAtIndex(uint32_t idx, ConstString &path, 279 ConstString &new_path) const { 280 if (idx < m_pairs.size()) { 281 path = m_pairs[idx].first; 282 new_path = m_pairs[idx].second; 283 return true; 284 } 285 return false; 286 } 287 288 uint32_t PathMappingList::FindIndexForPath(llvm::StringRef orig_path) const { 289 const ConstString path = ConstString(NormalizePath(orig_path)); 290 const_iterator pos; 291 const_iterator begin = m_pairs.begin(); 292 const_iterator end = m_pairs.end(); 293 294 for (pos = begin; pos != end; ++pos) { 295 if (pos->first == path) 296 return std::distance(begin, pos); 297 } 298 return UINT32_MAX; 299 } 300