blob: f1531ba605f66b2f3e24f082457b317b81a4305e [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/finish_physical_frame_thread_controller.h"
#include <lib/syslog/cpp/macros.h>
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/client/until_thread_controller.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/symbols/function.h"
namespace zxdb {
FinishPhysicalFrameThreadController::FinishPhysicalFrameThreadController(Stack& stack,
size_t frame_to_finish,
FunctionReturnCallback cb)
: frame_to_finish_(frame_to_finish),
function_return_callback_(std::move(cb)),
weak_factory_(this) {
FX_DCHECK(frame_to_finish < stack.size());
FX_DCHECK(!stack[frame_to_finish]->IsInline());
// Save the symbol being finished for later notifications.
function_being_finished_ = stack[frame_to_finish]->GetLocation().symbol();
#ifndef NDEBUG
// Stash for validation later.
frame_ip_ = stack[frame_to_finish]->GetAddress();
#endif
}
FinishPhysicalFrameThreadController::~FinishPhysicalFrameThreadController() = default;
FinishPhysicalFrameThreadController::StopOp FinishPhysicalFrameThreadController::OnThreadStop(
debug_ipc::ExceptionType stop_type,
const std::vector<fxl::WeakPtr<Breakpoint>>& hit_breakpoints) {
if (until_controller_) {
if (auto op = until_controller_->OnThreadStop(stop_type, hit_breakpoints); op != kStopDone)
return op;
// The until controller said to stop. The CPU is now at the address immediately following the
// function call. The tricky part is that this could be the first instruction of a new inline
// function following the call and the stack will now contain that inline expansion. Our caller
// expects to be in the frame that called the function being stepped out of.
//
// Rolling ambiguous frames back to "one before" the frame fingerprint being finished might
// sound right but isn't because that fingerprint won't exist any more (we just exited it).
//
// For a frame to be ambiguous the IP must be at the first instruction of a range of that
// inline. By virtue of just returning from a function call, we know any inline functions that
// start immediately after the call weren't in the stack of the original call.
Stack& stack = thread()->GetStack();
stack.SetHideAmbiguousInlineFrameCount(stack.GetAmbiguousInlineFrameCount());
if (function_return_callback_) {
function_return_callback_(
FunctionReturnInfo{.thread = thread(), .symbol = function_being_finished_});
}
return kStopDone;
}
// When there's no "until" controller, this controller just said "continue" to step out of the
// oldest stack frame. Therefore, any stops at this level aren't ours.
return kContinue;
}
void FinishPhysicalFrameThreadController::InitWithThread(Thread* thread,
fit::callback<void(const Err&)> cb) {
SetThread(thread);
Stack& stack = thread->GetStack();
#ifndef NDEBUG
// The stack must not have changed from construction to this call. There are no async requests
// that need to happen during this time, just registration with the thread. Otherwise the frame
// fingerprint computation needs to be scheduled in the constructor which complicates the async
// states of this function (though it's possible in the future if necessary).
FX_DCHECK(stack.size() > frame_to_finish_);
FX_DCHECK(stack[frame_to_finish_]->GetAddress() == frame_ip_);
#endif
if (enable_debug_logging()) {
auto function = stack[frame_to_finish_]->GetLocation().symbol().Get()->As<Function>();
if (function)
Log("Finishing %s", function->GetFullName().c_str());
else
Log("Finshing unsymbolized function");
}
InitWithFingerprint(stack.GetFrameFingerprint(frame_to_finish_));
cb(Err());
}
ThreadController::ContinueOp FinishPhysicalFrameThreadController::GetContinueOp() {
// Once this thread starts running, the frame index is invalid.
frame_to_finish_ = static_cast<size_t>(-1);
if (until_controller_)
return until_controller_->GetContinueOp();
// This will happen when there's no previous frame so there's no address to return to.
// Unconditionally continue.
return ContinueOp::Continue();
}
void FinishPhysicalFrameThreadController::InitWithFingerprint(FrameFingerprint fingerprint) {
if (frame_to_finish_ >= thread()->GetStack().size() - 1) {
// Finishing the last frame. There is no return address so there's no setup necessary to step,
// just continue.
return;
}
// The address we're returning to is that of the previous frame,
uint64_t to_addr = thread()->GetStack()[frame_to_finish_ + 1]->GetAddress();
if (!to_addr)
return; // Previous stack frame is null, just continue.
until_controller_ = std::make_unique<UntilThreadController>(
std::vector<InputLocation>{InputLocation(to_addr)}, fingerprint,
UntilThreadController::kRunUntilOlderFrame);
// Give the "until" controller a dummy callback and execute the callback ASAP. The until
// controller executes the callback once it knows that the breakpoint set has been complete
// (round-trip to the target system).
//
// Since we provide an address there's no weirdness with symbols and we don't have to worry about
// matching 0 locations. If the breakpoint set fails, the caller address is invalid and stepping
// is impossible so it doesn't matter. We can run faster without waiting for the round-trip, and
// the IPC will serialize so the breakpoint set happens before the thread resume.
until_controller_->InitWithThread(thread(), [](const Err&) {});
}
} // namespace zxdb