1//===-- StopPointSiteList.h -------------------------------------*- C++ -*-===//
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#ifndef LLDB_BREAKPOINT_STOPPOINTSITELIST_H
10#define LLDB_BREAKPOINT_STOPPOINTSITELIST_H
11
12#include <functional>
13#include <map>
14#include <mutex>
15
16#include <lldb/Breakpoint/BreakpointSite.h>
17#include <lldb/Utility/Iterable.h>
18#include <lldb/Utility/Stream.h>
19
20namespace lldb_private {
21
22template <typename StopPointSite> class StopPointSiteList {
23  // At present Process directly accesses the map of StopPointSites so it can
24  // do quick lookups into the map (using GetMap).
25  // FIXME: Find a better interface for this.
26  friend class Process;
27
28public:
29  using StopPointSiteSP = std::shared_ptr<StopPointSite>;
30
31  /// Add a site to the list.
32  ///
33  /// \param[in] site_sp
34  ///    A shared pointer to a site being added to the list.
35  ///
36  /// \return
37  ///    The ID of the site in the list.
38  typename StopPointSite::SiteID Add(const StopPointSiteSP &site_sp) {
39    lldb::addr_t site_load_addr = site_sp->GetLoadAddress();
40    std::lock_guard<std::recursive_mutex> guard(m_mutex);
41    typename collection::iterator iter = m_site_list.find(site_load_addr);
42
43    // Add site to the list.  However, if the element already exists in
44    // the list, then we don't add it, and return InvalidSiteID.
45    if (iter == m_site_list.end()) {
46      m_site_list[site_load_addr] = site_sp;
47      return site_sp->GetID();
48    } else {
49      return UINT32_MAX;
50    }
51  }
52
53  /// Standard Dump routine, doesn't do anything at present.
54  /// \param[in] s
55  ///     Stream into which to dump the description.
56  void Dump(Stream *s) const {
57    s->Printf("%p: ", static_cast<const void *>(this));
58    s->Printf("StopPointSiteList with %u ConstituentSites:\n",
59              (uint32_t)m_site_list.size());
60    s->IndentMore();
61    typename collection::const_iterator pos;
62    typename collection::const_iterator end = m_site_list.end();
63    for (pos = m_site_list.begin(); pos != end; ++pos)
64      pos->second->Dump(s);
65    s->IndentLess();
66  }
67
68  /// Returns a shared pointer to the site at address \a addr.
69  ///
70  /// \param[in] addr
71  ///     The address to look for.
72  ///
73  /// \result
74  ///     A shared pointer to the site. Nullptr if no site contains
75  ///     the address.
76  StopPointSiteSP FindByAddress(lldb::addr_t addr) {
77    StopPointSiteSP found_sp;
78    std::lock_guard<std::recursive_mutex> guard(m_mutex);
79    typename collection::iterator iter = m_site_list.find(addr);
80    if (iter != m_site_list.end())
81      found_sp = iter->second;
82    return found_sp;
83  }
84
85  /// Returns a shared pointer to the site with id \a site_id.
86  ///
87  /// \param[in] site_id
88  ///   The site ID to seek for.
89  ///
90  /// \result
91  ///   A shared pointer to the site. Nullptr if no matching site.
92  StopPointSiteSP FindByID(typename StopPointSite::SiteID site_id) {
93    std::lock_guard<std::recursive_mutex> guard(m_mutex);
94    StopPointSiteSP stop_sp;
95    typename collection::iterator pos = GetIDIterator(site_id);
96    if (pos != m_site_list.end())
97      stop_sp = pos->second;
98
99    return stop_sp;
100  }
101
102  /// Returns a shared pointer to the site with id \a site_id -
103  /// const version.
104  ///
105  /// \param[in] site_id
106  ///   The site ID to seek for.
107  ///
108  /// \result
109  ///   A shared pointer to the site. Nullptr if no matching site.
110  const StopPointSiteSP FindByID(typename StopPointSite::SiteID site_id) const {
111    std::lock_guard<std::recursive_mutex> guard(m_mutex);
112    StopPointSiteSP stop_sp;
113    typename collection::const_iterator pos = GetIDConstIterator(site_id);
114    if (pos != m_site_list.end())
115      stop_sp = pos->second;
116
117    return stop_sp;
118  }
119
120  /// Returns the site id to the site at address \a addr.
121  ///
122  /// \param[in] addr
123  ///   The address to match.
124  ///
125  /// \result
126  ///   The ID of the site, or LLDB_INVALID_SITE_ID.
127  typename StopPointSite::SiteID FindIDByAddress(lldb::addr_t addr) {
128    if (StopPointSiteSP site = FindByAddress(addr))
129      return site->GetID();
130    return UINT32_MAX;
131  }
132
133  /// Returns whether the BreakpointSite \a site_id has a BreakpointLocation
134  /// that is part of Breakpoint \a bp_id.
135  ///
136  /// NB this is only defined when StopPointSiteList is specialized for
137  /// BreakpointSite's.
138  ///
139  /// \param[in] site_id
140  ///   The site id to query.
141  ///
142  /// \param[in] bp_id
143  ///   The breakpoint id to look for in \a site_id's BreakpointLocations.
144  ///
145  /// \result
146  ///   True if \a site_id exists in the site list AND \a bp_id
147  ///   is the breakpoint for one of the BreakpointLocations.
148  bool StopPointSiteContainsBreakpoint(typename StopPointSite::SiteID,
149                                       lldb::break_id_t bp_id);
150
151  void ForEach(std::function<void(StopPointSite *)> const &callback) {
152    std::lock_guard<std::recursive_mutex> guard(m_mutex);
153    for (auto pair : m_site_list)
154      callback(pair.second.get());
155  }
156
157  /// Removes the site given by \a site_id from this list.
158  ///
159  /// \param[in] site_id
160  ///   The site ID to remove.
161  ///
162  /// \result
163  ///   \b true if the site \a site_id was in the list.
164  bool Remove(typename StopPointSite::SiteID site_id) {
165    std::lock_guard<std::recursive_mutex> guard(m_mutex);
166    typename collection::iterator pos = GetIDIterator(site_id); // Predicate
167    if (pos != m_site_list.end()) {
168      m_site_list.erase(pos);
169      return true;
170    }
171    return false;
172  }
173
174  /// Removes the site at address \a addr from this list.
175  ///
176  /// \param[in] addr
177  ///   The address from which to remove a site.
178  ///
179  /// \result
180  ///   \b true if \a addr had a site to remove from the list.
181  bool RemoveByAddress(lldb::addr_t addr) {
182    std::lock_guard<std::recursive_mutex> guard(m_mutex);
183    typename collection::iterator pos = m_site_list.find(addr);
184    if (pos != m_site_list.end()) {
185      m_site_list.erase(pos);
186      return true;
187    }
188    return false;
189  }
190
191  bool FindInRange(lldb::addr_t lower_bound, lldb::addr_t upper_bound,
192                   StopPointSiteList &bp_site_list) const {
193    if (lower_bound > upper_bound)
194      return false;
195
196    std::lock_guard<std::recursive_mutex> guard(m_mutex);
197    typename collection::const_iterator lower, upper, pos;
198    lower = m_site_list.lower_bound(lower_bound);
199    if (lower == m_site_list.end() || (*lower).first >= upper_bound)
200      return false;
201
202    // This is one tricky bit.  The site might overlap the bottom end of
203    // the range.  So we grab the site prior to the lower bound, and check
204    // that that + its byte size isn't in our range.
205    if (lower != m_site_list.begin()) {
206      typename collection::const_iterator prev_pos = lower;
207      prev_pos--;
208      const StopPointSiteSP &prev_site = (*prev_pos).second;
209      if (prev_site->GetLoadAddress() + prev_site->GetByteSize() > lower_bound)
210        bp_site_list.Add(prev_site);
211    }
212
213    upper = m_site_list.upper_bound(upper_bound);
214
215    for (pos = lower; pos != upper; pos++)
216      bp_site_list.Add((*pos).second);
217    return true;
218  }
219
220  typedef void (*StopPointSiteSPMapFunc)(StopPointSite &site, void *baton);
221
222  /// Enquires of the site on in this list with ID \a site_id
223  /// whether we should stop for the constituent or not.
224  ///
225  /// \param[in] context
226  ///    This contains the information about this stop.
227  ///
228  /// \param[in] site_id
229  ///    This site ID that we hit.
230  ///
231  /// \return
232  ///    \b true if we should stop, \b false otherwise.
233  bool ShouldStop(StoppointCallbackContext *context,
234                  typename StopPointSite::SiteID site_id) {
235    if (StopPointSiteSP site_sp = FindByID(site_id)) {
236      // Let the site decide if it should stop here (could not have
237      // reached it's target hit count yet, or it could have a callback that
238      // decided it shouldn't stop (shared library loads/unloads).
239      return site_sp->ShouldStop(context);
240    }
241    // We should stop here since this site isn't valid anymore or it
242    // doesn't exist.
243    return true;
244  }
245
246  /// Returns the number of elements in the list.
247  ///
248  /// \result
249  ///   The number of elements.
250  size_t GetSize() const {
251    std::lock_guard<std::recursive_mutex> guard(m_mutex);
252    return m_site_list.size();
253  }
254
255  bool IsEmpty() const {
256    std::lock_guard<std::recursive_mutex> guard(m_mutex);
257    return m_site_list.empty();
258  }
259
260  std::vector<StopPointSiteSP> Sites() {
261    std::vector<StopPointSiteSP> sites;
262    std::lock_guard<std::recursive_mutex> guard(m_mutex);
263    typename collection::iterator iter = m_site_list.begin();
264    while (iter != m_site_list.end()) {
265      sites.push_back(iter->second);
266      ++iter;
267    }
268
269    return sites;
270  }
271
272  void Clear() {
273    std::lock_guard<std::recursive_mutex> guard(m_mutex);
274    m_site_list.clear();
275  }
276
277protected:
278  typedef std::map<lldb::addr_t, StopPointSiteSP> collection;
279
280  typename collection::iterator
281  GetIDIterator(typename StopPointSite::SiteID site_id) {
282    std::lock_guard<std::recursive_mutex> guard(m_mutex);
283    auto id_matches =
284        [site_id](const std::pair<lldb::addr_t, StopPointSiteSP> s) {
285          return site_id == s.second->GetID();
286        };
287    return std::find_if(m_site_list.begin(),
288                        m_site_list.end(), // Search full range
289                        id_matches);
290  }
291
292  typename collection::const_iterator
293  GetIDConstIterator(typename StopPointSite::SiteID site_id) const {
294    std::lock_guard<std::recursive_mutex> guard(m_mutex);
295    auto id_matches =
296        [site_id](const std::pair<lldb::addr_t, StopPointSiteSP> s) {
297          return site_id == s.second->GetID();
298        };
299    return std::find_if(m_site_list.begin(),
300                        m_site_list.end(), // Search full range
301                        id_matches);
302  }
303
304  mutable std::recursive_mutex m_mutex;
305  collection m_site_list; // The site list.
306};
307
308} // namespace lldb_private
309
310#endif // LLDB_BREAKPOINT_STOPPOINTSITELIST_H
311