blob: 5ead5f4cdee05b64b4c512a566e3b8a338e73f1f [file] [log] [blame]
// Copyright 2024 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/debug_agent/process_info_iterator.h"
#include "src/developer/debug/debug_agent/backtrace_utils.h"
#include "src/developer/debug/debug_agent/component_manager.h"
#include "src/developer/debug/debug_agent/debug_agent.h"
#include "src/developer/debug/debug_agent/debugged_process.h"
#include "src/developer/debug/debug_agent/system_interface.h"
namespace debug_agent {
ProcessInfoIterator::ProcessInfoIterator(
fxl::WeakPtr<DebugAgent> debug_agent, std::vector<DebuggedProcess*> processes,
std::optional<fuchsia_debugger::ThreadDetailsInterest> interest)
: debug_agent_(std::move(debug_agent)),
interest_(std::move(interest)),
processes_(std::move(processes)) {
// Do initializations if we were given some processes. The case where no processes matched a
// filter or DebugAgent isn't actually attached to anything will return an error to the client
// when they call |GetNext|.
if (!processes_.empty()) {
current_process_ = processes_[process_index_];
if (current_process_) {
current_process_threads_ = current_process_->GetThreads();
}
}
}
void ProcessInfoIterator::GetNext(GetNextCompleter::Sync& completer) {
FX_DCHECK(debug_agent_);
fuchsia_debugger::ProcessInfoIteratorGetNextResponse response;
if (processes_.empty()) {
// Not attached to anything yet. Return an error and hang up.
completer.Reply(fit::error(fuchsia_debugger::ProcessInfoError::kNoProcesses));
completer.Close(ZX_ERR_BAD_STATE);
return;
}
if (!Advance()) {
// No more processes, we're done.
completer.Reply(fit::success(response));
completer.Close(ZX_OK);
return;
}
auto& info = response.info().emplace_back();
info.process(current_process_->koid());
info.moniker(current_moniker_);
info.thread(current_thread_->koid());
{
// Suspend the thread while we gather all the requested data.
auto suspend_handle = current_thread_->InternalSuspend(true);
if (CaptureBacktrace()) {
info.details().backtrace(GetBacktraceMarkupForThread(current_process_->process_handle(),
current_thread_->thread_handle()));
}
}
completer.Reply(fit::success(std::move(response)));
}
bool ProcessInfoIterator::Advance() {
if (thread_index_ == current_process_threads_.size()) {
// The check for empty here is to ensure we have valid behavior if we somehow get into the case
// where the calling code doesn't bail out before calling this function with an empty vector of
// processes.
if (processes_.empty() || process_index_ + 1 == processes_.size()) {
return false;
}
// Move on to the next process. This is a prefix operator since we initialized
// |current_process_| in the constructor.
current_process_ = processes_[++process_index_];
FX_DCHECK(current_process_);
current_process_threads_ = current_process_->GetThreads();
// Reset the thread index for the new process.
thread_index_ = 0;
// Update component moniker information.
auto component_info = debug_agent_->system_interface().GetComponentManager().FindComponentInfo(
current_process_->process_handle());
FX_DCHECK(!component_info.empty());
// Report the first matching moniker.
current_moniker_ = component_info[0].moniker;
}
// Grab this thread and advance the index.
current_thread_ = current_process_threads_[thread_index_++];
return true;
}
bool ProcessInfoIterator::CaptureBacktrace() const {
return interest_ && interest_->backtrace() && *interest_->backtrace();
}
} // namespace debug_agent