xref: /openbsd-src/gnu/llvm/lldb/source/Target/PathMappingList.cpp (revision be691f3bb6417f04a68938fadbcaee2d5795e764)
1dda28197Spatrick //===-- PathMappingList.cpp -----------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include <climits>
10061da546Spatrick #include <cstring>
11061da546Spatrick 
12061da546Spatrick #include "lldb/Host/FileSystem.h"
13061da546Spatrick #include "lldb/Host/PosixApi.h"
14061da546Spatrick #include "lldb/Target/PathMappingList.h"
15061da546Spatrick #include "lldb/Utility/FileSpec.h"
16061da546Spatrick #include "lldb/Utility/Status.h"
17061da546Spatrick #include "lldb/Utility/Stream.h"
18061da546Spatrick #include "lldb/lldb-private-enumerations.h"
19061da546Spatrick 
20061da546Spatrick using namespace lldb;
21061da546Spatrick using namespace lldb_private;
22061da546Spatrick 
23061da546Spatrick namespace {
24061da546Spatrick   // We must normalize our path pairs that we store because if we don't then
25061da546Spatrick   // things won't always work. We found a case where if we did:
26061da546Spatrick   // (lldb) settings set target.source-map . /tmp
27061da546Spatrick   // We would store a path pairs of "." and "/tmp" as raw strings. If the debug
28061da546Spatrick   // info contains "./foo/bar.c", the path will get normalized to "foo/bar.c".
29061da546Spatrick   // When PathMappingList::RemapPath() is called, it expects the path to start
30061da546Spatrick   // with the raw path pair, which doesn't work anymore because the paths have
31061da546Spatrick   // been normalized when the debug info was loaded. So we need to store
32061da546Spatrick   // nomalized path pairs to ensure things match up.
33061da546Spatrick   ConstString NormalizePath(ConstString path) {
34061da546Spatrick     // If we use "path" to construct a FileSpec, it will normalize the path for
35061da546Spatrick     // us. We then grab the string and turn it back into a ConstString.
36061da546Spatrick     return ConstString(FileSpec(path.GetStringRef()).GetPath());
37061da546Spatrick   }
38061da546Spatrick }
39061da546Spatrick // PathMappingList constructor
40*be691f3bSpatrick PathMappingList::PathMappingList() : m_pairs() {}
41061da546Spatrick 
42061da546Spatrick PathMappingList::PathMappingList(ChangedCallback callback, void *callback_baton)
43061da546Spatrick     : m_pairs(), m_callback(callback), m_callback_baton(callback_baton),
44061da546Spatrick       m_mod_id(0) {}
45061da546Spatrick 
46061da546Spatrick PathMappingList::PathMappingList(const PathMappingList &rhs)
47061da546Spatrick     : m_pairs(rhs.m_pairs), m_callback(nullptr), m_callback_baton(nullptr),
48061da546Spatrick       m_mod_id(0) {}
49061da546Spatrick 
50061da546Spatrick const PathMappingList &PathMappingList::operator=(const PathMappingList &rhs) {
51061da546Spatrick   if (this != &rhs) {
52061da546Spatrick     m_pairs = rhs.m_pairs;
53061da546Spatrick     m_callback = nullptr;
54061da546Spatrick     m_callback_baton = nullptr;
55061da546Spatrick     m_mod_id = rhs.m_mod_id;
56061da546Spatrick   }
57061da546Spatrick   return *this;
58061da546Spatrick }
59061da546Spatrick 
60061da546Spatrick PathMappingList::~PathMappingList() = default;
61061da546Spatrick 
62061da546Spatrick void PathMappingList::Append(ConstString path,
63061da546Spatrick                              ConstString replacement, bool notify) {
64061da546Spatrick   ++m_mod_id;
65061da546Spatrick   m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement)));
66061da546Spatrick   if (notify && m_callback)
67061da546Spatrick     m_callback(*this, m_callback_baton);
68061da546Spatrick }
69061da546Spatrick 
70061da546Spatrick void PathMappingList::Append(const PathMappingList &rhs, bool notify) {
71061da546Spatrick   ++m_mod_id;
72061da546Spatrick   if (!rhs.m_pairs.empty()) {
73061da546Spatrick     const_iterator pos, end = rhs.m_pairs.end();
74061da546Spatrick     for (pos = rhs.m_pairs.begin(); pos != end; ++pos)
75061da546Spatrick       m_pairs.push_back(*pos);
76061da546Spatrick     if (notify && m_callback)
77061da546Spatrick       m_callback(*this, m_callback_baton);
78061da546Spatrick   }
79061da546Spatrick }
80061da546Spatrick 
81061da546Spatrick void PathMappingList::Insert(ConstString path,
82061da546Spatrick                              ConstString replacement, uint32_t index,
83061da546Spatrick                              bool notify) {
84061da546Spatrick   ++m_mod_id;
85061da546Spatrick   iterator insert_iter;
86061da546Spatrick   if (index >= m_pairs.size())
87061da546Spatrick     insert_iter = m_pairs.end();
88061da546Spatrick   else
89061da546Spatrick     insert_iter = m_pairs.begin() + index;
90061da546Spatrick   m_pairs.emplace(insert_iter, pair(NormalizePath(path),
91061da546Spatrick                                     NormalizePath(replacement)));
92061da546Spatrick   if (notify && m_callback)
93061da546Spatrick     m_callback(*this, m_callback_baton);
94061da546Spatrick }
95061da546Spatrick 
96061da546Spatrick bool PathMappingList::Replace(ConstString path,
97061da546Spatrick                               ConstString replacement, uint32_t index,
98061da546Spatrick                               bool notify) {
99061da546Spatrick   if (index >= m_pairs.size())
100061da546Spatrick     return false;
101061da546Spatrick   ++m_mod_id;
102061da546Spatrick   m_pairs[index] = pair(NormalizePath(path), NormalizePath(replacement));
103061da546Spatrick   if (notify && m_callback)
104061da546Spatrick     m_callback(*this, m_callback_baton);
105061da546Spatrick   return true;
106061da546Spatrick }
107061da546Spatrick 
108061da546Spatrick bool PathMappingList::Remove(size_t index, bool notify) {
109061da546Spatrick   if (index >= m_pairs.size())
110061da546Spatrick     return false;
111061da546Spatrick 
112061da546Spatrick   ++m_mod_id;
113061da546Spatrick   iterator iter = m_pairs.begin() + index;
114061da546Spatrick   m_pairs.erase(iter);
115061da546Spatrick   if (notify && m_callback)
116061da546Spatrick     m_callback(*this, m_callback_baton);
117061da546Spatrick   return true;
118061da546Spatrick }
119061da546Spatrick 
120061da546Spatrick // For clients which do not need the pair index dumped, pass a pair_index >= 0
121061da546Spatrick // to only dump the indicated pair.
122061da546Spatrick void PathMappingList::Dump(Stream *s, int pair_index) {
123061da546Spatrick   unsigned int numPairs = m_pairs.size();
124061da546Spatrick 
125061da546Spatrick   if (pair_index < 0) {
126061da546Spatrick     unsigned int index;
127061da546Spatrick     for (index = 0; index < numPairs; ++index)
128061da546Spatrick       s->Printf("[%d] \"%s\" -> \"%s\"\n", index,
129061da546Spatrick                 m_pairs[index].first.GetCString(),
130061da546Spatrick                 m_pairs[index].second.GetCString());
131061da546Spatrick   } else {
132061da546Spatrick     if (static_cast<unsigned int>(pair_index) < numPairs)
133061da546Spatrick       s->Printf("%s -> %s", m_pairs[pair_index].first.GetCString(),
134061da546Spatrick                 m_pairs[pair_index].second.GetCString());
135061da546Spatrick   }
136061da546Spatrick }
137061da546Spatrick 
138061da546Spatrick void PathMappingList::Clear(bool notify) {
139061da546Spatrick   if (!m_pairs.empty())
140061da546Spatrick     ++m_mod_id;
141061da546Spatrick   m_pairs.clear();
142061da546Spatrick   if (notify && m_callback)
143061da546Spatrick     m_callback(*this, m_callback_baton);
144061da546Spatrick }
145061da546Spatrick 
146061da546Spatrick bool PathMappingList::RemapPath(ConstString path,
147061da546Spatrick                                 ConstString &new_path) const {
148*be691f3bSpatrick   if (llvm::Optional<FileSpec> remapped = RemapPath(path.GetStringRef())) {
149*be691f3bSpatrick     new_path.SetString(remapped->GetPath());
150061da546Spatrick     return true;
151061da546Spatrick   }
152061da546Spatrick   return false;
153061da546Spatrick }
154061da546Spatrick 
155*be691f3bSpatrick /// Append components to path, applying style.
156*be691f3bSpatrick static void AppendPathComponents(FileSpec &path, llvm::StringRef components,
157*be691f3bSpatrick                                  llvm::sys::path::Style style) {
158*be691f3bSpatrick     auto component = llvm::sys::path::begin(components, style);
159*be691f3bSpatrick     auto e = llvm::sys::path::end(components);
160*be691f3bSpatrick     while (component != e &&
161*be691f3bSpatrick         llvm::sys::path::is_separator(*component->data(), style))
162*be691f3bSpatrick       ++component;
163*be691f3bSpatrick     for (; component != e; ++component)
164*be691f3bSpatrick       path.AppendPathComponent(*component);
165*be691f3bSpatrick }
166*be691f3bSpatrick 
167*be691f3bSpatrick llvm::Optional<FileSpec>
168*be691f3bSpatrick PathMappingList::RemapPath(llvm::StringRef mapping_path,
169*be691f3bSpatrick                            bool only_if_exists) const {
170*be691f3bSpatrick   if (m_pairs.empty() || mapping_path.empty())
171*be691f3bSpatrick     return {};
172061da546Spatrick   LazyBool path_is_relative = eLazyBoolCalculate;
173*be691f3bSpatrick 
174061da546Spatrick   for (const auto &it : m_pairs) {
175*be691f3bSpatrick     llvm::StringRef prefix = it.first.GetStringRef();
176*be691f3bSpatrick     // We create a copy of mapping_path because StringRef::consume_from
177*be691f3bSpatrick     // effectively modifies the instance itself.
178*be691f3bSpatrick     llvm::StringRef path = mapping_path;
179061da546Spatrick     if (!path.consume_front(prefix)) {
180061da546Spatrick       // Relative paths won't have a leading "./" in them unless "." is the
181061da546Spatrick       // only thing in the relative path so we need to work around "."
182061da546Spatrick       // carefully.
183061da546Spatrick       if (prefix != ".")
184061da546Spatrick         continue;
185061da546Spatrick       // We need to figure out if the "path" argument is relative. If it is,
186061da546Spatrick       // then we should remap, else skip this entry.
187061da546Spatrick       if (path_is_relative == eLazyBoolCalculate) {
188061da546Spatrick         path_is_relative =
189061da546Spatrick             FileSpec(path).IsRelative() ? eLazyBoolYes : eLazyBoolNo;
190061da546Spatrick       }
191061da546Spatrick       if (!path_is_relative)
192061da546Spatrick         continue;
193061da546Spatrick     }
194061da546Spatrick     FileSpec remapped(it.second.GetStringRef());
195*be691f3bSpatrick     auto orig_style = FileSpec::GuessPathStyle(prefix).getValueOr(
196*be691f3bSpatrick         llvm::sys::path::Style::native);
197*be691f3bSpatrick     AppendPathComponents(remapped, path, orig_style);
198*be691f3bSpatrick     if (!only_if_exists || FileSystem::Instance().Exists(remapped))
199*be691f3bSpatrick       return remapped;
200061da546Spatrick   }
201*be691f3bSpatrick   return {};
202061da546Spatrick }
203061da546Spatrick 
204061da546Spatrick bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const {
205061da546Spatrick   std::string path = file.GetPath();
206061da546Spatrick   llvm::StringRef path_ref(path);
207061da546Spatrick   for (const auto &it : m_pairs) {
208061da546Spatrick     if (!path_ref.consume_front(it.second.GetStringRef()))
209061da546Spatrick       continue;
210*be691f3bSpatrick     auto orig_file = it.first.GetStringRef();
211*be691f3bSpatrick     auto orig_style = FileSpec::GuessPathStyle(orig_file).getValueOr(
212*be691f3bSpatrick         llvm::sys::path::Style::native);
213*be691f3bSpatrick     fixed.SetFile(orig_file, orig_style);
214*be691f3bSpatrick     AppendPathComponents(fixed, path_ref, orig_style);
215061da546Spatrick     return true;
216061da546Spatrick   }
217061da546Spatrick   return false;
218061da546Spatrick }
219061da546Spatrick 
220*be691f3bSpatrick llvm::Optional<FileSpec> PathMappingList::FindFile(const FileSpec &orig_spec) const {
221*be691f3bSpatrick   if (auto remapped = RemapPath(orig_spec.GetPath(), /*only_if_exists=*/true))
222*be691f3bSpatrick     return remapped;
223061da546Spatrick 
224*be691f3bSpatrick   return {};
225061da546Spatrick }
226061da546Spatrick 
227061da546Spatrick bool PathMappingList::Replace(ConstString path,
228061da546Spatrick                               ConstString new_path, bool notify) {
229061da546Spatrick   uint32_t idx = FindIndexForPath(path);
230061da546Spatrick   if (idx < m_pairs.size()) {
231061da546Spatrick     ++m_mod_id;
232061da546Spatrick     m_pairs[idx].second = new_path;
233061da546Spatrick     if (notify && m_callback)
234061da546Spatrick       m_callback(*this, m_callback_baton);
235061da546Spatrick     return true;
236061da546Spatrick   }
237061da546Spatrick   return false;
238061da546Spatrick }
239061da546Spatrick 
240061da546Spatrick bool PathMappingList::Remove(ConstString path, bool notify) {
241061da546Spatrick   iterator pos = FindIteratorForPath(path);
242061da546Spatrick   if (pos != m_pairs.end()) {
243061da546Spatrick     ++m_mod_id;
244061da546Spatrick     m_pairs.erase(pos);
245061da546Spatrick     if (notify && m_callback)
246061da546Spatrick       m_callback(*this, m_callback_baton);
247061da546Spatrick     return true;
248061da546Spatrick   }
249061da546Spatrick   return false;
250061da546Spatrick }
251061da546Spatrick 
252061da546Spatrick PathMappingList::const_iterator
253061da546Spatrick PathMappingList::FindIteratorForPath(ConstString path) const {
254061da546Spatrick   const_iterator pos;
255061da546Spatrick   const_iterator begin = m_pairs.begin();
256061da546Spatrick   const_iterator end = m_pairs.end();
257061da546Spatrick 
258061da546Spatrick   for (pos = begin; pos != end; ++pos) {
259061da546Spatrick     if (pos->first == path)
260061da546Spatrick       break;
261061da546Spatrick   }
262061da546Spatrick   return pos;
263061da546Spatrick }
264061da546Spatrick 
265061da546Spatrick PathMappingList::iterator
266061da546Spatrick PathMappingList::FindIteratorForPath(ConstString path) {
267061da546Spatrick   iterator pos;
268061da546Spatrick   iterator begin = m_pairs.begin();
269061da546Spatrick   iterator end = m_pairs.end();
270061da546Spatrick 
271061da546Spatrick   for (pos = begin; pos != end; ++pos) {
272061da546Spatrick     if (pos->first == path)
273061da546Spatrick       break;
274061da546Spatrick   }
275061da546Spatrick   return pos;
276061da546Spatrick }
277061da546Spatrick 
278061da546Spatrick bool PathMappingList::GetPathsAtIndex(uint32_t idx, ConstString &path,
279061da546Spatrick                                       ConstString &new_path) const {
280061da546Spatrick   if (idx < m_pairs.size()) {
281061da546Spatrick     path = m_pairs[idx].first;
282061da546Spatrick     new_path = m_pairs[idx].second;
283061da546Spatrick     return true;
284061da546Spatrick   }
285061da546Spatrick   return false;
286061da546Spatrick }
287061da546Spatrick 
288061da546Spatrick uint32_t PathMappingList::FindIndexForPath(ConstString orig_path) const {
289061da546Spatrick   const ConstString path = NormalizePath(orig_path);
290061da546Spatrick   const_iterator pos;
291061da546Spatrick   const_iterator begin = m_pairs.begin();
292061da546Spatrick   const_iterator end = m_pairs.end();
293061da546Spatrick 
294061da546Spatrick   for (pos = begin; pos != end; ++pos) {
295061da546Spatrick     if (pos->first == path)
296061da546Spatrick       return std::distance(begin, pos);
297061da546Spatrick   }
298061da546Spatrick   return UINT32_MAX;
299061da546Spatrick }
300