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