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