1 //===-- PathMappingList.cpp -------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 #include <climits>
13 #include <cstring>
14 
15 // Other libraries and framework includes
16 // Project includes
17 #include "lldb/Host/FileSystem.h"
18 #include "lldb/Host/PosixApi.h"
19 #include "lldb/Target/PathMappingList.h"
20 #include "lldb/Utility/FileSpec.h"
21 #include "lldb/Utility/Status.h"
22 #include "lldb/Utility/Stream.h"
23 #include "lldb/lldb-private-enumerations.h"
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 
28 namespace {
29   // We must normalize our path pairs that we store because if we don't then
30   // things won't always work. We found a case where if we did:
31   // (lldb) settings set target.source-map . /tmp
32   // We would store a path pairs of "." and "/tmp" as raw strings. If the debug
33   // info contains "./foo/bar.c", the path will get normalized to "foo/bar.c".
34   // When PathMappingList::RemapPath() is called, it expects the path to start
35   // with the raw path pair, which doesn't work anymore because the paths have
36   // been normalized when the debug info was loaded. So we need to store
37   // nomalized path pairs to ensure things match up.
38   ConstString NormalizePath(const ConstString &path) {
39     // If we use "path" to construct a FileSpec, it will normalize the path for
40     // us. We then grab the string and turn it back into a ConstString.
41     return ConstString(FileSpec(path.GetStringRef(), false).GetPath());
42   }
43 }
44 //----------------------------------------------------------------------
45 // PathMappingList constructor
46 //----------------------------------------------------------------------
47 PathMappingList::PathMappingList()
48     : m_pairs(), m_callback(nullptr), m_callback_baton(nullptr), m_mod_id(0) {}
49 
50 PathMappingList::PathMappingList(ChangedCallback callback, void *callback_baton)
51     : m_pairs(), m_callback(callback), m_callback_baton(callback_baton),
52       m_mod_id(0) {}
53 
54 PathMappingList::PathMappingList(const PathMappingList &rhs)
55     : m_pairs(rhs.m_pairs), m_callback(nullptr), m_callback_baton(nullptr),
56       m_mod_id(0) {}
57 
58 const PathMappingList &PathMappingList::operator=(const PathMappingList &rhs) {
59   if (this != &rhs) {
60     m_pairs = rhs.m_pairs;
61     m_callback = nullptr;
62     m_callback_baton = nullptr;
63     m_mod_id = rhs.m_mod_id;
64   }
65   return *this;
66 }
67 
68 PathMappingList::~PathMappingList() = default;
69 
70 void PathMappingList::Append(const ConstString &path,
71                              const ConstString &replacement, bool notify) {
72   ++m_mod_id;
73   m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement)));
74   if (notify && m_callback)
75     m_callback(*this, m_callback_baton);
76 }
77 
78 void PathMappingList::Append(const PathMappingList &rhs, bool notify) {
79   ++m_mod_id;
80   if (!rhs.m_pairs.empty()) {
81     const_iterator pos, end = rhs.m_pairs.end();
82     for (pos = rhs.m_pairs.begin(); pos != end; ++pos)
83       m_pairs.push_back(*pos);
84     if (notify && m_callback)
85       m_callback(*this, m_callback_baton);
86   }
87 }
88 
89 void PathMappingList::Insert(const ConstString &path,
90                              const ConstString &replacement, uint32_t index,
91                              bool notify) {
92   ++m_mod_id;
93   iterator insert_iter;
94   if (index >= m_pairs.size())
95     insert_iter = m_pairs.end();
96   else
97     insert_iter = m_pairs.begin() + index;
98   m_pairs.emplace(insert_iter, pair(NormalizePath(path),
99                                     NormalizePath(replacement)));
100   if (notify && m_callback)
101     m_callback(*this, m_callback_baton);
102 }
103 
104 bool PathMappingList::Replace(const ConstString &path,
105                               const ConstString &replacement, uint32_t index,
106                               bool notify) {
107   if (index >= m_pairs.size())
108     return false;
109   ++m_mod_id;
110   m_pairs[index] = pair(NormalizePath(path), NormalizePath(replacement));
111   if (notify && m_callback)
112     m_callback(*this, m_callback_baton);
113   return true;
114 }
115 
116 bool PathMappingList::Remove(size_t index, bool notify) {
117   if (index >= m_pairs.size())
118     return false;
119 
120   ++m_mod_id;
121   iterator iter = m_pairs.begin() + index;
122   m_pairs.erase(iter);
123   if (notify && m_callback)
124     m_callback(*this, m_callback_baton);
125   return true;
126 }
127 
128 // For clients which do not need the pair index dumped, pass a pair_index >= 0
129 // to only dump the indicated pair.
130 void PathMappingList::Dump(Stream *s, int pair_index) {
131   unsigned int numPairs = m_pairs.size();
132 
133   if (pair_index < 0) {
134     unsigned int index;
135     for (index = 0; index < numPairs; ++index)
136       s->Printf("[%d] \"%s\" -> \"%s\"\n", index,
137                 m_pairs[index].first.GetCString(),
138                 m_pairs[index].second.GetCString());
139   } else {
140     if (static_cast<unsigned int>(pair_index) < numPairs)
141       s->Printf("%s -> %s", m_pairs[pair_index].first.GetCString(),
142                 m_pairs[pair_index].second.GetCString());
143   }
144 }
145 
146 void PathMappingList::Clear(bool notify) {
147   if (!m_pairs.empty())
148     ++m_mod_id;
149   m_pairs.clear();
150   if (notify && m_callback)
151     m_callback(*this, m_callback_baton);
152 }
153 
154 bool PathMappingList::RemapPath(const ConstString &path,
155                                 ConstString &new_path) const {
156   std::string remapped;
157   if (RemapPath(path.GetStringRef(), remapped)) {
158     new_path.SetString(remapped);
159     return true;
160   }
161   return false;
162 }
163 
164 bool PathMappingList::RemapPath(llvm::StringRef path,
165                                 std::string &new_path) const {
166   if (m_pairs.empty() || path.empty())
167     return false;
168   LazyBool path_is_relative = eLazyBoolCalculate;
169   for (const auto &it : m_pairs) {
170     auto prefix = it.first.GetStringRef();
171     if (!path.consume_front(prefix)) {
172       // Relative paths won't have a leading "./" in them unless "." is the
173       // only thing in the relative path so we need to work around "."
174       // carefully.
175       if (prefix != ".")
176         continue;
177       // We need to figure out if the "path" argument is relative. If it is,
178       // then we should remap, else skip this entry.
179       if (path_is_relative == eLazyBoolCalculate) {
180         path_is_relative = FileSpec(path, false).IsRelative() ? eLazyBoolYes :
181         eLazyBoolNo;
182       }
183       if (!path_is_relative)
184         continue;
185     }
186     FileSpec remapped(it.second.GetStringRef(), false);
187     remapped.AppendPathComponent(path);
188     new_path = remapped.GetPath();
189     return true;
190   }
191   return false;
192 }
193 
194 bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const {
195   std::string path = file.GetPath();
196   llvm::StringRef path_ref(path);
197   for (const auto &it : m_pairs) {
198     if (!path_ref.consume_front(it.second.GetStringRef()))
199       continue;
200     fixed.SetFile(it.first.GetStringRef(), false, FileSpec::Style::native);
201     fixed.AppendPathComponent(path_ref);
202     return true;
203   }
204   return false;
205 }
206 
207 bool PathMappingList::FindFile(const FileSpec &orig_spec,
208                                FileSpec &new_spec) const {
209   if (!m_pairs.empty()) {
210     char orig_path[PATH_MAX];
211     const size_t orig_path_len =
212         orig_spec.GetPath(orig_path, sizeof(orig_path));
213     if (orig_path_len > 0) {
214       const_iterator pos, end = m_pairs.end();
215       for (pos = m_pairs.begin(); pos != end; ++pos) {
216         const size_t prefix_len = pos->first.GetLength();
217 
218         if (orig_path_len >= prefix_len) {
219           if (::strncmp(pos->first.GetCString(), orig_path, prefix_len) == 0) {
220             new_spec.SetFile(pos->second.GetCString(), false,
221                              FileSpec::Style::native);
222             new_spec.AppendPathComponent(orig_path + prefix_len);
223             if (FileSystem::Instance().Exists(new_spec))
224               return true;
225           }
226         }
227       }
228     }
229   }
230   new_spec.Clear();
231   return false;
232 }
233 
234 bool PathMappingList::Replace(const ConstString &path,
235                               const ConstString &new_path, bool notify) {
236   uint32_t idx = FindIndexForPath(path);
237   if (idx < m_pairs.size()) {
238     ++m_mod_id;
239     m_pairs[idx].second = new_path;
240     if (notify && m_callback)
241       m_callback(*this, m_callback_baton);
242     return true;
243   }
244   return false;
245 }
246 
247 bool PathMappingList::Remove(const ConstString &path, bool notify) {
248   iterator pos = FindIteratorForPath(path);
249   if (pos != m_pairs.end()) {
250     ++m_mod_id;
251     m_pairs.erase(pos);
252     if (notify && m_callback)
253       m_callback(*this, m_callback_baton);
254     return true;
255   }
256   return false;
257 }
258 
259 PathMappingList::const_iterator
260 PathMappingList::FindIteratorForPath(const ConstString &path) const {
261   const_iterator pos;
262   const_iterator begin = m_pairs.begin();
263   const_iterator end = m_pairs.end();
264 
265   for (pos = begin; pos != end; ++pos) {
266     if (pos->first == path)
267       break;
268   }
269   return pos;
270 }
271 
272 PathMappingList::iterator
273 PathMappingList::FindIteratorForPath(const ConstString &path) {
274   iterator pos;
275   iterator begin = m_pairs.begin();
276   iterator end = m_pairs.end();
277 
278   for (pos = begin; pos != end; ++pos) {
279     if (pos->first == path)
280       break;
281   }
282   return pos;
283 }
284 
285 bool PathMappingList::GetPathsAtIndex(uint32_t idx, ConstString &path,
286                                       ConstString &new_path) const {
287   if (idx < m_pairs.size()) {
288     path = m_pairs[idx].first;
289     new_path = m_pairs[idx].second;
290     return true;
291   }
292   return false;
293 }
294 
295 uint32_t PathMappingList::FindIndexForPath(const ConstString &orig_path) const {
296   const ConstString path = NormalizePath(orig_path);
297   const_iterator pos;
298   const_iterator begin = m_pairs.begin();
299   const_iterator end = m_pairs.end();
300 
301   for (pos = begin; pos != end; ++pos) {
302     if (pos->first == path)
303       return std::distance(begin, pos);
304   }
305   return UINT32_MAX;
306 }
307