| // 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/thread_controller.h" |
| |
| #include <stdarg.h> |
| |
| #include "src/developer/debug/zxdb/client/frame.h" |
| #include "src/developer/debug/zxdb/client/setting_schema_definition.h" |
| #include "src/developer/debug/zxdb/client/thread.h" |
| #include "src/developer/debug/zxdb/symbols/function.h" |
| |
| namespace zxdb { |
| |
| ThreadController::ResumeAsyncCallbackInfo::ResumeAsyncCallbackInfo( |
| fxl::WeakPtr<Thread> weak_thread, debug_ipc::ExceptionType exception_type) |
| : exception_type(exception_type), |
| called(std::make_shared<bool>(false)), |
| is_sync(std::make_shared<bool>(true)) { |
| callback = [weak_thread = std::move(weak_thread), exception_type, called = called, |
| is_sync = is_sync](const Err&) mutable { |
| // Only issue the resume if we're running in an async context. Otherwise this will try to resume |
| // from within the OnThreadStop() stack which will confuse the thread. |
| if (!*is_sync && weak_thread) |
| weak_thread->ResumeFromAsyncThreadController(exception_type); |
| *called = true; |
| }; |
| } |
| |
| ThreadController::ResumeAsyncCallbackInfo::~ResumeAsyncCallbackInfo() { |
| // Tell the callback that if the callback is issued from now on, the thread needs a Resume. |
| *is_sync = false; |
| |
| // The callback should have been moved out. If we still own it, it can't be called in the future |
| // this stop will never be completed. |
| FX_DCHECK(!callback); |
| } |
| |
| ThreadController::StopOp ThreadController::ResumeAsyncCallbackInfo::ForwardStopOrReturnFuture( |
| ThreadController* controller, const std::vector<fxl::WeakPtr<Breakpoint>>& hit_breakpoints) { |
| if (*called) { |
| // Callback has been issued, safe to forward to the controller. |
| return controller->OnThreadStop(exception_type, hit_breakpoints); |
| } |
| |
| // Callback still pending, the Thread will be resumed in the future. |
| return kFuture; |
| } |
| |
| ThreadController::ThreadController() = default; |
| |
| ThreadController::~ThreadController() = default; |
| |
| void ThreadController::Log(const char* format, ...) const { |
| FX_DCHECK(thread_); // If uninitialized, the log setting hasn't been read yet. |
| if (!enable_debug_logging_) |
| return; |
| |
| va_list ap; |
| va_start(ap, format); |
| |
| printf("%s controller: ", GetName()); |
| vprintf(format, ap); |
| |
| // Manually add \r so output will be reasonable even if the terminal is in |
| // raw mode. |
| printf("\r\n"); |
| |
| va_end(ap); |
| } |
| |
| // static |
| std::string ThreadController::FrameFunctionNameForLog(const Frame* frame) { |
| const char kNone[] = "<none>"; |
| |
| const Location& loc = frame->GetLocation(); |
| if (!loc.symbol()) |
| return kNone; |
| |
| const Function* func = loc.symbol().Get()->As<Function>(); |
| if (!func) |
| return kNone; |
| |
| return func->GetFullName(); |
| } |
| |
| void ThreadController::SetThread(Thread* thread) { |
| thread_ = thread; |
| enable_debug_logging_ = thread_->settings().GetBool(ClientSettings::Thread::kDebugStepping); |
| } |
| |
| void ThreadController::SetInlineFrameIfAmbiguous(InlineFrameIs comparison, |
| FrameFingerprint fingerprint) { |
| Stack& stack = thread()->GetStack(); |
| |
| // Reset any hidden inline frames so we can iterate through all of them (and we'll leave this |
| // reset to 0 if the requested one isn't found). |
| size_t old_hide_count = stack.hide_ambiguous_inline_frame_count(); |
| stack.SetHideAmbiguousInlineFrameCount(0); |
| |
| for (size_t i = 0; i < stack.size(); i++) { |
| const Frame* frame = stack[i]; |
| auto found = stack.GetFrameFingerprint(i); |
| |
| // To be ambiguous, all frames to here need to be at the same address and all inline frames need |
| // to be at the beginning of one of their ranges. (the physical frame also needs matching but |
| // its range doesn't count). |
| bool is_inline = frame->IsInline(); |
| |
| if (found == fingerprint) { |
| // Found it. |
| if (comparison == InlineFrameIs::kEqual) { |
| // Make this one the top of the stack. |
| stack.SetHideAmbiguousInlineFrameCount(i); |
| return; |
| } else { // comparison == InlineFrameIs::kOneBefore. |
| // Make the one below this frame topmost. That requires the current frame be inline since it |
| // will be hidden. |
| if (is_inline) { |
| stack.SetHideAmbiguousInlineFrameCount(i + 1); |
| return; |
| } |
| } |
| break; |
| } |
| |
| if (!is_inline) |
| break; // Don't check below the first physical frame. |
| |
| // The fingerprint can be set on a frame as long as all frames above it were ambiguous, but the |
| // frame being set to is usually not ambiguous (it's often the physical frame that calls an |
| // inline function, for example). |
| if (!frame->IsAmbiguousInlineLocation()) |
| break; |
| } |
| |
| if (old_hide_count) |
| stack.SetHideAmbiguousInlineFrameCount(old_hide_count); |
| } |
| |
| void ThreadController::NotifyControllerDone() { |
| thread_->NotifyControllerDone(this); |
| // Warning: |this| is likely deleted. |
| } |
| |
| ThreadController::ResumeAsyncCallbackInfo ThreadController::MakeResumeAsyncThreadCallback( |
| debug_ipc::ExceptionType type) const { |
| return ResumeAsyncCallbackInfo(thread_->GetWeakPtr(), type); |
| } |
| |
| } // namespace zxdb |