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/Target/Target.h"
15 #include "lldb/Utility/Args.h"
16 #include "lldb/Utility/StreamString.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/ADT/StringRef.h"
22 using namespace lldb_private
;
24 // class BreakpointIDList
26 BreakpointIDList::BreakpointIDList() : m_breakpoint_ids() {}
28 BreakpointIDList::~BreakpointIDList() = default;
30 size_t BreakpointIDList::GetSize() const { return m_breakpoint_ids
.size(); }
32 BreakpointID
BreakpointIDList::GetBreakpointIDAtIndex(size_t index
) const {
33 return ((index
< m_breakpoint_ids
.size()) ? m_breakpoint_ids
[index
]
37 bool BreakpointIDList::RemoveBreakpointIDAtIndex(size_t index
) {
38 if (index
>= m_breakpoint_ids
.size())
41 m_breakpoint_ids
.erase(m_breakpoint_ids
.begin() + index
);
45 void BreakpointIDList::Clear() { m_breakpoint_ids
.clear(); }
47 bool BreakpointIDList::AddBreakpointID(BreakpointID bp_id
) {
48 m_breakpoint_ids
.push_back(bp_id
);
50 return true; // We don't do any verification in this function, so always
54 bool BreakpointIDList::Contains(BreakpointID bp_id
) const {
55 return llvm::is_contained(m_breakpoint_ids
, bp_id
);
58 // This function takes OLD_ARGS, which is usually the result of breaking the
59 // command string arguments into
60 // an array of space-separated strings, and searches through the arguments for
61 // any breakpoint ID range specifiers.
62 // Any string in the array that is not part of an ID range specifier is copied
63 // directly into NEW_ARGS. If any
64 // ID range specifiers are found, the range is interpreted and a list of
65 // canonical breakpoint IDs corresponding to
66 // all the current breakpoints and locations in the range are added to
67 // NEW_ARGS. When this function is done,
68 // NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced
69 // by the members of the range.
71 llvm::Error
BreakpointIDList::FindAndReplaceIDRanges(
72 Args
&old_args
, Target
*target
, bool allow_locations
,
73 BreakpointName::Permissions ::PermissionKinds purpose
, Args
&new_args
) {
74 llvm::StringRef range_from
;
75 llvm::StringRef range_to
;
76 llvm::StringRef current_arg
;
77 std::set
<std::string
> names_found
;
79 for (size_t i
= 0; i
< old_args
.size(); ++i
) {
80 bool is_range
= false;
82 current_arg
= old_args
[i
].ref();
83 if (!allow_locations
&& current_arg
.contains('.')) {
85 return llvm::createStringError(
86 llvm::inconvertibleErrorCode(),
87 "Breakpoint locations not allowed, saw location: %s.",
88 current_arg
.str().c_str());
93 std::tie(range_from
, range_to
) =
94 BreakpointIDList::SplitIDRangeExpression(current_arg
);
95 if (!range_from
.empty() && !range_to
.empty()) {
97 } else if (BreakpointID::StringIsBreakpointName(current_arg
, error
)) {
98 if (!error
.Success()) {
100 return llvm::createStringError(llvm::inconvertibleErrorCode(),
103 names_found
.insert(std::string(current_arg
));
104 } else if ((i
+ 2 < old_args
.size()) &&
105 BreakpointID::IsRangeIdentifier(old_args
[i
+ 1].ref()) &&
106 BreakpointID::IsValidIDExpression(current_arg
) &&
107 BreakpointID::IsValidIDExpression(old_args
[i
+ 2].ref())) {
108 range_from
= current_arg
;
109 range_to
= old_args
[i
+ 2].ref();
113 // See if user has specified id.*
114 llvm::StringRef tmp_str
= old_args
[i
].ref();
115 auto [prefix
, suffix
] = tmp_str
.split('.');
116 if (suffix
== "*" && BreakpointID::IsValidIDExpression(prefix
)) {
118 BreakpointSP breakpoint_sp
;
119 auto bp_id
= BreakpointID::ParseCanonicalReference(prefix
);
121 breakpoint_sp
= target
->GetBreakpointByID(bp_id
->GetBreakpointID());
122 if (!breakpoint_sp
) {
124 return llvm::createStringError(llvm::inconvertibleErrorCode(),
125 "'%d' is not a valid breakpoint ID.\n",
126 bp_id
->GetBreakpointID());
128 const size_t num_locations
= breakpoint_sp
->GetNumLocations();
129 for (size_t j
= 0; j
< num_locations
; ++j
) {
130 BreakpointLocation
*bp_loc
=
131 breakpoint_sp
->GetLocationAtIndex(j
).get();
132 StreamString canonical_id_str
;
133 BreakpointID::GetCanonicalReference(
134 &canonical_id_str
, bp_id
->GetBreakpointID(), bp_loc
->GetID());
135 new_args
.AppendArgument(canonical_id_str
.GetString());
141 new_args
.AppendArgument(current_arg
);
145 auto start_bp
= BreakpointID::ParseCanonicalReference(range_from
);
146 auto end_bp
= BreakpointID::ParseCanonicalReference(range_to
);
149 !target
->GetBreakpointByID(start_bp
->GetBreakpointID())) {
151 return llvm::createStringError(llvm::inconvertibleErrorCode(),
152 "'%s' is not a valid breakpoint ID.\n",
153 range_from
.str().c_str());
157 !target
->GetBreakpointByID(end_bp
->GetBreakpointID())) {
159 return llvm::createStringError(llvm::inconvertibleErrorCode(),
160 "'%s' is not a valid breakpoint ID.\n",
161 range_to
.str().c_str());
163 break_id_t start_bp_id
= start_bp
->GetBreakpointID();
164 break_id_t start_loc_id
= start_bp
->GetLocationID();
165 break_id_t end_bp_id
= end_bp
->GetBreakpointID();
166 break_id_t end_loc_id
= end_bp
->GetLocationID();
167 if (((start_loc_id
== LLDB_INVALID_BREAK_ID
) &&
168 (end_loc_id
!= LLDB_INVALID_BREAK_ID
)) ||
169 ((start_loc_id
!= LLDB_INVALID_BREAK_ID
) &&
170 (end_loc_id
== LLDB_INVALID_BREAK_ID
))) {
172 return llvm::createStringError(llvm::inconvertibleErrorCode(),
173 "Invalid breakpoint id range: Either "
174 "both ends of range must specify"
175 " a breakpoint location, or neither can "
176 "specify a breakpoint location.");
179 // We have valid range starting & ending breakpoint IDs. Go through all
180 // the breakpoints in the target and find all the breakpoints that fit into
181 // this range, and add them to new_args.
183 // Next check to see if we have location id's. If so, make sure the
184 // start_bp_id and end_bp_id are for the same breakpoint; otherwise we have
185 // an illegal range: breakpoint id ranges that specify bp locations are NOT
186 // allowed to cross major bp id numbers.
188 if ((start_loc_id
!= LLDB_INVALID_BREAK_ID
) ||
189 (end_loc_id
!= LLDB_INVALID_BREAK_ID
)) {
190 if (start_bp_id
!= end_bp_id
) {
192 return llvm::createStringError(
193 llvm::inconvertibleErrorCode(),
194 "Invalid range: Ranges that specify particular breakpoint "
196 " must be within the same major breakpoint; you specified two"
197 " different major breakpoints, %d and %d.\n",
198 start_bp_id
, end_bp_id
);
202 const BreakpointList
&breakpoints
= target
->GetBreakpointList();
203 const size_t num_breakpoints
= breakpoints
.GetSize();
204 for (size_t j
= 0; j
< num_breakpoints
; ++j
) {
205 Breakpoint
*breakpoint
= breakpoints
.GetBreakpointAtIndex(j
).get();
206 break_id_t cur_bp_id
= breakpoint
->GetID();
208 if ((cur_bp_id
< start_bp_id
) || (cur_bp_id
> end_bp_id
))
211 const size_t num_locations
= breakpoint
->GetNumLocations();
213 if ((cur_bp_id
== start_bp_id
) &&
214 (start_loc_id
!= LLDB_INVALID_BREAK_ID
)) {
215 for (size_t k
= 0; k
< num_locations
; ++k
) {
216 BreakpointLocation
*bp_loc
= breakpoint
->GetLocationAtIndex(k
).get();
217 if ((bp_loc
->GetID() >= start_loc_id
) &&
218 (bp_loc
->GetID() <= end_loc_id
)) {
219 StreamString canonical_id_str
;
220 BreakpointID::GetCanonicalReference(&canonical_id_str
, cur_bp_id
,
222 new_args
.AppendArgument(canonical_id_str
.GetString());
225 } else if ((cur_bp_id
== end_bp_id
) &&
226 (end_loc_id
!= LLDB_INVALID_BREAK_ID
)) {
227 for (size_t k
= 0; k
< num_locations
; ++k
) {
228 BreakpointLocation
*bp_loc
= breakpoint
->GetLocationAtIndex(k
).get();
229 if (bp_loc
->GetID() <= end_loc_id
) {
230 StreamString canonical_id_str
;
231 BreakpointID::GetCanonicalReference(&canonical_id_str
, cur_bp_id
,
233 new_args
.AppendArgument(canonical_id_str
.GetString());
237 StreamString canonical_id_str
;
238 BreakpointID::GetCanonicalReference(&canonical_id_str
, cur_bp_id
,
239 LLDB_INVALID_BREAK_ID
);
240 new_args
.AppendArgument(canonical_id_str
.GetString());
245 // Okay, now see if we found any names, and if we did, add them:
246 if (target
&& !names_found
.empty()) {
248 // Remove any names that aren't visible for this purpose:
249 auto iter
= names_found
.begin();
250 while (iter
!= names_found
.end()) {
251 BreakpointName
*bp_name
= target
->FindBreakpointName(ConstString(*iter
),
254 if (bp_name
&& !bp_name
->GetPermission(purpose
))
255 iter
= names_found
.erase(iter
);
260 if (!names_found
.empty()) {
261 for (BreakpointSP bkpt_sp
: target
->GetBreakpointList().Breakpoints()) {
262 for (const std::string
&name
: names_found
) {
263 if (bkpt_sp
->MatchesName(name
.c_str())) {
264 StreamString canonical_id_str
;
265 BreakpointID::GetCanonicalReference(
266 &canonical_id_str
, bkpt_sp
->GetID(), LLDB_INVALID_BREAK_ID
);
267 new_args
.AppendArgument(canonical_id_str
.GetString());
273 return llvm::Error::success();
276 std::pair
<llvm::StringRef
, llvm::StringRef
>
277 BreakpointIDList::SplitIDRangeExpression(llvm::StringRef in_string
) {
278 for (auto specifier_str
: BreakpointID::GetRangeSpecifiers()) {
279 size_t idx
= in_string
.find(specifier_str
);
280 if (idx
== llvm::StringRef::npos
)
282 llvm::StringRef right1
= in_string
.drop_front(idx
);
284 llvm::StringRef from
= in_string
.take_front(idx
);
285 llvm::StringRef to
= right1
.drop_front(specifier_str
.size());
287 if (BreakpointID::IsValidIDExpression(from
) &&
288 BreakpointID::IsValidIDExpression(to
)) {
289 return std::make_pair(from
, to
);
293 return std::pair
<llvm::StringRef
, llvm::StringRef
>();