| //===-- BreakpointIDList.cpp ------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // C Includes |
| // C++ Includes |
| // Other libraries and framework includes |
| // Project includes |
| #include "lldb/Breakpoint/BreakpointIDList.h" |
| |
| #include "lldb/Breakpoint/Breakpoint.h" |
| #include "lldb/Breakpoint/BreakpointLocation.h" |
| #include "lldb/Interpreter/Args.h" |
| #include "lldb/Interpreter/CommandReturnObject.h" |
| #include "lldb/Target/Target.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| //---------------------------------------------------------------------- |
| // class BreakpointIDList |
| //---------------------------------------------------------------------- |
| |
| BreakpointIDList::BreakpointIDList() |
| : m_invalid_id(LLDB_INVALID_BREAK_ID, LLDB_INVALID_BREAK_ID) {} |
| |
| BreakpointIDList::~BreakpointIDList() = default; |
| |
| size_t BreakpointIDList::GetSize() { return m_breakpoint_ids.size(); } |
| |
| BreakpointID &BreakpointIDList::GetBreakpointIDAtIndex(size_t index) { |
| return ((index < m_breakpoint_ids.size()) ? m_breakpoint_ids[index] |
| : m_invalid_id); |
| } |
| |
| bool BreakpointIDList::RemoveBreakpointIDAtIndex(size_t index) { |
| if (index >= m_breakpoint_ids.size()) |
| return false; |
| |
| m_breakpoint_ids.erase(m_breakpoint_ids.begin() + index); |
| return true; |
| } |
| |
| void BreakpointIDList::Clear() { m_breakpoint_ids.clear(); } |
| |
| bool BreakpointIDList::AddBreakpointID(BreakpointID bp_id) { |
| m_breakpoint_ids.push_back(bp_id); |
| |
| return true; // We don't do any verification in this function, so always |
| // return true. |
| } |
| |
| bool BreakpointIDList::AddBreakpointID(const char *bp_id_str) { |
| BreakpointID temp_bp_id; |
| break_id_t bp_id; |
| break_id_t loc_id; |
| |
| bool success = |
| BreakpointID::ParseCanonicalReference(bp_id_str, &bp_id, &loc_id); |
| |
| if (success) { |
| temp_bp_id.SetID(bp_id, loc_id); |
| m_breakpoint_ids.push_back(temp_bp_id); |
| } |
| |
| return success; |
| } |
| |
| bool BreakpointIDList::FindBreakpointID(BreakpointID &bp_id, size_t *position) { |
| for (size_t i = 0; i < m_breakpoint_ids.size(); ++i) { |
| BreakpointID tmp_id = m_breakpoint_ids[i]; |
| if (tmp_id.GetBreakpointID() == bp_id.GetBreakpointID() && |
| tmp_id.GetLocationID() == bp_id.GetLocationID()) { |
| *position = i; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool BreakpointIDList::FindBreakpointID(const char *bp_id_str, |
| size_t *position) { |
| BreakpointID temp_bp_id; |
| break_id_t bp_id; |
| break_id_t loc_id; |
| |
| if (BreakpointID::ParseCanonicalReference(bp_id_str, &bp_id, &loc_id)) { |
| temp_bp_id.SetID(bp_id, loc_id); |
| return FindBreakpointID(temp_bp_id, position); |
| } else |
| return false; |
| } |
| |
| void BreakpointIDList::InsertStringArray(const char **string_array, |
| size_t array_size, |
| CommandReturnObject &result) { |
| if (string_array == nullptr) |
| return; |
| |
| for (uint32_t i = 0; i < array_size; ++i) { |
| break_id_t bp_id; |
| break_id_t loc_id; |
| |
| if (BreakpointID::ParseCanonicalReference(string_array[i], &bp_id, |
| &loc_id)) { |
| if (bp_id != LLDB_INVALID_BREAK_ID) { |
| BreakpointID temp_bp_id(bp_id, loc_id); |
| m_breakpoint_ids.push_back(temp_bp_id); |
| } else { |
| result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n", |
| string_array[i]); |
| result.SetStatus(eReturnStatusFailed); |
| return; |
| } |
| } |
| } |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| } |
| |
| // This function takes OLD_ARGS, which is usually the result of breaking the |
| // command string arguments into |
| // an array of space-separated strings, and searches through the arguments for |
| // any breakpoint ID range specifiers. |
| // Any string in the array that is not part of an ID range specifier is copied |
| // directly into NEW_ARGS. If any |
| // ID range specifiers are found, the range is interpreted and a list of |
| // canonical breakpoint IDs corresponding to |
| // all the current breakpoints and locations in the range are added to |
| // NEW_ARGS. When this function is done, |
| // NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced |
| // by the members of the range. |
| |
| void BreakpointIDList::FindAndReplaceIDRanges(Args &old_args, Target *target, |
| bool allow_locations, |
| CommandReturnObject &result, |
| Args &new_args) { |
| std::string range_start; |
| const char *range_end; |
| const char *current_arg; |
| const size_t num_old_args = old_args.GetArgumentCount(); |
| std::set<std::string> names_found; |
| |
| for (size_t i = 0; i < num_old_args; ++i) { |
| bool is_range = false; |
| |
| current_arg = old_args.GetArgumentAtIndex(i); |
| if (!allow_locations && strchr(current_arg, '.') != nullptr) { |
| result.AppendErrorWithFormat( |
| "Breakpoint locations not allowed, saw location: %s.", current_arg); |
| new_args.Clear(); |
| return; |
| } |
| |
| size_t range_start_len = 0; |
| size_t range_end_pos = 0; |
| Error error; |
| |
| if (BreakpointIDList::StringContainsIDRangeExpression( |
| current_arg, &range_start_len, &range_end_pos)) { |
| is_range = true; |
| range_start.assign(current_arg, range_start_len); |
| range_end = current_arg + range_end_pos; |
| } else if (BreakpointID::StringIsBreakpointName(current_arg, error)) { |
| if (!error.Success()) { |
| new_args.Clear(); |
| result.AppendError(error.AsCString()); |
| result.SetStatus(eReturnStatusFailed); |
| return; |
| } else |
| names_found.insert(current_arg); |
| } else if ((i + 2 < num_old_args) && |
| BreakpointID::IsRangeIdentifier( |
| old_args.GetArgumentAtIndex(i + 1)) && |
| BreakpointID::IsValidIDExpression(current_arg) && |
| BreakpointID::IsValidIDExpression( |
| old_args.GetArgumentAtIndex(i + 2))) { |
| range_start.assign(current_arg); |
| range_end = old_args.GetArgumentAtIndex(i + 2); |
| is_range = true; |
| i = i + 2; |
| } else { |
| // See if user has specified id.* |
| std::string tmp_str = old_args.GetArgumentAtIndex(i); |
| size_t pos = tmp_str.find('.'); |
| if (pos != std::string::npos) { |
| std::string bp_id_str = tmp_str.substr(0, pos); |
| if (BreakpointID::IsValidIDExpression(bp_id_str.c_str()) && |
| tmp_str[pos + 1] == '*' && tmp_str.length() == (pos + 2)) { |
| break_id_t bp_id; |
| break_id_t bp_loc_id; |
| |
| BreakpointID::ParseCanonicalReference(bp_id_str.c_str(), &bp_id, |
| &bp_loc_id); |
| BreakpointSP breakpoint_sp = target->GetBreakpointByID(bp_id); |
| if (!breakpoint_sp) { |
| new_args.Clear(); |
| result.AppendErrorWithFormat("'%d' is not a valid breakpoint ID.\n", |
| bp_id); |
| result.SetStatus(eReturnStatusFailed); |
| return; |
| } |
| const size_t num_locations = breakpoint_sp->GetNumLocations(); |
| for (size_t j = 0; j < num_locations; ++j) { |
| BreakpointLocation *bp_loc = |
| breakpoint_sp->GetLocationAtIndex(j).get(); |
| StreamString canonical_id_str; |
| BreakpointID::GetCanonicalReference(&canonical_id_str, bp_id, |
| bp_loc->GetID()); |
| new_args.AppendArgument(canonical_id_str.GetData()); |
| } |
| } |
| } |
| } |
| |
| if (is_range) { |
| break_id_t start_bp_id; |
| break_id_t end_bp_id; |
| break_id_t start_loc_id; |
| break_id_t end_loc_id; |
| |
| BreakpointID::ParseCanonicalReference(range_start.c_str(), &start_bp_id, |
| &start_loc_id); |
| BreakpointID::ParseCanonicalReference(range_end, &end_bp_id, &end_loc_id); |
| |
| if ((start_bp_id == LLDB_INVALID_BREAK_ID) || |
| (!target->GetBreakpointByID(start_bp_id))) { |
| new_args.Clear(); |
| result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n", |
| range_start.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return; |
| } |
| |
| if ((end_bp_id == LLDB_INVALID_BREAK_ID) || |
| (!target->GetBreakpointByID(end_bp_id))) { |
| new_args.Clear(); |
| result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n", |
| range_end); |
| result.SetStatus(eReturnStatusFailed); |
| return; |
| } |
| |
| if (((start_loc_id == LLDB_INVALID_BREAK_ID) && |
| (end_loc_id != LLDB_INVALID_BREAK_ID)) || |
| ((start_loc_id != LLDB_INVALID_BREAK_ID) && |
| (end_loc_id == LLDB_INVALID_BREAK_ID))) { |
| new_args.Clear(); |
| result.AppendErrorWithFormat("Invalid breakpoint id range: Either " |
| "both ends of range must specify" |
| " a breakpoint location, or neither can " |
| "specify a breakpoint location.\n"); |
| result.SetStatus(eReturnStatusFailed); |
| return; |
| } |
| |
| // We have valid range starting & ending breakpoint IDs. Go through all |
| // the breakpoints in the |
| // target and find all the breakpoints that fit into this range, and add |
| // them to new_args. |
| |
| // Next check to see if we have location id's. If so, make sure the |
| // start_bp_id and end_bp_id are |
| // for the same breakpoint; otherwise we have an illegal range: breakpoint |
| // id ranges that specify |
| // bp locations are NOT allowed to cross major bp id numbers. |
| |
| if ((start_loc_id != LLDB_INVALID_BREAK_ID) || |
| (end_loc_id != LLDB_INVALID_BREAK_ID)) { |
| if (start_bp_id != end_bp_id) { |
| new_args.Clear(); |
| result.AppendErrorWithFormat( |
| "Invalid range: Ranges that specify particular breakpoint " |
| "locations" |
| " must be within the same major breakpoint; you specified two" |
| " different major breakpoints, %d and %d.\n", |
| start_bp_id, end_bp_id); |
| result.SetStatus(eReturnStatusFailed); |
| return; |
| } |
| } |
| |
| const BreakpointList &breakpoints = target->GetBreakpointList(); |
| const size_t num_breakpoints = breakpoints.GetSize(); |
| for (size_t j = 0; j < num_breakpoints; ++j) { |
| Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(j).get(); |
| break_id_t cur_bp_id = breakpoint->GetID(); |
| |
| if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id)) |
| continue; |
| |
| const size_t num_locations = breakpoint->GetNumLocations(); |
| |
| if ((cur_bp_id == start_bp_id) && |
| (start_loc_id != LLDB_INVALID_BREAK_ID)) { |
| for (size_t k = 0; k < num_locations; ++k) { |
| BreakpointLocation *bp_loc = |
| breakpoint->GetLocationAtIndex(k).get(); |
| if ((bp_loc->GetID() >= start_loc_id) && |
| (bp_loc->GetID() <= end_loc_id)) { |
| StreamString canonical_id_str; |
| BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id, |
| bp_loc->GetID()); |
| new_args.AppendArgument(canonical_id_str.GetData()); |
| } |
| } |
| } else if ((cur_bp_id == end_bp_id) && |
| (end_loc_id != LLDB_INVALID_BREAK_ID)) { |
| for (size_t k = 0; k < num_locations; ++k) { |
| BreakpointLocation *bp_loc = |
| breakpoint->GetLocationAtIndex(k).get(); |
| if (bp_loc->GetID() <= end_loc_id) { |
| StreamString canonical_id_str; |
| BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id, |
| bp_loc->GetID()); |
| new_args.AppendArgument(canonical_id_str.GetData()); |
| } |
| } |
| } else { |
| StreamString canonical_id_str; |
| BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id, |
| LLDB_INVALID_BREAK_ID); |
| new_args.AppendArgument(canonical_id_str.GetData()); |
| } |
| } |
| } else // else is_range was false |
| { |
| new_args.AppendArgument(current_arg); |
| } |
| } |
| |
| // Okay, now see if we found any names, and if we did, add them: |
| if (target && names_found.size()) { |
| for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) { |
| for (std::string name : names_found) { |
| if (bkpt_sp->MatchesName(name.c_str())) { |
| StreamString canonical_id_str; |
| BreakpointID::GetCanonicalReference( |
| &canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID); |
| new_args.AppendArgument(canonical_id_str.GetData()); |
| } |
| } |
| } |
| } |
| |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| } |
| |
| bool BreakpointIDList::StringContainsIDRangeExpression(const char *in_string, |
| size_t *range_start_len, |
| size_t *range_end_pos) { |
| bool is_range_expression = false; |
| std::string arg_str = in_string; |
| std::string::size_type idx; |
| std::string::size_type start_pos = 0; |
| |
| *range_start_len = 0; |
| *range_end_pos = 0; |
| |
| int specifiers_size = 0; |
| for (int i = 0; BreakpointID::g_range_specifiers[i] != nullptr; ++i) |
| ++specifiers_size; |
| |
| for (int i = 0; i < specifiers_size && !is_range_expression; ++i) { |
| const char *specifier_str = BreakpointID::g_range_specifiers[i]; |
| size_t len = strlen(specifier_str); |
| idx = arg_str.find(BreakpointID::g_range_specifiers[i]); |
| if (idx != std::string::npos) { |
| *range_start_len = idx - start_pos; |
| std::string start_str = arg_str.substr(start_pos, *range_start_len); |
| if (idx + len < arg_str.length()) { |
| *range_end_pos = idx + len; |
| std::string end_str = arg_str.substr(*range_end_pos); |
| if (BreakpointID::IsValidIDExpression(start_str.c_str()) && |
| BreakpointID::IsValidIDExpression(end_str.c_str())) { |
| is_range_expression = true; |
| //*range_start = start_str; |
| //*range_end = end_str; |
| } |
| } |
| } |
| } |
| |
| if (!is_range_expression) { |
| *range_start_len = 0; |
| *range_end_pos = 0; |
| } |
| |
| return is_range_expression; |
| } |