| // 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) | |
| : frame_to_finish_(frame_to_finish), weak_factory_(this) { | |
| FX_DCHECK(frame_to_finish < stack.size()); | |
| FX_DCHECK(!stack[frame_to_finish]->IsInline()); | |
| #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()); | |
| 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()->AsFunction(); | |
| 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 |