tokio/runtime/metrics/runtime.rs
1use crate::runtime::Handle;
2use std::time::Duration;
3
4cfg_64bit_metrics! {
5 use std::sync::atomic::Ordering::Relaxed;
6}
7
8cfg_unstable_metrics! {
9 use std::ops::Range;
10 use std::thread::ThreadId;
11}
12
13/// Handle to the runtime's metrics.
14///
15/// This handle is internally reference-counted and can be freely cloned. A
16/// `RuntimeMetrics` handle is obtained using the [`Runtime::metrics`] method.
17///
18/// [`Runtime::metrics`]: crate::runtime::Runtime::metrics()
19#[derive(Clone, Debug)]
20pub struct RuntimeMetrics {
21 handle: Handle,
22}
23
24impl RuntimeMetrics {
25 pub(crate) fn new(handle: Handle) -> RuntimeMetrics {
26 RuntimeMetrics { handle }
27 }
28
29 /// Returns the number of worker threads used by the runtime.
30 ///
31 /// The number of workers is set by configuring `worker_threads` on
32 /// `runtime::Builder`. When using the `current_thread` runtime, the return
33 /// value is always `1`.
34 ///
35 /// # Examples
36 ///
37 /// ```
38 /// use tokio::runtime::Handle;
39 ///
40 /// # #[tokio::main(flavor = "current_thread")]
41 /// # async fn main() {
42 /// let metrics = Handle::current().metrics();
43 ///
44 /// let n = metrics.num_workers();
45 /// println!("Runtime is using {} workers", n);
46 /// # }
47 /// ```
48 pub fn num_workers(&self) -> usize {
49 self.handle.inner.num_workers()
50 }
51
52 /// Returns the current number of alive tasks in the runtime.
53 ///
54 /// This counter increases when a task is spawned and decreases when a
55 /// task exits.
56 ///
57 /// Note: When using the multi-threaded runtime this number may not
58 /// not have strong consistency i.e. no tasks may be running but the metric
59 /// reports otherwise.
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// use tokio::runtime::Handle;
65 ///
66 /// # #[tokio::main(flavor = "current_thread")]
67 /// # async fn main() {
68 /// let metrics = Handle::current().metrics();
69 ///
70 /// let n = metrics.num_alive_tasks();
71 /// println!("Runtime has {} alive tasks", n);
72 /// # }
73 /// ```
74 pub fn num_alive_tasks(&self) -> usize {
75 self.handle.inner.num_alive_tasks()
76 }
77
78 /// Returns the number of tasks currently scheduled in the runtime's
79 /// global queue.
80 ///
81 /// Tasks that are spawned or notified from a non-runtime thread are
82 /// scheduled using the runtime's global queue. This metric returns the
83 /// **current** number of tasks pending in the global queue. As such, the
84 /// returned value may increase or decrease as new tasks are scheduled and
85 /// processed.
86 ///
87 /// # Examples
88 ///
89 /// ```
90 /// use tokio::runtime::Handle;
91 ///
92 /// # #[tokio::main(flavor = "current_thread")]
93 /// # async fn main() {
94 /// let metrics = Handle::current().metrics();
95 ///
96 /// let n = metrics.global_queue_depth();
97 /// println!("{} tasks currently pending in the runtime's global queue", n);
98 /// # }
99 /// ```
100 pub fn global_queue_depth(&self) -> usize {
101 self.handle.inner.injection_queue_depth()
102 }
103
104 cfg_64bit_metrics! {
105 /// Returns the amount of time the given worker thread has been busy.
106 ///
107 /// The worker busy duration starts at zero when the runtime is created and
108 /// increases whenever the worker is spending time processing work. Using
109 /// this value can indicate the load of the given worker. If a lot of time
110 /// is spent busy, then the worker is under load and will check for inbound
111 /// events less often.
112 ///
113 /// The timer is monotonically increasing. It is never decremented or reset
114 /// to zero.
115 ///
116 /// # Arguments
117 ///
118 /// `worker` is the index of the worker being queried. The given value must
119 /// be between 0 and `num_workers()`. The index uniquely identifies a single
120 /// worker and will continue to identify the worker throughout the lifetime
121 /// of the runtime instance.
122 ///
123 /// # Panics
124 ///
125 /// The method panics when `worker` represents an invalid worker, i.e. is
126 /// greater than or equal to `num_workers()`.
127 ///
128 /// # Examples
129 ///
130 /// ```
131 /// use tokio::runtime::Handle;
132 ///
133 /// # #[tokio::main(flavor = "current_thread")]
134 /// # async fn main() {
135 /// let metrics = Handle::current().metrics();
136 ///
137 /// let n = metrics.worker_total_busy_duration(0);
138 /// println!("worker 0 was busy for a total of {:?}", n);
139 /// # }
140 /// ```
141 pub fn worker_total_busy_duration(&self, worker: usize) -> Duration {
142 let nanos = self
143 .handle
144 .inner
145 .worker_metrics(worker)
146 .busy_duration_total
147 .load(Relaxed);
148 Duration::from_nanos(nanos)
149 }
150
151 /// Returns the total number of times the given worker thread has parked.
152 ///
153 /// The worker park count starts at zero when the runtime is created and
154 /// increases by one each time the worker parks the thread waiting for new
155 /// inbound events to process. This usually means the worker has processed
156 /// all pending work and is currently idle.
157 ///
158 /// The counter is monotonically increasing. It is never decremented or
159 /// reset to zero.
160 ///
161 /// # Arguments
162 ///
163 /// `worker` is the index of the worker being queried. The given value must
164 /// be between 0 and `num_workers()`. The index uniquely identifies a single
165 /// worker and will continue to identify the worker throughout the lifetime
166 /// of the runtime instance.
167 ///
168 /// # Panics
169 ///
170 /// The method panics when `worker` represents an invalid worker, i.e. is
171 /// greater than or equal to `num_workers()`.
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// use tokio::runtime::Handle;
177 ///
178 /// # #[tokio::main(flavor = "current_thread")]
179 /// # async fn main() {
180 /// let metrics = Handle::current().metrics();
181 ///
182 /// let n = metrics.worker_park_count(0);
183 /// println!("worker 0 parked {} times", n);
184 /// # }
185 /// ```
186 pub fn worker_park_count(&self, worker: usize) -> u64 {
187 self.handle
188 .inner
189 .worker_metrics(worker)
190 .park_count
191 .load(Relaxed)
192 }
193
194 /// Returns the total number of times the given worker thread has parked
195 /// and unparked.
196 ///
197 /// The worker park/unpark count starts at zero when the runtime is created
198 /// and increases by one each time the worker parks the thread waiting for
199 /// new inbound events to process. This usually means the worker has processed
200 /// all pending work and is currently idle. When new work becomes available,
201 /// the worker is unparked and the park/unpark count is again increased by one.
202 ///
203 /// An odd count means that the worker is currently parked.
204 /// An even count means that the worker is currently active.
205 ///
206 /// The counter is monotonically increasing. It is never decremented or
207 /// reset to zero.
208 ///
209 /// # Arguments
210 ///
211 /// `worker` is the index of the worker being queried. The given value must
212 /// be between 0 and `num_workers()`. The index uniquely identifies a single
213 /// worker and will continue to identify the worker throughout the lifetime
214 /// of the runtime instance.
215 ///
216 /// # Panics
217 ///
218 /// The method panics when `worker` represents an invalid worker, i.e. is
219 /// greater than or equal to `num_workers()`.
220 ///
221 /// # Examples
222 ///
223 /// ```
224 /// use tokio::runtime::Handle;
225 ///
226 /// # #[tokio::main(flavor = "current_thread")]
227 /// # async fn main() {
228 /// let metrics = Handle::current().metrics();
229 /// let n = metrics.worker_park_unpark_count(0);
230 ///
231 /// println!("worker 0 parked and unparked {} times", n);
232 ///
233 /// if n % 2 == 0 {
234 /// println!("worker 0 is active");
235 /// } else {
236 /// println!("worker 0 is parked");
237 /// }
238 /// # }
239 /// ```
240 pub fn worker_park_unpark_count(&self, worker: usize) -> u64 {
241 self.handle
242 .inner
243 .worker_metrics(worker)
244 .park_unpark_count
245 .load(Relaxed)
246 }
247 }
248
249 cfg_unstable_metrics! {
250
251 /// Returns the number of additional threads spawned by the runtime.
252 ///
253 /// The number of workers is set by configuring `max_blocking_threads` on
254 /// `runtime::Builder`.
255 ///
256 /// # Examples
257 ///
258 /// ```
259 /// # #[cfg(not(target_family = "wasm"))]
260 /// # {
261 /// use tokio::runtime::Handle;
262 ///
263 /// # #[tokio::main(flavor = "current_thread")]
264 /// # async fn main() {
265 /// let _ = tokio::task::spawn_blocking(move || {
266 /// // Stand-in for compute-heavy work or using synchronous APIs
267 /// 1 + 1
268 /// }).await;
269 /// let metrics = Handle::current().metrics();
270 ///
271 /// let n = metrics.num_blocking_threads();
272 /// println!("Runtime has created {} threads", n);
273 /// # }
274 /// # }
275 /// ```
276 pub fn num_blocking_threads(&self) -> usize {
277 self.handle.inner.num_blocking_threads()
278 }
279
280 #[deprecated = "Renamed to num_alive_tasks"]
281 /// Renamed to [`RuntimeMetrics::num_alive_tasks`]
282 pub fn active_tasks_count(&self) -> usize {
283 self.num_alive_tasks()
284 }
285
286 /// Returns the number of idle threads, which have spawned by the runtime
287 /// for `spawn_blocking` calls.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// # #[cfg(not(target_family = "wasm"))]
293 /// # {
294 /// use tokio::runtime::Handle;
295 ///
296 /// #[tokio::main]
297 /// async fn main() {
298 /// let _ = tokio::task::spawn_blocking(move || {
299 /// // Stand-in for compute-heavy work or using synchronous APIs
300 /// 1 + 1
301 /// }).await;
302 /// let metrics = Handle::current().metrics();
303 ///
304 /// let n = metrics.num_idle_blocking_threads();
305 /// println!("Runtime has {} idle blocking thread pool threads", n);
306 /// }
307 /// # }
308 /// ```
309 pub fn num_idle_blocking_threads(&self) -> usize {
310 self.handle.inner.num_idle_blocking_threads()
311 }
312
313 /// Returns the thread id of the given worker thread.
314 ///
315 /// The returned value is `None` if the worker thread has not yet finished
316 /// starting up.
317 ///
318 /// If additional information about the thread, such as its native id, are
319 /// required, those can be collected in [`on_thread_start`] and correlated
320 /// using the thread id.
321 ///
322 /// [`on_thread_start`]: crate::runtime::Builder::on_thread_start
323 ///
324 /// # Arguments
325 ///
326 /// `worker` is the index of the worker being queried. The given value must
327 /// be between 0 and `num_workers()`. The index uniquely identifies a single
328 /// worker and will continue to identify the worker throughout the lifetime
329 /// of the runtime instance.
330 ///
331 /// # Panics
332 ///
333 /// The method panics when `worker` represents an invalid worker, i.e. is
334 /// greater than or equal to `num_workers()`.
335 ///
336 /// # Examples
337 ///
338 /// ```
339 /// use tokio::runtime::Handle;
340 ///
341 /// # #[tokio::main(flavor = "current_thread")]
342 /// # async fn main() {
343 /// let metrics = Handle::current().metrics();
344 ///
345 /// let id = metrics.worker_thread_id(0);
346 /// println!("worker 0 has id {:?}", id);
347 /// # }
348 /// ```
349 pub fn worker_thread_id(&self, worker: usize) -> Option<ThreadId> {
350 self.handle
351 .inner
352 .worker_metrics(worker)
353 .thread_id()
354 }
355
356 /// Renamed to [`RuntimeMetrics::global_queue_depth`]
357 #[deprecated = "Renamed to global_queue_depth"]
358 #[doc(hidden)]
359 pub fn injection_queue_depth(&self) -> usize {
360 self.handle.inner.injection_queue_depth()
361 }
362
363 /// Returns the number of tasks currently scheduled in the given worker's
364 /// local queue.
365 ///
366 /// Tasks that are spawned or notified from within a runtime thread are
367 /// scheduled using that worker's local queue. This metric returns the
368 /// **current** number of tasks pending in the worker's local queue. As
369 /// such, the returned value may increase or decrease as new tasks are
370 /// scheduled and processed.
371 ///
372 /// # Arguments
373 ///
374 /// `worker` is the index of the worker being queried. The given value must
375 /// be between 0 and `num_workers()`. The index uniquely identifies a single
376 /// worker and will continue to identify the worker throughout the lifetime
377 /// of the runtime instance.
378 ///
379 /// # Panics
380 ///
381 /// The method panics when `worker` represents an invalid worker, i.e. is
382 /// greater than or equal to `num_workers()`.
383 ///
384 /// # Examples
385 ///
386 /// ```
387 /// use tokio::runtime::Handle;
388 ///
389 /// # #[tokio::main(flavor = "current_thread")]
390 /// # async fn main() {
391 /// let metrics = Handle::current().metrics();
392 ///
393 /// let n = metrics.worker_local_queue_depth(0);
394 /// println!("{} tasks currently pending in worker 0's local queue", n);
395 /// # }
396 /// ```
397 pub fn worker_local_queue_depth(&self, worker: usize) -> usize {
398 self.handle.inner.worker_local_queue_depth(worker)
399 }
400
401 /// Returns `true` if the runtime is tracking the distribution of task poll
402 /// times.
403 ///
404 /// Task poll times are not instrumented by default as doing so requires
405 /// calling [`Instant::now()`] twice per task poll. The feature is enabled
406 /// by calling [`enable_metrics_poll_time_histogram()`] when building the
407 /// runtime.
408 ///
409 /// # Examples
410 ///
411 /// ```
412 /// use tokio::runtime::{self, Handle};
413 ///
414 /// fn main() {
415 /// runtime::Builder::new_current_thread()
416 /// .enable_metrics_poll_time_histogram()
417 /// .build()
418 /// .unwrap()
419 /// .block_on(async {
420 /// let metrics = Handle::current().metrics();
421 /// let enabled = metrics.poll_time_histogram_enabled();
422 ///
423 /// println!("Tracking task poll time distribution: {:?}", enabled);
424 /// });
425 /// }
426 /// ```
427 ///
428 /// [`enable_metrics_poll_time_histogram()`]: crate::runtime::Builder::enable_metrics_poll_time_histogram
429 /// [`Instant::now()`]: std::time::Instant::now
430 pub fn poll_time_histogram_enabled(&self) -> bool {
431 self.handle
432 .inner
433 .worker_metrics(0)
434 .poll_count_histogram
435 .is_some()
436 }
437
438 #[deprecated(note = "Renamed to `poll_time_histogram_enabled`")]
439 #[doc(hidden)]
440 pub fn poll_count_histogram_enabled(&self) -> bool {
441 self.poll_time_histogram_enabled()
442 }
443
444 /// Returns the number of histogram buckets tracking the distribution of
445 /// task poll times.
446 ///
447 /// This value is configured by calling
448 /// [`metrics_poll_time_histogram_configuration()`] when building the runtime.
449 ///
450 /// # Examples
451 ///
452 /// ```
453 /// use tokio::runtime::{self, Handle};
454 ///
455 /// fn main() {
456 /// runtime::Builder::new_current_thread()
457 /// .enable_metrics_poll_time_histogram()
458 /// .build()
459 /// .unwrap()
460 /// .block_on(async {
461 /// let metrics = Handle::current().metrics();
462 /// let buckets = metrics.poll_time_histogram_num_buckets();
463 ///
464 /// println!("Histogram buckets: {:?}", buckets);
465 /// });
466 /// }
467 /// ```
468 ///
469 /// [`metrics_poll_time_histogram_configuration()`]:
470 /// crate::runtime::Builder::metrics_poll_time_histogram_configuration
471 pub fn poll_time_histogram_num_buckets(&self) -> usize {
472 self.handle
473 .inner
474 .worker_metrics(0)
475 .poll_count_histogram
476 .as_ref()
477 .map_or(0, |histogram| histogram.num_buckets())
478 }
479
480 /// Deprecated. Use [`poll_time_histogram_num_buckets()`] instead.
481 ///
482 /// [`poll_time_histogram_num_buckets()`]: Self::poll_time_histogram_num_buckets
483 #[doc(hidden)]
484 #[deprecated(note = "renamed to `poll_time_histogram_num_buckets`.")]
485 pub fn poll_count_histogram_num_buckets(&self) -> usize {
486 self.poll_time_histogram_num_buckets()
487 }
488
489 /// Returns the range of task poll times tracked by the given bucket.
490 ///
491 /// This value is configured by calling
492 /// [`metrics_poll_time_histogram_configuration()`] when building the runtime.
493 ///
494 /// # Panics
495 ///
496 /// The method panics if `bucket` represents an invalid bucket index, i.e.
497 /// is greater than or equal to `poll_time_histogram_num_buckets()`.
498 ///
499 /// # Examples
500 ///
501 /// ```
502 /// use tokio::runtime::{self, Handle};
503 ///
504 /// fn main() {
505 /// runtime::Builder::new_current_thread()
506 /// .enable_metrics_poll_time_histogram()
507 /// .build()
508 /// .unwrap()
509 /// .block_on(async {
510 /// let metrics = Handle::current().metrics();
511 /// let buckets = metrics.poll_time_histogram_num_buckets();
512 ///
513 /// for i in 0..buckets {
514 /// let range = metrics.poll_time_histogram_bucket_range(i);
515 /// println!("Histogram bucket {} range: {:?}", i, range);
516 /// }
517 /// });
518 /// }
519 /// ```
520 ///
521 /// [`metrics_poll_time_histogram_configuration()`]:
522 /// crate::runtime::Builder::metrics_poll_time_histogram_configuration
523 #[track_caller]
524 pub fn poll_time_histogram_bucket_range(&self, bucket: usize) -> Range<Duration> {
525 self.handle
526 .inner
527 .worker_metrics(0)
528 .poll_count_histogram
529 .as_ref()
530 .map_or_else(Range::default, |histogram| {
531 let range = histogram.bucket_range(bucket);
532 std::ops::Range {
533 start: Duration::from_nanos(range.start),
534 end: Duration::from_nanos(range.end),
535 }
536 })
537 }
538
539 /// Deprecated. Use [`poll_time_histogram_bucket_range()`] instead.
540 ///
541 /// [`poll_time_histogram_bucket_range()`]: Self::poll_time_histogram_bucket_range
542 #[track_caller]
543 #[doc(hidden)]
544 #[deprecated(note = "renamed to `poll_time_histogram_bucket_range`")]
545 pub fn poll_count_histogram_bucket_range(&self, bucket: usize) -> Range<Duration> {
546 self.poll_time_histogram_bucket_range(bucket)
547 }
548
549 /// Returns the number of tasks currently scheduled in the blocking
550 /// thread pool, spawned using `spawn_blocking`.
551 ///
552 /// This metric returns the **current** number of tasks pending in
553 /// blocking thread pool. As such, the returned value may increase
554 /// or decrease as new tasks are scheduled and processed.
555 ///
556 /// # Examples
557 ///
558 /// ```
559 /// use tokio::runtime::Handle;
560 ///
561 /// # #[tokio::main(flavor = "current_thread")]
562 /// # async fn main() {
563 /// let metrics = Handle::current().metrics();
564 ///
565 /// let n = metrics.blocking_queue_depth();
566 /// println!("{} tasks currently pending in the blocking thread pool", n);
567 /// # }
568 /// ```
569 pub fn blocking_queue_depth(&self) -> usize {
570 self.handle.inner.blocking_queue_depth()
571 }
572 }
573
574 feature! {
575 #![all(
576 tokio_unstable,
577 target_has_atomic = "64"
578 )]
579 /// Returns the number of tasks spawned in this runtime since it was created.
580 ///
581 /// This count starts at zero when the runtime is created and increases by one each time a task is spawned.
582 ///
583 /// The counter is monotonically increasing. It is never decremented or
584 /// reset to zero.
585 ///
586 /// # Examples
587 ///
588 /// ```
589 /// use tokio::runtime::Handle;
590 ///
591 /// # #[tokio::main(flavor = "current_thread")]
592 /// # async fn main() {
593 /// let metrics = Handle::current().metrics();
594 ///
595 /// let n = metrics.spawned_tasks_count();
596 /// println!("Runtime has had {} tasks spawned", n);
597 /// # }
598 /// ```
599 pub fn spawned_tasks_count(&self) -> u64 {
600 self.handle.inner.spawned_tasks_count()
601 }
602
603 /// Returns the number of tasks scheduled from **outside** of the runtime.
604 ///
605 /// The remote schedule count starts at zero when the runtime is created and
606 /// increases by one each time a task is woken from **outside** of the
607 /// runtime. This usually means that a task is spawned or notified from a
608 /// non-runtime thread and must be queued using the Runtime's injection
609 /// queue, which tends to be slower.
610 ///
611 /// The counter is monotonically increasing. It is never decremented or
612 /// reset to zero.
613 ///
614 /// # Examples
615 ///
616 /// ```
617 /// use tokio::runtime::Handle;
618 ///
619 /// # #[tokio::main(flavor = "current_thread")]
620 /// # async fn main() {
621 /// let metrics = Handle::current().metrics();
622 ///
623 /// let n = metrics.remote_schedule_count();
624 /// println!("{} tasks were scheduled from outside the runtime", n);
625 /// # }
626 /// ```
627 pub fn remote_schedule_count(&self) -> u64 {
628 self.handle
629 .inner
630 .scheduler_metrics()
631 .remote_schedule_count
632 .load(Relaxed)
633 }
634
635 /// Returns the number of times that tasks have been forced to yield back to the scheduler
636 /// after exhausting their task budgets.
637 ///
638 /// This count starts at zero when the runtime is created and increases by one each time a task yields due to exhausting its budget.
639 ///
640 /// The counter is monotonically increasing. It is never decremented or
641 /// reset to zero.
642 pub fn budget_forced_yield_count(&self) -> u64 {
643 self.handle
644 .inner
645 .scheduler_metrics()
646 .budget_forced_yield_count
647 .load(Relaxed)
648 }
649
650 /// Returns the number of times the given worker thread unparked but
651 /// performed no work before parking again.
652 ///
653 /// The worker no-op count starts at zero when the runtime is created and
654 /// increases by one each time the worker unparks the thread but finds no
655 /// new work and goes back to sleep. This indicates a false-positive wake up.
656 ///
657 /// The counter is monotonically increasing. It is never decremented or
658 /// reset to zero.
659 ///
660 /// # Arguments
661 ///
662 /// `worker` is the index of the worker being queried. The given value must
663 /// be between 0 and `num_workers()`. The index uniquely identifies a single
664 /// worker and will continue to identify the worker throughout the lifetime
665 /// of the runtime instance.
666 ///
667 /// # Panics
668 ///
669 /// The method panics when `worker` represents an invalid worker, i.e. is
670 /// greater than or equal to `num_workers()`.
671 ///
672 /// # Examples
673 ///
674 /// ```
675 /// use tokio::runtime::Handle;
676 ///
677 /// # #[tokio::main(flavor = "current_thread")]
678 /// # async fn main() {
679 /// let metrics = Handle::current().metrics();
680 ///
681 /// let n = metrics.worker_noop_count(0);
682 /// println!("worker 0 had {} no-op unparks", n);
683 /// # }
684 /// ```
685 pub fn worker_noop_count(&self, worker: usize) -> u64 {
686 self.handle
687 .inner
688 .worker_metrics(worker)
689 .noop_count
690 .load(Relaxed)
691 }
692
693 /// Returns the number of tasks the given worker thread stole from
694 /// another worker thread.
695 ///
696 /// This metric only applies to the **multi-threaded** runtime and will
697 /// always return `0` when using the current thread runtime.
698 ///
699 /// The worker steal count starts at zero when the runtime is created and
700 /// increases by `N` each time the worker has processed its scheduled queue
701 /// and successfully steals `N` more pending tasks from another worker.
702 ///
703 /// The counter is monotonically increasing. It is never decremented or
704 /// reset to zero.
705 ///
706 /// # Arguments
707 ///
708 /// `worker` is the index of the worker being queried. The given value must
709 /// be between 0 and `num_workers()`. The index uniquely identifies a single
710 /// worker and will continue to identify the worker throughout the lifetime
711 /// of the runtime instance.
712 ///
713 /// # Panics
714 ///
715 /// The method panics when `worker` represents an invalid worker, i.e. is
716 /// greater than or equal to `num_workers()`.
717 ///
718 /// # Examples
719 ///
720 /// ```
721 /// use tokio::runtime::Handle;
722 ///
723 /// # #[tokio::main(flavor = "current_thread")]
724 /// # async fn main() {
725 /// let metrics = Handle::current().metrics();
726 ///
727 /// let n = metrics.worker_steal_count(0);
728 /// println!("worker 0 has stolen {} tasks", n);
729 /// # }
730 /// ```
731 pub fn worker_steal_count(&self, worker: usize) -> u64 {
732 self.handle
733 .inner
734 .worker_metrics(worker)
735 .steal_count
736 .load(Relaxed)
737 }
738
739 /// Returns the number of times the given worker thread stole tasks from
740 /// another worker thread.
741 ///
742 /// This metric only applies to the **multi-threaded** runtime and will
743 /// always return `0` when using the current thread runtime.
744 ///
745 /// The worker steal count starts at zero when the runtime is created and
746 /// increases by one each time the worker has processed its scheduled queue
747 /// and successfully steals more pending tasks from another worker.
748 ///
749 /// The counter is monotonically increasing. It is never decremented or
750 /// reset to zero.
751 ///
752 /// # Arguments
753 ///
754 /// `worker` is the index of the worker being queried. The given value must
755 /// be between 0 and `num_workers()`. The index uniquely identifies a single
756 /// worker and will continue to identify the worker throughout the lifetime
757 /// of the runtime instance.
758 ///
759 /// # Panics
760 ///
761 /// The method panics when `worker` represents an invalid worker, i.e. is
762 /// greater than or equal to `num_workers()`.
763 ///
764 /// # Examples
765 ///
766 /// ```
767 /// use tokio::runtime::Handle;
768 ///
769 /// # #[tokio::main(flavor = "current_thread")]
770 /// # async fn main() {
771 /// let metrics = Handle::current().metrics();
772 ///
773 /// let n = metrics.worker_steal_operations(0);
774 /// println!("worker 0 has stolen tasks {} times", n);
775 /// # }
776 /// ```
777 pub fn worker_steal_operations(&self, worker: usize) -> u64 {
778 self.handle
779 .inner
780 .worker_metrics(worker)
781 .steal_operations
782 .load(Relaxed)
783 }
784
785 /// Returns the number of tasks the given worker thread has polled.
786 ///
787 /// The worker poll count starts at zero when the runtime is created and
788 /// increases by one each time the worker polls a scheduled task.
789 ///
790 /// The counter is monotonically increasing. It is never decremented or
791 /// reset to zero.
792 ///
793 /// # Arguments
794 ///
795 /// `worker` is the index of the worker being queried. The given value must
796 /// be between 0 and `num_workers()`. The index uniquely identifies a single
797 /// worker and will continue to identify the worker throughout the lifetime
798 /// of the runtime instance.
799 ///
800 /// # Panics
801 ///
802 /// The method panics when `worker` represents an invalid worker, i.e. is
803 /// greater than or equal to `num_workers()`.
804 ///
805 /// # Examples
806 ///
807 /// ```
808 /// use tokio::runtime::Handle;
809 ///
810 /// # #[tokio::main(flavor = "current_thread")]
811 /// # async fn main() {
812 /// let metrics = Handle::current().metrics();
813 ///
814 /// let n = metrics.worker_poll_count(0);
815 /// println!("worker 0 has polled {} tasks", n);
816 /// # }
817 /// ```
818 pub fn worker_poll_count(&self, worker: usize) -> u64 {
819 self.handle
820 .inner
821 .worker_metrics(worker)
822 .poll_count
823 .load(Relaxed)
824 }
825
826 /// Returns the number of tasks scheduled from **within** the runtime on the
827 /// given worker's local queue.
828 ///
829 /// The local schedule count starts at zero when the runtime is created and
830 /// increases by one each time a task is woken from **inside** of the
831 /// runtime on the given worker. This usually means that a task is spawned
832 /// or notified from within a runtime thread and will be queued on the
833 /// worker-local queue.
834 ///
835 /// The counter is monotonically increasing. It is never decremented or
836 /// reset to zero.
837 ///
838 /// # Arguments
839 ///
840 /// `worker` is the index of the worker being queried. The given value must
841 /// be between 0 and `num_workers()`. The index uniquely identifies a single
842 /// worker and will continue to identify the worker throughout the lifetime
843 /// of the runtime instance.
844 ///
845 /// # Panics
846 ///
847 /// The method panics when `worker` represents an invalid worker, i.e. is
848 /// greater than or equal to `num_workers()`.
849 ///
850 /// # Examples
851 ///
852 /// ```
853 /// use tokio::runtime::Handle;
854 ///
855 /// # #[tokio::main(flavor = "current_thread")]
856 /// # async fn main() {
857 /// let metrics = Handle::current().metrics();
858 ///
859 /// let n = metrics.worker_local_schedule_count(0);
860 /// println!("{} tasks were scheduled on the worker's local queue", n);
861 /// # }
862 /// ```
863 pub fn worker_local_schedule_count(&self, worker: usize) -> u64 {
864 self.handle
865 .inner
866 .worker_metrics(worker)
867 .local_schedule_count
868 .load(Relaxed)
869 }
870
871 /// Returns the number of times the given worker thread saturated its local
872 /// queue.
873 ///
874 /// This metric only applies to the **multi-threaded** scheduler.
875 ///
876 /// The worker overflow count starts at zero when the runtime is created and
877 /// increases by one each time the worker attempts to schedule a task
878 /// locally, but its local queue is full. When this happens, half of the
879 /// local queue is moved to the injection queue.
880 ///
881 /// The counter is monotonically increasing. It is never decremented or
882 /// reset to zero.
883 ///
884 /// # Arguments
885 ///
886 /// `worker` is the index of the worker being queried. The given value must
887 /// be between 0 and `num_workers()`. The index uniquely identifies a single
888 /// worker and will continue to identify the worker throughout the lifetime
889 /// of the runtime instance.
890 ///
891 /// # Panics
892 ///
893 /// The method panics when `worker` represents an invalid worker, i.e. is
894 /// greater than or equal to `num_workers()`.
895 ///
896 /// # Examples
897 ///
898 /// ```
899 /// use tokio::runtime::Handle;
900 ///
901 /// # #[tokio::main(flavor = "current_thread")]
902 /// # async fn main() {
903 /// let metrics = Handle::current().metrics();
904 ///
905 /// let n = metrics.worker_overflow_count(0);
906 /// println!("worker 0 has overflowed its queue {} times", n);
907 /// # }
908 /// ```
909 pub fn worker_overflow_count(&self, worker: usize) -> u64 {
910 self.handle
911 .inner
912 .worker_metrics(worker)
913 .overflow_count
914 .load(Relaxed)
915 }
916
917 /// Returns the number of times the given worker polled tasks with a poll
918 /// duration within the given bucket's range.
919 ///
920 /// Each worker maintains its own histogram and the counts for each bucket
921 /// starts at zero when the runtime is created. Each time the worker polls a
922 /// task, it tracks the duration the task poll time took and increments the
923 /// associated bucket by 1.
924 ///
925 /// Each bucket is a monotonically increasing counter. It is never
926 /// decremented or reset to zero.
927 ///
928 /// # Arguments
929 ///
930 /// `worker` is the index of the worker being queried. The given value must
931 /// be between 0 and `num_workers()`. The index uniquely identifies a single
932 /// worker and will continue to identify the worker throughout the lifetime
933 /// of the runtime instance.
934 ///
935 /// `bucket` is the index of the bucket being queried. The bucket is scoped
936 /// to the worker. The range represented by the bucket can be queried by
937 /// calling [`poll_time_histogram_bucket_range()`]. Each worker maintains
938 /// identical bucket ranges.
939 ///
940 /// # Panics
941 ///
942 /// The method panics when `worker` represents an invalid worker, i.e. is
943 /// greater than or equal to `num_workers()` or if `bucket` represents an
944 /// invalid bucket.
945 ///
946 /// # Examples
947 ///
948 /// ```
949 /// use tokio::runtime::{self, Handle};
950 ///
951 /// fn main() {
952 /// runtime::Builder::new_current_thread()
953 /// .enable_metrics_poll_time_histogram()
954 /// .build()
955 /// .unwrap()
956 /// .block_on(async {
957 /// let metrics = Handle::current().metrics();
958 /// let buckets = metrics.poll_time_histogram_num_buckets();
959 ///
960 /// for worker in 0..metrics.num_workers() {
961 /// for i in 0..buckets {
962 /// let count = metrics.poll_time_histogram_bucket_count(worker, i);
963 /// println!("Poll count {}", count);
964 /// }
965 /// }
966 /// });
967 /// }
968 /// ```
969 ///
970 /// [`poll_time_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::poll_time_histogram_bucket_range
971 #[track_caller]
972 pub fn poll_time_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 {
973 self.handle
974 .inner
975 .worker_metrics(worker)
976 .poll_count_histogram
977 .as_ref()
978 .map_or(0, |histogram| histogram.get(bucket))
979 }
980
981 #[doc(hidden)]
982 #[deprecated(note = "use `poll_time_histogram_bucket_count` instead")]
983 pub fn poll_count_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 {
984 self.poll_time_histogram_bucket_count(worker, bucket)
985 }
986
987 /// Returns the mean duration of task polls, in nanoseconds.
988 ///
989 /// This is an exponentially weighted moving average. Currently, this metric
990 /// is only provided by the multi-threaded runtime.
991 ///
992 /// # Arguments
993 ///
994 /// `worker` is the index of the worker being queried. The given value must
995 /// be between 0 and `num_workers()`. The index uniquely identifies a single
996 /// worker and will continue to identify the worker throughout the lifetime
997 /// of the runtime instance.
998 ///
999 /// # Panics
1000 ///
1001 /// The method panics when `worker` represents an invalid worker, i.e. is
1002 /// greater than or equal to `num_workers()`.
1003 ///
1004 /// # Examples
1005 ///
1006 /// ```
1007 /// use tokio::runtime::Handle;
1008 ///
1009 /// # #[tokio::main(flavor = "current_thread")]
1010 /// # async fn main() {
1011 /// let metrics = Handle::current().metrics();
1012 ///
1013 /// let n = metrics.worker_mean_poll_time(0);
1014 /// println!("worker 0 has a mean poll time of {:?}", n);
1015 /// # }
1016 /// ```
1017 #[track_caller]
1018 pub fn worker_mean_poll_time(&self, worker: usize) -> Duration {
1019 let nanos = self
1020 .handle
1021 .inner
1022 .worker_metrics(worker)
1023 .mean_poll_time
1024 .load(Relaxed);
1025 Duration::from_nanos(nanos)
1026 }
1027 }
1028
1029 feature! {
1030 #![all(tokio_unstable, feature = "schedule-latency")]
1031 /// Returns `true` if the runtime is tracking the distribution of task
1032 /// schedule latencies.
1033 ///
1034 /// Task schedule latencies are not instrumented by default as doing so
1035 /// requires calling [`Instant::now()`] when a task is scheduled and when
1036 /// it is polled. The feature is enabled by calling
1037 /// [`enable_metrics_schedule_latency_histogram()`] when building the runtime.
1038 ///
1039 /// # Examples
1040 ///
1041 /// ```
1042 /// use tokio::runtime::{self, Handle};
1043 ///
1044 /// fn main() {
1045 /// runtime::Builder::new_current_thread()
1046 /// .enable_metrics_schedule_latency_histogram()
1047 /// .build()
1048 /// .unwrap()
1049 /// .block_on(async {
1050 /// let metrics = Handle::current().metrics();
1051 /// let enabled = metrics.schedule_latency_histogram_enabled();
1052 ///
1053 /// println!("Tracking task schedule latency distribution: {:?}", enabled);
1054 /// });
1055 /// }
1056 /// ```
1057 ///
1058 /// [`enable_metrics_schedule_latency_histogram()`]: crate::runtime::Builder::enable_metrics_schedule_latency_histogram
1059 /// [`Instant::now()`]: std::time::Instant::now
1060 pub fn schedule_latency_histogram_enabled(&self) -> bool {
1061 self.handle.inner.worker_metrics(0).schedule_latency_histogram.is_some()
1062 }
1063
1064 /// Returns the number of histogram buckets tracking the distribution of
1065 /// task schedule latencies.
1066 ///
1067 /// This value is configured by calling
1068 /// [`metrics_schedule_latency_histogram_configuration()`] when building the runtime.
1069 ///
1070 /// # Examples
1071 ///
1072 /// ```
1073 /// use tokio::runtime::{self, Handle};
1074 ///
1075 /// fn main() {
1076 /// runtime::Builder::new_current_thread()
1077 /// .enable_metrics_schedule_latency_histogram()
1078 /// .build()
1079 /// .unwrap()
1080 /// .block_on(async {
1081 /// let metrics = Handle::current().metrics();
1082 /// let buckets = metrics.schedule_latency_histogram_num_buckets();
1083 ///
1084 /// println!("Histogram buckets: {:?}", buckets);
1085 /// });
1086 /// }
1087 /// ```
1088 ///
1089 /// [`metrics_schedule_latency_histogram_configuration()`]: crate::runtime::Builder::metrics_schedule_latency_histogram_configuration
1090 pub fn schedule_latency_histogram_num_buckets(&self) -> usize {
1091 self.handle
1092 .inner
1093 .worker_metrics(0)
1094 .schedule_latency_histogram
1095 .as_ref()
1096 .map_or(0, |histogram| histogram.num_buckets())
1097 }
1098
1099 /// Returns the range of task schedule latencies tracked by the given bucket.
1100 ///
1101 /// This value is configured by calling
1102 /// [`metrics_schedule_latency_histogram_configuration()`] when building the runtime.
1103 ///
1104 /// # Panics
1105 ///
1106 /// The method panics if `bucket` represents an invalid bucket index, i.e.
1107 /// is greater than or equal to `schedule_latency_histogram_num_buckets()`.
1108 ///
1109 /// # Examples
1110 ///
1111 /// ```
1112 /// use tokio::runtime::{self, Handle};
1113 ///
1114 /// fn main() {
1115 /// runtime::Builder::new_current_thread()
1116 /// .enable_metrics_schedule_latency_histogram()
1117 /// .build()
1118 /// .unwrap()
1119 /// .block_on(async {
1120 /// let metrics = Handle::current().metrics();
1121 /// let buckets = metrics.schedule_latency_histogram_num_buckets();
1122 ///
1123 /// for i in 0..buckets {
1124 /// let range = metrics.schedule_latency_histogram_bucket_range(i);
1125 /// println!("Histogram bucket {} range: {:?}", i, range);
1126 /// }
1127 /// });
1128 /// }
1129 /// ```
1130 ///
1131 /// [`metrics_schedule_latency_histogram_configuration()`]: crate::runtime::Builder::metrics_schedule_latency_histogram_configuration
1132 #[track_caller]
1133 pub fn schedule_latency_histogram_bucket_range(&self, bucket: usize) -> Range<Duration> {
1134 self.handle
1135 .inner
1136 .worker_metrics(0)
1137 .schedule_latency_histogram
1138 .as_ref()
1139 .map_or_else(Range::default, |histogram| {
1140 let range = histogram.bucket_range(bucket);
1141 std::ops::Range {
1142 start: Duration::from_nanos(range.start),
1143 end: Duration::from_nanos(range.end),
1144 }
1145 })
1146 }
1147
1148 /// Returns the number of times the given worker polled tasks with a schedule
1149 /// latency within the given bucket's range.
1150 ///
1151 /// Each worker maintains its own histogram and the counts for each bucket
1152 /// starts at zero when the runtime is created. Each time the worker polls a
1153 /// task, it tracks the time elapsed between when the task was scheduled and
1154 /// when it was polled and increments the associated bucket by 1.
1155 ///
1156 /// Each bucket is a monotonically increasing counter. It is never
1157 /// decremented or reset to zero.
1158 ///
1159 /// # Arguments
1160 ///
1161 /// `worker` is the index of the worker being queried. The given value must
1162 /// be between 0 and `num_workers()`. The index uniquely identifies a single
1163 /// worker and will continue to identify the worker throughout the lifetime
1164 /// of the runtime instance.
1165 ///
1166 /// `bucket` is the index of the bucket being queried. The bucket is scoped
1167 /// to the worker. The range represented by the bucket can be queried by
1168 /// calling [`schedule_latency_histogram_bucket_range()`]. Each worker maintains
1169 /// identical bucket ranges.
1170 ///
1171 /// # Panics
1172 ///
1173 /// The method panics when `worker` represents an invalid worker, i.e. is
1174 /// greater than or equal to `num_workers()` or if `bucket` represents an
1175 /// invalid bucket.
1176 ///
1177 /// # Examples
1178 ///
1179 /// ```
1180 /// use tokio::runtime::{self, Handle};
1181 ///
1182 /// fn main() {
1183 /// runtime::Builder::new_current_thread()
1184 /// .enable_metrics_schedule_latency_histogram()
1185 /// .build()
1186 /// .unwrap()
1187 /// .block_on(async {
1188 /// let metrics = Handle::current().metrics();
1189 /// let buckets = metrics.schedule_latency_histogram_num_buckets();
1190 ///
1191 /// for worker in 0..metrics.num_workers() {
1192 /// for i in 0..buckets {
1193 /// let range = metrics.schedule_latency_histogram_bucket_range(i);
1194 /// let count = metrics.schedule_latency_histogram_bucket_count(worker, i);
1195 /// println!("{} tasks encountered a scheduling latency between {}us and {}us", count, range.start.as_micros(), range.end.as_micros());
1196 /// }
1197 /// }
1198 /// });
1199 /// }
1200 /// ```
1201 ///
1202 /// [`schedule_latency_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::schedule_latency_histogram_bucket_range
1203 #[track_caller]
1204 pub fn schedule_latency_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 {
1205 self.handle
1206 .inner
1207 .worker_metrics(worker)
1208 .schedule_latency_histogram
1209 .as_ref()
1210 .map_or(0, |histogram| histogram.get(bucket))
1211 }
1212 }
1213
1214 feature! {
1215 #![all(
1216 tokio_unstable,
1217 target_has_atomic = "64",
1218 feature = "net"
1219 )]
1220 /// Returns the number of file descriptors that have been registered with the
1221 /// runtime's I/O driver.
1222 ///
1223 /// # Examples
1224 ///
1225 /// ```
1226 /// use tokio::runtime::Handle;
1227 ///
1228 /// #[tokio::main]
1229 /// async fn main() {
1230 /// let metrics = Handle::current().metrics();
1231 ///
1232 /// let registered_fds = metrics.io_driver_fd_registered_count();
1233 /// println!("{} fds have been registered with the runtime's I/O driver.", registered_fds);
1234 ///
1235 /// let deregistered_fds = metrics.io_driver_fd_deregistered_count();
1236 ///
1237 /// let current_fd_count = registered_fds - deregistered_fds;
1238 /// println!("{} fds are currently registered by the runtime's I/O driver.", current_fd_count);
1239 /// }
1240 /// ```
1241 pub fn io_driver_fd_registered_count(&self) -> u64 {
1242 self.with_io_driver_metrics(|m| {
1243 m.fd_registered_count.load(Relaxed)
1244 })
1245 }
1246
1247 /// Returns the number of file descriptors that have been deregistered by the
1248 /// runtime's I/O driver.
1249 ///
1250 /// # Examples
1251 ///
1252 /// ```
1253 /// use tokio::runtime::Handle;
1254 ///
1255 /// #[tokio::main]
1256 /// async fn main() {
1257 /// let metrics = Handle::current().metrics();
1258 ///
1259 /// let n = metrics.io_driver_fd_deregistered_count();
1260 /// println!("{} fds have been deregistered by the runtime's I/O driver.", n);
1261 /// }
1262 /// ```
1263 pub fn io_driver_fd_deregistered_count(&self) -> u64 {
1264 self.with_io_driver_metrics(|m| {
1265 m.fd_deregistered_count.load(Relaxed)
1266 })
1267 }
1268
1269 /// Returns the number of ready events processed by the runtime's
1270 /// I/O driver.
1271 ///
1272 /// # Examples
1273 ///
1274 /// ```
1275 /// use tokio::runtime::Handle;
1276 ///
1277 /// #[tokio::main]
1278 /// async fn main() {
1279 /// let metrics = Handle::current().metrics();
1280 ///
1281 /// let n = metrics.io_driver_ready_count();
1282 /// println!("{} ready events processed by the runtime's I/O driver.", n);
1283 /// }
1284 /// ```
1285 pub fn io_driver_ready_count(&self) -> u64 {
1286 self.with_io_driver_metrics(|m| m.ready_count.load(Relaxed))
1287 }
1288
1289 fn with_io_driver_metrics<F>(&self, f: F) -> u64
1290 where
1291 F: Fn(&super::IoDriverMetrics) -> u64,
1292 {
1293 // TODO: Investigate if this should return 0, most of our metrics always increase
1294 // thus this breaks that guarantee.
1295 self.handle
1296 .inner
1297 .driver()
1298 .io
1299 .as_ref()
1300 .map_or(0, |h| f(&h.metrics))
1301 }
1302 }
1303}