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