LLDB mainline
PathMappingList.cpp
Go to the documentation of this file.
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
14#include "lldb/Host/PosixApi.h"
17#include "lldb/Utility/Status.h"
18#include "lldb/Utility/Stream.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
42
43PathMappingList::PathMappingList(ChangedCallback callback, void *callback_baton)
44 : m_pairs(), m_callback(callback), m_callback_baton(callback_baton) {}
45
47 : m_pairs(rhs.m_pairs) {}
48
50 if (this != &rhs) {
51 std::scoped_lock<std::mutex, std::mutex, std::mutex> locks(
53 m_pairs = rhs.m_pairs;
54 m_callback = nullptr;
55 m_callback_baton = nullptr;
56 m_mod_id = rhs.m_mod_id;
57 }
58 return *this;
59}
60
62
63void PathMappingList::AppendNoLock(llvm::StringRef path,
64 llvm::StringRef replacement) {
65 ++m_mod_id;
66 m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement)));
67}
68
69void PathMappingList::Notify(bool notify) const {
70 ChangedCallback callback = nullptr;
71 void *baton = nullptr;
72 {
73 std::lock_guard<std::mutex> lock(m_callback_mutex);
74 callback = m_callback;
75 baton = m_callback_baton;
76 }
77 if (notify && callback)
78 callback(*this, baton);
79}
80
81void PathMappingList::Append(llvm::StringRef path, llvm::StringRef replacement,
82 bool notify) {
83 {
84 std::lock_guard<std::mutex> lock(m_pairs_mutex);
85 AppendNoLock(path, replacement);
86 }
87 Notify(notify);
88}
89
90void PathMappingList::Append(const PathMappingList &rhs, bool notify) {
91 {
92 std::scoped_lock<std::mutex, std::mutex> locks(m_pairs_mutex,
93 rhs.m_pairs_mutex);
94 ++m_mod_id;
95 if (rhs.m_pairs.empty())
96 return;
97 const_iterator pos, end = rhs.m_pairs.end();
98 for (pos = rhs.m_pairs.begin(); pos != end; ++pos)
99 m_pairs.push_back(*pos);
100 }
101 Notify(notify);
102}
103
104bool PathMappingList::AppendUnique(llvm::StringRef path,
105 llvm::StringRef replacement, bool notify) {
106 auto normalized_path = NormalizePath(path);
107 auto normalized_replacement = NormalizePath(replacement);
108 {
109 std::lock_guard<std::mutex> lock(m_pairs_mutex);
110 for (const auto &pair : m_pairs) {
111 if (pair.first.GetStringRef() == normalized_path &&
112 pair.second.GetStringRef() == normalized_replacement)
113 return false;
114 }
115 AppendNoLock(path, replacement);
116 }
117 Notify(notify);
118 return true;
119}
120
121void PathMappingList::Insert(llvm::StringRef path, llvm::StringRef replacement,
122 uint32_t index, bool notify) {
123 {
124 std::lock_guard<std::mutex> lock(m_pairs_mutex);
125 ++m_mod_id;
126 iterator insert_iter;
127 if (index >= m_pairs.size())
128 insert_iter = m_pairs.end();
129 else
130 insert_iter = m_pairs.begin() + index;
131 m_pairs.emplace(insert_iter,
132 pair(NormalizePath(path), NormalizePath(replacement)));
133 }
134 Notify(notify);
135}
136
137bool PathMappingList::Replace(llvm::StringRef path, llvm::StringRef replacement,
138 uint32_t index, bool notify) {
139 {
140 std::lock_guard<std::mutex> lock(m_pairs_mutex);
141 if (index >= m_pairs.size())
142 return false;
143 ++m_mod_id;
144 m_pairs[index] = pair(NormalizePath(path), NormalizePath(replacement));
145 }
146 Notify(notify);
147 return true;
148}
149
150bool PathMappingList::Remove(size_t index, bool notify) {
151 {
152 std::lock_guard<std::mutex> lock(m_pairs_mutex);
153 if (index >= m_pairs.size())
154 return false;
155
156 ++m_mod_id;
157 iterator iter = m_pairs.begin() + index;
158 m_pairs.erase(iter);
159 }
160 Notify(notify);
161 return true;
162}
163
164// For clients which do not need the pair index dumped, pass a pair_index >= 0
165// to only dump the indicated pair.
166void PathMappingList::Dump(Stream *s, int pair_index) {
167 std::lock_guard<std::mutex> lock(m_pairs_mutex);
168 unsigned int numPairs = m_pairs.size();
169
170 if (pair_index < 0) {
171 unsigned int index;
172 for (index = 0; index < numPairs; ++index)
173 s->Printf("[%d] \"%s\" -> \"%s\"\n", index,
174 m_pairs[index].first.GetCString(),
175 m_pairs[index].second.GetCString());
176 } else {
177 if (static_cast<unsigned int>(pair_index) < numPairs)
178 s->Printf("%s -> %s", m_pairs[pair_index].first.GetCString(),
179 m_pairs[pair_index].second.GetCString());
180 }
181}
182
183llvm::json::Value PathMappingList::ToJSON() {
184 llvm::json::Array entries;
185 std::lock_guard<std::mutex> lock(m_pairs_mutex);
186 for (const auto &pair : m_pairs) {
187 llvm::json::Array entry{pair.first.GetStringRef().str(),
188 pair.second.GetStringRef().str()};
189 entries.emplace_back(std::move(entry));
190 }
191 return entries;
192}
193
194void PathMappingList::Clear(bool notify) {
195 {
196 std::lock_guard<std::mutex> lock(m_pairs_mutex);
197 if (!m_pairs.empty())
198 ++m_mod_id;
199 m_pairs.clear();
200 }
201 Notify(notify);
202}
203
205 ConstString &new_path) const {
206 if (std::optional<FileSpec> remapped = RemapPath(path.GetStringRef())) {
207 new_path.SetString(remapped->GetPath());
208 return true;
209 }
210 return false;
211}
212
213/// Append components to path, applying style.
214static void AppendPathComponents(FileSpec &path, llvm::StringRef components,
215 llvm::sys::path::Style style) {
216 auto component = llvm::sys::path::begin(components, style);
217 auto e = llvm::sys::path::end(components);
218 while (component != e &&
219 llvm::sys::path::is_separator(*component->data(), style))
220 ++component;
221 for (; component != e; ++component)
222 path.AppendPathComponent(*component);
223}
224
225std::optional<FileSpec> PathMappingList::RemapPath(llvm::StringRef mapping_path,
226 bool only_if_exists) const {
227 std::lock_guard<std::mutex> lock(m_pairs_mutex);
228 if (m_pairs.empty() || mapping_path.empty())
229 return {};
230 LazyBool path_is_relative = eLazyBoolCalculate;
231
232 for (const auto &it : m_pairs) {
233 llvm::StringRef prefix = it.first.GetStringRef();
234 // We create a copy of mapping_path because StringRef::consume_from
235 // effectively modifies the instance itself.
236 llvm::StringRef path = mapping_path;
237 if (!path.consume_front(prefix)) {
238 // Relative paths won't have a leading "./" in them unless "." is the
239 // only thing in the relative path so we need to work around "."
240 // carefully.
241 if (prefix != ".")
242 continue;
243 // We need to figure out if the "path" argument is relative. If it is,
244 // then we should remap, else skip this entry.
245 if (path_is_relative == eLazyBoolCalculate) {
246 path_is_relative =
248 }
249 if (!path_is_relative)
250 continue;
251 }
252 FileSpec remapped(it.second.GetStringRef());
253 auto orig_style = FileSpec::GuessPathStyle(prefix).value_or(
254 llvm::sys::path::Style::native);
255 AppendPathComponents(remapped, path, orig_style);
256 if (!only_if_exists || FileSystem::Instance().Exists(remapped))
257 return remapped;
258 }
259 return {};
260}
261
262std::optional<llvm::StringRef>
264 std::string path = file.GetPath();
265 llvm::StringRef path_ref(path);
266 std::lock_guard<std::mutex> lock(m_pairs_mutex);
267 for (const auto &it : m_pairs) {
268 llvm::StringRef removed_prefix = it.second.GetStringRef();
269 if (!path_ref.consume_front(it.second.GetStringRef()))
270 continue;
271 auto orig_file = it.first.GetStringRef();
272 auto orig_style = FileSpec::GuessPathStyle(orig_file).value_or(
273 llvm::sys::path::Style::native);
274 fixed.SetFile(orig_file, orig_style);
275 AppendPathComponents(fixed, path_ref, orig_style);
276 return removed_prefix;
277 }
278 return std::nullopt;
279}
280
281std::optional<FileSpec>
282PathMappingList::FindFile(const FileSpec &orig_spec) const {
283 // We must normalize the orig_spec again using the host's path style,
284 // otherwise there will be mismatch between the host and remote platform
285 // if they use different path styles.
286 if (auto remapped = RemapPath(NormalizePath(orig_spec.GetPath()),
287 /*only_if_exists=*/true))
288 return remapped;
289
290 return {};
291}
292
293bool PathMappingList::Replace(llvm::StringRef path, llvm::StringRef new_path,
294 bool notify) {
295 {
296 std::lock_guard<std::mutex> lock(m_pairs_mutex);
297 uint32_t idx = FindIndexForPathNoLock(path);
298 if (idx >= m_pairs.size())
299 return false;
300 ++m_mod_id;
301 m_pairs[idx].second = ConstString(new_path);
302 }
303 Notify(notify);
304 return true;
305}
306
307bool PathMappingList::Remove(ConstString path, bool notify) {
308 {
309 std::lock_guard<std::mutex> lock(m_pairs_mutex);
310 iterator pos = FindIteratorForPath(path);
311 if (pos == m_pairs.end())
312 return false;
313
314 ++m_mod_id;
315 m_pairs.erase(pos);
316 }
317 Notify(notify);
318 return true;
319}
320
323 std::lock_guard<std::mutex> lock(m_pairs_mutex);
324 const_iterator pos;
325 const_iterator begin = m_pairs.begin();
326 const_iterator end = m_pairs.end();
327
328 for (pos = begin; pos != end; ++pos) {
329 if (pos->first == path)
330 break;
331 }
332 return pos;
333}
334
337 std::lock_guard<std::mutex> lock(m_pairs_mutex);
338 iterator pos;
339 iterator begin = m_pairs.begin();
340 iterator end = m_pairs.end();
341
342 for (pos = begin; pos != end; ++pos) {
343 if (pos->first == path)
344 break;
345 }
346 return pos;
347}
348
350 ConstString &new_path) const {
351 std::lock_guard<std::mutex> lock(m_pairs_mutex);
352 if (idx < m_pairs.size()) {
353 path = m_pairs[idx].first;
354 new_path = m_pairs[idx].second;
355 return true;
356 }
357 return false;
358}
359
360uint32_t
361PathMappingList::FindIndexForPathNoLock(llvm::StringRef orig_path) const {
362 const ConstString path = ConstString(NormalizePath(orig_path));
363 const_iterator pos;
364 const_iterator begin = m_pairs.begin();
365 const_iterator end = m_pairs.end();
366
367 for (pos = begin; pos != end; ++pos) {
368 if (pos->first == path)
369 return std::distance(begin, pos);
370 }
371 return UINT32_MAX;
372}
static void AppendPathComponents(FileSpec &path, llvm::StringRef components, llvm::sys::path::Style style)
Append components to path, applying style.
A uniqued constant string class.
Definition: ConstString.h:40
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
Definition: ConstString.h:197
void SetString(llvm::StringRef s)
A file utility class.
Definition: FileSpec.h:56
void SetFile(llvm::StringRef path, Style style)
Change the file specified with a new path.
Definition: FileSpec.cpp:174
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:447
static std::optional< Style > GuessPathStyle(llvm::StringRef absolute_path)
Attempt to guess path style for a given path string.
Definition: FileSpec.cpp:310
bool IsRelative() const
Returns true if the filespec represents a relative path.
Definition: FileSpec.cpp:507
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:367
static FileSystem & Instance()
bool Remove(size_t index, bool notify)
bool AppendUnique(llvm::StringRef path, llvm::StringRef replacement, bool notify)
Append <path, replacement> pair without duplication.
uint32_t FindIndexForPathNoLock(llvm::StringRef path) const
iterator FindIteratorForPath(ConstString path)
std::optional< FileSpec > FindFile(const FileSpec &orig_spec) const
Finds a source file given a file spec using the path remappings.
bool Replace(llvm::StringRef path, llvm::StringRef replacement, bool notify)
void(* ChangedCallback)(const PathMappingList &path_list, void *baton)
void Insert(llvm::StringRef path, llvm::StringRef replacement, uint32_t insert_idx, bool notify)
collection::const_iterator const_iterator
const PathMappingList & operator=(const PathMappingList &rhs)
void AppendNoLock(llvm::StringRef path, llvm::StringRef replacement)
void Append(llvm::StringRef path, llvm::StringRef replacement, bool notify)
bool RemapPath(ConstString path, ConstString &new_path) const
bool GetPathsAtIndex(uint32_t idx, ConstString &path, ConstString &new_path) const
collection::iterator iterator
std::pair< ConstString, ConstString > pair
uint32_t m_mod_id
Incremented anytime anything is added to or removed from m_pairs.
void Notify(bool notify) const
std::optional< llvm::StringRef > ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const
Perform reverse source path remap for input file.
void Dump(Stream *s, int pair_index=-1)
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:134
#define UINT32_MAX
Definition: lldb-defines.h:19
A class that represents a running process on the host machine.
Definition: SBAddress.h:15