This moves the following classes from Core -> Utility. ConstString Error RegularExpression Stream StreamString The goal here is to get lldbUtility into a state where it has no dependendencies except on itself and LLVM, so it can be the starting point at which to start untangling LLDB's dependencies. These are all low level and very widely used classes, and previously lldbUtility had dependencies up to lldbCore in order to use these classes. So moving then down to lldbUtility makes sense from both the short term and long term perspective in solving this problem. Differential Revision: https://reviews.llvm.org/D29427 llvm-svn: 293941
324 lines
11 KiB
C++
324 lines
11 KiB
C++
//===-- BreakpointResolver.cpp ----------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Breakpoint/BreakpointResolver.h"
|
|
|
|
// C Includes
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
// Project includes
|
|
#include "lldb/Breakpoint/Breakpoint.h"
|
|
#include "lldb/Breakpoint/BreakpointLocation.h"
|
|
// Have to include the other breakpoint resolver types here so the static create
|
|
// from StructuredData can call them.
|
|
#include "lldb/Breakpoint/BreakpointResolverAddress.h"
|
|
#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
|
|
#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
|
|
#include "lldb/Breakpoint/BreakpointResolverName.h"
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Core/Log.h"
|
|
#include "lldb/Core/ModuleList.h"
|
|
#include "lldb/Core/SearchFilter.h"
|
|
#include "lldb/Symbol/CompileUnit.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace lldb;
|
|
|
|
//----------------------------------------------------------------------
|
|
// BreakpointResolver:
|
|
//----------------------------------------------------------------------
|
|
const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",
|
|
"SymbolName", "SourceRegex",
|
|
"Exception", "Unknown"};
|
|
|
|
const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(
|
|
BreakpointResolver::OptionNames::LastOptionName)] = {
|
|
"AddressOffset", "Exact", "FileName", "Inlines", "Language",
|
|
"LineNumber", "ModuleName", "NameMask", "Offset", "Regex",
|
|
"SectionName", "SkipPrologue", "SymbolNames"};
|
|
|
|
const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) {
|
|
if (type > LastKnownResolverType)
|
|
return g_ty_to_name[UnknownResolver];
|
|
|
|
return g_ty_to_name[type];
|
|
}
|
|
|
|
BreakpointResolver::ResolverTy
|
|
BreakpointResolver::NameToResolverTy(const char *name) {
|
|
for (size_t i = 0; i < LastKnownResolverType; i++) {
|
|
if (strcmp(name, g_ty_to_name[i]) == 0)
|
|
return (ResolverTy)i;
|
|
}
|
|
return UnknownResolver;
|
|
}
|
|
|
|
BreakpointResolver::BreakpointResolver(Breakpoint *bkpt,
|
|
const unsigned char resolverTy,
|
|
lldb::addr_t offset)
|
|
: m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {}
|
|
|
|
BreakpointResolver::~BreakpointResolver() {}
|
|
|
|
BreakpointResolverSP BreakpointResolver::CreateFromStructuredData(
|
|
const StructuredData::Dictionary &resolver_dict, Error &error) {
|
|
BreakpointResolverSP result_sp;
|
|
if (!resolver_dict.IsValid()) {
|
|
error.SetErrorString("Can't deserialize from an invalid data object.");
|
|
return result_sp;
|
|
}
|
|
|
|
std::string subclass_name;
|
|
|
|
bool success = resolver_dict.GetValueForKeyAsString(
|
|
GetSerializationSubclassKey(), subclass_name);
|
|
|
|
if (!success) {
|
|
error.SetErrorStringWithFormat(
|
|
"Resolver data missing subclass resolver key");
|
|
return result_sp;
|
|
}
|
|
|
|
ResolverTy resolver_type = NameToResolverTy(subclass_name.c_str());
|
|
if (resolver_type == UnknownResolver) {
|
|
error.SetErrorStringWithFormat("Unknown resolver type: %s.",
|
|
subclass_name.c_str());
|
|
return result_sp;
|
|
}
|
|
|
|
StructuredData::Dictionary *subclass_options = nullptr;
|
|
success = resolver_dict.GetValueForKeyAsDictionary(
|
|
GetSerializationSubclassOptionsKey(), subclass_options);
|
|
if (!success || !subclass_options || !subclass_options->IsValid()) {
|
|
error.SetErrorString("Resolver data missing subclass options key.");
|
|
return result_sp;
|
|
}
|
|
|
|
lldb::addr_t offset;
|
|
success = subclass_options->GetValueForKeyAsInteger(
|
|
GetKey(OptionNames::Offset), offset);
|
|
if (!success) {
|
|
error.SetErrorString("Resolver data missing offset options key.");
|
|
return result_sp;
|
|
}
|
|
|
|
BreakpointResolver *resolver;
|
|
|
|
switch (resolver_type) {
|
|
case FileLineResolver:
|
|
resolver = BreakpointResolverFileLine::CreateFromStructuredData(
|
|
nullptr, *subclass_options, error);
|
|
break;
|
|
case AddressResolver:
|
|
resolver = BreakpointResolverAddress::CreateFromStructuredData(
|
|
nullptr, *subclass_options, error);
|
|
break;
|
|
case NameResolver:
|
|
resolver = BreakpointResolverName::CreateFromStructuredData(
|
|
nullptr, *subclass_options, error);
|
|
break;
|
|
case FileRegexResolver:
|
|
resolver = BreakpointResolverFileRegex::CreateFromStructuredData(
|
|
nullptr, *subclass_options, error);
|
|
break;
|
|
case ExceptionResolver:
|
|
error.SetErrorString("Exception resolvers are hard.");
|
|
break;
|
|
default:
|
|
llvm_unreachable("Should never get an unresolvable resolver type.");
|
|
}
|
|
|
|
if (!error.Success()) {
|
|
return result_sp;
|
|
} else {
|
|
// Add on the global offset option:
|
|
resolver->SetOffset(offset);
|
|
return BreakpointResolverSP(resolver);
|
|
}
|
|
}
|
|
|
|
StructuredData::DictionarySP BreakpointResolver::WrapOptionsDict(
|
|
StructuredData::DictionarySP options_dict_sp) {
|
|
if (!options_dict_sp || !options_dict_sp->IsValid())
|
|
return StructuredData::DictionarySP();
|
|
|
|
StructuredData::DictionarySP type_dict_sp(new StructuredData::Dictionary());
|
|
type_dict_sp->AddStringItem(GetSerializationSubclassKey(), GetResolverName());
|
|
type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);
|
|
|
|
// Add the m_offset to the dictionary:
|
|
options_dict_sp->AddIntegerItem(GetKey(OptionNames::Offset), m_offset);
|
|
|
|
return type_dict_sp;
|
|
}
|
|
|
|
void BreakpointResolver::SetBreakpoint(Breakpoint *bkpt) {
|
|
m_breakpoint = bkpt;
|
|
}
|
|
|
|
void BreakpointResolver::ResolveBreakpointInModules(SearchFilter &filter,
|
|
ModuleList &modules) {
|
|
filter.SearchInModuleList(*this, modules);
|
|
}
|
|
|
|
void BreakpointResolver::ResolveBreakpoint(SearchFilter &filter) {
|
|
filter.Search(*this);
|
|
}
|
|
|
|
void BreakpointResolver::SetSCMatchesByLine(SearchFilter &filter,
|
|
SymbolContextList &sc_list,
|
|
bool skip_prologue,
|
|
llvm::StringRef log_ident) {
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
|
|
|
|
while (sc_list.GetSize() > 0) {
|
|
SymbolContextList tmp_sc_list;
|
|
unsigned current_idx = 0;
|
|
SymbolContext sc;
|
|
bool first_entry = true;
|
|
|
|
FileSpec match_file_spec;
|
|
FileSpec match_original_file_spec;
|
|
uint32_t closest_line_number = UINT32_MAX;
|
|
|
|
// Pull out the first entry, and all the others that match its file spec,
|
|
// and stuff them in the tmp list.
|
|
while (current_idx < sc_list.GetSize()) {
|
|
bool matches;
|
|
|
|
sc_list.GetContextAtIndex(current_idx, sc);
|
|
if (first_entry) {
|
|
match_file_spec = sc.line_entry.file;
|
|
match_original_file_spec = sc.line_entry.original_file;
|
|
matches = true;
|
|
first_entry = false;
|
|
} else
|
|
matches = ((sc.line_entry.file == match_file_spec) ||
|
|
(sc.line_entry.original_file == match_original_file_spec));
|
|
|
|
if (matches) {
|
|
tmp_sc_list.Append(sc);
|
|
sc_list.RemoveContextAtIndex(current_idx);
|
|
|
|
// ResolveSymbolContext will always return a number that is >= the line
|
|
// number you pass in.
|
|
// So the smaller line number is always better.
|
|
if (sc.line_entry.line < closest_line_number)
|
|
closest_line_number = sc.line_entry.line;
|
|
} else
|
|
current_idx++;
|
|
}
|
|
|
|
// Okay, we've found the closest line number match, now throw away all the
|
|
// others:
|
|
|
|
current_idx = 0;
|
|
while (current_idx < tmp_sc_list.GetSize()) {
|
|
if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) {
|
|
if (sc.line_entry.line != closest_line_number)
|
|
tmp_sc_list.RemoveContextAtIndex(current_idx);
|
|
else
|
|
current_idx++;
|
|
}
|
|
}
|
|
|
|
// Next go through and see if there are line table entries that are
|
|
// contiguous, and if so keep only the
|
|
// first of the contiguous range:
|
|
|
|
current_idx = 0;
|
|
std::map<Block *, lldb::addr_t> blocks_with_breakpoints;
|
|
|
|
while (current_idx < tmp_sc_list.GetSize()) {
|
|
if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) {
|
|
if (blocks_with_breakpoints.find(sc.block) !=
|
|
blocks_with_breakpoints.end())
|
|
tmp_sc_list.RemoveContextAtIndex(current_idx);
|
|
else {
|
|
blocks_with_breakpoints.insert(std::pair<Block *, lldb::addr_t>(
|
|
sc.block, sc.line_entry.range.GetBaseAddress().GetFileAddress()));
|
|
current_idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// and make breakpoints out of the closest line number match.
|
|
|
|
uint32_t tmp_sc_list_size = tmp_sc_list.GetSize();
|
|
|
|
for (uint32_t i = 0; i < tmp_sc_list_size; i++) {
|
|
if (tmp_sc_list.GetContextAtIndex(i, sc)) {
|
|
Address line_start = sc.line_entry.range.GetBaseAddress();
|
|
if (line_start.IsValid()) {
|
|
if (filter.AddressPasses(line_start)) {
|
|
// If the line number is before the prologue end, move it there...
|
|
bool skipped_prologue = false;
|
|
if (skip_prologue) {
|
|
if (sc.function) {
|
|
Address prologue_addr(
|
|
sc.function->GetAddressRange().GetBaseAddress());
|
|
if (prologue_addr.IsValid() && (line_start == prologue_addr)) {
|
|
const uint32_t prologue_byte_size =
|
|
sc.function->GetPrologueByteSize();
|
|
if (prologue_byte_size) {
|
|
prologue_addr.Slide(prologue_byte_size);
|
|
|
|
if (filter.AddressPasses(prologue_addr)) {
|
|
skipped_prologue = true;
|
|
line_start = prologue_addr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BreakpointLocationSP bp_loc_sp(AddLocation(line_start));
|
|
if (log && bp_loc_sp && !m_breakpoint->IsInternal()) {
|
|
StreamString s;
|
|
bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
|
|
log->Printf("Added location (skipped prologue: %s): %s \n",
|
|
skipped_prologue ? "yes" : "no", s.GetData());
|
|
}
|
|
} else if (log) {
|
|
log->Printf("Breakpoint %s at file address 0x%" PRIx64
|
|
" didn't pass the filter.\n",
|
|
log_ident.str().c_str(), line_start.GetFileAddress());
|
|
}
|
|
} else {
|
|
if (log)
|
|
log->Printf(
|
|
"error: Unable to set breakpoint %s at file address 0x%" PRIx64
|
|
"\n",
|
|
log_ident.str().c_str(), line_start.GetFileAddress());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr,
|
|
bool *new_location) {
|
|
loc_addr.Slide(m_offset);
|
|
return m_breakpoint->AddLocation(loc_addr, new_location);
|
|
}
|
|
|
|
void BreakpointResolver::SetOffset(lldb::addr_t offset) {
|
|
// There may already be an offset, so we are actually adjusting location
|
|
// addresses by the difference.
|
|
// lldb::addr_t slide = offset - m_offset;
|
|
// FIXME: We should go fix up all the already set locations for the new slide.
|
|
|
|
m_offset = offset;
|
|
}
|