1 //===-- BreakpointIDList.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 // Other libraries and framework includes
13 // Project includes
14 #include "lldb/Breakpoint/BreakpointIDList.h"
15 
16 #include "lldb/Breakpoint/Breakpoint.h"
17 #include "lldb/Breakpoint/BreakpointLocation.h"
18 #include "lldb/Interpreter/Args.h"
19 #include "lldb/Interpreter/CommandReturnObject.h"
20 #include "lldb/Target/Target.h"
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 //----------------------------------------------------------------------
26 // class BreakpointIDList
27 //----------------------------------------------------------------------
28 
29 BreakpointIDList::BreakpointIDList()
30     : m_invalid_id(LLDB_INVALID_BREAK_ID, LLDB_INVALID_BREAK_ID) {}
31 
32 BreakpointIDList::~BreakpointIDList() = default;
33 
34 size_t BreakpointIDList::GetSize() { return m_breakpoint_ids.size(); }
35 
36 BreakpointID &BreakpointIDList::GetBreakpointIDAtIndex(size_t index) {
37   return ((index < m_breakpoint_ids.size()) ? m_breakpoint_ids[index]
38                                             : m_invalid_id);
39 }
40 
41 bool BreakpointIDList::RemoveBreakpointIDAtIndex(size_t index) {
42   if (index >= m_breakpoint_ids.size())
43     return false;
44 
45   m_breakpoint_ids.erase(m_breakpoint_ids.begin() + index);
46   return true;
47 }
48 
49 void BreakpointIDList::Clear() { m_breakpoint_ids.clear(); }
50 
51 bool BreakpointIDList::AddBreakpointID(BreakpointID bp_id) {
52   m_breakpoint_ids.push_back(bp_id);
53 
54   return true; // We don't do any verification in this function, so always
55                // return true.
56 }
57 
58 bool BreakpointIDList::AddBreakpointID(const char *bp_id_str) {
59   BreakpointID temp_bp_id;
60   break_id_t bp_id;
61   break_id_t loc_id;
62 
63   bool success =
64       BreakpointID::ParseCanonicalReference(bp_id_str, &bp_id, &loc_id);
65 
66   if (success) {
67     temp_bp_id.SetID(bp_id, loc_id);
68     m_breakpoint_ids.push_back(temp_bp_id);
69   }
70 
71   return success;
72 }
73 
74 bool BreakpointIDList::FindBreakpointID(BreakpointID &bp_id, size_t *position) {
75   for (size_t i = 0; i < m_breakpoint_ids.size(); ++i) {
76     BreakpointID tmp_id = m_breakpoint_ids[i];
77     if (tmp_id.GetBreakpointID() == bp_id.GetBreakpointID() &&
78         tmp_id.GetLocationID() == bp_id.GetLocationID()) {
79       *position = i;
80       return true;
81     }
82   }
83 
84   return false;
85 }
86 
87 bool BreakpointIDList::FindBreakpointID(const char *bp_id_str,
88                                         size_t *position) {
89   BreakpointID temp_bp_id;
90   break_id_t bp_id;
91   break_id_t loc_id;
92 
93   if (BreakpointID::ParseCanonicalReference(bp_id_str, &bp_id, &loc_id)) {
94     temp_bp_id.SetID(bp_id, loc_id);
95     return FindBreakpointID(temp_bp_id, position);
96   } else
97     return false;
98 }
99 
100 void BreakpointIDList::InsertStringArray(const char **string_array,
101                                          size_t array_size,
102                                          CommandReturnObject &result) {
103   if (string_array == nullptr)
104     return;
105 
106   for (uint32_t i = 0; i < array_size; ++i) {
107     break_id_t bp_id;
108     break_id_t loc_id;
109 
110     if (BreakpointID::ParseCanonicalReference(string_array[i], &bp_id,
111                                               &loc_id)) {
112       if (bp_id != LLDB_INVALID_BREAK_ID) {
113         BreakpointID temp_bp_id(bp_id, loc_id);
114         m_breakpoint_ids.push_back(temp_bp_id);
115       } else {
116         result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n",
117                                      string_array[i]);
118         result.SetStatus(eReturnStatusFailed);
119         return;
120       }
121     }
122   }
123   result.SetStatus(eReturnStatusSuccessFinishNoResult);
124 }
125 
126 //  This function takes OLD_ARGS, which is usually the result of breaking the
127 //  command string arguments into
128 //  an array of space-separated strings, and searches through the arguments for
129 //  any breakpoint ID range specifiers.
130 //  Any string in the array that is not part of an ID range specifier is copied
131 //  directly into NEW_ARGS.  If any
132 //  ID range specifiers are found, the range is interpreted and a list of
133 //  canonical breakpoint IDs corresponding to
134 //  all the current breakpoints and locations in the range are added to
135 //  NEW_ARGS.  When this function is done,
136 //  NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced
137 //  by the members of the range.
138 
139 void BreakpointIDList::FindAndReplaceIDRanges(Args &old_args, Target *target,
140                                               bool allow_locations,
141                                               CommandReturnObject &result,
142                                               Args &new_args) {
143   std::string range_start;
144   const char *range_end;
145   const char *current_arg;
146   const size_t num_old_args = old_args.GetArgumentCount();
147   std::set<std::string> names_found;
148 
149   for (size_t i = 0; i < num_old_args; ++i) {
150     bool is_range = false;
151 
152     current_arg = old_args.GetArgumentAtIndex(i);
153     if (!allow_locations && strchr(current_arg, '.') != nullptr) {
154       result.AppendErrorWithFormat(
155           "Breakpoint locations not allowed, saw location: %s.", current_arg);
156       new_args.Clear();
157       return;
158     }
159 
160     size_t range_start_len = 0;
161     size_t range_end_pos = 0;
162     Error error;
163 
164     if (BreakpointIDList::StringContainsIDRangeExpression(
165             current_arg, &range_start_len, &range_end_pos)) {
166       is_range = true;
167       range_start.assign(current_arg, range_start_len);
168       range_end = current_arg + range_end_pos;
169     } else if (BreakpointID::StringIsBreakpointName(current_arg, error)) {
170       if (!error.Success()) {
171         new_args.Clear();
172         result.AppendError(error.AsCString());
173         result.SetStatus(eReturnStatusFailed);
174         return;
175       } else
176         names_found.insert(current_arg);
177     } else if ((i + 2 < num_old_args) &&
178                BreakpointID::IsRangeIdentifier(
179                    old_args.GetArgumentAtIndex(i + 1)) &&
180                BreakpointID::IsValidIDExpression(current_arg) &&
181                BreakpointID::IsValidIDExpression(
182                    old_args.GetArgumentAtIndex(i + 2))) {
183       range_start.assign(current_arg);
184       range_end = old_args.GetArgumentAtIndex(i + 2);
185       is_range = true;
186       i = i + 2;
187     } else {
188       // See if user has specified id.*
189       std::string tmp_str = old_args.GetArgumentAtIndex(i);
190       size_t pos = tmp_str.find('.');
191       if (pos != std::string::npos) {
192         std::string bp_id_str = tmp_str.substr(0, pos);
193         if (BreakpointID::IsValidIDExpression(bp_id_str.c_str()) &&
194             tmp_str[pos + 1] == '*' && tmp_str.length() == (pos + 2)) {
195           break_id_t bp_id;
196           break_id_t bp_loc_id;
197 
198           BreakpointID::ParseCanonicalReference(bp_id_str.c_str(), &bp_id,
199                                                 &bp_loc_id);
200           BreakpointSP breakpoint_sp = target->GetBreakpointByID(bp_id);
201           if (!breakpoint_sp) {
202             new_args.Clear();
203             result.AppendErrorWithFormat("'%d' is not a valid breakpoint ID.\n",
204                                          bp_id);
205             result.SetStatus(eReturnStatusFailed);
206             return;
207           }
208           const size_t num_locations = breakpoint_sp->GetNumLocations();
209           for (size_t j = 0; j < num_locations; ++j) {
210             BreakpointLocation *bp_loc =
211                 breakpoint_sp->GetLocationAtIndex(j).get();
212             StreamString canonical_id_str;
213             BreakpointID::GetCanonicalReference(&canonical_id_str, bp_id,
214                                                 bp_loc->GetID());
215             new_args.AppendArgument(canonical_id_str.GetData());
216           }
217         }
218       }
219     }
220 
221     if (is_range) {
222       break_id_t start_bp_id;
223       break_id_t end_bp_id;
224       break_id_t start_loc_id;
225       break_id_t end_loc_id;
226 
227       BreakpointID::ParseCanonicalReference(range_start.c_str(), &start_bp_id,
228                                             &start_loc_id);
229       BreakpointID::ParseCanonicalReference(range_end, &end_bp_id, &end_loc_id);
230 
231       if ((start_bp_id == LLDB_INVALID_BREAK_ID) ||
232           (!target->GetBreakpointByID(start_bp_id))) {
233         new_args.Clear();
234         result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n",
235                                      range_start.c_str());
236         result.SetStatus(eReturnStatusFailed);
237         return;
238       }
239 
240       if ((end_bp_id == LLDB_INVALID_BREAK_ID) ||
241           (!target->GetBreakpointByID(end_bp_id))) {
242         new_args.Clear();
243         result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n",
244                                      range_end);
245         result.SetStatus(eReturnStatusFailed);
246         return;
247       }
248 
249       if (((start_loc_id == LLDB_INVALID_BREAK_ID) &&
250            (end_loc_id != LLDB_INVALID_BREAK_ID)) ||
251           ((start_loc_id != LLDB_INVALID_BREAK_ID) &&
252            (end_loc_id == LLDB_INVALID_BREAK_ID))) {
253         new_args.Clear();
254         result.AppendErrorWithFormat("Invalid breakpoint id range:  Either "
255                                      "both ends of range must specify"
256                                      " a breakpoint location, or neither can "
257                                      "specify a breakpoint location.\n");
258         result.SetStatus(eReturnStatusFailed);
259         return;
260       }
261 
262       // We have valid range starting & ending breakpoint IDs.  Go through all
263       // the breakpoints in the
264       // target and find all the breakpoints that fit into this range, and add
265       // them to new_args.
266 
267       // Next check to see if we have location id's.  If so, make sure the
268       // start_bp_id and end_bp_id are
269       // for the same breakpoint; otherwise we have an illegal range: breakpoint
270       // id ranges that specify
271       // bp locations are NOT allowed to cross major bp id numbers.
272 
273       if ((start_loc_id != LLDB_INVALID_BREAK_ID) ||
274           (end_loc_id != LLDB_INVALID_BREAK_ID)) {
275         if (start_bp_id != end_bp_id) {
276           new_args.Clear();
277           result.AppendErrorWithFormat(
278               "Invalid range: Ranges that specify particular breakpoint "
279               "locations"
280               " must be within the same major breakpoint; you specified two"
281               " different major breakpoints, %d and %d.\n",
282               start_bp_id, end_bp_id);
283           result.SetStatus(eReturnStatusFailed);
284           return;
285         }
286       }
287 
288       const BreakpointList &breakpoints = target->GetBreakpointList();
289       const size_t num_breakpoints = breakpoints.GetSize();
290       for (size_t j = 0; j < num_breakpoints; ++j) {
291         Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(j).get();
292         break_id_t cur_bp_id = breakpoint->GetID();
293 
294         if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id))
295           continue;
296 
297         const size_t num_locations = breakpoint->GetNumLocations();
298 
299         if ((cur_bp_id == start_bp_id) &&
300             (start_loc_id != LLDB_INVALID_BREAK_ID)) {
301           for (size_t k = 0; k < num_locations; ++k) {
302             BreakpointLocation *bp_loc =
303                 breakpoint->GetLocationAtIndex(k).get();
304             if ((bp_loc->GetID() >= start_loc_id) &&
305                 (bp_loc->GetID() <= end_loc_id)) {
306               StreamString canonical_id_str;
307               BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
308                                                   bp_loc->GetID());
309               new_args.AppendArgument(canonical_id_str.GetData());
310             }
311           }
312         } else if ((cur_bp_id == end_bp_id) &&
313                    (end_loc_id != LLDB_INVALID_BREAK_ID)) {
314           for (size_t k = 0; k < num_locations; ++k) {
315             BreakpointLocation *bp_loc =
316                 breakpoint->GetLocationAtIndex(k).get();
317             if (bp_loc->GetID() <= end_loc_id) {
318               StreamString canonical_id_str;
319               BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
320                                                   bp_loc->GetID());
321               new_args.AppendArgument(canonical_id_str.GetData());
322             }
323           }
324         } else {
325           StreamString canonical_id_str;
326           BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
327                                               LLDB_INVALID_BREAK_ID);
328           new_args.AppendArgument(canonical_id_str.GetData());
329         }
330       }
331     } else // else is_range was false
332     {
333       new_args.AppendArgument(current_arg);
334     }
335   }
336 
337   // Okay, now see if we found any names, and if we did, add them:
338   if (target && names_found.size()) {
339     for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) {
340       for (std::string name : names_found) {
341         if (bkpt_sp->MatchesName(name.c_str())) {
342           StreamString canonical_id_str;
343           BreakpointID::GetCanonicalReference(
344               &canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID);
345           new_args.AppendArgument(canonical_id_str.GetData());
346         }
347       }
348     }
349   }
350 
351   result.SetStatus(eReturnStatusSuccessFinishNoResult);
352 }
353 
354 bool BreakpointIDList::StringContainsIDRangeExpression(const char *in_string,
355                                                        size_t *range_start_len,
356                                                        size_t *range_end_pos) {
357   bool is_range_expression = false;
358   std::string arg_str = in_string;
359   std::string::size_type idx;
360   std::string::size_type start_pos = 0;
361 
362   *range_start_len = 0;
363   *range_end_pos = 0;
364 
365   int specifiers_size = 0;
366   for (int i = 0; BreakpointID::g_range_specifiers[i] != nullptr; ++i)
367     ++specifiers_size;
368 
369   for (int i = 0; i < specifiers_size && !is_range_expression; ++i) {
370     const char *specifier_str = BreakpointID::g_range_specifiers[i];
371     size_t len = strlen(specifier_str);
372     idx = arg_str.find(BreakpointID::g_range_specifiers[i]);
373     if (idx != std::string::npos) {
374       *range_start_len = idx - start_pos;
375       std::string start_str = arg_str.substr(start_pos, *range_start_len);
376       if (idx + len < arg_str.length()) {
377         *range_end_pos = idx + len;
378         std::string end_str = arg_str.substr(*range_end_pos);
379         if (BreakpointID::IsValidIDExpression(start_str.c_str()) &&
380             BreakpointID::IsValidIDExpression(end_str.c_str())) {
381           is_range_expression = true;
382           //*range_start = start_str;
383           //*range_end = end_str;
384         }
385       }
386     }
387   }
388 
389   if (!is_range_expression) {
390     *range_start_len = 0;
391     *range_end_pos = 0;
392   }
393 
394   return is_range_expression;
395 }
396