summaryrefslogtreecommitdiff
path: root/lldb/source/Target/StopInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Target/StopInfo.cpp')
-rw-r--r--lldb/source/Target/StopInfo.cpp1198
1 files changed, 1198 insertions, 0 deletions
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
new file mode 100644
index 0000000000000..28179b7e1ce00
--- /dev/null
+++ b/lldb/source/Target/StopInfo.cpp
@@ -0,0 +1,1198 @@
+//===-- StopInfo.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 <string>
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/Watchpoint.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/UnixSignals.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+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) {
+ // This is a little tricky. We want to count "run and stopped again
+ // before you could ask this question as a "TRUE" answer to
+ // HasTargetRunSinceMe. But we don't want to include any running of the
+ // target done for expressions. So we track both resumes, and resumes
+ // caused by expressions, and check if there are any resumes
+ // NOT caused
+ // by expressions.
+
+ 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;
+}
+
+// StopInfoBreakpoint
+
+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_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_one_shot(false) {
+ StoreBPInfo();
+ }
+
+ ~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) {
+ if (bp_site_sp->GetNumberOfOwners() == 1) {
+ BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(0);
+ if (bp_loc_sp) {
+ m_break_id = bp_loc_sp->GetBreakpoint().GetID();
+ m_was_one_shot = bp_loc_sp->GetBreakpoint().IsOneShot();
+ }
+ }
+ 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;
+ }
+
+ StopReason GetStopReason() const override { return eStopReasonBreakpoint; }
+
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ if (!m_should_stop_is_valid) {
+ // Only check once if we should stop at a breakpoint
+ 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 {
+ 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) {
+ bool all_internal = true;
+
+ for (uint32_t i = 0; i < bp_site_sp->GetNumberOfOwners(); i++) {
+ if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) {
+ all_internal = false;
+ break;
+ }
+ }
+ return !all_internal;
+ }
+ }
+ return true;
+ }
+
+ 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 we have just hit an internal breakpoint, and it has a kind
+ // description, print that instead of the full breakpoint printing:
+ if (bp_site_sp->IsInternal()) {
+ size_t num_owners = bp_site_sp->GetNumberOfOwners();
+ for (size_t idx = 0; idx < num_owners; idx++) {
+ const char *kind = bp_site_sp->GetOwnerAtIndex(idx)
+ ->GetBreakpoint()
+ .GetBreakpointKind();
+ if (kind != nullptr) {
+ m_description.assign(kind);
+ return kind;
+ }
+ }
+ }
+
+ strm.Printf("breakpoint ");
+ bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief);
+ m_description = 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 = strm.GetString();
+ }
+ }
+ }
+ return m_description.c_str();
+ }
+
+protected:
+ bool ShouldStop(Event *event_ptr) override {
+ // This just reports the work done by PerformAction or the synchronous
+ // stop. It should only ever get called after they have had a chance to
+ // run.
+ 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 internal_breakpoint = true;
+
+ ThreadSP thread_sp(m_thread_wp.lock());
+
+ if (thread_sp) {
+ Log *log = lldb_private::GetLogIfAnyCategoriesSet(
+ LIBLLDB_LOG_BREAKPOINTS | LIBLLDB_LOG_STEP);
+
+ if (!thread_sp->IsValid()) {
+ // This shouldn't ever happen, but just in case, don't do more harm.
+ 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;
+
+ if (bp_site_sp) {
+ // Let's copy the owners list out of the site and store them in a local
+ // list. That way if one of the breakpoint actions changes the site,
+ // then we won't be operating on a bad list.
+ BreakpointLocationCollection site_locations;
+ size_t num_owners = bp_site_sp->CopyOwnersList(site_locations);
+
+ if (num_owners == 0) {
+ m_should_stop = true;
+ } else {
+ // We go through each location, and test first its precondition -
+ // this overrides everything. Note, we only do this once per
+ // breakpoint - not once per location... Then check the condition.
+ // If the condition says to stop, then we run the callback for that
+ // location. If that callback says to stop as well, then we set
+ // m_should_stop to true; we are going to stop. But we still want to
+ // give all the breakpoints whose conditions say we are going to stop
+ // a chance to run their callbacks. Of course if any callback
+ // restarts the target by putting "continue" in the callback, then
+ // we're going to restart, without running the rest of the callbacks.
+ // And in this case we will end up not stopping even if another
+ // location said we should stop. But that's better than not running
+ // all the callbacks.
+
+ m_should_stop = false;
+
+ // We don't select threads as we go through them testing breakpoint
+ // conditions and running commands. So we need to set the thread for
+ // expression evaluation here:
+ ThreadList::ExpressionExecutionThreadPusher thread_pusher(thread_sp);
+
+ ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process->GetModIDRef().IsLastResumeForUserExpression()) {
+ // If we are in the middle of evaluating an expression, don't run
+ // asynchronous breakpoint commands or expressions. That could
+ // lead to infinite recursion if the command or condition re-calls
+ // the function with this breakpoint.
+ // TODO: We can keep a list of the breakpoints we've seen while
+ // running expressions in the nested
+ // PerformAction calls that can arise when the action runs a
+ // function that hits another breakpoint, and only stop running
+ // commands when we see the same breakpoint hit a second time.
+
+ m_should_stop_is_valid = true;
+
+ // It is possible that the user has a breakpoint at the same site
+ // as the completed plan had (e.g. user has a breakpoint
+ // on a module entry point, and `ThreadPlanCallFunction` ends
+ // also there). We can't find an internal breakpoint in the loop
+ // later because it was already removed on the plan completion.
+ // So check if the plan was completed, and stop if so.
+ 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 (ignoring_breakpoints) {
+ m_should_stop = false;
+ // Internal breakpoints will always stop.
+ for (size_t j = 0; j < num_owners; j++) {
+ lldb::BreakpointLocationSP bp_loc_sp =
+ bp_site_sp->GetOwnerAtIndex(j);
+ if (bp_loc_sp->GetBreakpoint().IsInternal()) {
+ m_should_stop = true;
+ break;
+ }
+ }
+ } else {
+ m_should_stop = true;
+ }
+ LLDB_LOGF(log,
+ "StopInfoBreakpoint::PerformAction - in expression, "
+ "continuing: %s.",
+ m_should_stop ? "true" : "false");
+ process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: hit breakpoint while running function, skipping "
+ "commands and conditions to prevent recursion.\n");
+ return;
+ }
+
+ StoppointCallbackContext context(event_ptr, exe_ctx, false);
+
+ // For safety's sake let's also grab an extra reference to the
+ // breakpoint owners of the locations we're going to examine, since
+ // the locations are going to have to get back to their breakpoints,
+ // and the locations don't keep their owners alive. I'm just
+ // sticking the BreakpointSP's in a vector since I'm only using it to
+ // locally increment their retain counts.
+
+ std::vector<lldb::BreakpointSP> location_owners;
+
+ for (size_t j = 0; j < num_owners; j++) {
+ BreakpointLocationSP loc(site_locations.GetByIndex(j));
+ location_owners.push_back(loc->GetBreakpoint().shared_from_this());
+ }
+
+ for (size_t j = 0; j < num_owners; j++) {
+ lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j);
+ StreamString loc_desc;
+ if (log) {
+ bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief);
+ }
+ // If another action disabled this breakpoint or its location, then
+ // don't run the actions.
+ if (!bp_loc_sp->IsEnabled() ||
+ !bp_loc_sp->GetBreakpoint().IsEnabled())
+ continue;
+
+ // The breakpoint site may have many locations associated with it,
+ // not all of them valid for this thread. Skip the ones that
+ // aren't:
+ if (!bp_loc_sp->ValidForThisThread(thread_sp.get())) {
+ 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;
+ }
+
+ internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal();
+
+ // First run the precondition, but since the precondition is per
+ // breakpoint, only run it once per breakpoint.
+ 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)
+ continue;
+
+ // Next run the condition for the breakpoint. If that says we
+ // should stop, then we'll run the callback for the breakpoint. If
+ // the callback says we shouldn't stop that will win.
+
+ if (bp_loc_sp->GetConditionText() != nullptr) {
+ Status condition_error;
+ bool condition_says_stop =
+ bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error);
+
+ if (!condition_error.Success()) {
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP error_sp = debugger.GetAsyncErrorStream();
+ error_sp->Printf("Stopped due to an error evaluating condition "
+ "of breakpoint ");
+ bp_loc_sp->GetDescription(error_sp.get(),
+ eDescriptionLevelBrief);
+ error_sp->Printf(": \"%s\"", bp_loc_sp->GetConditionText());
+ error_sp->EOL();
+ const char *err_str =
+ condition_error.AsCString("<Unknown Error>");
+ LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str);
+
+ error_sp->PutCString(err_str);
+ error_sp->EOL();
+ error_sp->Flush();
+ } else {
+ LLDB_LOGF(log,
+ "Condition evaluated for breakpoint %s on thread "
+ "0x%llx conditon_says_stop: %i.",
+ loc_desc.GetData(),
+ static_cast<unsigned long long>(thread_sp->GetID()),
+ condition_says_stop);
+ if (!condition_says_stop) {
+ // We don't want to increment the hit count of breakpoints if
+ // the condition fails. We've already bumped it by the time
+ // we get here, so undo the bump:
+ bp_loc_sp->UndoBumpHitCount();
+ continue;
+ }
+ }
+ }
+
+ // Check the auto-continue bit on the location, do this before the
+ // callback since it may change this, but that would be for the
+ // NEXT hit. Note, you might think you could check auto-continue
+ // before the condition, and not evaluate the condition if it says
+ // to continue. But failing the condition means the breakpoint was
+ // effectively NOT HIT. So these two states are different.
+ bool auto_continue_says_stop = true;
+ if (bp_loc_sp->IsAutoContinue())
+ {
+ LLDB_LOGF(log,
+ "Continuing breakpoint %s as AutoContinue was set.",
+ loc_desc.GetData());
+ // We want this stop reported, so you will know we auto-continued
+ // but only for external breakpoints:
+ if (!internal_breakpoint)
+ thread_sp->SetShouldReportStop(eVoteYes);
+ auto_continue_says_stop = false;
+ }
+
+ bool callback_says_stop = true;
+
+ // FIXME: For now the callbacks have to run in async mode - the
+ // first time we restart we need
+ // to get out of there. So set it here.
+ // When we figure out how to nest breakpoint hits then this will
+ // change.
+
+ 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;
+
+ // If we are going to stop for this breakpoint, then remove the
+ // breakpoint.
+ if (callback_says_stop && bp_loc_sp &&
+ bp_loc_sp->GetBreakpoint().IsOneShot()) {
+ thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID(
+ bp_loc_sp->GetBreakpoint().GetID());
+ }
+ // Also make sure that the callback hasn't continued the target. If
+ // it did, when we'll set m_should_start to false and get out of
+ // here.
+ if (HasTargetRunSinceMe()) {
+ m_should_stop = false;
+ break;
+ }
+ }
+ }
+ // We've figured out what this stop wants to do, so mark it as valid so
+ // we don't compute it again.
+ m_should_stop_is_valid = true;
+ } else {
+ m_should_stop = true;
+ m_should_stop_is_valid = true;
+ Log *log_process(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ LLDB_LOGF(log_process,
+ "Process::%s could not find breakpoint site id: %" PRId64
+ "...",
+ __FUNCTION__, m_value);
+ }
+
+ if ((!m_should_stop || internal_breakpoint) &&
+ thread_sp->CompletedPlanOverridesBreakpoint()) {
+
+ // Override should_stop decision when we have completed step plan
+ // additionally to the breakpoint
+ m_should_stop = true;
+
+ // We know we're stopping for a completed plan and we don't want to
+ // show the breakpoint stop, so compute the public stop info immediately
+ // here.
+ thread_sp->CalculatePublicStopInfo();
+ }
+
+ 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; // Since we are trying to preserve the "state"
+ // of the system even if we run functions
+ // etc. behind the users backs, we need to make sure we only REALLY perform
+ // the action once.
+ lldb::addr_t m_address; // We use this to capture the breakpoint site address
+ // when we create the StopInfo,
+ // in case somebody deletes it between the time the StopInfo is made and the
+ // description is asked for.
+ lldb::break_id_t m_break_id;
+ bool m_was_one_shot;
+};
+
+// StopInfoWatchpoint
+
+class StopInfoWatchpoint : public StopInfo {
+public:
+ // Make sure watchpoint is properly disabled and subsequently enabled while
+ // performing watchpoint actions.
+ 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.get(), 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.get(), notify);
+ } else {
+ process_sp->EnableWatchpoint(watchpoint_sp.get(), 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,
+ lldb::addr_t watch_hit_addr)
+ : StopInfo(thread, watch_id), m_should_stop(false),
+ m_should_stop_is_valid(false), m_watch_hit_addr(watch_hit_addr) {}
+
+ ~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 = strm.GetString();
+ }
+ return m_description.c_str();
+ }
+
+protected:
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ // ShouldStop() method is idempotent and should not affect hit count. See
+ // Process::RunPrivateStateThread()->Process()->HandlePrivateEvent()
+ // -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()->
+ // Thread::ShouldStop()->ThreadPlanBase::ShouldStop()->
+ // StopInfoWatchpoint::ShouldStop() and
+ // Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()->
+ // StopInfoWatchpoint::PerformAction().
+ if (m_should_stop_is_valid)
+ return m_should_stop;
+
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ WatchpointSP wp_sp(
+ thread_sp->CalculateTarget()->GetWatchpointList().FindByID(
+ GetValue()));
+ if (wp_sp) {
+ // Check if we should stop at a watchpoint.
+ ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
+ StoppointCallbackContext context(event_ptr, exe_ctx, true);
+ m_should_stop = wp_sp->ShouldStop(&context);
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ 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 m_should_stop;
+ }
+
+ bool ShouldStop(Event *event_ptr) override {
+ // This just reports the work done by PerformAction or the synchronous
+ // stop. It should only ever get called after they have had a chance to
+ // run.
+ assert(m_should_stop_is_valid);
+ return m_should_stop;
+ }
+
+ void PerformAction(Event *event_ptr) override {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS);
+ // We're going to calculate if we should stop or not in some way during the
+ // course of this code. Also by default we're going to stop, so set that
+ // here.
+ 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();
+
+ {
+ // check if this process is running on an architecture where
+ // watchpoints trigger before the associated instruction runs. if so,
+ // disable the WP, single-step and then re-enable the watchpoint
+ if (process_sp) {
+ uint32_t num;
+ bool wp_triggers_after;
+
+ if (process_sp->GetWatchpointSupportInfo(num, wp_triggers_after)
+ .Success()) {
+ if (!wp_triggers_after) {
+ // We need to preserve the watch_index before watchpoint is
+ // disable. Since Watchpoint::SetEnabled will clear the watch
+ // index. This will fix TestWatchpointIter failure
+ Watchpoint *wp = wp_sp.get();
+ uint32_t watch_index = wp->GetHardwareIndex();
+ process_sp->DisableWatchpoint(wp, false);
+ StopInfoSP stored_stop_info_sp = thread_sp->GetStopInfo();
+ assert(stored_stop_info_sp.get() == this);
+
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(
+ thread_sp->QueueThreadPlanForStepSingleInstruction(
+ false, // step-over
+ false, // abort_other_plans
+ true, // stop_other_threads
+ new_plan_status));
+ if (new_plan_sp && new_plan_status.Success()) {
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+ new_plan_sp->SetPrivate(true);
+ }
+ process_sp->GetThreadList().SetSelectedThreadByID(
+ thread_sp->GetID());
+ process_sp->ResumeSynchronous(nullptr);
+ process_sp->GetThreadList().SetSelectedThreadByID(
+ thread_sp->GetID());
+ thread_sp->SetStopInfo(stored_stop_info_sp);
+ process_sp->EnableWatchpoint(wp, false);
+ wp->SetHardwareIndex(watch_index);
+ }
+ }
+ }
+ }
+
+ // This sentry object makes sure the current watchpoint is disabled
+ // while performing watchpoint actions, and it is then enabled after we
+ // are finished.
+ WatchpointSentry sentry(process_sp, wp_sp);
+
+ /*
+ * MIPS: Last 3bits of the watchpoint address are masked by the kernel.
+ * For example:
+ * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is
+ * set at 'm', then
+ * watch exception is generated even when 'n' is read/written. To handle
+ * this case,
+ * server emulates the instruction at PC and finds the base address of
+ * the load/store
+ * instruction and appends it in the description of the stop-info
+ * packet. If watchpoint
+ * is not set on this address by user then this do not stop.
+ */
+ if (m_watch_hit_addr != LLDB_INVALID_ADDRESS) {
+ WatchpointSP wp_hit_sp =
+ thread_sp->CalculateTarget()->GetWatchpointList().FindByAddress(
+ m_watch_hit_addr);
+ if (!wp_hit_sp) {
+ m_should_stop = false;
+ wp_sp->IncrementFalseAlarmsAndReviseHitCount();
+ }
+ }
+
+ // TODO: This condition should be checked in the synchronous part of the
+ // watchpoint code
+ // (Watchpoint::ShouldStop), so that we avoid pulling an event even if
+ // the watchpoint fails the ignore count condition. It is moved here
+ // temporarily, because for archs with
+ // watchpoint_exceptions_received=before, the code in the previous
+ // lines takes care of moving the inferior to next PC. We have to check
+ // the ignore count condition after this is done, otherwise we will hit
+ // same watchpoint multiple times until we pass ignore condition, but
+ // we won't actually be ignoring them.
+ if (wp_sp->GetHitCount() <= wp_sp->GetIgnoreCount())
+ m_should_stop = false;
+
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+
+ if (m_should_stop && wp_sp->GetConditionText() != nullptr) {
+ // 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.
+ 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) {
+ // We have been vetoed. This takes precedence over querying
+ // the watchpoint whether it should stop (aka ignore count
+ // and friends). See also StopInfoWatchpoint::ShouldStop()
+ // as well as Process::ProcessEventData::DoOnRemoval().
+ 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 {
+ StreamSP error_sp = debugger.GetAsyncErrorStream();
+ error_sp->Printf(
+ "Stopped due to an error evaluating condition of watchpoint ");
+ wp_sp->GetDescription(error_sp.get(), eDescriptionLevelBrief);
+ error_sp->Printf(": \"%s\"", wp_sp->GetConditionText());
+ error_sp->EOL();
+ const char *err_str = error.AsCString("<Unknown Error>");
+ LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str);
+
+ error_sp->PutCString(err_str);
+ error_sp->EOL();
+ error_sp->Flush();
+ // If the condition fails to be parsed or run, we should stop.
+ m_should_stop = true;
+ }
+ }
+
+ // If the condition says to stop, we run the callback to further decide
+ // whether to stop.
+ if (m_should_stop) {
+ // FIXME: For now the callbacks have to run in async mode - the
+ // first time we restart we need
+ // to get out of there. So set it here.
+ // When we figure out how to nest watchpoint hits then this will
+ // change.
+
+ 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);
+
+ // Also make sure that the callback hasn't continued the target. If
+ // it did, when we'll set m_should_stop to false and get out of here.
+ if (HasTargetRunSinceMe())
+ m_should_stop = false;
+
+ if (m_should_stop && !stop_requested) {
+ // We have been vetoed by the callback mechanism.
+ m_should_stop = false;
+ }
+ }
+ // Finally, if we are going to stop, print out the new & old values:
+ if (m_should_stop) {
+ wp_sp->CaptureWatchedValue(exe_ctx);
+
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP output_sp = debugger.GetAsyncOutputStream();
+ wp_sp->DumpSnapshots(output_sp.get());
+ output_sp->EOL();
+ output_sp->Flush();
+ }
+
+ } else {
+ Log *log_process(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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:
+ bool m_should_stop;
+ bool m_should_stop_is_valid;
+ lldb::addr_t m_watch_hit_addr;
+};
+
+// StopInfoUnixSignal
+
+class StopInfoUnixSignal : public StopInfo {
+public:
+ StopInfoUnixSignal(Thread &thread, int signo, const char *description)
+ : StopInfo(thread, signo) {
+ 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;
+ }
+
+ // If should stop returns false, check if we should notify of this event
+ 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.Printf(
+ "thread %d received signal: %s", thread_sp->GetIndexID(),
+ thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString(
+ 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) {
+ StreamString strm;
+ const char *signal_name =
+ thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString(
+ m_value);
+ if (signal_name)
+ strm.Printf("signal %s", signal_name);
+ else
+ strm.Printf("signal %" PRIi64, m_value);
+ m_description = strm.GetString();
+ }
+ }
+ return m_description.c_str();
+ }
+};
+
+// StopInfoTrace
+
+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();
+ }
+};
+
+// StopInfoException
+
+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();
+ }
+};
+
+// StopInfoThreadPlan
+
+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 = 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;
+};
+
+// StopInfoExec
+
+class StopInfoExec : public StopInfo {
+public:
+ StopInfoExec(Thread &thread)
+ : StopInfo(thread, LLDB_INVALID_UID), m_performed_action(false) {}
+
+ ~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 {
+ // Only perform the action once
+ 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;
+};
+
+} // namespace lldb_private
+
+StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
+ break_id_t break_id) {
+ return StopInfoSP(new StopInfoBreakpoint(thread, break_id));
+}
+
+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,
+ lldb::addr_t watch_hit_addr) {
+ return StopInfoSP(new StopInfoWatchpoint(thread, watch_id, watch_hit_addr));
+}
+
+StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo,
+ const char *description) {
+ return StopInfoSP(new StopInfoUnixSignal(thread, signo, description));
+}
+
+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::CreateStopReasonWithExec(Thread &thread) {
+ return StopInfoSP(new StopInfoExec(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();
+
+ 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);
+}