//===-- BreakpointLocation.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/lldb-python.h" // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "lldb/lldb-private-log.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/BreakpointID.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/StreamString.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Target.h" #include "lldb/Target/Process.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.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 ) : StoppointLocation (loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), hardware), m_being_created(true), m_address (addr), m_owner (owner), m_options_ap (), m_bp_site_sp (), m_condition_mutex () { SetThreadID (tid); m_being_created = false; } BreakpointLocation::~BreakpointLocation() { ClearBreakpointSite(); } lldb::addr_t BreakpointLocation::GetLoadAddress () const { return m_address.GetOpcodeLoadAddress (&m_owner.GetTarget()); } Address & BreakpointLocation::GetAddress () { return m_address; } Breakpoint & BreakpointLocation::GetBreakpoint () { return m_owner; } bool BreakpointLocation::IsEnabled () const { if (!m_owner.IsEnabled()) return false; else if (m_options_ap.get() != NULL) 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); } 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() != NULL) m_options_ap->SetThreadID (thread_id); } SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); } lldb::tid_t BreakpointLocation::GetThreadID () { if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) return GetOptionsNoCreate()->GetThreadSpecNoCreate()->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() != NULL) m_options_ap->GetThreadSpec()->SetIndex(index); } SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); } uint32_t BreakpointLocation::GetThreadIndex() const { if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetIndex(); else return 0; } void BreakpointLocation::SetThreadName (const char *thread_name) { if (thread_name != NULL) 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() != NULL) m_options_ap->GetThreadSpec()->SetName(thread_name); } SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); } const char * BreakpointLocation::GetThreadName () const { if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetName(); else return NULL; } void BreakpointLocation::SetQueueName (const char *queue_name) { if (queue_name != NULL) 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() != NULL) m_options_ap->GetThreadSpec()->SetQueueName(queue_name); } SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); } const char * BreakpointLocation::GetQueueName () const { if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetQueueName(); else return NULL; } bool BreakpointLocation::InvokeCallback (StoppointCallbackContext *context) { if (m_options_ap.get() != NULL && 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, BatonSP (new Baton(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 GetOptionsNoCreate()->GetConditionText(hash); } bool BreakpointLocation::ConditionSaysStop (ExecutionContext &exe_ctx, Error &error) { Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); Mutex::Locker evaluation_locker(m_condition_mutex); size_t condition_hash; const char *condition_text = GetConditionText(&condition_hash); if (!condition_text) { m_user_expression_sp.reset(); return false; } if (condition_hash != m_condition_hash || !m_user_expression_sp || !m_user_expression_sp->MatchesContext(exe_ctx)) { m_user_expression_sp.reset(new ClangUserExpression(condition_text, NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny)); StreamString errors; if (!m_user_expression_sp->Parse(errors, exe_ctx, eExecutionPolicyOnlyWhenNeeded, true)) { error.SetErrorStringWithFormat("Couldn't parse conditional expression:\n%s", errors.GetData()); m_user_expression_sp.reset(); return false; } 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.SetRunOthers(true); Error expr_error; StreamString execution_errors; ClangExpressionVariableSP result_variable_sp; ExecutionResults result_code = m_user_expression_sp->Execute(execution_errors, exe_ctx, options, m_user_expression_sp, result_variable_sp); bool ret; if (result_code == eExecutionCompleted) { if (!result_variable_sp) { ret = false; error.SetErrorString("Expression did not return a result"); return false; } result_value_sp = result_variable_sp->GetValueObject(); if (result_value_sp) { Scalar scalar_value; if (result_value_sp->ResolveValue (scalar_value)) { if (scalar_value.ULongLong(1) == 0) ret = false; else ret = true; if (log) log->Printf("Condition successfully evaluated, result is %s.\n", ret ? "true" : "false"); } else { ret = false; error.SetErrorString("Failed to get an integer result from the expression"); } } else { ret = false; error.SetErrorString("Failed to get any result from the expression"); } } else { ret = false; error.SetErrorStringWithFormat("Couldn't execute expression:\n%s", execution_errors.GetData()); } return ret; } uint32_t BreakpointLocation::GetIgnoreCount () { return GetOptionsNoCreate()->GetIgnoreCount(); } void BreakpointLocation::SetIgnoreCount (uint32_t n) { GetLocationOptions()->SetIgnoreCount(n); SendBreakpointLocationChangedEvent (eBreakpointEventTypeIgnoreChanged); } void BreakpointLocation::DecrementIgnoreCount() { if (m_options_ap.get() != NULL) { 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() != NULL) { 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; } const BreakpointOptions * BreakpointLocation::GetOptionsNoCreate () const { if (m_options_ap.get() != NULL) return m_options_ap.get(); else return m_owner.GetOptions (); } 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() == NULL) m_options_ap.reset(BreakpointOptions::CopyOptionsNoCallback(*m_owner.GetOptions ())); return m_options_ap.get(); } bool BreakpointLocation::ValidForThisThread (Thread *thread) { return thread->MatchesSpec(GetOptionsNoCreate()->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); IncrementHitCount(); 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; } bool BreakpointLocation::IsResolved () const { return m_bp_site_sp.get() != NULL; } 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 == NULL) 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; return true; } bool BreakpointLocation::ClearBreakpointSite () { if (m_bp_site_sp.get()) { m_owner.GetTarget().GetProcessSP()->RemoveOwnerFromBreakpointSite (GetBreakpoint().GetID(), GetID(), m_bp_site_sp); 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) { s->PutCString("where = "); sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); } else { if (sc.module_sp) { s->EOL(); s->Indent("module = "); sc.module_sp->GetFileSpec().Dump (s); } if (sc.comp_unit != NULL) { s->EOL(); s->Indent("compile unit = "); static_cast(sc.comp_unit)->GetFilename().Dump (s); if (sc.function != NULL) { s->EOL(); s->Indent("function = "); s->PutCString (sc.function->GetMangled().GetName().AsCString("")); } 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(); s->Indent("symbol = "); s->PutCString(sc.symbol->GetMangled().GetName().AsCString("")); } } } } if (level == lldb::eDescriptionLevelVerbose) { s->EOL(); s->Indent(); } if (m_address.IsSectionOffset() && (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)) s->Printf (", "); s->Printf ("address = "); ExecutionContextScope *exe_scope = NULL; Target *target = &m_owner.GetTarget(); if (target) exe_scope = target->GetProcessSP().get(); if (exe_scope == NULL) exe_scope = target; if (eDescriptionLevelInitial) m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); else m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); 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 == NULL) return; 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(), GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetTID(), (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(), GetOptionsNoCreate()->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); } }