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
44 : m_pairs(), m_callback(callback), m_callback_baton(callback_baton) {}
45
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 (llvm::StringRef(pair.first) == normalized_path &&
112 llvm::StringRef(pair.second) == 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, m_pairs[index].first.c_str(),
174 m_pairs[index].second.c_str());
175 } else {
176 if (static_cast<unsigned int>(pair_index) < numPairs)
177 s->Printf("%s -> %s", m_pairs[pair_index].first.c_str(),
178 m_pairs[pair_index].second.c_str());
179 }
180}
181
182llvm::json::Value PathMappingList::ToJSON() const {
183 llvm::json::Array entries;
184 std::lock_guard<std::mutex> lock(m_pairs_mutex);
185 for (const auto &pair : m_pairs) {
186 llvm::json::Array entry{pair.first, pair.second};
187 entries.emplace_back(std::move(entry));
188 }
189 return entries;
190}
191
192void PathMappingList::Clear(bool notify) {
193 {
194 std::lock_guard<std::mutex> lock(m_pairs_mutex);
195 if (!m_pairs.empty())
196 ++m_mod_id;
197 m_pairs.clear();
198 }
199 Notify(notify);
200}
201
202bool PathMappingList::RemapPath(llvm::StringRef path,
203 std::string &new_path) const {
204 if (std::optional<FileSpec> remapped = RemapPath(path)) {
205 new_path = remapped->GetPath();
206 return true;
207 }
208 return false;
209}
210
211/// Append components to path, applying style.
212static void AppendPathComponents(FileSpec &path, llvm::StringRef components,
213 llvm::sys::path::Style style) {
214 auto component = llvm::sys::path::begin(components, style);
215 auto e = llvm::sys::path::end(components);
216 while (component != e &&
217 llvm::sys::path::is_separator(*component->data(), style))
218 ++component;
219 for (; component != e; ++component)
220 path.AppendPathComponent(*component);
221}
222
223std::optional<FileSpec> PathMappingList::RemapPath(llvm::StringRef mapping_path,
224 bool only_if_exists) const {
225 std::lock_guard<std::mutex> lock(m_pairs_mutex);
226 if (m_pairs.empty() || mapping_path.empty())
227 return {};
228 LazyBool path_is_relative = eLazyBoolCalculate;
229
230 for (const auto &it : m_pairs) {
231 llvm::StringRef prefix(it.first);
232 // We create a copy of mapping_path because StringRef::consume_from
233 // effectively modifies the instance itself.
234 llvm::StringRef path = mapping_path;
235 if (!path.consume_front(prefix)) {
236 // Relative paths won't have a leading "./" in them unless "." is the
237 // only thing in the relative path so we need to work around "."
238 // carefully.
239 if (prefix != ".")
240 continue;
241 // We need to figure out if the "path" argument is relative. If it is,
242 // then we should remap, else skip this entry.
243 if (path_is_relative == eLazyBoolCalculate) {
244 path_is_relative =
246 }
247 if (!path_is_relative)
248 continue;
249 }
250 FileSpec remapped(it.second);
251 auto orig_style = FileSpec::GuessPathStyle(prefix).value_or(
252 llvm::sys::path::Style::native);
253 AppendPathComponents(remapped, path, orig_style);
254 if (!only_if_exists || FileSystem::Instance().Exists(remapped))
255 return remapped;
256 }
257 return {};
258}
259
260std::optional<llvm::StringRef>
262 std::string path = file.GetPath();
263 llvm::StringRef path_ref(path);
264 std::lock_guard<std::mutex> lock(m_pairs_mutex);
265 for (const auto &it : m_pairs) {
266 llvm::StringRef removed_prefix(it.second);
267 if (!path_ref.consume_front(it.second))
268 continue;
269 llvm::StringRef orig_file(it.first);
270 auto orig_style = FileSpec::GuessPathStyle(orig_file).value_or(
271 llvm::sys::path::Style::native);
272 fixed.SetFile(orig_file, orig_style);
273 AppendPathComponents(fixed, path_ref, orig_style);
274 return removed_prefix;
275 }
276 return std::nullopt;
277}
278
279std::optional<FileSpec>
280PathMappingList::FindFile(const FileSpec &orig_spec) const {
281 // We must normalize the orig_spec again using the host's path style,
282 // otherwise there will be mismatch between the host and remote platform
283 // if they use different path styles.
284 if (auto remapped = RemapPath(NormalizePath(orig_spec.GetPath()),
285 /*only_if_exists=*/true))
286 return remapped;
287
288 return {};
289}
290
291bool PathMappingList::Replace(llvm::StringRef path, llvm::StringRef new_path,
292 bool notify) {
293 {
294 std::lock_guard<std::mutex> lock(m_pairs_mutex);
295 uint32_t idx = FindIndexForPathNoLock(path);
296 if (idx >= m_pairs.size())
297 return false;
298 ++m_mod_id;
299 m_pairs[idx].second = new_path.str();
300 }
301 Notify(notify);
302 return true;
303}
304
305bool PathMappingList::Remove(llvm::StringRef path, bool notify) {
306 {
307 std::lock_guard<std::mutex> lock(m_pairs_mutex);
308 iterator pos = FindIteratorForPath(path);
309 if (pos == m_pairs.end())
310 return false;
311
312 ++m_mod_id;
313 m_pairs.erase(pos);
314 }
315 Notify(notify);
316 return true;
317}
318
321 std::lock_guard<std::mutex> lock(m_pairs_mutex);
322 iterator pos;
323 iterator begin = m_pairs.begin();
324 iterator end = m_pairs.end();
325
326 for (pos = begin; pos != end; ++pos) {
327 if (pos->first == path)
328 break;
329 }
330 return pos;
331}
332
333bool PathMappingList::GetPathsAtIndex(uint32_t idx, std::string &path,
334 std::string &new_path) const {
335 std::lock_guard<std::mutex> lock(m_pairs_mutex);
336 if (idx < m_pairs.size()) {
337 path = m_pairs[idx].first;
338 new_path = m_pairs[idx].second;
339 return true;
340 }
341 return false;
342}
343
344uint32_t
345PathMappingList::FindIndexForPathNoLock(llvm::StringRef orig_path) const {
346 const std::string path = NormalizePath(orig_path);
347 const_iterator pos;
348 const_iterator begin = m_pairs.begin();
349 const_iterator end = m_pairs.end();
350
351 for (pos = begin; pos != end; ++pos) {
352 if (pos->first == path)
353 return std::distance(begin, pos);
354 }
355 return UINT32_MAX;
356}
static void AppendPathComponents(FileSpec &path, llvm::StringRef components, llvm::sys::path::Style style)
Append components to path, applying style.
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:454
static std::optional< Style > GuessPathStyle(llvm::StringRef absolute_path)
Attempt to guess path style for a given path string.
Definition FileSpec.cpp:326
bool IsRelative() const
Returns true if the filespec represents a relative path.
Definition FileSpec.cpp:514
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:380
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
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)
llvm::json::Value ToJSON() const
bool RemapPath(llvm::StringRef path, std::string &new_path) const
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)
iterator FindIteratorForPath(llvm::StringRef path)
std::pair< std::string, std::string > pair
collection::iterator iterator
bool GetPathsAtIndex(uint32_t idx, std::string &path, std::string &new_path) const
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
A class that represents a running process on the host machine.