1 //===-- PathMappingList.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
22 using namespace lldb_private
;
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.
34 std::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();
40 // PathMappingList constructor
41 PathMappingList::PathMappingList() : m_pairs() {}
43 PathMappingList::PathMappingList(ChangedCallback callback
, void *callback_baton
)
44 : m_pairs(), m_callback(callback
), m_callback_baton(callback_baton
) {}
46 PathMappingList::PathMappingList(const PathMappingList
&rhs
)
47 : m_pairs(rhs
.m_pairs
) {}
49 const PathMappingList
&PathMappingList::operator=(const PathMappingList
&rhs
) {
51 std::scoped_lock
<std::mutex
, std::mutex
, std::mutex
> locks(
52 m_callback_mutex
, m_pairs_mutex
, rhs
.m_pairs_mutex
);
53 m_pairs
= rhs
.m_pairs
;
55 m_callback_baton
= nullptr;
56 m_mod_id
= rhs
.m_mod_id
;
61 PathMappingList::~PathMappingList() = default;
63 void PathMappingList::AppendNoLock(llvm::StringRef path
,
64 llvm::StringRef replacement
) {
66 m_pairs
.emplace_back(pair(NormalizePath(path
), NormalizePath(replacement
)));
69 void PathMappingList::Notify(bool notify
) const {
70 ChangedCallback callback
= nullptr;
71 void *baton
= nullptr;
73 std::lock_guard
<std::mutex
> lock(m_callback_mutex
);
74 callback
= m_callback
;
75 baton
= m_callback_baton
;
77 if (notify
&& callback
)
78 callback(*this, baton
);
81 void PathMappingList::Append(llvm::StringRef path
, llvm::StringRef replacement
,
84 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
85 AppendNoLock(path
, replacement
);
90 void PathMappingList::Append(const PathMappingList
&rhs
, bool notify
) {
92 std::scoped_lock
<std::mutex
, std::mutex
> locks(m_pairs_mutex
,
95 if (rhs
.m_pairs
.empty())
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
);
104 bool PathMappingList::AppendUnique(llvm::StringRef path
,
105 llvm::StringRef replacement
, bool notify
) {
106 auto normalized_path
= NormalizePath(path
);
107 auto normalized_replacement
= NormalizePath(replacement
);
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
)
115 AppendNoLock(path
, replacement
);
121 void PathMappingList::Insert(llvm::StringRef path
, llvm::StringRef replacement
,
122 uint32_t index
, bool notify
) {
124 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
126 iterator insert_iter
;
127 if (index
>= m_pairs
.size())
128 insert_iter
= m_pairs
.end();
130 insert_iter
= m_pairs
.begin() + index
;
131 m_pairs
.emplace(insert_iter
,
132 pair(NormalizePath(path
), NormalizePath(replacement
)));
137 bool PathMappingList::Replace(llvm::StringRef path
, llvm::StringRef replacement
,
138 uint32_t index
, bool notify
) {
140 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
141 if (index
>= m_pairs
.size())
144 m_pairs
[index
] = pair(NormalizePath(path
), NormalizePath(replacement
));
150 bool PathMappingList::Remove(size_t index
, bool notify
) {
152 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
153 if (index
>= m_pairs
.size())
157 iterator iter
= m_pairs
.begin() + index
;
164 // For clients which do not need the pair index dumped, pass a pair_index >= 0
165 // to only dump the indicated pair.
166 void PathMappingList::Dump(Stream
*s
, int pair_index
) {
167 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
168 unsigned int numPairs
= m_pairs
.size();
170 if (pair_index
< 0) {
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());
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());
183 llvm::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
));
194 void PathMappingList::Clear(bool notify
) {
196 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
197 if (!m_pairs
.empty())
204 bool PathMappingList::RemapPath(ConstString path
,
205 ConstString
&new_path
) const {
206 if (std::optional
<FileSpec
> remapped
= RemapPath(path
.GetStringRef())) {
207 new_path
.SetString(remapped
->GetPath());
213 /// Append components to path, applying style.
214 static 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
))
221 for (; component
!= e
; ++component
)
222 path
.AppendPathComponent(*component
);
225 std::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())
230 LazyBool path_is_relative
= eLazyBoolCalculate
;
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 "."
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
) {
247 FileSpec(path
).IsRelative() ? eLazyBoolYes
: eLazyBoolNo
;
249 if (!path_is_relative
)
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
))
262 std::optional
<llvm::StringRef
>
263 PathMappingList::ReverseRemapPath(const FileSpec
&file
, FileSpec
&fixed
) const {
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()))
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
;
281 std::optional
<FileSpec
>
282 PathMappingList::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))
293 bool PathMappingList::Replace(llvm::StringRef path
, llvm::StringRef new_path
,
296 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
297 uint32_t idx
= FindIndexForPathNoLock(path
);
298 if (idx
>= m_pairs
.size())
301 m_pairs
[idx
].second
= ConstString(new_path
);
307 bool PathMappingList::Remove(ConstString path
, bool notify
) {
309 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
310 iterator pos
= FindIteratorForPath(path
);
311 if (pos
== m_pairs
.end())
321 PathMappingList::const_iterator
322 PathMappingList::FindIteratorForPath(ConstString path
) const {
323 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
325 const_iterator begin
= m_pairs
.begin();
326 const_iterator end
= m_pairs
.end();
328 for (pos
= begin
; pos
!= end
; ++pos
) {
329 if (pos
->first
== path
)
335 PathMappingList::iterator
336 PathMappingList::FindIteratorForPath(ConstString path
) {
337 std::lock_guard
<std::mutex
> lock(m_pairs_mutex
);
339 iterator begin
= m_pairs
.begin();
340 iterator end
= m_pairs
.end();
342 for (pos
= begin
; pos
!= end
; ++pos
) {
343 if (pos
->first
== path
)
349 bool PathMappingList::GetPathsAtIndex(uint32_t idx
, ConstString
&path
,
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
;
361 PathMappingList::FindIndexForPathNoLock(llvm::StringRef orig_path
) const {
362 const ConstString path
= ConstString(NormalizePath(orig_path
));
364 const_iterator begin
= m_pairs
.begin();
365 const_iterator end
= m_pairs
.end();
367 for (pos
= begin
; pos
!= end
; ++pos
) {
368 if (pos
->first
== path
)
369 return std::distance(begin
, pos
);