diff options
Diffstat (limited to 'lldb/source/Breakpoint/BreakpointLocation.cpp')
| -rw-r--r-- | lldb/source/Breakpoint/BreakpointLocation.cpp | 659 | 
1 files changed, 659 insertions, 0 deletions
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp new file mode 100644 index 0000000000000..46b8f25c56682 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -0,0 +1,659 @@ +//===-- BreakpointLocation.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner, +                                       const Address &addr, lldb::tid_t tid, +                                       bool hardware, bool check_for_resolver) +    : StoppointLocation(loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), +                        hardware), +      m_being_created(true), m_should_resolve_indirect_functions(false), +      m_is_reexported(false), m_is_indirect(false), m_address(addr), +      m_owner(owner), m_options_up(), m_bp_site_sp(), m_condition_mutex() { +  if (check_for_resolver) { +    Symbol *symbol = m_address.CalculateSymbolContextSymbol(); +    if (symbol && symbol->IsIndirect()) { +      SetShouldResolveIndirectFunctions(true); +    } +  } + +  SetThreadID(tid); +  m_being_created = false; +} + +BreakpointLocation::~BreakpointLocation() { ClearBreakpointSite(); } + +lldb::addr_t BreakpointLocation::GetLoadAddress() const { +  return m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()); +} + +const BreakpointOptions * +BreakpointLocation::GetOptionsSpecifyingKind(BreakpointOptions::OptionKind kind) +const { +  if (m_options_up && m_options_up->IsOptionSet(kind)) +    return m_options_up.get(); +  else +    return m_owner.GetOptions(); +} + +Address &BreakpointLocation::GetAddress() { return m_address; } + +Breakpoint &BreakpointLocation::GetBreakpoint() { return m_owner; } + +Target &BreakpointLocation::GetTarget() { return m_owner.GetTarget(); } + +bool BreakpointLocation::IsEnabled() const { +  if (!m_owner.IsEnabled()) +    return false; +  else if (m_options_up != nullptr) +    return m_options_up->IsEnabled(); +  else +    return true; +} + +void BreakpointLocation::SetEnabled(bool enabled) { +  GetLocationOptions()->SetEnabled(enabled); +  if (enabled) { +    ResolveBreakpointSite(); +  } else { +    ClearBreakpointSite(); +  } +  SendBreakpointLocationChangedEvent(enabled ? eBreakpointEventTypeEnabled +                                             : eBreakpointEventTypeDisabled); +} + +bool BreakpointLocation::IsAutoContinue() const { +  if (m_options_up && +      m_options_up->IsOptionSet(BreakpointOptions::eAutoContinue)) +    return m_options_up->IsAutoContinue(); +  else +    return m_owner.IsAutoContinue(); +} + +void BreakpointLocation::SetAutoContinue(bool auto_continue) { +  GetLocationOptions()->SetAutoContinue(auto_continue); +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeAutoContinueChanged); +} + +void BreakpointLocation::SetThreadID(lldb::tid_t thread_id) { +  if (thread_id != LLDB_INVALID_THREAD_ID) +    GetLocationOptions()->SetThreadID(thread_id); +  else { +    // If we're resetting this to an invalid thread id, then don't make an +    // options pointer just to do that. +    if (m_options_up != nullptr) +      m_options_up->SetThreadID(thread_id); +  } +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t BreakpointLocation::GetThreadID() { +  const ThreadSpec *thread_spec =  +      GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) +          ->GetThreadSpecNoCreate(); +  if (thread_spec) +    return thread_spec->GetTID(); +  else +    return LLDB_INVALID_THREAD_ID; +} + +void BreakpointLocation::SetThreadIndex(uint32_t index) { +  if (index != 0) +    GetLocationOptions()->GetThreadSpec()->SetIndex(index); +  else { +    // If we're resetting this to an invalid thread id, then don't make an +    // options pointer just to do that. +    if (m_options_up != nullptr) +      m_options_up->GetThreadSpec()->SetIndex(index); +  } +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); +} + +uint32_t BreakpointLocation::GetThreadIndex() const { +  const ThreadSpec *thread_spec =  +      GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) +          ->GetThreadSpecNoCreate(); +  if (thread_spec) +    return thread_spec->GetIndex(); +  else +    return 0; +} + +void BreakpointLocation::SetThreadName(const char *thread_name) { +  if (thread_name != nullptr) +    GetLocationOptions()->GetThreadSpec()->SetName(thread_name); +  else { +    // If we're resetting this to an invalid thread id, then don't make an +    // options pointer just to do that. +    if (m_options_up != nullptr) +      m_options_up->GetThreadSpec()->SetName(thread_name); +  } +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); +} + +const char *BreakpointLocation::GetThreadName() const { +  const ThreadSpec *thread_spec =  +      GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) +          ->GetThreadSpecNoCreate(); +  if (thread_spec) +    return thread_spec->GetName(); +  else +    return nullptr; +} + +void BreakpointLocation::SetQueueName(const char *queue_name) { +  if (queue_name != nullptr) +    GetLocationOptions()->GetThreadSpec()->SetQueueName(queue_name); +  else { +    // If we're resetting this to an invalid thread id, then don't make an +    // options pointer just to do that. +    if (m_options_up != nullptr) +      m_options_up->GetThreadSpec()->SetQueueName(queue_name); +  } +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); +} + +const char *BreakpointLocation::GetQueueName() const { +  const ThreadSpec *thread_spec =  +      GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) +          ->GetThreadSpecNoCreate(); +  if (thread_spec) +    return thread_spec->GetQueueName(); +  else +    return nullptr; +} + +bool BreakpointLocation::InvokeCallback(StoppointCallbackContext *context) { +  if (m_options_up != nullptr && m_options_up->HasCallback()) +    return m_options_up->InvokeCallback(context, m_owner.GetID(), GetID()); +  else +    return m_owner.InvokeCallback(context, GetID()); +} + +void BreakpointLocation::SetCallback(BreakpointHitCallback callback, +                                     void *baton, bool is_synchronous) { +  // The default "Baton" class will keep a copy of "baton" and won't free or +  // delete it when it goes goes out of scope. +  GetLocationOptions()->SetCallback( +      callback, std::make_shared<UntypedBaton>(baton), is_synchronous); +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged); +} + +void BreakpointLocation::SetCallback(BreakpointHitCallback callback, +                                     const BatonSP &baton_sp, +                                     bool is_synchronous) { +  GetLocationOptions()->SetCallback(callback, baton_sp, is_synchronous); +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged); +} + +void BreakpointLocation::ClearCallback() { +  GetLocationOptions()->ClearCallback(); +} + +void BreakpointLocation::SetCondition(const char *condition) { +  GetLocationOptions()->SetCondition(condition); +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeConditionChanged); +} + +const char *BreakpointLocation::GetConditionText(size_t *hash) const { +  return GetOptionsSpecifyingKind(BreakpointOptions::eCondition) +      ->GetConditionText(hash); +} + +bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx, +                                           Status &error) { +  Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); + +  std::lock_guard<std::mutex> guard(m_condition_mutex); + +  size_t condition_hash; +  const char *condition_text = GetConditionText(&condition_hash); + +  if (!condition_text) { +    m_user_expression_sp.reset(); +    return false; +  } + +  error.Clear(); + +  DiagnosticManager diagnostics; + +  if (condition_hash != m_condition_hash || !m_user_expression_sp || +      !m_user_expression_sp->MatchesContext(exe_ctx)) { +    LanguageType language = eLanguageTypeUnknown; +    // See if we can figure out the language from the frame, otherwise use the +    // default language: +    CompileUnit *comp_unit = m_address.CalculateSymbolContextCompileUnit(); +    if (comp_unit) +      language = comp_unit->GetLanguage(); + +    m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage( +        condition_text, llvm::StringRef(), language, Expression::eResultTypeAny, +        EvaluateExpressionOptions(), nullptr, error)); +    if (error.Fail()) { +      LLDB_LOGF(log, "Error getting condition expression: %s.", +                error.AsCString()); +      m_user_expression_sp.reset(); +      return true; +    } + +    if (!m_user_expression_sp->Parse(diagnostics, exe_ctx, +                                     eExecutionPolicyOnlyWhenNeeded, true, +                                     false)) { +      error.SetErrorStringWithFormat( +          "Couldn't parse conditional expression:\n%s", +          diagnostics.GetString().c_str()); +      m_user_expression_sp.reset(); +      return true; +    } + +    m_condition_hash = condition_hash; +  } + +  // We need to make sure the user sees any parse errors in their condition, so +  // we'll hook the constructor errors up to the debugger's Async I/O. + +  ValueObjectSP result_value_sp; + +  EvaluateExpressionOptions options; +  options.SetUnwindOnError(true); +  options.SetIgnoreBreakpoints(true); +  options.SetTryAllThreads(true); +  options.SetResultIsInternal( +      true); // Don't generate a user variable for condition expressions. + +  Status expr_error; + +  diagnostics.Clear(); + +  ExpressionVariableSP result_variable_sp; + +  ExpressionResults result_code = m_user_expression_sp->Execute( +      diagnostics, exe_ctx, options, m_user_expression_sp, result_variable_sp); + +  bool ret; + +  if (result_code == eExpressionCompleted) { +    if (!result_variable_sp) { +      error.SetErrorString("Expression did not return a result"); +      return false; +    } + +    result_value_sp = result_variable_sp->GetValueObject(); + +    if (result_value_sp) { +      ret = result_value_sp->IsLogicalTrue(error); +      if (log) { +        if (error.Success()) { +          LLDB_LOGF(log, "Condition successfully evaluated, result is %s.\n", +                    ret ? "true" : "false"); +        } else { +          error.SetErrorString( +              "Failed to get an integer result from the expression"); +          ret = false; +        } +      } +    } else { +      ret = false; +      error.SetErrorString("Failed to get any result from the expression"); +    } +  } else { +    ret = false; +    error.SetErrorStringWithFormat("Couldn't execute expression:\n%s", +                                   diagnostics.GetString().c_str()); +  } + +  return ret; +} + +uint32_t BreakpointLocation::GetIgnoreCount() { +  return GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount) +      ->GetIgnoreCount(); +} + +void BreakpointLocation::SetIgnoreCount(uint32_t n) { +  GetLocationOptions()->SetIgnoreCount(n); +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeIgnoreChanged); +} + +void BreakpointLocation::DecrementIgnoreCount() { +  if (m_options_up != nullptr) { +    uint32_t loc_ignore = m_options_up->GetIgnoreCount(); +    if (loc_ignore != 0) +      m_options_up->SetIgnoreCount(loc_ignore - 1); +  } +} + +bool BreakpointLocation::IgnoreCountShouldStop() { +  if (m_options_up != nullptr) { +    uint32_t loc_ignore = m_options_up->GetIgnoreCount(); +    if (loc_ignore != 0) { +      m_owner.DecrementIgnoreCount(); +      DecrementIgnoreCount(); // Have to decrement our owners' ignore count, +                              // since it won't get a +                              // chance to. +      return false; +    } +  } +  return true; +} + +BreakpointOptions *BreakpointLocation::GetLocationOptions() { +  // If we make the copy we don't copy the callbacks because that is +  // potentially expensive and we don't want to do that for the simple case +  // where someone is just disabling the location. +  if (m_options_up == nullptr) +    m_options_up.reset(new BreakpointOptions(false)); + +  return m_options_up.get(); +} + +bool BreakpointLocation::ValidForThisThread(Thread *thread) { +  return thread +      ->MatchesSpec(GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) +      ->GetThreadSpecNoCreate()); +} + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue.  Note, we don't check the thread spec for the breakpoint +// here, since if the breakpoint is not for this thread, then the event won't +// even get reported, so the check is redundant. + +bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) { +  bool should_stop = true; +  Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); + +  // Do this first, if a location is disabled, it shouldn't increment its hit +  // count. +  if (!IsEnabled()) +    return false; + +  if (!IgnoreCountShouldStop()) +    return false; + +  if (!m_owner.IgnoreCountShouldStop()) +    return false; + +  // We only run synchronous callbacks in ShouldStop: +  context->is_synchronous = true; +  should_stop = InvokeCallback(context); + +  if (log) { +    StreamString s; +    GetDescription(&s, lldb::eDescriptionLevelVerbose); +    LLDB_LOGF(log, "Hit breakpoint location: %s, %s.\n", s.GetData(), +              should_stop ? "stopping" : "continuing"); +  } + +  return should_stop; +} + +void BreakpointLocation::BumpHitCount() { +  if (IsEnabled()) { +    // Step our hit count, and also step the hit count of the owner. +    IncrementHitCount(); +    m_owner.IncrementHitCount(); +  } +} + +void BreakpointLocation::UndoBumpHitCount() { +  if (IsEnabled()) { +    // Step our hit count, and also step the hit count of the owner. +    DecrementHitCount(); +    m_owner.DecrementHitCount(); +  } +} + +bool BreakpointLocation::IsResolved() const { +  return m_bp_site_sp.get() != nullptr; +} + +lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const { +  return m_bp_site_sp; +} + +bool BreakpointLocation::ResolveBreakpointSite() { +  if (m_bp_site_sp) +    return true; + +  Process *process = m_owner.GetTarget().GetProcessSP().get(); +  if (process == nullptr) +    return false; + +  lldb::break_id_t new_id = +      process->CreateBreakpointSite(shared_from_this(), m_owner.IsHardware()); + +  if (new_id == LLDB_INVALID_BREAK_ID) { +    Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); +    if (log) +      log->Warning("Failed to add breakpoint site at 0x%" PRIx64, +                   m_address.GetOpcodeLoadAddress(&m_owner.GetTarget())); +  } + +  return IsResolved(); +} + +bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) { +  m_bp_site_sp = bp_site_sp; +  SendBreakpointLocationChangedEvent(eBreakpointEventTypeLocationsResolved); +  return true; +} + +bool BreakpointLocation::ClearBreakpointSite() { +  if (m_bp_site_sp.get()) { +    ProcessSP process_sp(m_owner.GetTarget().GetProcessSP()); +    // If the process exists, get it to remove the owner, it will remove the +    // physical implementation of the breakpoint as well if there are no more +    // owners.  Otherwise just remove this owner. +    if (process_sp) +      process_sp->RemoveOwnerFromBreakpointSite(GetBreakpoint().GetID(), +                                                GetID(), m_bp_site_sp); +    else +      m_bp_site_sp->RemoveOwner(GetBreakpoint().GetID(), GetID()); + +    m_bp_site_sp.reset(); +    return true; +  } +  return false; +} + +void BreakpointLocation::GetDescription(Stream *s, +                                        lldb::DescriptionLevel level) { +  SymbolContext sc; + +  // If the description level is "initial" then the breakpoint is printing out +  // our initial state, and we should let it decide how it wants to print our +  // label. +  if (level != eDescriptionLevelInitial) { +    s->Indent(); +    BreakpointID::GetCanonicalReference(s, m_owner.GetID(), GetID()); +  } + +  if (level == lldb::eDescriptionLevelBrief) +    return; + +  if (level != eDescriptionLevelInitial) +    s->PutCString(": "); + +  if (level == lldb::eDescriptionLevelVerbose) +    s->IndentMore(); + +  if (m_address.IsSectionOffset()) { +    m_address.CalculateSymbolContext(&sc); + +    if (level == lldb::eDescriptionLevelFull || +        level == eDescriptionLevelInitial) { +      if (IsReExported()) +        s->PutCString("re-exported target = "); +      else +        s->PutCString("where = "); +      sc.DumpStopContext(s, m_owner.GetTarget().GetProcessSP().get(), m_address, +                         false, true, false, true, true); +    } else { +      if (sc.module_sp) { +        s->EOL(); +        s->Indent("module = "); +        sc.module_sp->GetFileSpec().Dump(s); +      } + +      if (sc.comp_unit != nullptr) { +        s->EOL(); +        s->Indent("compile unit = "); +        static_cast<FileSpec *>(sc.comp_unit)->GetFilename().Dump(s); + +        if (sc.function != nullptr) { +          s->EOL(); +          s->Indent("function = "); +          s->PutCString(sc.function->GetName().AsCString("<unknown>")); +        } + +        if (sc.line_entry.line > 0) { +          s->EOL(); +          s->Indent("location = "); +          sc.line_entry.DumpStopContext(s, true); +        } + +      } else { +        // If we don't have a comp unit, see if we have a symbol we can print. +        if (sc.symbol) { +          s->EOL(); +          if (IsReExported()) +            s->Indent("re-exported target = "); +          else +            s->Indent("symbol = "); +          s->PutCString(sc.symbol->GetName().AsCString("<unknown>")); +        } +      } +    } +  } + +  if (level == lldb::eDescriptionLevelVerbose) { +    s->EOL(); +    s->Indent(); +  } + +  if (m_address.IsSectionOffset() && +      (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)) +    s->Printf(", "); +  s->Printf("address = "); + +  ExecutionContextScope *exe_scope = nullptr; +  Target *target = &m_owner.GetTarget(); +  if (target) +    exe_scope = target->GetProcessSP().get(); +  if (exe_scope == nullptr) +    exe_scope = target; + +  if (level == eDescriptionLevelInitial) +    m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, +                   Address::DumpStyleFileAddress); +  else +    m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, +                   Address::DumpStyleModuleWithFileAddress); + +  if (IsIndirect() && m_bp_site_sp) { +    Address resolved_address; +    resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target); +    Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol(); +    if (resolved_symbol) { +      if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial) +        s->Printf(", "); +      else if (level == lldb::eDescriptionLevelVerbose) { +        s->EOL(); +        s->Indent(); +      } +      s->Printf("indirect target = %s", +                resolved_symbol->GetName().GetCString()); +    } +  } + +  if (level == lldb::eDescriptionLevelVerbose) { +    s->EOL(); +    s->Indent(); +    s->Printf("resolved = %s\n", IsResolved() ? "true" : "false"); + +    s->Indent(); +    s->Printf("hit count = %-4u\n", GetHitCount()); + +    if (m_options_up) { +      s->Indent(); +      m_options_up->GetDescription(s, level); +      s->EOL(); +    } +    s->IndentLess(); +  } else if (level != eDescriptionLevelInitial) { +    s->Printf(", %sresolved, hit count = %u ", (IsResolved() ? "" : "un"), +              GetHitCount()); +    if (m_options_up) { +      m_options_up->GetDescription(s, level); +    } +  } +} + +void BreakpointLocation::Dump(Stream *s) const { +  if (s == nullptr) +    return; + +  lldb::tid_t tid = GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) +      ->GetThreadSpecNoCreate()->GetTID(); +  s->Printf("BreakpointLocation %u: tid = %4.4" PRIx64 +            "  load addr = 0x%8.8" PRIx64 "  state = %s  type = %s breakpoint  " +            "hw_index = %i  hit_count = %-4u  ignore_count = %-4u", +            GetID(), tid, +            (uint64_t)m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()), +            (m_options_up ? m_options_up->IsEnabled() : m_owner.IsEnabled()) +                ? "enabled " +                : "disabled", +            IsHardware() ? "hardware" : "software", GetHardwareIndex(), +            GetHitCount(), +            GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount) +                ->GetIgnoreCount()); +} + +void BreakpointLocation::SendBreakpointLocationChangedEvent( +    lldb::BreakpointEventType eventKind) { +  if (!m_being_created && !m_owner.IsInternal() && +      m_owner.GetTarget().EventTypeHasListeners( +          Target::eBroadcastBitBreakpointChanged)) { +    Breakpoint::BreakpointEventData *data = new Breakpoint::BreakpointEventData( +        eventKind, m_owner.shared_from_this()); +    data->GetBreakpointLocationCollection().Add(shared_from_this()); +    m_owner.GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, +                                       data); +  } +} + +void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) { +  m_address = swap_from->m_address; +  m_should_resolve_indirect_functions = +      swap_from->m_should_resolve_indirect_functions; +  m_is_reexported = swap_from->m_is_reexported; +  m_is_indirect = swap_from->m_is_indirect; +  m_user_expression_sp.reset(); +}  | 
