text: //===-- BreakpointLocation.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/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_ap(), 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_ap && m_options_ap->IsOptionSet(kind))
        return m_options_ap.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_ap.get() != nullptr)
      return m_options_ap->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_ap 
        && m_options_ap->IsOptionSet(BreakpointOptions::eAutoContinue))
      return m_options_ap->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_ap.get() != nullptr)
        m_options_ap->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_ap.get() != nullptr)
        m_options_ap->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_ap.get() != nullptr)
        m_options_ap->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_ap.get() != nullptr)
        m_options_ap->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_ap.get() != nullptr && m_options_ap->HasCallback())
      return m_options_ap->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(), error));
      if (error.Fail()) {
        if (log)
          log->Printf("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()) {
            log->Printf("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_ap.get() != nullptr) {
      uint32_t loc_ignore = m_options_ap->GetIgnoreCount();
      if (loc_ignore != 0)
        m_options_ap->SetIgnoreCount(loc_ignore - 1);
    }
  }
  
  bool BreakpointLocation::IgnoreCountShouldStop() {
    if (m_options_ap.get() != nullptr) {
      uint32_t loc_ignore = m_options_ap->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_ap.get() == nullptr)
      m_options_ap.reset(
          new BreakpointOptions(false));
  
    return m_options_ap.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);
      log->Printf("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("Tried to add breakpoint site at 0x%" PRIx64
                     " but it was already present.\n",
                     m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()));
      return false;
    }
  
    return true;
  }
  
  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_ap.get()) {
        s->Indent();
        m_options_ap->GetDescription(s, level);
        s->EOL();
      }
      s->IndentLess();
    } else if (level != eDescriptionLevelInitial) {
      s->Printf(", %sresolved, hit count = %u ", (IsResolved() ? "" : "un"),
                GetHitCount());
      if (m_options_ap.get()) {
        m_options_ap->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_ap.get() ? m_options_ap->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();
  }
  