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