Tracing
This commit is contained in:
@@ -25,6 +25,14 @@ compile_error!("ruin-runtime currently supports only Linux x86_64.");
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub(crate) mod trace_targets {
|
||||
pub const DRIVER: &str = "ruin_runtime::driver";
|
||||
pub const RUNTIME: &str = "ruin_runtime::runtime";
|
||||
pub const SCHEDULER: &str = "ruin_runtime::scheduler";
|
||||
pub const TIMER: &str = "ruin_runtime::timer";
|
||||
pub const ASYNC: &str = "ruin_runtime::async";
|
||||
}
|
||||
|
||||
pub mod channel;
|
||||
pub mod fs;
|
||||
pub mod net;
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::uring::{IORING_OP_ASYNC_CANCEL, IoUring, IoUringCqe, IoUringSqe};
|
||||
use crate::trace_targets;
|
||||
|
||||
const WAKE_TARGET_TOKEN: u64 = 1;
|
||||
const TOKEN_KIND_SHIFT: u64 = 56;
|
||||
@@ -34,6 +35,13 @@ struct NotifierInner {
|
||||
|
||||
impl NotifierInner {
|
||||
fn notify(&self) -> io::Result<()> {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::DRIVER,
|
||||
event = "notify",
|
||||
ring_fd = self.ring_fd,
|
||||
"sending cross-thread driver wake"
|
||||
);
|
||||
if self.closed.load(Ordering::Acquire) {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::BrokenPipe,
|
||||
@@ -96,6 +104,12 @@ pub fn create() -> io::Result<(Driver, ThreadNotifier)> {
|
||||
/// emphasize driver construction.
|
||||
pub fn create_driver() -> io::Result<(Driver, ThreadNotifier)> {
|
||||
let ring = IoUring::new(64)?;
|
||||
tracing::debug!(
|
||||
target: trace_targets::DRIVER,
|
||||
event = "create_driver",
|
||||
ring_fd = ring.ring_fd(),
|
||||
"created runtime driver"
|
||||
);
|
||||
let notifier = Arc::new(NotifierInner {
|
||||
ring_fd: ring.ring_fd(),
|
||||
closed: AtomicBool::new(false),
|
||||
@@ -130,11 +144,27 @@ impl Driver {
|
||||
let saw_any = self
|
||||
.ring
|
||||
.drain_completions(|cqe| self.process_cqe(cqe, &mut ready));
|
||||
#[cfg(debug_assertions)]
|
||||
if saw_any {
|
||||
tracing::trace!(
|
||||
target: trace_targets::DRIVER,
|
||||
event = "poll_ready",
|
||||
timer_ready = ready.timer,
|
||||
wake_ready = ready.wake,
|
||||
"driver poll produced ready events"
|
||||
);
|
||||
}
|
||||
if saw_any { Ok(Some(ready)) } else { Ok(None) }
|
||||
}
|
||||
|
||||
/// Blocks until at least one completion is available.
|
||||
pub fn wait(&self) -> io::Result<()> {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::DRIVER,
|
||||
event = "wait",
|
||||
"waiting for driver completion"
|
||||
);
|
||||
self.ring.wait_for_cqe()
|
||||
}
|
||||
|
||||
@@ -142,6 +172,13 @@ impl Driver {
|
||||
///
|
||||
/// Passing `None` removes any active timer.
|
||||
pub fn rearm_timer(&self, deadline: Option<Duration>) -> io::Result<()> {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::TIMER,
|
||||
event = "rearm_timer",
|
||||
deadline_ns = deadline.map(|value| value.as_nanos() as u64),
|
||||
"rearming driver timer"
|
||||
);
|
||||
match (self.active_timer_token.get(), deadline) {
|
||||
(Some(active), Some(deadline)) => {
|
||||
self.ring.submit_timeout_update(active, deadline)?;
|
||||
@@ -168,6 +205,13 @@ impl Driver {
|
||||
on_complete: impl FnOnce(IoUringCqe) + Send + 'static,
|
||||
) -> io::Result<u64> {
|
||||
let token = self.next_token(CompletionKind::Operation);
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::ASYNC,
|
||||
event = "submit_operation",
|
||||
token,
|
||||
"submitting async driver operation"
|
||||
);
|
||||
self.completions
|
||||
.borrow_mut()
|
||||
.insert(token, Box::new(on_complete));
|
||||
@@ -181,6 +225,13 @@ impl Driver {
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_operation(&self, token: u64) -> io::Result<()> {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::ASYNC,
|
||||
event = "cancel_operation",
|
||||
token,
|
||||
"submitting async driver cancellation"
|
||||
);
|
||||
self.ring
|
||||
.submit_with_token(self.next_token(CompletionKind::OperationCancel), |sqe| {
|
||||
sqe.opcode = IORING_OP_ASYNC_CANCEL;
|
||||
@@ -216,6 +267,14 @@ impl Driver {
|
||||
}
|
||||
|
||||
fn process_cqe(&self, cqe: IoUringCqe, ready: &mut ReadyEvents) {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::DRIVER,
|
||||
event = "process_cqe",
|
||||
user_data = cqe.user_data,
|
||||
result = cqe.res,
|
||||
"processing io_uring completion"
|
||||
);
|
||||
if cqe.user_data == WAKE_TARGET_TOKEN {
|
||||
ready.wake = true;
|
||||
let wakes = cqe.res.max(1) as u64;
|
||||
@@ -256,6 +315,11 @@ impl Driver {
|
||||
|
||||
impl Drop for Driver {
|
||||
fn drop(&mut self) {
|
||||
tracing::debug!(
|
||||
target: trace_targets::DRIVER,
|
||||
event = "drop_driver",
|
||||
"dropping runtime driver"
|
||||
);
|
||||
self.notifier.closed.store(true, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::driver::{Driver, ThreadNotifier, create_driver, monotonic_now};
|
||||
use crate::trace_targets;
|
||||
|
||||
type LocalTask = Box<dyn FnOnce() + 'static>;
|
||||
type SendTask = Box<dyn FnOnce() + Send + 'static>;
|
||||
@@ -95,6 +96,13 @@ pub fn queue_task<F>(task: F)
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::SCHEDULER,
|
||||
event = "queue_task",
|
||||
queue = "local_macro",
|
||||
"queueing local macrotask"
|
||||
);
|
||||
push_local_macrotask(Box::new(task));
|
||||
}
|
||||
|
||||
@@ -110,6 +118,13 @@ pub fn queue_microtask<F>(task: F)
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::SCHEDULER,
|
||||
event = "queue_microtask",
|
||||
queue = "local_micro",
|
||||
"queueing local microtask"
|
||||
);
|
||||
current_thread()
|
||||
.local_microtasks
|
||||
.borrow_mut()
|
||||
@@ -128,6 +143,15 @@ where
|
||||
let owner = current_thread_ptr();
|
||||
let id = allocate_timer_id();
|
||||
let deadline = deadline_from_now(delay);
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::TIMER,
|
||||
event = "set_timeout",
|
||||
timer_id = id,
|
||||
delay_ns = delay.as_nanos() as u64,
|
||||
deadline_ns = deadline.as_nanos() as u64,
|
||||
"scheduling timeout"
|
||||
);
|
||||
let timer = TimerNode::timeout(id, deadline, Box::new(callback));
|
||||
|
||||
current_thread().timers.borrow_mut().insert(timer);
|
||||
@@ -146,6 +170,13 @@ where
|
||||
///
|
||||
/// Panics if called from a different runtime thread than the one that created `handle`.
|
||||
pub fn clear_timeout(handle: &TimeoutHandle) {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::TIMER,
|
||||
event = "clear_timeout",
|
||||
timer_id = handle.id,
|
||||
"clearing timeout"
|
||||
);
|
||||
clear_timer(handle.owner, handle.id);
|
||||
}
|
||||
|
||||
@@ -163,6 +194,15 @@ where
|
||||
let owner = current_thread_ptr();
|
||||
let id = allocate_timer_id();
|
||||
let deadline = deadline_from_now(delay);
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::TIMER,
|
||||
event = "set_interval",
|
||||
timer_id = id,
|
||||
delay_ns = delay.as_nanos() as u64,
|
||||
deadline_ns = deadline.as_nanos() as u64,
|
||||
"scheduling interval"
|
||||
);
|
||||
let timer = TimerNode::interval(
|
||||
id,
|
||||
deadline,
|
||||
@@ -186,6 +226,13 @@ where
|
||||
///
|
||||
/// Panics if called from a different runtime thread than the one that created `handle`.
|
||||
pub fn clear_interval(handle: &IntervalHandle) {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::TIMER,
|
||||
event = "clear_interval",
|
||||
timer_id = handle.id,
|
||||
"clearing interval"
|
||||
);
|
||||
clear_timer(handle.owner, handle.id);
|
||||
}
|
||||
|
||||
@@ -212,6 +259,12 @@ where
|
||||
F: Future + 'static,
|
||||
F::Output: 'static,
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::ASYNC,
|
||||
event = "queue_future",
|
||||
"queueing local future"
|
||||
);
|
||||
let state = Rc::new(JoinState::new());
|
||||
let completion = Rc::clone(&state);
|
||||
let task = Rc::new(FutureTask {
|
||||
@@ -240,6 +293,11 @@ where
|
||||
Init: FnOnce() + Send + 'static,
|
||||
Exit: FnOnce() + 'static,
|
||||
{
|
||||
tracing::debug!(
|
||||
target: trace_targets::RUNTIME,
|
||||
event = "spawn_worker",
|
||||
"spawning runtime worker thread"
|
||||
);
|
||||
let parent = current_thread();
|
||||
let (driver, notifier) = create_driver().expect("worker driver should initialize");
|
||||
let shared = Arc::new(ThreadShared::new(notifier));
|
||||
@@ -281,6 +339,16 @@ where
|
||||
///
|
||||
/// Panics if runtime initialization fails or if the underlying driver returns an unexpected error.
|
||||
pub fn run() {
|
||||
let _span = tracing::debug_span!(
|
||||
target: trace_targets::RUNTIME,
|
||||
"runtime.run"
|
||||
)
|
||||
.entered();
|
||||
tracing::debug!(
|
||||
target: trace_targets::RUNTIME,
|
||||
event = "run_enter",
|
||||
"entering runtime event loop"
|
||||
);
|
||||
let _ = current_thread();
|
||||
|
||||
loop {
|
||||
@@ -316,6 +384,15 @@ pub fn run() {
|
||||
|
||||
if has_pending_timers() || state.has_live_children() || state.has_live_async_operations() {
|
||||
state.shared.closing.store(false, Ordering::Release);
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::RUNTIME,
|
||||
event = "run_wait",
|
||||
pending_timers = has_pending_timers(),
|
||||
live_children = state.has_live_children(),
|
||||
live_async = state.has_live_async_operations(),
|
||||
"runtime waiting for more work"
|
||||
);
|
||||
state.driver.wait().expect("driver wait should succeed");
|
||||
continue;
|
||||
}
|
||||
@@ -327,6 +404,11 @@ pub fn run() {
|
||||
|
||||
state.shared.closed.store(true, Ordering::Release);
|
||||
state.shared.notify();
|
||||
tracing::debug!(
|
||||
target: trace_targets::RUNTIME,
|
||||
event = "run_exit",
|
||||
"runtime event loop exiting"
|
||||
);
|
||||
teardown_thread();
|
||||
return;
|
||||
}
|
||||
@@ -351,7 +433,16 @@ impl ThreadHandle {
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
self.shared.enqueue_macro(Box::new(task))
|
||||
let queued = self.shared.enqueue_macro(Box::new(task));
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::SCHEDULER,
|
||||
event = "queue_remote_task",
|
||||
queue = "remote_macro",
|
||||
queued,
|
||||
"queueing remote macrotask"
|
||||
);
|
||||
queued
|
||||
}
|
||||
|
||||
/// Queues a microtask onto this runtime thread.
|
||||
@@ -361,7 +452,16 @@ impl ThreadHandle {
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
self.shared.enqueue_micro(Box::new(task))
|
||||
let queued = self.shared.enqueue_micro(Box::new(task));
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::SCHEDULER,
|
||||
event = "queue_remote_microtask",
|
||||
queue = "remote_micro",
|
||||
queued,
|
||||
"queueing remote microtask"
|
||||
);
|
||||
queued
|
||||
}
|
||||
|
||||
/// Returns `true` if the target runtime thread has shut down.
|
||||
@@ -859,12 +959,24 @@ fn drain_driver_events() {
|
||||
|
||||
let state = current_thread();
|
||||
if ready.wake {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::DRIVER,
|
||||
event = "drain_wake",
|
||||
"draining driver wake notifications"
|
||||
);
|
||||
let _ = state
|
||||
.driver
|
||||
.drain_wake()
|
||||
.expect("wake drain should succeed");
|
||||
}
|
||||
if ready.timer {
|
||||
#[cfg(debug_assertions)]
|
||||
tracing::trace!(
|
||||
target: trace_targets::TIMER,
|
||||
event = "drain_timer",
|
||||
"draining expired runtime timers"
|
||||
);
|
||||
let _ = state
|
||||
.driver
|
||||
.drain_timer()
|
||||
|
||||
Reference in New Issue
Block a user