blob: ea56a32f14881e735a6408c5d7108b0c83ab2683 [file] [log] [blame]
// Copyright 2022 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/virtualization/tests/backtrace_watchdog.h"
#include <lib/syslog/cpp/macros.h>
#include <inspector/inspector.h>
#include <task-utils/walker.h>
namespace {
// Helper class to implement the TaskEnumerator for job walker.
class Enumerator : public TaskEnumerator {
public:
Enumerator() = default;
~Enumerator() = default;
zx_status_t OnProcess(int depth, zx_handle_t process, zx_koid_t koid,
zx_koid_t parent_koid) override {
inspector_print_debug_info_for_all_threads(stdout, process);
return ZX_OK;
}
bool has_on_process() const override { return true; }
};
} // namespace
zx_status_t BacktraceWatchdog::Start(zx::job job, zx::duration wait_time) {
if (running_) {
return ZX_ERR_ALREADY_EXISTS;
}
zx_status_t status = zx::event::create(0, &event_);
if (status != ZX_OK) {
return status;
}
job_ = std::move(job);
timeout_ = zx::deadline_after(wait_time);
// This completes the state mutation and so perform a barrier to ensure the spawned thread sees
// the writes. Until the thread is joined we are therefore forbidden from state mutation.
std::atomic_thread_fence(std::memory_order_seq_cst);
thread_ = std::thread(&BacktraceWatchdog::WatchdogThread, this);
running_ = true;
return ZX_OK;
}
BacktraceWatchdog::~BacktraceWatchdog() { Stop(); }
void BacktraceWatchdog::Stop() {
if (!running_) {
return;
}
zx_status_t status = event_.signal(0, ZX_USER_SIGNAL_0);
FX_CHECK(status == ZX_OK);
thread_.join();
// thread_ has been joined, so we can safely manipulate the state now.
job_.reset();
event_.reset();
running_ = false;
}
int BacktraceWatchdog::WatchdogThread() {
zx_signals_t pending;
zx_status_t status = event_.wait_one(ZX_USER_SIGNAL_0, timeout_, &pending);
if (status == ZX_ERR_TIMED_OUT) {
Backtrace();
}
// Nothing to be done for any error status, just return.
return 0;
}
void BacktraceWatchdog::Backtrace() {
Enumerator enumerator;
enumerator.WalkJobTree(job_.get());
}