#include <string>
#include "lldb/Breakpoint/Breakpoint.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Breakpoint/WatchpointResource.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanStepInstruction.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
#ifdef MS_DEBUGGER
#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
#endif
using namespace lldb;
using namespace lldb_private;
StopInfo::StopInfo(Thread &thread, uint64_t value)
: m_thread_wp(thread.shared_from_this()),
m_stop_id(thread.GetProcess()->GetStopID()),
m_resume_id(thread.GetProcess()->GetResumeID()), m_value(value),
m_description(), m_override_should_notify(eLazyBoolCalculate),
m_override_should_stop(eLazyBoolCalculate), m_extended_info() {}
bool StopInfo::IsValid() const {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
return thread_sp->GetProcess()->GetStopID() == m_stop_id;
return false;
}
void StopInfo::MakeStopInfoValid() {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
m_stop_id = thread_sp->GetProcess()->GetStopID();
m_resume_id = thread_sp->GetProcess()->GetResumeID();
}
}
bool StopInfo::HasTargetRunSinceMe() {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
lldb::StateType ret_type = thread_sp->GetProcess()->GetPrivateState();
if (ret_type == eStateRunning) {
return true;
} else if (ret_type == eStateStopped) {
uint32_t curr_resume_id = thread_sp->GetProcess()->GetResumeID();
uint32_t last_user_expression_id =
thread_sp->GetProcess()->GetLastUserExpressionResumeID();
if (curr_resume_id == m_resume_id) {
return false;
} else if (curr_resume_id > last_user_expression_id) {
return true;
}
}
}
return false;
}
namespace lldb_private {
class StopInfoBreakpoint : public StopInfo {
public:
StopInfoBreakpoint(Thread &thread, break_id_t break_id)
: StopInfo(thread, break_id), m_should_stop(false),
m_should_stop_is_valid(false), m_should_perform_action(true),
m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID),
m_was_all_internal(false), m_was_one_shot(false) {
StoreBPInfo();
}
StopInfoBreakpoint(Thread &thread, break_id_t break_id, bool should_stop)
: StopInfo(thread, break_id), m_should_stop(should_stop),
m_should_stop_is_valid(true), m_should_perform_action(true),
m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID),
m_was_all_internal(false), m_was_one_shot(false) {
StoreBPInfo();
}
#ifdef MS_DEBUGGER
StopInfoBreakpoint(Thread &thread, break_id_t break_id, const ArchSpec& arch_spec)
: StopInfo(thread, break_id), m_should_stop(false),
m_should_stop_is_valid(false), m_should_perform_action(true),
m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID),
m_was_all_internal(false), m_was_one_shot(false), m_arch_spec(arch_spec) {
StoreBPInfo();
}
#endif
~StopInfoBreakpoint() override = default;
void StoreBPInfo() {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
BreakpointSiteSP bp_site_sp(
thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
if (bp_site_sp) {
uint32_t num_constituents = bp_site_sp->GetNumberOfConstituents();
if (num_constituents == 1) {
BreakpointLocationSP bp_loc_sp = bp_site_sp->GetConstituentAtIndex(0);
if (bp_loc_sp) {
Breakpoint & bkpt = bp_loc_sp->GetBreakpoint();
m_break_id = bkpt.GetID();
m_was_one_shot = bkpt.IsOneShot();
m_was_all_internal = bkpt.IsInternal();
}
} else {
m_was_all_internal = true;
for (uint32_t i = 0; i < num_constituents; i++) {
if (!bp_site_sp->GetConstituentAtIndex(i)
->GetBreakpoint()
.IsInternal()) {
m_was_all_internal = false;
break;
}
}
}
m_address = bp_site_sp->GetLoadAddress();
}
}
}
bool IsValidForOperatingSystemThread(Thread &thread) override {
ProcessSP process_sp(thread.GetProcess());
if (process_sp) {
BreakpointSiteSP bp_site_sp(
process_sp->GetBreakpointSiteList().FindByID(m_value));
if (bp_site_sp)
return bp_site_sp->ValidForThisThread(thread);
}
return false;
}
#ifdef MS_DEBUGGER
StopReason GetStopReason() const override {
if (m_arch_spec.GetMachine() == llvm::Triple::hiipu64) {
return eStopReasonDeviceBreakpoint;
}
return eStopReasonBreakpoint;
}
#else
StopReason GetStopReason() const override { return eStopReasonBreakpoint; }
#endif
bool ShouldStopSynchronous(Event *event_ptr) override {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
if (!m_should_stop_is_valid) {
BreakpointSiteSP bp_site_sp(
thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
if (bp_site_sp) {
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
StoppointCallbackContext context(event_ptr, exe_ctx, true);
bp_site_sp->BumpHitCounts();
m_should_stop = bp_site_sp->ShouldStop(&context);
} else {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log,
"Process::%s could not find breakpoint site id: %" PRId64
"...",
__FUNCTION__, m_value);
m_should_stop = true;
}
m_should_stop_is_valid = true;
}
return m_should_stop;
}
return false;
}
bool DoShouldNotify(Event *event_ptr) override {
return !m_was_all_internal;
}
const char *GetDescription() override {
if (m_description.empty()) {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
BreakpointSiteSP bp_site_sp(
thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
if (bp_site_sp) {
StreamString strm;
if (bp_site_sp->IsInternal()) {
size_t num_constituents = bp_site_sp->GetNumberOfConstituents();
for (size_t idx = 0; idx < num_constituents; idx++) {
const char *kind = bp_site_sp->GetConstituentAtIndex(idx)
->GetBreakpoint()
.GetBreakpointKind();
if (kind != nullptr) {
m_description.assign(kind);
return kind;
}
}
}
strm.Printf("breakpoint ");
bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief);
m_description = std::string(strm.GetString());
} else {
StreamString strm;
if (m_break_id != LLDB_INVALID_BREAK_ID) {
BreakpointSP break_sp =
thread_sp->GetProcess()->GetTarget().GetBreakpointByID(
m_break_id);
if (break_sp) {
if (break_sp->IsInternal()) {
const char *kind = break_sp->GetBreakpointKind();
if (kind)
strm.Printf("internal %s breakpoint(%d).", kind, m_break_id);
else
strm.Printf("internal breakpoint(%d).", m_break_id);
} else {
strm.Printf("breakpoint %d.", m_break_id);
}
} else {
if (m_was_one_shot)
strm.Printf("one-shot breakpoint %d", m_break_id);
else
strm.Printf("breakpoint %d which has been deleted.",
m_break_id);
}
} else if (m_address == LLDB_INVALID_ADDRESS)
strm.Printf("breakpoint site %" PRIi64
" which has been deleted - unknown address",
m_value);
else
strm.Printf("breakpoint site %" PRIi64
" which has been deleted - was at 0x%" PRIx64,
m_value, m_address);
m_description = std::string(strm.GetString());
}
}
}
return m_description.c_str();
}
protected:
bool ShouldStop(Event *event_ptr) override {
assert(m_should_stop_is_valid);
return m_should_stop;
}
void PerformAction(Event *event_ptr) override {
if (!m_should_perform_action)
return;
m_should_perform_action = false;
bool all_stopping_locs_internal = true;
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
Log *log = GetLog(LLDBLog::Breakpoints | LLDBLog::Step);
if (!thread_sp->IsValid()) {
if (log) {
LLDB_LOGF(log, "PerformAction got called with an invalid thread.");
}
m_should_stop = true;
m_should_stop_is_valid = true;
return;
}
BreakpointSiteSP bp_site_sp(
thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
std::unordered_set<break_id_t> precondition_breakpoints;
bool actually_hit_any_locations = false;
if (bp_site_sp) {
BreakpointLocationCollection site_locations;
size_t num_constituents =
bp_site_sp->CopyConstituentsList(site_locations);
if (num_constituents == 0) {
m_should_stop = true;
actually_hit_any_locations = true;
} else {
bool async_should_stop = false;
if (m_should_stop_is_valid)
async_should_stop = m_should_stop;
bool actually_said_continue = false;
m_should_stop = false;
ThreadList::ExpressionExecutionThreadPusher thread_pusher(thread_sp);
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
Process *process = exe_ctx.GetProcessPtr();
if (process->GetModIDRef().IsRunningExpression()) {
m_should_stop_is_valid = true;
if (thread_sp->CompletedPlanOverridesBreakpoint()) {
m_should_stop = true;
thread_sp->ResetStopInfo();
return;
}
LLDB_LOGF(log, "StopInfoBreakpoint::PerformAction - Hit a "
"breakpoint while running an expression,"
" not running commands to avoid recursion.");
bool ignoring_breakpoints =
process->GetIgnoreBreakpointsInExpressions();
if (!m_was_all_internal) {
m_should_stop = !ignoring_breakpoints;
LLDB_LOGF(log,
"StopInfoBreakpoint::PerformAction - in expression, "
"continuing: %s.",
m_should_stop ? "true" : "false");
Debugger::ReportWarning(
"hit breakpoint while running function, skipping commands "
"and conditions to prevent recursion",
process->GetTarget().GetDebugger().GetID());
return;
}
}
StoppointCallbackContext context(event_ptr, exe_ctx, false);
std::vector<lldb::BreakpointSP> location_constituents;
for (size_t j = 0; j < num_constituents; j++) {
BreakpointLocationSP loc(site_locations.GetByIndex(j));
location_constituents.push_back(
loc->GetBreakpoint().shared_from_this());
}
for (size_t j = 0; j < num_constituents; j++) {
lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j);
StreamString loc_desc;
if (log) {
bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief);
}
if (!bp_loc_sp->IsEnabled() ||
!bp_loc_sp->GetBreakpoint().IsEnabled())
continue;
if (!bp_loc_sp->ValidForThisThread(*thread_sp)) {
if (log) {
LLDB_LOGF(log,
"Breakpoint %s hit on thread 0x%llx but it was not "
"for this thread, continuing.",
loc_desc.GetData(),
static_cast<unsigned long long>(thread_sp->GetID()));
}
continue;
}
std::pair<std::unordered_set<break_id_t>::iterator, bool> result =
precondition_breakpoints.insert(
bp_loc_sp->GetBreakpoint().GetID());
if (!result.second)
continue;
bool precondition_result =
bp_loc_sp->GetBreakpoint().EvaluatePrecondition(context);
if (!precondition_result) {
actually_said_continue = true;
continue;
}
if (bp_loc_sp->GetConditionText() == nullptr)
actually_hit_any_locations = true;
else {
Status condition_error;
bool condition_says_stop =
bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error);
if (!condition_error.Success()) {
actually_hit_any_locations = true;
const char *err_str =
condition_error.AsCString("<unknown error>");
LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str);
StreamString strm;
strm << "stopped due to an error evaluating condition of "
"breakpoint ";
bp_loc_sp->GetDescription(&strm, eDescriptionLevelBrief);
strm << ": \"" << bp_loc_sp->GetConditionText() << "\"\n";
strm << err_str;
Debugger::ReportError(
strm.GetString().str(),
exe_ctx.GetTargetRef().GetDebugger().GetID());
} else {
LLDB_LOGF(log,
"Condition evaluated for breakpoint %s on thread "
"0x%llx condition_says_stop: %i.",
loc_desc.GetData(),
static_cast<unsigned long long>(thread_sp->GetID()),
condition_says_stop);
if (condition_says_stop)
actually_hit_any_locations = true;
else {
bp_loc_sp->UndoBumpHitCount();
actually_said_continue = true;
continue;
}
}
}
if (!bp_loc_sp->IgnoreCountShouldStop()) {
actually_said_continue = true;
continue;
}
bool auto_continue_says_stop = true;
if (bp_loc_sp->IsAutoContinue())
{
LLDB_LOGF(log,
"Continuing breakpoint %s as AutoContinue was set.",
loc_desc.GetData());
if (!bp_loc_sp->GetBreakpoint().IsInternal())
thread_sp->SetShouldReportStop(eVoteYes);
auto_continue_says_stop = false;
}
bool callback_says_stop = true;
if (!bp_loc_sp->IsCallbackSynchronous()) {
Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger();
bool old_async = debugger.GetAsyncExecution();
debugger.SetAsyncExecution(true);
callback_says_stop = bp_loc_sp->InvokeCallback(&context);
debugger.SetAsyncExecution(old_async);
if (callback_says_stop && auto_continue_says_stop)
m_should_stop = true;
else
actually_said_continue = true;
}
if (m_should_stop && !bp_loc_sp->GetBreakpoint().IsInternal())
all_stopping_locs_internal = false;
if (callback_says_stop && bp_loc_sp &&
bp_loc_sp->GetBreakpoint().IsOneShot()) {
thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID(
bp_loc_sp->GetBreakpoint().GetID());
}
if (HasTargetRunSinceMe()) {
m_should_stop = false;
actually_said_continue = true;
break;
}
}
if (!actually_said_continue && !m_should_stop) {
m_should_stop = async_should_stop;
}
}
m_should_stop_is_valid = true;
} else {
m_should_stop = true;
m_should_stop_is_valid = true;
actually_hit_any_locations = true;
Log *log_process(GetLog(LLDBLog::Process));
LLDB_LOGF(log_process,
"Process::%s could not find breakpoint site id: %" PRId64
"...",
__FUNCTION__, m_value);
}
if ((!m_should_stop || all_stopping_locs_internal) &&
thread_sp->CompletedPlanOverridesBreakpoint()) {
m_should_stop = true;
thread_sp->CalculatePublicStopInfo();
} else if (!actually_hit_any_locations) {
GetThread()->ResetStopInfo();
LLDB_LOGF(log, "Process::%s all locations failed condition checks.",
__FUNCTION__);
}
LLDB_LOGF(log,
"Process::%s returning from action with m_should_stop: %d.",
__FUNCTION__, m_should_stop);
}
}
private:
bool m_should_stop;
bool m_should_stop_is_valid;
bool m_should_perform_action;
lldb::addr_t m_address;
lldb::break_id_t m_break_id;
bool m_was_all_internal;
bool m_was_one_shot;
#ifdef MS_DEBUGGER
ArchSpec m_arch_spec = ArchSpec();
#endif
};
class StopInfoWatchpoint : public StopInfo {
public:
class WatchpointSentry {
public:
WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp),
watchpoint_sp(w_sp) {
if (process_sp && watchpoint_sp) {
const bool notify = false;
watchpoint_sp->TurnOnEphemeralMode();
process_sp->DisableWatchpoint(watchpoint_sp, notify);
process_sp->AddPreResumeAction(SentryPreResumeAction, this);
}
}
void DoReenable() {
if (process_sp && watchpoint_sp) {
bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode();
watchpoint_sp->TurnOffEphemeralMode();
const bool notify = false;
if (was_disabled) {
process_sp->DisableWatchpoint(watchpoint_sp, notify);
} else {
process_sp->EnableWatchpoint(watchpoint_sp, notify);
}
}
}
~WatchpointSentry() {
DoReenable();
if (process_sp)
process_sp->ClearPreResumeAction(SentryPreResumeAction, this);
}
static bool SentryPreResumeAction(void *sentry_void) {
WatchpointSentry *sentry = (WatchpointSentry *) sentry_void;
sentry->DoReenable();
return true;
}
private:
ProcessSP process_sp;
WatchpointSP watchpoint_sp;
};
StopInfoWatchpoint(Thread &thread, break_id_t watch_id, bool silently_skip_wp)
: StopInfo(thread, watch_id), m_silently_skip_wp(silently_skip_wp) {}
~StopInfoWatchpoint() override = default;
StopReason GetStopReason() const override { return eStopReasonWatchpoint; }
const char *GetDescription() override {
if (m_description.empty()) {
StreamString strm;
strm.Printf("watchpoint %" PRIi64, m_value);
m_description = std::string(strm.GetString());
}
return m_description.c_str();
}
protected:
using StopInfoWatchpointSP = std::shared_ptr<StopInfoWatchpoint>;
class ThreadPlanStepOverWatchpoint : public ThreadPlanStepInstruction {
public:
ThreadPlanStepOverWatchpoint(Thread &thread,
StopInfoWatchpointSP stop_info_sp,
WatchpointSP watch_sp)
: ThreadPlanStepInstruction(thread, false, true, eVoteNoOpinion,
eVoteNoOpinion),
m_stop_info_sp(stop_info_sp), m_watch_sp(watch_sp) {
assert(watch_sp);
}
bool DoWillResume(lldb::StateType resume_state,
bool current_plan) override {
if (resume_state == eStateSuspended)
return true;
if (!m_did_disable_wp) {
GetThread().GetProcess()->DisableWatchpoint(m_watch_sp, false);
m_did_disable_wp = true;
}
return true;
}
bool DoPlanExplainsStop(Event *event_ptr) override {
if (ThreadPlanStepInstruction::DoPlanExplainsStop(event_ptr))
return true;
StopInfoSP stop_info_sp = GetThread().GetPrivateStopInfo();
if (stop_info_sp
&& stop_info_sp->GetStopReason() == eStopReasonWatchpoint)
return true;
return false;
}
void DidPop() override {
m_watch_sp.reset();
}
bool ShouldStop(Event *event_ptr) override {
bool should_stop = ThreadPlanStepInstruction::ShouldStop(event_ptr);
bool plan_done = MischiefManaged();
if (plan_done) {
m_stop_info_sp->SetStepOverPlanComplete();
GetThread().SetStopInfo(m_stop_info_sp);
ResetWatchpoint();
}
return should_stop;
}
bool ShouldRunBeforePublicStop() override {
return true;
}
protected:
void ResetWatchpoint() {
if (!m_did_disable_wp)
return;
m_did_disable_wp = true;
GetThread().GetProcess()->EnableWatchpoint(m_watch_sp, true);
}
private:
StopInfoWatchpointSP m_stop_info_sp;
WatchpointSP m_watch_sp;
bool m_did_disable_wp = false;
};
bool ShouldStopSynchronous(Event *event_ptr) override {
if (m_should_stop_is_valid)
return m_should_stop;
if (m_using_step_over_plan)
return m_step_over_plan_complete;
Log *log = GetLog(LLDBLog::Process);
ThreadSP thread_sp(m_thread_wp.lock());
assert(thread_sp);
if (thread_sp->GetTemporaryResumeState() == eStateSuspended) {
LLDB_LOG(log, "We didn't run but stopped with a StopInfoWatchpoint, we "
"have already handled this one, don't do it again.");
m_should_stop = false;
m_should_stop_is_valid = true;
return m_should_stop;
}
WatchpointSP wp_sp(
thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue()));
if (!wp_sp) {
LLDB_LOGF(log,
"Process::%s could not find watchpoint location id: %" PRId64
"...",
__FUNCTION__, GetValue());
m_should_stop = true;
m_should_stop_is_valid = true;
return true;
}
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
StoppointCallbackContext context(event_ptr, exe_ctx, true);
m_should_stop = wp_sp->ShouldStop(&context);
if (!m_should_stop) {
m_should_stop_is_valid = true;
return m_should_stop;
}
ProcessSP process_sp = exe_ctx.GetProcessSP();
bool wp_triggers_after = process_sp->GetWatchpointReportedAfter();
if (!wp_triggers_after) {
StopInfoWatchpointSP me_as_siwp_sp
= std::static_pointer_cast<StopInfoWatchpoint>(shared_from_this());
ThreadPlanSP step_over_wp_sp(new ThreadPlanStepOverWatchpoint(
*(thread_sp.get()), me_as_siwp_sp, wp_sp));
step_over_wp_sp->SetIsControllingPlan(true);
step_over_wp_sp->SetOkayToDiscard(false);
Status error;
error = thread_sp->QueueThreadPlan(step_over_wp_sp, false);
if (!error.Success()) {
LLDB_LOGF(log, "Could not push our step over watchpoint plan: %s",
error.AsCString());
m_should_stop = true;
m_should_stop_is_valid = true;
return true;
} else {
thread_sp->SetShouldRunBeforePublicStop(true);
m_using_step_over_plan = true;
return false;
}
} else {
m_should_stop_is_valid = true;
return m_should_stop;
}
return m_should_stop;
}
bool ShouldStop(Event *event_ptr) override {
assert(m_should_stop_is_valid);
return m_should_stop;
}
void PerformAction(Event *event_ptr) override {
Log *log = GetLog(LLDBLog::Watchpoints);
m_should_stop = true;
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
WatchpointSP wp_sp(
thread_sp->CalculateTarget()->GetWatchpointList().FindByID(
GetValue()));
if (wp_sp) {
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
ProcessSP process_sp = exe_ctx.GetProcessSP();
WatchpointSentry sentry(process_sp, wp_sp);
if (m_silently_skip_wp) {
m_should_stop = false;
wp_sp->UndoHitCount();
}
if (wp_sp->GetHitCount() <= wp_sp->GetIgnoreCount()) {
m_should_stop = false;
m_should_stop_is_valid = true;
}
Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
if (m_should_stop && wp_sp->GetConditionText() != nullptr) {
ExpressionResults result_code;
EvaluateExpressionOptions expr_options;
expr_options.SetUnwindOnError(true);
expr_options.SetIgnoreBreakpoints(true);
ValueObjectSP result_value_sp;
Status error;
result_code = UserExpression::Evaluate(
exe_ctx, expr_options, wp_sp->GetConditionText(),
llvm::StringRef(), result_value_sp, error);
if (result_code == eExpressionCompleted) {
if (result_value_sp) {
Scalar scalar_value;
if (result_value_sp->ResolveValue(scalar_value)) {
if (scalar_value.ULongLong(1) == 0) {
wp_sp->UndoHitCount();
m_should_stop = false;
} else
m_should_stop = true;
LLDB_LOGF(log,
"Condition successfully evaluated, result is %s.\n",
m_should_stop ? "true" : "false");
} else {
m_should_stop = true;
LLDB_LOGF(
log,
"Failed to get an integer result from the expression.");
}
}
} else {
const char *err_str = error.AsCString("<unknown error>");
LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str);
StreamString strm;
strm << "stopped due to an error evaluating condition of "
"watchpoint ";
wp_sp->GetDescription(&strm, eDescriptionLevelBrief);
strm << ": \"" << wp_sp->GetConditionText() << "\"\n";
strm << err_str;
Debugger::ReportError(strm.GetString().str(),
exe_ctx.GetTargetRef().GetDebugger().GetID());
}
}
if (m_should_stop) {
bool old_async = debugger.GetAsyncExecution();
debugger.SetAsyncExecution(true);
StoppointCallbackContext context(event_ptr, exe_ctx, false);
bool stop_requested = wp_sp->InvokeCallback(&context);
debugger.SetAsyncExecution(old_async);
if (HasTargetRunSinceMe())
m_should_stop = false;
if (m_should_stop && !stop_requested) {
m_should_stop = false;
}
}
if (m_should_stop && !wp_sp->WatchedValueReportable(exe_ctx)) {
wp_sp->UndoHitCount();
m_should_stop = false;
}
if (m_should_stop) {
wp_sp->CaptureWatchedValue(exe_ctx);
Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
StreamSP output_sp = debugger.GetAsyncOutputStream();
if (wp_sp->DumpSnapshots(output_sp.get())) {
output_sp->EOL();
output_sp->Flush();
}
}
} else {
Log *log_process(GetLog(LLDBLog::Process));
LLDB_LOGF(log_process,
"Process::%s could not find watchpoint id: %" PRId64 "...",
__FUNCTION__, m_value);
}
LLDB_LOGF(log,
"Process::%s returning from action with m_should_stop: %d.",
__FUNCTION__, m_should_stop);
m_should_stop_is_valid = true;
}
}
private:
void SetStepOverPlanComplete() {
assert(m_using_step_over_plan);
m_step_over_plan_complete = true;
}
bool m_should_stop = false;
bool m_should_stop_is_valid = false;
bool m_silently_skip_wp = false;
bool m_step_over_plan_complete = false;
bool m_using_step_over_plan = false;
};
class StopInfoUnixSignal : public StopInfo {
public:
StopInfoUnixSignal(Thread &thread, int signo, const char *description,
std::optional<int> code)
: StopInfo(thread, signo), m_code(code) {
SetDescription(description);
}
~StopInfoUnixSignal() override = default;
StopReason GetStopReason() const override { return eStopReasonSignal; }
bool ShouldStopSynchronous(Event *event_ptr) override {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value);
return false;
}
bool ShouldStop(Event *event_ptr) override {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value);
return false;
}
bool DoShouldNotify(Event *event_ptr) override {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
bool should_notify =
thread_sp->GetProcess()->GetUnixSignals()->GetShouldNotify(m_value);
if (should_notify) {
StreamString strm;
strm.Format(
"thread {0:d} received signal: {1}", thread_sp->GetIndexID(),
thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsStringRef(
m_value));
Process::ProcessEventData::AddRestartedReason(event_ptr,
strm.GetData());
}
return should_notify;
}
return true;
}
void WillResume(lldb::StateType resume_state) override {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
if (!thread_sp->GetProcess()->GetUnixSignals()->GetShouldSuppress(
m_value))
thread_sp->SetResumeSignal(m_value);
}
}
const char *GetDescription() override {
if (m_description.empty()) {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
UnixSignalsSP unix_signals = thread_sp->GetProcess()->GetUnixSignals();
StreamString strm;
strm << "signal ";
std::string signal_name =
unix_signals->GetSignalDescription(m_value, m_code);
if (signal_name.size())
strm << signal_name;
else
strm.Printf("%" PRIi64, m_value);
m_description = std::string(strm.GetString());
}
}
return m_description.c_str();
}
private:
std::optional<int> m_code;
};
class StopInfoTrace : public StopInfo {
public:
StopInfoTrace(Thread &thread) : StopInfo(thread, LLDB_INVALID_UID) {}
~StopInfoTrace() override = default;
StopReason GetStopReason() const override { return eStopReasonTrace; }
const char *GetDescription() override {
if (m_description.empty())
return "trace";
else
return m_description.c_str();
}
};
#ifdef MS_DEBUGGER
class StopInfoInternalBreak: public StopInfo {
public:
StopInfoInternalBreak(Thread &thread) : StopInfo(thread, LLDB_INVALID_UID) {}
~StopInfoInternalBreak() override = default;
StopReason GetStopReason() const override { return eStopReasonTrace; }
const char *GetDescription() override {
if (m_description.empty())
return "trace";
else
return m_description.c_str();
}
bool ShouldStop(Event *event_ptr) override {
RefreshDeviceModules();
return false;
}
private:
void RefreshDeviceModules() {
Log *log = GetLog(LLDBLog::DynamicLoader);
ThreadSP thread_sp(m_thread_wp.lock());
if (!thread_sp) {
LLDB_LOG(log, "Empty thread");
return;
}
ProcessSP process_sp(thread_sp->GetProcess());
if (!process_sp) {
LLDB_LOG(log, "Empty process");
return;
}
auto *baton = process_sp->GetDynamicLoader();
DynamicLoaderPOSIXDYLD *const dyld_instance = static_cast<DynamicLoaderPOSIXDYLD *>(baton);
if (!dyld_instance) {
LLDB_LOG(log, "Get empty dyld.");
}
dyld_instance->RefreshDeviceModules();
}
};
#endif
class StopInfoException : public StopInfo {
public:
StopInfoException(Thread &thread, const char *description)
: StopInfo(thread, LLDB_INVALID_UID) {
if (description)
SetDescription(description);
}
~StopInfoException() override = default;
StopReason GetStopReason() const override { return eStopReasonException; }
const char *GetDescription() override {
if (m_description.empty())
return "exception";
else
return m_description.c_str();
}
};
class StopInfoProcessorTrace : public StopInfo {
public:
StopInfoProcessorTrace(Thread &thread, const char *description)
: StopInfo(thread, LLDB_INVALID_UID) {
if (description)
SetDescription(description);
}
~StopInfoProcessorTrace() override = default;
StopReason GetStopReason() const override {
return eStopReasonProcessorTrace;
}
const char *GetDescription() override {
if (m_description.empty())
return "processor trace event";
else
return m_description.c_str();
}
};
class StopInfoThreadPlan : public StopInfo {
public:
StopInfoThreadPlan(ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp,
ExpressionVariableSP &expression_variable_sp)
: StopInfo(plan_sp->GetThread(), LLDB_INVALID_UID), m_plan_sp(plan_sp),
m_return_valobj_sp(return_valobj_sp),
m_expression_variable_sp(expression_variable_sp) {}
~StopInfoThreadPlan() override = default;
StopReason GetStopReason() const override { return eStopReasonPlanComplete; }
const char *GetDescription() override {
if (m_description.empty()) {
StreamString strm;
m_plan_sp->GetDescription(&strm, eDescriptionLevelBrief);
m_description = std::string(strm.GetString());
}
return m_description.c_str();
}
ValueObjectSP GetReturnValueObject() { return m_return_valobj_sp; }
ExpressionVariableSP GetExpressionVariable() {
return m_expression_variable_sp;
}
protected:
bool ShouldStop(Event *event_ptr) override {
if (m_plan_sp)
return m_plan_sp->ShouldStop(event_ptr);
else
return StopInfo::ShouldStop(event_ptr);
}
private:
ThreadPlanSP m_plan_sp;
ValueObjectSP m_return_valobj_sp;
ExpressionVariableSP m_expression_variable_sp;
};
class StopInfoExec : public StopInfo {
public:
StopInfoExec(Thread &thread) : StopInfo(thread, LLDB_INVALID_UID) {}
~StopInfoExec() override = default;
bool ShouldStop(Event *event_ptr) override {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
return thread_sp->GetProcess()->GetStopOnExec();
return false;
}
StopReason GetStopReason() const override { return eStopReasonExec; }
const char *GetDescription() override { return "exec"; }
protected:
void PerformAction(Event *event_ptr) override {
if (m_performed_action)
return;
m_performed_action = true;
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
thread_sp->GetProcess()->DidExec();
}
bool m_performed_action = false;
};
class StopInfoFork : public StopInfo {
public:
StopInfoFork(Thread &thread, lldb::pid_t child_pid, lldb::tid_t child_tid)
: StopInfo(thread, child_pid), m_child_pid(child_pid),
m_child_tid(child_tid) {}
~StopInfoFork() override = default;
bool ShouldStop(Event *event_ptr) override { return false; }
StopReason GetStopReason() const override { return eStopReasonFork; }
const char *GetDescription() override { return "fork"; }
protected:
void PerformAction(Event *event_ptr) override {
if (m_performed_action)
return;
m_performed_action = true;
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
thread_sp->GetProcess()->DidFork(m_child_pid, m_child_tid);
}
bool m_performed_action = false;
private:
lldb::pid_t m_child_pid;
lldb::tid_t m_child_tid;
};
class StopInfoVFork : public StopInfo {
public:
StopInfoVFork(Thread &thread, lldb::pid_t child_pid, lldb::tid_t child_tid)
: StopInfo(thread, child_pid), m_child_pid(child_pid),
m_child_tid(child_tid) {}
~StopInfoVFork() override = default;
bool ShouldStop(Event *event_ptr) override { return false; }
StopReason GetStopReason() const override { return eStopReasonVFork; }
const char *GetDescription() override { return "vfork"; }
protected:
void PerformAction(Event *event_ptr) override {
if (m_performed_action)
return;
m_performed_action = true;
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
thread_sp->GetProcess()->DidVFork(m_child_pid, m_child_tid);
}
bool m_performed_action = false;
private:
lldb::pid_t m_child_pid;
lldb::tid_t m_child_tid;
};
class StopInfoVForkDone : public StopInfo {
public:
StopInfoVForkDone(Thread &thread) : StopInfo(thread, 0) {}
~StopInfoVForkDone() override = default;
bool ShouldStop(Event *event_ptr) override { return false; }
StopReason GetStopReason() const override { return eStopReasonVForkDone; }
const char *GetDescription() override { return "vforkdone"; }
protected:
void PerformAction(Event *event_ptr) override {
if (m_performed_action)
return;
m_performed_action = true;
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp)
thread_sp->GetProcess()->DidVForkDone();
}
bool m_performed_action = false;
};
}
StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
break_id_t break_id) {
return StopInfoSP(new StopInfoBreakpoint(thread, break_id));
}
#ifdef MS_DEBUGGER
StopInfoSP StopInfo::CreateStopReasonWithBreakpointSite(Thread &thread, lldb::BreakpointSiteSP break_site) {
return StopInfoSP(new StopInfoBreakpoint(thread, break_site->GetID(), break_site->GetArchSpec()));
}
StopInfoSP StopInfo::CreateInternalBreakStopReason(Thread &thread) {
return StopInfoSP(new StopInfoInternalBreak(thread));
}
#endif
StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
break_id_t break_id,
bool should_stop) {
return StopInfoSP(new StopInfoBreakpoint(thread, break_id, should_stop));
}
StopInfoSP StopInfo::CreateStopReasonWithWatchpointID(Thread &thread,
break_id_t watch_id,
bool silently_continue) {
return StopInfoSP(
new StopInfoWatchpoint(thread, watch_id, silently_continue));
}
StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo,
const char *description,
std::optional<int> code) {
thread.GetProcess()->GetUnixSignals()->IncrementSignalHitCount(signo);
return StopInfoSP(new StopInfoUnixSignal(thread, signo, description, code));
}
StopInfoSP StopInfo::CreateStopReasonToTrace(Thread &thread) {
return StopInfoSP(new StopInfoTrace(thread));
}
StopInfoSP StopInfo::CreateStopReasonWithPlan(
ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp,
ExpressionVariableSP expression_variable_sp) {
return StopInfoSP(new StopInfoThreadPlan(plan_sp, return_valobj_sp,
expression_variable_sp));
}
StopInfoSP StopInfo::CreateStopReasonWithException(Thread &thread,
const char *description) {
return StopInfoSP(new StopInfoException(thread, description));
}
StopInfoSP StopInfo::CreateStopReasonProcessorTrace(Thread &thread,
const char *description) {
return StopInfoSP(new StopInfoProcessorTrace(thread, description));
}
StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) {
return StopInfoSP(new StopInfoExec(thread));
}
StopInfoSP StopInfo::CreateStopReasonFork(Thread &thread,
lldb::pid_t child_pid,
lldb::tid_t child_tid) {
return StopInfoSP(new StopInfoFork(thread, child_pid, child_tid));
}
StopInfoSP StopInfo::CreateStopReasonVFork(Thread &thread,
lldb::pid_t child_pid,
lldb::tid_t child_tid) {
return StopInfoSP(new StopInfoVFork(thread, child_pid, child_tid));
}
StopInfoSP StopInfo::CreateStopReasonVForkDone(Thread &thread) {
return StopInfoSP(new StopInfoVForkDone(thread));
}
ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) {
if (stop_info_sp &&
stop_info_sp->GetStopReason() == eStopReasonPlanComplete) {
StopInfoThreadPlan *plan_stop_info =
static_cast<StopInfoThreadPlan *>(stop_info_sp.get());
return plan_stop_info->GetReturnValueObject();
} else
return ValueObjectSP();
}
ExpressionVariableSP StopInfo::GetExpressionVariable(StopInfoSP &stop_info_sp) {
if (stop_info_sp &&
stop_info_sp->GetStopReason() == eStopReasonPlanComplete) {
StopInfoThreadPlan *plan_stop_info =
static_cast<StopInfoThreadPlan *>(stop_info_sp.get());
return plan_stop_info->GetExpressionVariable();
} else
return ExpressionVariableSP();
}
lldb::ValueObjectSP
StopInfo::GetCrashingDereference(StopInfoSP &stop_info_sp,
lldb::addr_t *crashing_address) {
if (!stop_info_sp) {
return ValueObjectSP();
}
const char *description = stop_info_sp->GetDescription();
if (!description) {
return ValueObjectSP();
}
ThreadSP thread_sp = stop_info_sp->GetThread();
if (!thread_sp) {
return ValueObjectSP();
}
StackFrameSP frame_sp =
thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
if (!frame_sp) {
return ValueObjectSP();
}
const char address_string[] = "address=";
const char *address_loc = strstr(description, address_string);
if (!address_loc) {
return ValueObjectSP();
}
address_loc += (sizeof(address_string) - 1);
uint64_t address = strtoull(address_loc, nullptr, 0);
if (crashing_address) {
*crashing_address = address;
}
return frame_sp->GuessValueForAddress(address);
}