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::recursive_mutex
, std::recursive_mutex
> locks(m_mutex
, rhs
.m_mutex
);
52 m_pairs
= rhs
.m_pairs
;
54 m_callback_baton
= nullptr;
55 m_mod_id
= rhs
.m_mod_id
;
60 PathMappingList::~PathMappingList() = default;
62 void PathMappingList::Append(llvm::StringRef path
, llvm::StringRef replacement
,
64 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
66 m_pairs
.emplace_back(pair(NormalizePath(path
), NormalizePath(replacement
)));
67 if (notify
&& m_callback
)
68 m_callback(*this, m_callback_baton
);
71 void PathMappingList::Append(const PathMappingList
&rhs
, bool notify
) {
72 std::scoped_lock
<std::recursive_mutex
, std::recursive_mutex
> locks(m_mutex
, rhs
.m_mutex
);
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
);
83 bool 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
))
93 Append(path
, replacement
, notify
);
97 void PathMappingList::Insert(llvm::StringRef path
, llvm::StringRef replacement
,
98 uint32_t index
, bool notify
) {
99 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
101 iterator insert_iter
;
102 if (index
>= m_pairs
.size())
103 insert_iter
= m_pairs
.end();
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
);
112 bool 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())
118 m_pairs
[index
] = pair(NormalizePath(path
), NormalizePath(replacement
));
119 if (notify
&& m_callback
)
120 m_callback(*this, m_callback_baton
);
124 bool PathMappingList::Remove(size_t index
, bool notify
) {
125 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
126 if (index
>= m_pairs
.size())
130 iterator iter
= m_pairs
.begin() + index
;
132 if (notify
&& m_callback
)
133 m_callback(*this, m_callback_baton
);
137 // For clients which do not need the pair index dumped, pass a pair_index >= 0
138 // to only dump the indicated pair.
139 void PathMappingList::Dump(Stream
*s
, int pair_index
) {
140 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
141 unsigned int numPairs
= m_pairs
.size();
143 if (pair_index
< 0) {
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());
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());
156 llvm::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
));
167 void PathMappingList::Clear(bool notify
) {
168 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
169 if (!m_pairs
.empty())
172 if (notify
&& m_callback
)
173 m_callback(*this, m_callback_baton
);
176 bool PathMappingList::RemapPath(ConstString path
,
177 ConstString
&new_path
) const {
178 if (std::optional
<FileSpec
> remapped
= RemapPath(path
.GetStringRef())) {
179 new_path
.SetString(remapped
->GetPath());
185 /// Append components to path, applying style.
186 static 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
))
193 for (; component
!= e
; ++component
)
194 path
.AppendPathComponent(*component
);
197 std::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())
202 LazyBool path_is_relative
= eLazyBoolCalculate
;
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 "."
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
) {
219 FileSpec(path
).IsRelative() ? eLazyBoolYes
: eLazyBoolNo
;
221 if (!path_is_relative
)
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
))
234 std::optional
<llvm::StringRef
>
235 PathMappingList::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()))
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
;
253 std::optional
<FileSpec
>
254 PathMappingList::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))
265 bool PathMappingList::Replace(llvm::StringRef path
, llvm::StringRef new_path
,
267 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
268 uint32_t idx
= FindIndexForPath(path
);
269 if (idx
< m_pairs
.size()) {
271 m_pairs
[idx
].second
= ConstString(new_path
);
272 if (notify
&& m_callback
)
273 m_callback(*this, m_callback_baton
);
279 bool 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()) {
285 if (notify
&& m_callback
)
286 m_callback(*this, m_callback_baton
);
292 PathMappingList::const_iterator
293 PathMappingList::FindIteratorForPath(ConstString path
) const {
294 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
296 const_iterator begin
= m_pairs
.begin();
297 const_iterator end
= m_pairs
.end();
299 for (pos
= begin
; pos
!= end
; ++pos
) {
300 if (pos
->first
== path
)
306 PathMappingList::iterator
307 PathMappingList::FindIteratorForPath(ConstString path
) {
308 std::lock_guard
<std::recursive_mutex
> lock(m_mutex
);
310 iterator begin
= m_pairs
.begin();
311 iterator end
= m_pairs
.end();
313 for (pos
= begin
; pos
!= end
; ++pos
) {
314 if (pos
->first
== path
)
320 bool 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
;
331 uint32_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
);
335 const_iterator begin
= m_pairs
.begin();
336 const_iterator end
= m_pairs
.end();
338 for (pos
= begin
; pos
!= end
; ++pos
) {
339 if (pos
->first
== path
)
340 return std::distance(begin
, pos
);