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
18using namespace lldb;
19using namespace lldb_private;
20
21//----------------------------------------------------------------------
22// class BreakpointIDList
23//----------------------------------------------------------------------
24
25BreakpointIDList::BreakpointIDList () :
26m_invalid_id (LLDB_INVALID_BREAK_ID, LLDB_INVALID_BREAK_ID)
27{
28}
29
30BreakpointIDList::~BreakpointIDList ()
31{
32}
33
34size_t
35BreakpointIDList::GetSize()
36{
37    return m_breakpoint_ids.size();
38}
39
40BreakpointID &
41BreakpointIDList::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
49bool
50BreakpointIDList::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
59void
60BreakpointIDList::Clear()
61{
62    m_breakpoint_ids.clear ();
63}
64
65bool
66BreakpointIDList::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
73bool
74BreakpointIDList::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
91bool
92BreakpointIDList::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
108bool
109BreakpointIDList::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
124void
125BreakpointIDList::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
161void
162BreakpointIDList::FindAndReplaceIDRanges (Args &old_args, Target *target, CommandReturnObject &result,
163                                          Args &new_args)
164{
165    std::string range_start;
166    const char *range_end;
167    const char *current_arg;
168    const size_t num_old_args = old_args.GetArgumentCount();
169
170    for (size_t i = 0; i < num_old_args; ++i)
171    {
172        bool is_range = false;
173        current_arg = old_args.GetArgumentAtIndex (i);
174
175        size_t range_start_len = 0;
176        size_t range_end_pos = 0;
177        if (BreakpointIDList::StringContainsIDRangeExpression (current_arg, &range_start_len, &range_end_pos))
178        {
179            is_range = true;
180            range_start.assign (current_arg, range_start_len);
181            range_end = current_arg + range_end_pos;
182        }
183        else if ((i + 2 < num_old_args)
184                 && BreakpointID::IsRangeIdentifier (old_args.GetArgumentAtIndex (i+1))
185                 && BreakpointID::IsValidIDExpression (current_arg)
186                 && BreakpointID::IsValidIDExpression (old_args.GetArgumentAtIndex (i+2)))
187        {
188            range_start.assign (current_arg);
189            range_end = old_args.GetArgumentAtIndex (i+2);
190            is_range = true;
191            i = i+2;
192        }
193        else
194        {
195            // See if user has specified id.*
196            std::string tmp_str = old_args.GetArgumentAtIndex (i);
197            size_t pos = tmp_str.find ('.');
198            if (pos != std::string::npos)
199            {
200                std::string bp_id_str = tmp_str.substr (0, pos);
201                if (BreakpointID::IsValidIDExpression (bp_id_str.c_str())
202                    && tmp_str[pos+1] == '*'
203                    && tmp_str.length() == (pos + 2))
204                {
205                    break_id_t bp_id;
206                    break_id_t bp_loc_id;
207
208                    BreakpointID::ParseCanonicalReference (bp_id_str.c_str(), &bp_id, &bp_loc_id);
209                    BreakpointSP breakpoint_sp = target->GetBreakpointByID (bp_id);
210                    if (! breakpoint_sp)
211                    {
212                        new_args.Clear();
213                        result.AppendErrorWithFormat ("'%d' is not a valid breakpoint ID.\n", bp_id);
214                        result.SetStatus (eReturnStatusFailed);
215                        return;
216                    }
217                    const size_t num_locations = breakpoint_sp->GetNumLocations();
218                    for (size_t j = 0; j < num_locations; ++j)
219                    {
220                        BreakpointLocation *bp_loc = breakpoint_sp->GetLocationAtIndex(j).get();
221                        StreamString canonical_id_str;
222                        BreakpointID::GetCanonicalReference (&canonical_id_str, bp_id, bp_loc->GetID());
223                        new_args.AppendArgument (canonical_id_str.GetData());
224                    }
225                }
226
227            }
228        }
229
230        if (is_range)
231        {
232            break_id_t start_bp_id;
233            break_id_t end_bp_id;
234            break_id_t start_loc_id;
235            break_id_t end_loc_id;
236
237            BreakpointID::ParseCanonicalReference (range_start.c_str(), &start_bp_id, &start_loc_id);
238            BreakpointID::ParseCanonicalReference (range_end, &end_bp_id, &end_loc_id);
239
240            if ((start_bp_id == LLDB_INVALID_BREAK_ID)
241                || (! target->GetBreakpointByID (start_bp_id)))
242            {
243                new_args.Clear();
244                result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_start.c_str());
245                result.SetStatus (eReturnStatusFailed);
246                return;
247            }
248
249            if ((end_bp_id == LLDB_INVALID_BREAK_ID)
250                || (! target->GetBreakpointByID (end_bp_id)))
251            {
252                new_args.Clear();
253                result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_end);
254                result.SetStatus (eReturnStatusFailed);
255                return;
256            }
257
258
259            if (((start_loc_id == LLDB_INVALID_BREAK_ID)
260                 && (end_loc_id != LLDB_INVALID_BREAK_ID))
261                || ((start_loc_id != LLDB_INVALID_BREAK_ID)
262                    && (end_loc_id == LLDB_INVALID_BREAK_ID)))
263            {
264                new_args.Clear ();
265                result.AppendErrorWithFormat ("Invalid breakpoint id range:  Either both ends of range must specify"
266                                              " a breakpoint location, or neither can specify a breakpoint location.\n");
267                result.SetStatus (eReturnStatusFailed);
268                return;
269            }
270
271            // We have valid range starting & ending breakpoint IDs.  Go through all the breakpoints in the
272            // target and find all the breakpoints that fit into this range, and add them to new_args.
273
274            // Next check to see if we have location id's.  If so, make sure the start_bp_id and end_bp_id are
275            // for the same breakpoint; otherwise we have an illegal range: breakpoint id ranges that specify
276            // bp locations are NOT allowed to cross major bp id numbers.
277
278            if  ((start_loc_id != LLDB_INVALID_BREAK_ID)
279                || (end_loc_id != LLDB_INVALID_BREAK_ID))
280            {
281                if (start_bp_id != end_bp_id)
282                {
283                    new_args.Clear();
284                    result.AppendErrorWithFormat ("Invalid range: Ranges that specify particular breakpoint locations"
285                                                  " must be within the same major breakpoint; you specified two"
286                                                  " different major breakpoints, %d and %d.\n",
287                                                  start_bp_id, end_bp_id);
288                    result.SetStatus (eReturnStatusFailed);
289                    return;
290                }
291            }
292
293            const BreakpointList& breakpoints = target->GetBreakpointList();
294            const size_t num_breakpoints = breakpoints.GetSize();
295            for (size_t j = 0; j < num_breakpoints; ++j)
296            {
297                Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex (j).get();
298                break_id_t cur_bp_id = breakpoint->GetID();
299
300                if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id))
301                    continue;
302
303                const size_t num_locations = breakpoint->GetNumLocations();
304
305                if ((cur_bp_id == start_bp_id) && (start_loc_id != LLDB_INVALID_BREAK_ID))
306                {
307                    for (size_t k = 0; k < num_locations; ++k)
308                    {
309                        BreakpointLocation * bp_loc = breakpoint->GetLocationAtIndex(k).get();
310                        if ((bp_loc->GetID() >= start_loc_id) && (bp_loc->GetID() <= end_loc_id))
311                        {
312                            StreamString canonical_id_str;
313                            BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, bp_loc->GetID());
314                            new_args.AppendArgument (canonical_id_str.GetData());
315                        }
316                    }
317                }
318                else if ((cur_bp_id == end_bp_id) && (end_loc_id != LLDB_INVALID_BREAK_ID))
319                {
320                    for (size_t k = 0; k < num_locations; ++k)
321                    {
322                        BreakpointLocation * bp_loc = breakpoint->GetLocationAtIndex(k).get();
323                        if (bp_loc->GetID() <= end_loc_id)
324                        {
325                            StreamString canonical_id_str;
326                            BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, bp_loc->GetID());
327                            new_args.AppendArgument (canonical_id_str.GetData());
328                        }
329                    }
330                }
331                else
332                {
333                    StreamString canonical_id_str;
334                    BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, LLDB_INVALID_BREAK_ID);
335                    new_args.AppendArgument (canonical_id_str.GetData());
336                }
337            }
338        }
339        else  // else is_range was false
340        {
341            new_args.AppendArgument (current_arg);
342        }
343    }
344
345    result.SetStatus (eReturnStatusSuccessFinishNoResult);
346    return;
347}
348
349bool
350BreakpointIDList::StringContainsIDRangeExpression (const char *in_string,
351                                                   size_t *range_start_len,
352                                                   size_t *range_end_pos)
353{
354    bool is_range_expression = false;
355    std::string arg_str = in_string;
356    std::string::size_type idx;
357    std::string::size_type start_pos = 0;
358
359    *range_start_len = 0;
360    *range_end_pos = 0;
361
362    int specifiers_size = 0;
363    for (int i = 0; BreakpointID::g_range_specifiers[i] != NULL; ++i)
364        ++specifiers_size;
365
366    for (int i = 0; i < specifiers_size && !is_range_expression; ++i)
367    {
368        const char *specifier_str = BreakpointID::g_range_specifiers[i];
369        size_t len = strlen (specifier_str);
370        idx = arg_str.find (BreakpointID::g_range_specifiers[i]);
371        if (idx != std::string::npos)
372        {
373            *range_start_len = idx - start_pos;
374            std::string start_str = arg_str.substr (start_pos, *range_start_len);
375            if (idx + len < arg_str.length())
376            {
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                {
382                    is_range_expression = true;
383                    //*range_start = start_str;
384                    //*range_end = end_str;
385                }
386            }
387        }
388    }
389
390    if (!is_range_expression)
391    {
392        *range_start_len = 0;
393        *range_end_pos = 0;
394    }
395
396    return is_range_expression;
397}
398