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