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