1 //===-- BreakpointIDList.cpp ----------------------------------------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "lldb/lldb-enumerations.h" 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/Target/Target.h" 16 #include "lldb/Utility/Args.h" 17 18 using namespace lldb; 19 using namespace lldb_private; 20 21 // class BreakpointIDList 22 23 BreakpointIDList::BreakpointIDList() : m_breakpoint_ids() {} 24 25 BreakpointIDList::~BreakpointIDList() = default; 26 27 size_t BreakpointIDList::GetSize() const { return m_breakpoint_ids.size(); } 28 29 BreakpointID BreakpointIDList::GetBreakpointIDAtIndex(size_t index) const { 30 return ((index < m_breakpoint_ids.size()) ? m_breakpoint_ids[index] 31 : BreakpointID()); 32 } 33 34 bool BreakpointIDList::RemoveBreakpointIDAtIndex(size_t index) { 35 if (index >= m_breakpoint_ids.size()) 36 return false; 37 38 m_breakpoint_ids.erase(m_breakpoint_ids.begin() + index); 39 return true; 40 } 41 42 void BreakpointIDList::Clear() { m_breakpoint_ids.clear(); } 43 44 bool BreakpointIDList::AddBreakpointID(BreakpointID bp_id) { 45 m_breakpoint_ids.push_back(bp_id); 46 47 return true; // We don't do any verification in this function, so always 48 // return true. 49 } 50 51 bool BreakpointIDList::AddBreakpointID(const char *bp_id_str) { 52 auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str); 53 if (!bp_id) 54 return false; 55 56 m_breakpoint_ids.push_back(*bp_id); 57 return true; 58 } 59 60 bool BreakpointIDList::FindBreakpointID(BreakpointID &bp_id, 61 size_t *position) const { 62 for (size_t i = 0; i < m_breakpoint_ids.size(); ++i) { 63 BreakpointID tmp_id = m_breakpoint_ids[i]; 64 if (tmp_id.GetBreakpointID() == bp_id.GetBreakpointID() && 65 tmp_id.GetLocationID() == bp_id.GetLocationID()) { 66 *position = i; 67 return true; 68 } 69 } 70 71 return false; 72 } 73 74 bool BreakpointIDList::FindBreakpointID(const char *bp_id_str, 75 size_t *position) const { 76 auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str); 77 if (!bp_id) 78 return false; 79 80 return FindBreakpointID(*bp_id, position); 81 } 82 83 // This function takes OLD_ARGS, which is usually the result of breaking the 84 // command string arguments into 85 // an array of space-separated strings, and searches through the arguments for 86 // any breakpoint ID range specifiers. 87 // Any string in the array that is not part of an ID range specifier is copied 88 // directly into NEW_ARGS. If any 89 // ID range specifiers are found, the range is interpreted and a list of 90 // canonical breakpoint IDs corresponding to 91 // all the current breakpoints and locations in the range are added to 92 // NEW_ARGS. When this function is done, 93 // NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced 94 // by the members of the range. 95 96 void BreakpointIDList::FindAndReplaceIDRanges(Args &old_args, Target *target, 97 bool allow_locations, 98 BreakpointName::Permissions 99 ::PermissionKinds purpose, 100 CommandReturnObject &result, 101 Args &new_args) { 102 llvm::StringRef range_from; 103 llvm::StringRef range_to; 104 llvm::StringRef current_arg; 105 std::set<std::string> names_found; 106 107 for (size_t i = 0; i < old_args.size(); ++i) { 108 bool is_range = false; 109 110 current_arg = old_args[i].ref(); 111 if (!allow_locations && current_arg.contains('.')) { 112 result.AppendErrorWithFormat( 113 "Breakpoint locations not allowed, saw location: %s.", 114 current_arg.str().c_str()); 115 new_args.Clear(); 116 return; 117 } 118 119 Status error; 120 121 std::tie(range_from, range_to) = 122 BreakpointIDList::SplitIDRangeExpression(current_arg); 123 if (!range_from.empty() && !range_to.empty()) { 124 is_range = true; 125 } else if (BreakpointID::StringIsBreakpointName(current_arg, error)) { 126 if (!error.Success()) { 127 new_args.Clear(); 128 result.AppendError(error.AsCString()); 129 return; 130 } else 131 names_found.insert(std::string(current_arg)); 132 } else if ((i + 2 < old_args.size()) && 133 BreakpointID::IsRangeIdentifier(old_args[i + 1].ref()) && 134 BreakpointID::IsValidIDExpression(current_arg) && 135 BreakpointID::IsValidIDExpression(old_args[i + 2].ref())) { 136 range_from = current_arg; 137 range_to = old_args[i + 2].ref(); 138 is_range = true; 139 i = i + 2; 140 } else { 141 // See if user has specified id.* 142 llvm::StringRef tmp_str = old_args[i].ref(); 143 size_t pos = tmp_str.find('.'); 144 if (pos != llvm::StringRef::npos) { 145 llvm::StringRef bp_id_str = tmp_str.substr(0, pos); 146 if (BreakpointID::IsValidIDExpression(bp_id_str) && 147 tmp_str[pos + 1] == '*' && tmp_str.size() == (pos + 2)) { 148 149 BreakpointSP breakpoint_sp; 150 auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str); 151 if (bp_id) 152 breakpoint_sp = target->GetBreakpointByID(bp_id->GetBreakpointID()); 153 if (!breakpoint_sp) { 154 new_args.Clear(); 155 result.AppendErrorWithFormat("'%d' is not a valid breakpoint ID.\n", 156 bp_id->GetBreakpointID()); 157 return; 158 } 159 const size_t num_locations = breakpoint_sp->GetNumLocations(); 160 for (size_t j = 0; j < num_locations; ++j) { 161 BreakpointLocation *bp_loc = 162 breakpoint_sp->GetLocationAtIndex(j).get(); 163 StreamString canonical_id_str; 164 BreakpointID::GetCanonicalReference( 165 &canonical_id_str, bp_id->GetBreakpointID(), bp_loc->GetID()); 166 new_args.AppendArgument(canonical_id_str.GetString()); 167 } 168 } 169 } 170 } 171 172 if (!is_range) { 173 new_args.AppendArgument(current_arg); 174 continue; 175 } 176 177 auto start_bp = BreakpointID::ParseCanonicalReference(range_from); 178 auto end_bp = BreakpointID::ParseCanonicalReference(range_to); 179 180 if (!start_bp || 181 !target->GetBreakpointByID(start_bp->GetBreakpointID())) { 182 new_args.Clear(); 183 result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n", 184 range_from.str().c_str()); 185 return; 186 } 187 188 if (!end_bp || 189 !target->GetBreakpointByID(end_bp->GetBreakpointID())) { 190 new_args.Clear(); 191 result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n", 192 range_to.str().c_str()); 193 return; 194 } 195 break_id_t start_bp_id = start_bp->GetBreakpointID(); 196 break_id_t start_loc_id = start_bp->GetLocationID(); 197 break_id_t end_bp_id = end_bp->GetBreakpointID(); 198 break_id_t end_loc_id = end_bp->GetLocationID(); 199 if (((start_loc_id == LLDB_INVALID_BREAK_ID) && 200 (end_loc_id != LLDB_INVALID_BREAK_ID)) || 201 ((start_loc_id != LLDB_INVALID_BREAK_ID) && 202 (end_loc_id == LLDB_INVALID_BREAK_ID))) { 203 new_args.Clear(); 204 result.AppendError("Invalid breakpoint id range: Either " 205 "both ends of range must specify" 206 " a breakpoint location, or neither can " 207 "specify a breakpoint location."); 208 return; 209 } 210 211 // We have valid range starting & ending breakpoint IDs. Go through all 212 // the breakpoints in the target and find all the breakpoints that fit into 213 // this range, and add them to new_args. 214 215 // Next check to see if we have location id's. If so, make sure the 216 // start_bp_id and end_bp_id are for the same breakpoint; otherwise we have 217 // an illegal range: breakpoint id ranges that specify bp locations are NOT 218 // allowed to cross major bp id numbers. 219 220 if ((start_loc_id != LLDB_INVALID_BREAK_ID) || 221 (end_loc_id != LLDB_INVALID_BREAK_ID)) { 222 if (start_bp_id != end_bp_id) { 223 new_args.Clear(); 224 result.AppendErrorWithFormat( 225 "Invalid range: Ranges that specify particular breakpoint " 226 "locations" 227 " must be within the same major breakpoint; you specified two" 228 " different major breakpoints, %d and %d.\n", 229 start_bp_id, end_bp_id); 230 return; 231 } 232 } 233 234 const BreakpointList &breakpoints = target->GetBreakpointList(); 235 const size_t num_breakpoints = breakpoints.GetSize(); 236 for (size_t j = 0; j < num_breakpoints; ++j) { 237 Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(j).get(); 238 break_id_t cur_bp_id = breakpoint->GetID(); 239 240 if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id)) 241 continue; 242 243 const size_t num_locations = breakpoint->GetNumLocations(); 244 245 if ((cur_bp_id == start_bp_id) && 246 (start_loc_id != LLDB_INVALID_BREAK_ID)) { 247 for (size_t k = 0; k < num_locations; ++k) { 248 BreakpointLocation *bp_loc = breakpoint->GetLocationAtIndex(k).get(); 249 if ((bp_loc->GetID() >= start_loc_id) && 250 (bp_loc->GetID() <= end_loc_id)) { 251 StreamString canonical_id_str; 252 BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id, 253 bp_loc->GetID()); 254 new_args.AppendArgument(canonical_id_str.GetString()); 255 } 256 } 257 } else if ((cur_bp_id == end_bp_id) && 258 (end_loc_id != LLDB_INVALID_BREAK_ID)) { 259 for (size_t k = 0; k < num_locations; ++k) { 260 BreakpointLocation *bp_loc = breakpoint->GetLocationAtIndex(k).get(); 261 if (bp_loc->GetID() <= end_loc_id) { 262 StreamString canonical_id_str; 263 BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id, 264 bp_loc->GetID()); 265 new_args.AppendArgument(canonical_id_str.GetString()); 266 } 267 } 268 } else { 269 StreamString canonical_id_str; 270 BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id, 271 LLDB_INVALID_BREAK_ID); 272 new_args.AppendArgument(canonical_id_str.GetString()); 273 } 274 } 275 } 276 277 // Okay, now see if we found any names, and if we did, add them: 278 if (target && !names_found.empty()) { 279 Status error; 280 // Remove any names that aren't visible for this purpose: 281 auto iter = names_found.begin(); 282 while (iter != names_found.end()) { 283 BreakpointName *bp_name = target->FindBreakpointName(ConstString(*iter), 284 true, 285 error); 286 if (bp_name && !bp_name->GetPermission(purpose)) 287 iter = names_found.erase(iter); 288 else 289 iter++; 290 } 291 292 if (!names_found.empty()) { 293 for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) { 294 for (std::string name : names_found) { 295 if (bkpt_sp->MatchesName(name.c_str())) { 296 StreamString canonical_id_str; 297 BreakpointID::GetCanonicalReference( 298 &canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID); 299 new_args.AppendArgument(canonical_id_str.GetString()); 300 } 301 } 302 } 303 } 304 } 305 306 result.SetStatus(eReturnStatusSuccessFinishNoResult); 307 } 308 309 std::pair<llvm::StringRef, llvm::StringRef> 310 BreakpointIDList::SplitIDRangeExpression(llvm::StringRef in_string) { 311 for (auto specifier_str : BreakpointID::GetRangeSpecifiers()) { 312 size_t idx = in_string.find(specifier_str); 313 if (idx == llvm::StringRef::npos) 314 continue; 315 llvm::StringRef right1 = in_string.drop_front(idx); 316 317 llvm::StringRef from = in_string.take_front(idx); 318 llvm::StringRef to = right1.drop_front(specifier_str.size()); 319 320 if (BreakpointID::IsValidIDExpression(from) && 321 BreakpointID::IsValidIDExpression(to)) { 322 return std::make_pair(from, to); 323 } 324 } 325 326 return std::pair<llvm::StringRef, llvm::StringRef>(); 327 } 328