blob: d1e977fa79c79fc49d08b9c45a0acbdf4c501a33 [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/step_into_thread_controller.h"
#include <inttypes.h>
#include "src/developer/debug/zxdb/client/finish_thread_controller.h"
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/step_thread_controller.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/symbols/input_location.h"
#include "src/developer/debug/zxdb/symbols/line_details.h"
#include "src/developer/debug/zxdb/symbols/location.h"
#include "src/developer/debug/zxdb/symbols/process_symbols.h"
#include "src/developer/debug/zxdb/symbols/resolve_options.h"
namespace zxdb {
StepIntoThreadController::StepIntoThreadController(StepMode mode)
: step_into_(std::make_unique<StepThreadController>(mode)) {}
StepIntoThreadController::StepIntoThreadController(const FileLine& line)
: step_into_(std::make_unique<StepThreadController>(line)) {}
StepIntoThreadController::StepIntoThreadController(AddressRanges ranges)
: step_into_(std::make_unique<StepThreadController>(ranges)) {}
StepIntoThreadController::~StepIntoThreadController() = default;
void StepIntoThreadController::InitWithThread(Thread* thread, fit::callback<void(const Err&)> cb) {
SetThread(thread);
if (thread->GetStack().empty()) {
cb(Err("Can't step, no frames."));
return;
}
original_frame_fingerprint_ = thread->GetStack().GetFrameFingerprint(0);
step_into_->InitWithThread(thread, std::move(cb));
}
ThreadController::ContinueOp StepIntoThreadController::GetContinueOp() {
if (skip_prologue_)
skip_prologue_->GetContinueOp();
return step_into_->GetContinueOp();
}
ThreadController::StopOp StepIntoThreadController::OnThreadStop(
debug_ipc::ExceptionType stop_type,
const std::vector<fxl::WeakPtr<Breakpoint>>& hit_breakpoints) {
// Once we're doing the skip_prologue operation, it runs until complete and then we're done.
if (skip_prologue_)
return skip_prologue_->OnThreadStop(stop_type, hit_breakpoints);
// Handle normal low-level "step into".
StopOp op = step_into_->OnThreadStop(stop_type, hit_breakpoints);
if (op != kStopDone)
return op;
if (!should_skip_prologue_)
return kStopDone; // Don't need to do anything on top of the normal step.
// If we get here the step controller thinks it's done. If we're not in a prologue now, we're
// done. Otherwise we need to step through the prologue.
// We can only be in a prologue if we've stepped into a new physical frame.
//
// This check is unnecessary as the symbol lookup below should handle all cases since stepping by
// line should never leave you in a function prologue that's not a new frame. But most of the time
// we're stepping in the same frame and a symbol lookup is relatively heavyweight. This is a nice
// filter before doing the full lookup.
const Stack& stack = thread()->GetStack();
if (stack[0]->IsInline())
return kStopDone; // Inline frames don't have prologues.
if (!FrameFingerprint::Newer(stack.GetFrameFingerprint(0), original_frame_fingerprint_))
return kStopDone; // Not in a newer frame, no prologue to skip.
if (stack.empty()) {
FX_NOTREACHED(); // Should always have a current frame on stop.
return kUnexpected;
}
uint64_t current_ip = stack[0]->GetAddress();
// Symbolize the current address and ask to skip the prologue. This will automatically adjust the
// resulting address to be after the prologue if there is one.
ResolveOptions resolve_options;
resolve_options.symbolize = true;
resolve_options.skip_function_prologue = true;
std::vector<Location> symbolized_locs =
thread()->GetProcess()->GetSymbols()->ResolveInputLocation(InputLocation(current_ip),
resolve_options);
FX_DCHECK(symbolized_locs.size() == 1); // Should always return one match.
const Location& after_prologue = symbolized_locs[0];
if (current_ip == after_prologue.address()) {
Log("Not in a function prologue, stopping.");
return kStopDone;
}
// Got to a prologue, now step to the end.
Log("Stepped to function prologue ending at 0x%" PRIx64 ". Going over it.",
after_prologue.address());
skip_prologue_ = std::make_unique<StepThreadController>(
AddressRanges(AddressRange(current_ip, after_prologue.address())));
// Init for this object is guaranteed synchronous so we don't have to wait for the callback.
skip_prologue_->InitWithThread(thread(), [](const Err&) {});
// Don't pass the exception type or breakpoints to the new controller. Depending on how we got
// here, the exception type may not match what the step controller expects. it just needs to know
// that execution stopped.
return skip_prologue_->OnThreadStop(debug_ipc::ExceptionType::kNone, {});
}
} // namespace zxdb