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