1 //===-- BreakpointIDList.cpp ----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/lldb-enumerations.h"
10 #include "lldb/Breakpoint/BreakpointIDList.h"
12 #include "lldb/Breakpoint/Breakpoint.h"
13 #include "lldb/Breakpoint/BreakpointLocation.h"
14 #include "lldb/Interpreter/CommandReturnObject.h"
15 #include "lldb/Target/Target.h"
16 #include "lldb/Utility/Args.h"
19 using namespace lldb_private
;
21 // class BreakpointIDList
23 BreakpointIDList::BreakpointIDList()
24 : m_invalid_id(LLDB_INVALID_BREAK_ID
, LLDB_INVALID_BREAK_ID
) {}
26 BreakpointIDList::~BreakpointIDList() = default;
28 size_t BreakpointIDList::GetSize() const { return m_breakpoint_ids
.size(); }
31 BreakpointIDList::GetBreakpointIDAtIndex(size_t index
) const {
32 return ((index
< m_breakpoint_ids
.size()) ? m_breakpoint_ids
[index
]
36 bool BreakpointIDList::RemoveBreakpointIDAtIndex(size_t index
) {
37 if (index
>= m_breakpoint_ids
.size())
40 m_breakpoint_ids
.erase(m_breakpoint_ids
.begin() + index
);
44 void BreakpointIDList::Clear() { m_breakpoint_ids
.clear(); }
46 bool BreakpointIDList::AddBreakpointID(BreakpointID bp_id
) {
47 m_breakpoint_ids
.push_back(bp_id
);
49 return true; // We don't do any verification in this function, so always
53 bool BreakpointIDList::AddBreakpointID(const char *bp_id_str
) {
54 auto bp_id
= BreakpointID::ParseCanonicalReference(bp_id_str
);
58 m_breakpoint_ids
.push_back(*bp_id
);
62 bool BreakpointIDList::FindBreakpointID(BreakpointID
&bp_id
,
63 size_t *position
) const {
64 for (size_t i
= 0; i
< m_breakpoint_ids
.size(); ++i
) {
65 BreakpointID tmp_id
= m_breakpoint_ids
[i
];
66 if (tmp_id
.GetBreakpointID() == bp_id
.GetBreakpointID() &&
67 tmp_id
.GetLocationID() == bp_id
.GetLocationID()) {
76 bool BreakpointIDList::FindBreakpointID(const char *bp_id_str
,
77 size_t *position
) const {
78 auto bp_id
= BreakpointID::ParseCanonicalReference(bp_id_str
);
82 return FindBreakpointID(*bp_id
, position
);
85 void BreakpointIDList::InsertStringArray(
86 llvm::ArrayRef
<const char *> string_array
, CommandReturnObject
&result
) {
87 if(string_array
.empty())
90 for (const char *str
: string_array
) {
91 auto bp_id
= BreakpointID::ParseCanonicalReference(str
);
93 m_breakpoint_ids
.push_back(*bp_id
);
95 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
98 // This function takes OLD_ARGS, which is usually the result of breaking the
99 // command string arguments into
100 // an array of space-separated strings, and searches through the arguments for
101 // any breakpoint ID range specifiers.
102 // Any string in the array that is not part of an ID range specifier is copied
103 // directly into NEW_ARGS. If any
104 // ID range specifiers are found, the range is interpreted and a list of
105 // canonical breakpoint IDs corresponding to
106 // all the current breakpoints and locations in the range are added to
107 // NEW_ARGS. When this function is done,
108 // NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced
109 // by the members of the range.
111 void BreakpointIDList::FindAndReplaceIDRanges(Args
&old_args
, Target
*target
,
112 bool allow_locations
,
113 BreakpointName::Permissions
114 ::PermissionKinds purpose
,
115 CommandReturnObject
&result
,
117 llvm::StringRef range_from
;
118 llvm::StringRef range_to
;
119 llvm::StringRef current_arg
;
120 std::set
<std::string
> names_found
;
122 for (size_t i
= 0; i
< old_args
.size(); ++i
) {
123 bool is_range
= false;
125 current_arg
= old_args
[i
].ref();
126 if (!allow_locations
&& current_arg
.contains('.')) {
127 result
.AppendErrorWithFormat(
128 "Breakpoint locations not allowed, saw location: %s.",
129 current_arg
.str().c_str());
136 std::tie(range_from
, range_to
) =
137 BreakpointIDList::SplitIDRangeExpression(current_arg
);
138 if (!range_from
.empty() && !range_to
.empty()) {
140 } else if (BreakpointID::StringIsBreakpointName(current_arg
, error
)) {
141 if (!error
.Success()) {
143 result
.AppendError(error
.AsCString());
146 names_found
.insert(std::string(current_arg
));
147 } else if ((i
+ 2 < old_args
.size()) &&
148 BreakpointID::IsRangeIdentifier(old_args
[i
+ 1].ref()) &&
149 BreakpointID::IsValidIDExpression(current_arg
) &&
150 BreakpointID::IsValidIDExpression(old_args
[i
+ 2].ref())) {
151 range_from
= current_arg
;
152 range_to
= old_args
[i
+ 2].ref();
156 // See if user has specified id.*
157 llvm::StringRef tmp_str
= old_args
[i
].ref();
158 size_t pos
= tmp_str
.find('.');
159 if (pos
!= llvm::StringRef::npos
) {
160 llvm::StringRef bp_id_str
= tmp_str
.substr(0, pos
);
161 if (BreakpointID::IsValidIDExpression(bp_id_str
) &&
162 tmp_str
[pos
+ 1] == '*' && tmp_str
.size() == (pos
+ 2)) {
164 BreakpointSP breakpoint_sp
;
165 auto bp_id
= BreakpointID::ParseCanonicalReference(bp_id_str
);
167 breakpoint_sp
= target
->GetBreakpointByID(bp_id
->GetBreakpointID());
168 if (!breakpoint_sp
) {
170 result
.AppendErrorWithFormat("'%d' is not a valid breakpoint ID.\n",
171 bp_id
->GetBreakpointID());
174 const size_t num_locations
= breakpoint_sp
->GetNumLocations();
175 for (size_t j
= 0; j
< num_locations
; ++j
) {
176 BreakpointLocation
*bp_loc
=
177 breakpoint_sp
->GetLocationAtIndex(j
).get();
178 StreamString canonical_id_str
;
179 BreakpointID::GetCanonicalReference(
180 &canonical_id_str
, bp_id
->GetBreakpointID(), bp_loc
->GetID());
181 new_args
.AppendArgument(canonical_id_str
.GetString());
188 new_args
.AppendArgument(current_arg
);
192 auto start_bp
= BreakpointID::ParseCanonicalReference(range_from
);
193 auto end_bp
= BreakpointID::ParseCanonicalReference(range_to
);
196 !target
->GetBreakpointByID(start_bp
->GetBreakpointID())) {
198 result
.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n",
199 range_from
.str().c_str());
204 !target
->GetBreakpointByID(end_bp
->GetBreakpointID())) {
206 result
.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n",
207 range_to
.str().c_str());
210 break_id_t start_bp_id
= start_bp
->GetBreakpointID();
211 break_id_t start_loc_id
= start_bp
->GetLocationID();
212 break_id_t end_bp_id
= end_bp
->GetBreakpointID();
213 break_id_t end_loc_id
= end_bp
->GetLocationID();
214 if (((start_loc_id
== LLDB_INVALID_BREAK_ID
) &&
215 (end_loc_id
!= LLDB_INVALID_BREAK_ID
)) ||
216 ((start_loc_id
!= LLDB_INVALID_BREAK_ID
) &&
217 (end_loc_id
== LLDB_INVALID_BREAK_ID
))) {
219 result
.AppendError("Invalid breakpoint id range: Either "
220 "both ends of range must specify"
221 " a breakpoint location, or neither can "
222 "specify a breakpoint location.");
226 // We have valid range starting & ending breakpoint IDs. Go through all
227 // the breakpoints in the target and find all the breakpoints that fit into
228 // this range, and add them to new_args.
230 // Next check to see if we have location id's. If so, make sure the
231 // start_bp_id and end_bp_id are for the same breakpoint; otherwise we have
232 // an illegal range: breakpoint id ranges that specify bp locations are NOT
233 // allowed to cross major bp id numbers.
235 if ((start_loc_id
!= LLDB_INVALID_BREAK_ID
) ||
236 (end_loc_id
!= LLDB_INVALID_BREAK_ID
)) {
237 if (start_bp_id
!= end_bp_id
) {
239 result
.AppendErrorWithFormat(
240 "Invalid range: Ranges that specify particular breakpoint "
242 " must be within the same major breakpoint; you specified two"
243 " different major breakpoints, %d and %d.\n",
244 start_bp_id
, end_bp_id
);
249 const BreakpointList
&breakpoints
= target
->GetBreakpointList();
250 const size_t num_breakpoints
= breakpoints
.GetSize();
251 for (size_t j
= 0; j
< num_breakpoints
; ++j
) {
252 Breakpoint
*breakpoint
= breakpoints
.GetBreakpointAtIndex(j
).get();
253 break_id_t cur_bp_id
= breakpoint
->GetID();
255 if ((cur_bp_id
< start_bp_id
) || (cur_bp_id
> end_bp_id
))
258 const size_t num_locations
= breakpoint
->GetNumLocations();
260 if ((cur_bp_id
== start_bp_id
) &&
261 (start_loc_id
!= LLDB_INVALID_BREAK_ID
)) {
262 for (size_t k
= 0; k
< num_locations
; ++k
) {
263 BreakpointLocation
*bp_loc
= breakpoint
->GetLocationAtIndex(k
).get();
264 if ((bp_loc
->GetID() >= start_loc_id
) &&
265 (bp_loc
->GetID() <= end_loc_id
)) {
266 StreamString canonical_id_str
;
267 BreakpointID::GetCanonicalReference(&canonical_id_str
, cur_bp_id
,
269 new_args
.AppendArgument(canonical_id_str
.GetString());
272 } else if ((cur_bp_id
== end_bp_id
) &&
273 (end_loc_id
!= LLDB_INVALID_BREAK_ID
)) {
274 for (size_t k
= 0; k
< num_locations
; ++k
) {
275 BreakpointLocation
*bp_loc
= breakpoint
->GetLocationAtIndex(k
).get();
276 if (bp_loc
->GetID() <= end_loc_id
) {
277 StreamString canonical_id_str
;
278 BreakpointID::GetCanonicalReference(&canonical_id_str
, cur_bp_id
,
280 new_args
.AppendArgument(canonical_id_str
.GetString());
284 StreamString canonical_id_str
;
285 BreakpointID::GetCanonicalReference(&canonical_id_str
, cur_bp_id
,
286 LLDB_INVALID_BREAK_ID
);
287 new_args
.AppendArgument(canonical_id_str
.GetString());
292 // Okay, now see if we found any names, and if we did, add them:
293 if (target
&& !names_found
.empty()) {
295 // Remove any names that aren't visible for this purpose:
296 auto iter
= names_found
.begin();
297 while (iter
!= names_found
.end()) {
298 BreakpointName
*bp_name
= target
->FindBreakpointName(ConstString(*iter
),
301 if (bp_name
&& !bp_name
->GetPermission(purpose
))
302 iter
= names_found
.erase(iter
);
307 if (!names_found
.empty()) {
308 for (BreakpointSP bkpt_sp
: target
->GetBreakpointList().Breakpoints()) {
309 for (std::string name
: names_found
) {
310 if (bkpt_sp
->MatchesName(name
.c_str())) {
311 StreamString canonical_id_str
;
312 BreakpointID::GetCanonicalReference(
313 &canonical_id_str
, bkpt_sp
->GetID(), LLDB_INVALID_BREAK_ID
);
314 new_args
.AppendArgument(canonical_id_str
.GetString());
321 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
324 std::pair
<llvm::StringRef
, llvm::StringRef
>
325 BreakpointIDList::SplitIDRangeExpression(llvm::StringRef in_string
) {
326 for (auto specifier_str
: BreakpointID::GetRangeSpecifiers()) {
327 size_t idx
= in_string
.find(specifier_str
);
328 if (idx
== llvm::StringRef::npos
)
330 llvm::StringRef right1
= in_string
.drop_front(idx
);
332 llvm::StringRef from
= in_string
.take_front(idx
);
333 llvm::StringRef to
= right1
.drop_front(specifier_str
.size());
335 if (BreakpointID::IsValidIDExpression(from
) &&
336 BreakpointID::IsValidIDExpression(to
)) {
337 return std::make_pair(from
, to
);
341 return std::pair
<llvm::StringRef
, llvm::StringRef
>();