aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Target/StopInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Target/StopInfo.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Target/StopInfo.cpp1494
1 files changed, 1494 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Target/StopInfo.cpp b/contrib/llvm-project/lldb/source/Target/StopInfo.cpp
new file mode 100644
index 000000000000..95f78056b164
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/StopInfo.cpp
@@ -0,0 +1,1494 @@
+//===-- StopInfo.cpp ------------------------------------------------------===//
+//
+// 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/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"
+
+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_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();
+ }
+
+ ~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;
+ }
+
+ 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 = 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 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_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 {
+ // 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 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()) {
+ // 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;
+ // Breakpoints that fail their condition check are not considered to
+ // have been hit. If the only locations at this site have failed their
+ // conditions, we should change the stop-info to none. Otherwise, if we
+ // hit another breakpoint on a different thread which does stop, users
+ // will see a breakpont hit with a failed condition, which is wrong.
+ // Use this variable to tell us if that is true.
+ bool actually_hit_any_locations = false;
+ if (bp_site_sp) {
+ // Let's copy the constituents 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_constituents =
+ bp_site_sp->CopyConstituentsList(site_locations);
+
+ if (num_constituents == 0) {
+ m_should_stop = true;
+ actually_hit_any_locations = true; // We're going to stop, don't
+ // change the stop info.
+ } 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.
+
+ // There's one other complication here. We may have run an async
+ // breakpoint callback that said we should stop. We only want to
+ // override that if another breakpoint action says we shouldn't
+ // stop. If nobody else has an opinion, then we should stop if the
+ // async callback says we should. An example of this is the async
+ // shared library load notification breakpoint and the setting
+ // stop-on-sharedlibrary-events.
+ // We'll keep the async value in async_should_stop, and track whether
+ // anyone said we should NOT stop in actually_said_continue.
+ 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;
+
+ // 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().IsRunningExpression()) {
+ // 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();
+ // Internal breakpoints should be allowed to do their job, we
+ // can make sure they don't do anything that would cause recursive
+ // command execution:
+ 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);
+
+ // For safety's sake let's also grab an extra reference to the
+ // breakpoint constituents 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 constituents 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_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 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)) {
+ 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;
+ }
+
+ // 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) {
+ actually_said_continue = true;
+ 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)
+ 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()) {
+ // If the condition fails to evaluate, we are going to stop
+ // at it, so the location was hit.
+ 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 {
+ // 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();
+ actually_said_continue = true;
+ continue;
+ }
+ }
+ }
+
+ // We've done all the checks whose failure means "we consider lldb
+ // not to have hit the breakpoint". Now we're going to check for
+ // conditions that might continue after hitting. Start with the
+ // ignore count:
+ if (!bp_loc_sp->IgnoreCountShouldStop()) {
+ actually_said_continue = true;
+ 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 (!bp_loc_sp->GetBreakpoint().IsInternal())
+ 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.
+
+ // Don't run async callbacks in PerformAction. They have already
+ // been taken into account with async_should_stop.
+ 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 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;
+ actually_said_continue = true;
+ break;
+ }
+ }
+ // At this point if nobody actually told us to continue, we should
+ // give the async breakpoint callback a chance to weigh in:
+ if (!actually_said_continue && !m_should_stop) {
+ m_should_stop = async_should_stop;
+ }
+ }
+ // 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;
+ 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()) {
+
+ // 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();
+ } else if (!actually_hit_any_locations) {
+ // In the end, we didn't actually have any locations that passed their
+ // "was I hit" checks. So say we aren't stopped.
+ 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; // 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_all_internal;
+ 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, 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>;
+ // This plan is used to orchestrate stepping over the watchpoint for
+ // architectures (e.g. ARM) that report the watch before running the watched
+ // access. This is the sort of job you have to defer to the thread plans,
+ // if you try to do it directly in the stop info and there are other threads
+ // that needed to process this stop you will have yanked control away from
+ // them and they won't behave correctly.
+ 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();
+ // lldb-server resets the stop info for threads that didn't get to run,
+ // so we might have not gotten to run, but still have a watchpoint stop
+ // reason, in which case this will indeed be for us.
+ if (stop_info_sp
+ && stop_info_sp->GetStopReason() == eStopReasonWatchpoint)
+ return true;
+ return false;
+ }
+
+ void DidPop() override {
+ // Don't artifically keep the watchpoint alive.
+ 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 we are running our step-over the watchpoint plan, stop if it's done
+ // and continue if it's not:
+ if (m_should_stop_is_valid)
+ return m_should_stop;
+
+ // If we are running our step over plan, then stop here and let the regular
+ // ShouldStop figure out what we should do: Otherwise, give our plan
+ // more time to get run:
+ 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) {
+ // This is the second firing of a watchpoint so don't process it again.
+ 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 we can no longer find the watchpoint, we just have to stop:
+ 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) {
+ // This won't happen at present because we only allow one watchpoint per
+ // watched range. So we won't stop at a watched address with a disabled
+ // watchpoint. If we start allowing overlapping watchpoints, then we
+ // will have to make watchpoints be real "WatchpointSite" and delegate to
+ // all the watchpoints sharing the site. In that case, the code below
+ // would be the right thing to do.
+ m_should_stop_is_valid = true;
+ return m_should_stop;
+ }
+ // If this is a system where we need to execute the watchpoint by hand
+ // after the hit, queue a thread plan to do that, and then say not to stop.
+ // Otherwise, let the async action figure out whether the watchpoint should
+ // stop
+
+ ProcessSP process_sp = exe_ctx.GetProcessSP();
+ bool wp_triggers_after = process_sp->GetWatchpointReportedAfter();
+
+ if (!wp_triggers_after) {
+ // We have to step over the watchpoint before we know what to do:
+ 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));
+ // When this plan is done we want to stop, so set this as a Controlling
+ // plan.
+ step_over_wp_sp->SetIsControllingPlan(true);
+ step_over_wp_sp->SetOkayToDiscard(false);
+
+ Status error;
+ error = thread_sp->QueueThreadPlan(step_over_wp_sp, false);
+ // If we couldn't push the thread plan, just stop here:
+ 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 {
+ // Otherwise, don't set m_should_stop, we don't know that yet. Just
+ // say we should continue, and tell the thread we really should do so:
+ thread_sp->SetShouldRunBeforePublicStop(true);
+ m_using_step_over_plan = true;
+ return false;
+ }
+ } else {
+ // We didn't have to do anything special
+ m_should_stop_is_valid = true;
+ return m_should_stop;
+ }
+
+ 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 = GetLog(LLDBLog::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) {
+ // This sentry object makes sure the current watchpoint is disabled
+ // while performing watchpoint actions, and it is then enabled after we
+ // are finished.
+ 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) {
+ // 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) {
+ // The condition failed, which we consider "not having hit
+ // the watchpoint" so undo the hit count here.
+ 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 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;
+ }
+ }
+
+ // Don't stop if the watched region value is unmodified, and
+ // this is a Modify-type watchpoint.
+ if (m_should_stop && !wp_sp->WatchedValueReportable(exe_ctx)) {
+ wp_sp->UndoHitCount();
+ 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();
+ 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;
+ // A false watchpoint hit has happened -
+ // the thread stopped with a watchpoint
+ // hit notification, but the watched region
+ // was not actually accessed (as determined
+ // by the gdb stub we're talking to).
+ // Continue past this watchpoint without
+ // notifying the user; on some targets this
+ // may mean disable wp, instruction step,
+ // re-enable wp, continue.
+ // On others, just continue.
+ bool m_silently_skip_wp = false;
+ bool m_step_over_plan_complete = false;
+ bool m_using_step_over_plan = false;
+};
+
+// StopInfoUnixSignal
+
+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;
+ }
+
+ // 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.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:
+ // In siginfo_t terms, if m_value is si_signo, m_code is si_code.
+ std::optional<int> m_code;
+};
+
+// 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();
+ }
+};
+
+// StopInfoProcessorTrace
+
+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();
+ }
+};
+
+// 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 = 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;
+};
+
+// StopInfoExec
+
+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 {
+ // 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 = false;
+};
+
+// StopInfoFork
+
+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 {
+ // 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()->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;
+};
+
+// StopInfoVFork
+
+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 {
+ // 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()->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;
+};
+
+// StopInfoVForkDone
+
+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 {
+ // 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()->DidVForkDone();
+ }
+
+ bool m_performed_action = false;
+};
+
+} // 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));
+}
+
+// LWP_TODO: We'll need a CreateStopReasonWithWatchpointResourceID akin
+// to CreateStopReasonWithBreakpointSiteID
+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);
+}