blob: 61d0c1ae9fa872d266d9372f4e081af6cd70577c [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/client/until_thread_controller.h"
#include <lib/syslog/cpp/macros.h>
#include "src/developer/debug/zxdb/client/breakpoint.h"
#include "src/developer/debug/zxdb/client/breakpoint_settings.h"
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/system.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/symbols/input_location.h"
namespace zxdb {
UntilThreadController::UntilThreadController(std::vector<InputLocation> locations)
: ThreadController(), locations_(std::move(locations)), weak_factory_(this) {}
UntilThreadController::UntilThreadController(std::vector<InputLocation> locations,
FrameFingerprint newest_frame, FrameComparison cmp)
: ThreadController(),
locations_(std::move(locations)),
threshold_frame_(newest_frame),
comparison_(cmp),
weak_factory_(this) {}
UntilThreadController::~UntilThreadController() {
if (breakpoint_)
GetSystem()->DeleteBreakpoint(breakpoint_.get());
}
void UntilThreadController::InitWithThread(Thread* thread, fit::callback<void(const Err&)> cb) {
SetThread(thread);
BreakpointSettings settings;
settings.scope = ExecutionScope(thread);
settings.locations = std::move(locations_);
// Frame-tied triggers can't be one-shot because we need to check the stack every time it
// triggers. In the non-frame case the one-shot breakpoint will be slightly more efficient.
settings.one_shot = !threshold_frame_.is_valid();
breakpoint_ = GetSystem()->CreateNewInternalBreakpoint()->GetWeakPtr();
breakpoint_->SetSettings(settings);
if (breakpoint_->GetLocations().empty()) {
// Setting the breakpoint may have resolved to no locations and the breakpoint is now pending.
// For "until" this is not good because if the user does "until SomethingNonexistant" they would
// like to see the error rather than have the thread transparently continue without stopping.
cb(Err("Destination to run until matched no location."));
}
}
ThreadController::ContinueOp UntilThreadController::GetContinueOp() {
// Stopping the thread is done via a breakpoint, so the thread can always be resumed with no
// qualifications.
return ContinueOp::Continue();
}
ThreadController::StopOp UntilThreadController::OnThreadStop(
debug_ipc::ExceptionType stop_type,
const std::vector<fxl::WeakPtr<Breakpoint>>& hit_breakpoints) {
// Other controllers such as the StepOverRangeThreadController can use this as a sub-controller.
// If the controllers don't care about breakpoint set failures, they may start using the thread
// right away without waiting for the callback in InitWithThread() to asynchronously complete
// (indicating the breakpoint was set successful).
//
// This is generally fine, we just need to be careful not to do anything in OnBreakpointSet() that
// the code in this function depends on.
if (!breakpoint_) {
// Our internal breakpoint shouldn't be deleted out from under ourselves.
FX_NOTREACHED();
return kUnexpected;
}
// Only care about stops if one of the breakpoints hit was ours. Don't check the stop_type since
// as long as the breakpoint was hit, we don't care how the program got there (it could have
// single-stepped to the breakpoint).
Breakpoint* our_breakpoint = breakpoint_.get();
bool is_our_breakpoint = true;
for (auto& hit : hit_breakpoints) {
if (hit && hit.get() == our_breakpoint) {
is_our_breakpoint = true;
break;
}
}
if (!is_our_breakpoint) {
Log("Not our breakpoint.");
return kUnexpected;
}
if (!threshold_frame_.is_valid()) {
Log("No frame check required.");
return kStopDone;
}
const Stack& stack = thread()->GetStack();
if (stack.empty()) {
FX_NOTREACHED(); // Should always have a current frame on stop.
return kUnexpected;
}
// If inline frames are ambiguous and the one we want is one of the ambiguous ones, use it.
if (comparison_ == kRunUntilEqualOrOlderFrame)
SetInlineFrameIfAmbiguous(InlineFrameIs::kEqual, threshold_frame_);
else
SetInlineFrameIfAmbiguous(InlineFrameIs::kOneBefore, threshold_frame_);
// Check frames.
FrameFingerprint current_frame = stack.GetFrameFingerprint(0);
if (FrameFingerprint::Newer(current_frame, threshold_frame_)) {
Log("In newer frame, ignoring.");
return kContinue;
}
if (comparison_ == kRunUntilOlderFrame && current_frame == threshold_frame_) {
// In kRunUntilOlderFrame mode, the threshold frame fingerprint itself is one that should
// continue running.
Log("In threshold frame, ignoring.");
return kContinue;
}
Log("Found target frame (or older).");
return kStopDone;
}
System* UntilThreadController::GetSystem() { return &thread()->session()->system(); }
Target* UntilThreadController::GetTarget() { return thread()->GetProcess()->GetTarget(); }
} // namespace zxdb