madsim_real_tokio/runtime/metrics/runtime.rs
1use crate::runtime::Handle;
2
3use std::ops::Range;
4use std::sync::atomic::Ordering::Relaxed;
5use std::time::Duration;
6
7/// Handle to the runtime's metrics.
8///
9/// This handle is internally reference-counted and can be freely cloned. A
10/// `RuntimeMetrics` handle is obtained using the [`Runtime::metrics`] method.
11///
12/// [`Runtime::metrics`]: crate::runtime::Runtime::metrics()
13#[derive(Clone, Debug)]
14pub struct RuntimeMetrics {
15 handle: Handle,
16}
17
18impl RuntimeMetrics {
19 pub(crate) fn new(handle: Handle) -> RuntimeMetrics {
20 RuntimeMetrics { handle }
21 }
22
23 /// Returns the number of worker threads used by the runtime.
24 ///
25 /// The number of workers is set by configuring `worker_threads` on
26 /// `runtime::Builder`. When using the `current_thread` runtime, the return
27 /// value is always `1`.
28 ///
29 /// # Examples
30 ///
31 /// ```
32 /// use tokio::runtime::Handle;
33 ///
34 /// #[tokio::main]
35 /// async fn main() {
36 /// let metrics = Handle::current().metrics();
37 ///
38 /// let n = metrics.num_workers();
39 /// println!("Runtime is using {} workers", n);
40 /// }
41 /// ```
42 pub fn num_workers(&self) -> usize {
43 self.handle.inner.num_workers()
44 }
45
46 /// Returns the number of additional threads spawned by the runtime.
47 ///
48 /// The number of workers is set by configuring `max_blocking_threads` on
49 /// `runtime::Builder`.
50 ///
51 /// # Examples
52 ///
53 /// ```
54 /// use tokio::runtime::Handle;
55 ///
56 /// #[tokio::main]
57 /// async fn main() {
58 /// let _ = tokio::task::spawn_blocking(move || {
59 /// // Stand-in for compute-heavy work or using synchronous APIs
60 /// 1 + 1
61 /// }).await;
62 /// let metrics = Handle::current().metrics();
63 ///
64 /// let n = metrics.num_blocking_threads();
65 /// println!("Runtime has created {} threads", n);
66 /// }
67 /// ```
68 pub fn num_blocking_threads(&self) -> usize {
69 self.handle.inner.num_blocking_threads()
70 }
71
72 /// Returns the number of active tasks in the runtime.
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// use tokio::runtime::Handle;
78 ///
79 /// #[tokio::main]
80 /// async fn main() {
81 /// let metrics = Handle::current().metrics();
82 ///
83 /// let n = metrics.active_tasks_count();
84 /// println!("Runtime has {} active tasks", n);
85 /// }
86 /// ```
87 pub fn active_tasks_count(&self) -> usize {
88 self.handle.inner.active_tasks_count()
89 }
90
91 /// Returns the number of idle threads, which have spawned by the runtime
92 /// for `spawn_blocking` calls.
93 ///
94 /// # Examples
95 ///
96 /// ```
97 /// use tokio::runtime::Handle;
98 ///
99 /// #[tokio::main]
100 /// async fn main() {
101 /// let _ = tokio::task::spawn_blocking(move || {
102 /// // Stand-in for compute-heavy work or using synchronous APIs
103 /// 1 + 1
104 /// }).await;
105 /// let metrics = Handle::current().metrics();
106 ///
107 /// let n = metrics.num_idle_blocking_threads();
108 /// println!("Runtime has {} idle blocking thread pool threads", n);
109 /// }
110 /// ```
111 pub fn num_idle_blocking_threads(&self) -> usize {
112 self.handle.inner.num_idle_blocking_threads()
113 }
114
115 /// Returns the number of tasks scheduled from **outside** of the runtime.
116 ///
117 /// The remote schedule count starts at zero when the runtime is created and
118 /// increases by one each time a task is woken from **outside** of the
119 /// runtime. This usually means that a task is spawned or notified from a
120 /// non-runtime thread and must be queued using the Runtime's injection
121 /// queue, which tends to be slower.
122 ///
123 /// The counter is monotonically increasing. It is never decremented or
124 /// reset to zero.
125 ///
126 /// # Examples
127 ///
128 /// ```
129 /// use tokio::runtime::Handle;
130 ///
131 /// #[tokio::main]
132 /// async fn main() {
133 /// let metrics = Handle::current().metrics();
134 ///
135 /// let n = metrics.remote_schedule_count();
136 /// println!("{} tasks were scheduled from outside the runtime", n);
137 /// }
138 /// ```
139 pub fn remote_schedule_count(&self) -> u64 {
140 self.handle
141 .inner
142 .scheduler_metrics()
143 .remote_schedule_count
144 .load(Relaxed)
145 }
146
147 /// Returns the number of times that tasks have been forced to yield back to the scheduler
148 /// after exhausting their task budgets.
149 ///
150 /// This count starts at zero when the runtime is created and increases by one each time a task yields due to exhausting its budget.
151 ///
152 /// The counter is monotonically increasing. It is never decremented or
153 /// reset to zero.
154 pub fn budget_forced_yield_count(&self) -> u64 {
155 self.handle
156 .inner
157 .scheduler_metrics()
158 .budget_forced_yield_count
159 .load(Relaxed)
160 }
161
162 /// Returns the total number of times the given worker thread has parked.
163 ///
164 /// The worker park count starts at zero when the runtime is created and
165 /// increases by one each time the worker parks the thread waiting for new
166 /// inbound events to process. This usually means the worker has processed
167 /// all pending work and is currently idle.
168 ///
169 /// The counter is monotonically increasing. It is never decremented or
170 /// reset to zero.
171 ///
172 /// # Arguments
173 ///
174 /// `worker` is the index of the worker being queried. The given value must
175 /// be between 0 and `num_workers()`. The index uniquely identifies a single
176 /// worker and will continue to identify the worker throughout the lifetime
177 /// of the runtime instance.
178 ///
179 /// # Panics
180 ///
181 /// The method panics when `worker` represents an invalid worker, i.e. is
182 /// greater than or equal to `num_workers()`.
183 ///
184 /// # Examples
185 ///
186 /// ```
187 /// use tokio::runtime::Handle;
188 ///
189 /// #[tokio::main]
190 /// async fn main() {
191 /// let metrics = Handle::current().metrics();
192 ///
193 /// let n = metrics.worker_park_count(0);
194 /// println!("worker 0 parked {} times", n);
195 /// }
196 /// ```
197 pub fn worker_park_count(&self, worker: usize) -> u64 {
198 self.handle
199 .inner
200 .worker_metrics(worker)
201 .park_count
202 .load(Relaxed)
203 }
204
205 /// Returns the number of times the given worker thread unparked but
206 /// performed no work before parking again.
207 ///
208 /// The worker no-op count starts at zero when the runtime is created and
209 /// increases by one each time the worker unparks the thread but finds no
210 /// new work and goes back to sleep. This indicates a false-positive wake up.
211 ///
212 /// The counter is monotonically increasing. It is never decremented or
213 /// reset to zero.
214 ///
215 /// # Arguments
216 ///
217 /// `worker` is the index of the worker being queried. The given value must
218 /// be between 0 and `num_workers()`. The index uniquely identifies a single
219 /// worker and will continue to identify the worker throughout the lifetime
220 /// of the runtime instance.
221 ///
222 /// # Panics
223 ///
224 /// The method panics when `worker` represents an invalid worker, i.e. is
225 /// greater than or equal to `num_workers()`.
226 ///
227 /// # Examples
228 ///
229 /// ```
230 /// use tokio::runtime::Handle;
231 ///
232 /// #[tokio::main]
233 /// async fn main() {
234 /// let metrics = Handle::current().metrics();
235 ///
236 /// let n = metrics.worker_noop_count(0);
237 /// println!("worker 0 had {} no-op unparks", n);
238 /// }
239 /// ```
240 pub fn worker_noop_count(&self, worker: usize) -> u64 {
241 self.handle
242 .inner
243 .worker_metrics(worker)
244 .noop_count
245 .load(Relaxed)
246 }
247
248 /// Returns the number of tasks the given worker thread stole from
249 /// another worker thread.
250 ///
251 /// This metric only applies to the **multi-threaded** runtime and will
252 /// always return `0` when using the current thread runtime.
253 ///
254 /// The worker steal count starts at zero when the runtime is created and
255 /// increases by `N` each time the worker has processed its scheduled queue
256 /// and successfully steals `N` more pending tasks from another worker.
257 ///
258 /// The counter is monotonically increasing. It is never decremented or
259 /// reset to zero.
260 ///
261 /// # Arguments
262 ///
263 /// `worker` is the index of the worker being queried. The given value must
264 /// be between 0 and `num_workers()`. The index uniquely identifies a single
265 /// worker and will continue to identify the worker throughout the lifetime
266 /// of the runtime instance.
267 ///
268 /// # Panics
269 ///
270 /// The method panics when `worker` represents an invalid worker, i.e. is
271 /// greater than or equal to `num_workers()`.
272 ///
273 /// # Examples
274 ///
275 /// ```
276 /// use tokio::runtime::Handle;
277 ///
278 /// #[tokio::main]
279 /// async fn main() {
280 /// let metrics = Handle::current().metrics();
281 ///
282 /// let n = metrics.worker_steal_count(0);
283 /// println!("worker 0 has stolen {} tasks", n);
284 /// }
285 /// ```
286 pub fn worker_steal_count(&self, worker: usize) -> u64 {
287 self.handle
288 .inner
289 .worker_metrics(worker)
290 .steal_count
291 .load(Relaxed)
292 }
293
294 /// Returns the number of times the given worker thread stole tasks from
295 /// another worker thread.
296 ///
297 /// This metric only applies to the **multi-threaded** runtime and will
298 /// always return `0` when using the current thread runtime.
299 ///
300 /// The worker steal count starts at zero when the runtime is created and
301 /// increases by one each time the worker has processed its scheduled queue
302 /// and successfully steals more pending tasks from another worker.
303 ///
304 /// The counter is monotonically increasing. It is never decremented or
305 /// reset to zero.
306 ///
307 /// # Arguments
308 ///
309 /// `worker` is the index of the worker being queried. The given value must
310 /// be between 0 and `num_workers()`. The index uniquely identifies a single
311 /// worker and will continue to identify the worker throughout the lifetime
312 /// of the runtime instance.
313 ///
314 /// # Panics
315 ///
316 /// The method panics when `worker` represents an invalid worker, i.e. is
317 /// greater than or equal to `num_workers()`.
318 ///
319 /// # Examples
320 ///
321 /// ```
322 /// use tokio::runtime::Handle;
323 ///
324 /// #[tokio::main]
325 /// async fn main() {
326 /// let metrics = Handle::current().metrics();
327 ///
328 /// let n = metrics.worker_steal_operations(0);
329 /// println!("worker 0 has stolen tasks {} times", n);
330 /// }
331 /// ```
332 pub fn worker_steal_operations(&self, worker: usize) -> u64 {
333 self.handle
334 .inner
335 .worker_metrics(worker)
336 .steal_operations
337 .load(Relaxed)
338 }
339
340 /// Returns the number of tasks the given worker thread has polled.
341 ///
342 /// The worker poll count starts at zero when the runtime is created and
343 /// increases by one each time the worker polls a scheduled task.
344 ///
345 /// The counter is monotonically increasing. It is never decremented or
346 /// reset to zero.
347 ///
348 /// # Arguments
349 ///
350 /// `worker` is the index of the worker being queried. The given value must
351 /// be between 0 and `num_workers()`. The index uniquely identifies a single
352 /// worker and will continue to identify the worker throughout the lifetime
353 /// of the runtime instance.
354 ///
355 /// # Panics
356 ///
357 /// The method panics when `worker` represents an invalid worker, i.e. is
358 /// greater than or equal to `num_workers()`.
359 ///
360 /// # Examples
361 ///
362 /// ```
363 /// use tokio::runtime::Handle;
364 ///
365 /// #[tokio::main]
366 /// async fn main() {
367 /// let metrics = Handle::current().metrics();
368 ///
369 /// let n = metrics.worker_poll_count(0);
370 /// println!("worker 0 has polled {} tasks", n);
371 /// }
372 /// ```
373 pub fn worker_poll_count(&self, worker: usize) -> u64 {
374 self.handle
375 .inner
376 .worker_metrics(worker)
377 .poll_count
378 .load(Relaxed)
379 }
380
381 /// Returns the amount of time the given worker thread has been busy.
382 ///
383 /// The worker busy duration starts at zero when the runtime is created and
384 /// increases whenever the worker is spending time processing work. Using
385 /// this value can indicate the load of the given worker. If a lot of time
386 /// is spent busy, then the worker is under load and will check for inbound
387 /// events less often.
388 ///
389 /// The timer is monotonically increasing. It is never decremented or reset
390 /// to zero.
391 ///
392 /// # Arguments
393 ///
394 /// `worker` is the index of the worker being queried. The given value must
395 /// be between 0 and `num_workers()`. The index uniquely identifies a single
396 /// worker and will continue to identify the worker throughout the lifetime
397 /// of the runtime instance.
398 ///
399 /// # Panics
400 ///
401 /// The method panics when `worker` represents an invalid worker, i.e. is
402 /// greater than or equal to `num_workers()`.
403 ///
404 /// # Examples
405 ///
406 /// ```
407 /// use tokio::runtime::Handle;
408 ///
409 /// #[tokio::main]
410 /// async fn main() {
411 /// let metrics = Handle::current().metrics();
412 ///
413 /// let n = metrics.worker_total_busy_duration(0);
414 /// println!("worker 0 was busy for a total of {:?}", n);
415 /// }
416 /// ```
417 pub fn worker_total_busy_duration(&self, worker: usize) -> Duration {
418 let nanos = self
419 .handle
420 .inner
421 .worker_metrics(worker)
422 .busy_duration_total
423 .load(Relaxed);
424 Duration::from_nanos(nanos)
425 }
426
427 /// Returns the number of tasks scheduled from **within** the runtime on the
428 /// given worker's local queue.
429 ///
430 /// The local schedule count starts at zero when the runtime is created and
431 /// increases by one each time a task is woken from **inside** of the
432 /// runtime on the given worker. This usually means that a task is spawned
433 /// or notified from within a runtime thread and will be queued on the
434 /// worker-local queue.
435 ///
436 /// The counter is monotonically increasing. It is never decremented or
437 /// reset to zero.
438 ///
439 /// # Arguments
440 ///
441 /// `worker` is the index of the worker being queried. The given value must
442 /// be between 0 and `num_workers()`. The index uniquely identifies a single
443 /// worker and will continue to identify the worker throughout the lifetime
444 /// of the runtime instance.
445 ///
446 /// # Panics
447 ///
448 /// The method panics when `worker` represents an invalid worker, i.e. is
449 /// greater than or equal to `num_workers()`.
450 ///
451 /// # Examples
452 ///
453 /// ```
454 /// use tokio::runtime::Handle;
455 ///
456 /// #[tokio::main]
457 /// async fn main() {
458 /// let metrics = Handle::current().metrics();
459 ///
460 /// let n = metrics.worker_local_schedule_count(0);
461 /// println!("{} tasks were scheduled on the worker's local queue", n);
462 /// }
463 /// ```
464 pub fn worker_local_schedule_count(&self, worker: usize) -> u64 {
465 self.handle
466 .inner
467 .worker_metrics(worker)
468 .local_schedule_count
469 .load(Relaxed)
470 }
471
472 /// Returns the number of times the given worker thread saturated its local
473 /// queue.
474 ///
475 /// This metric only applies to the **multi-threaded** scheduler.
476 ///
477 /// The worker overflow count starts at zero when the runtime is created and
478 /// increases by one each time the worker attempts to schedule a task
479 /// locally, but its local queue is full. When this happens, half of the
480 /// local queue is moved to the injection queue.
481 ///
482 /// The counter is monotonically increasing. It is never decremented or
483 /// reset to zero.
484 ///
485 /// # Arguments
486 ///
487 /// `worker` is the index of the worker being queried. The given value must
488 /// be between 0 and `num_workers()`. The index uniquely identifies a single
489 /// worker and will continue to identify the worker throughout the lifetime
490 /// of the runtime instance.
491 ///
492 /// # Panics
493 ///
494 /// The method panics when `worker` represents an invalid worker, i.e. is
495 /// greater than or equal to `num_workers()`.
496 ///
497 /// # Examples
498 ///
499 /// ```
500 /// use tokio::runtime::Handle;
501 ///
502 /// #[tokio::main]
503 /// async fn main() {
504 /// let metrics = Handle::current().metrics();
505 ///
506 /// let n = metrics.worker_overflow_count(0);
507 /// println!("worker 0 has overflowed its queue {} times", n);
508 /// }
509 /// ```
510 pub fn worker_overflow_count(&self, worker: usize) -> u64 {
511 self.handle
512 .inner
513 .worker_metrics(worker)
514 .overflow_count
515 .load(Relaxed)
516 }
517
518 /// Returns the number of tasks currently scheduled in the runtime's
519 /// injection queue.
520 ///
521 /// Tasks that are spawned or notified from a non-runtime thread are
522 /// scheduled using the runtime's injection queue. This metric returns the
523 /// **current** number of tasks pending in the injection queue. As such, the
524 /// returned value may increase or decrease as new tasks are scheduled and
525 /// processed.
526 ///
527 /// # Examples
528 ///
529 /// ```
530 /// use tokio::runtime::Handle;
531 ///
532 /// #[tokio::main]
533 /// async fn main() {
534 /// let metrics = Handle::current().metrics();
535 ///
536 /// let n = metrics.injection_queue_depth();
537 /// println!("{} tasks currently pending in the runtime's injection queue", n);
538 /// }
539 /// ```
540 pub fn injection_queue_depth(&self) -> usize {
541 self.handle.inner.injection_queue_depth()
542 }
543
544 /// Returns the number of tasks currently scheduled in the given worker's
545 /// local queue.
546 ///
547 /// Tasks that are spawned or notified from within a runtime thread are
548 /// scheduled using that worker's local queue. This metric returns the
549 /// **current** number of tasks pending in the worker's local queue. As
550 /// such, the returned value may increase or decrease as new tasks are
551 /// scheduled and processed.
552 ///
553 /// # Arguments
554 ///
555 /// `worker` is the index of the worker being queried. The given value must
556 /// be between 0 and `num_workers()`. The index uniquely identifies a single
557 /// worker and will continue to identify the worker throughout the lifetime
558 /// of the runtime instance.
559 ///
560 /// # Panics
561 ///
562 /// The method panics when `worker` represents an invalid worker, i.e. is
563 /// greater than or equal to `num_workers()`.
564 ///
565 /// # Examples
566 ///
567 /// ```
568 /// use tokio::runtime::Handle;
569 ///
570 /// #[tokio::main]
571 /// async fn main() {
572 /// let metrics = Handle::current().metrics();
573 ///
574 /// let n = metrics.worker_local_queue_depth(0);
575 /// println!("{} tasks currently pending in worker 0's local queue", n);
576 /// }
577 /// ```
578 pub fn worker_local_queue_depth(&self, worker: usize) -> usize {
579 self.handle.inner.worker_local_queue_depth(worker)
580 }
581
582 /// Returns `true` if the runtime is tracking the distribution of task poll
583 /// times.
584 ///
585 /// Task poll times are not instrumented by default as doing so requires
586 /// calling [`Instant::now()`] twice per task poll. The feature is enabled
587 /// by calling [`enable_metrics_poll_count_histogram()`] when building the
588 /// runtime.
589 ///
590 /// # Examples
591 ///
592 /// ```
593 /// use tokio::runtime::{self, Handle};
594 ///
595 /// fn main() {
596 /// runtime::Builder::new_current_thread()
597 /// .enable_metrics_poll_count_histogram()
598 /// .build()
599 /// .unwrap()
600 /// .block_on(async {
601 /// let metrics = Handle::current().metrics();
602 /// let enabled = metrics.poll_count_histogram_enabled();
603 ///
604 /// println!("Tracking task poll time distribution: {:?}", enabled);
605 /// });
606 /// }
607 /// ```
608 ///
609 /// [`enable_metrics_poll_count_histogram()`]: crate::runtime::Builder::enable_metrics_poll_count_histogram
610 /// [`Instant::now()`]: std::time::Instant::now
611 pub fn poll_count_histogram_enabled(&self) -> bool {
612 self.handle
613 .inner
614 .worker_metrics(0)
615 .poll_count_histogram
616 .is_some()
617 }
618
619 /// Returns the number of histogram buckets tracking the distribution of
620 /// task poll times.
621 ///
622 /// This value is configured by calling
623 /// [`metrics_poll_count_histogram_buckets()`] when building the runtime.
624 ///
625 /// # Examples
626 ///
627 /// ```
628 /// use tokio::runtime::{self, Handle};
629 ///
630 /// fn main() {
631 /// runtime::Builder::new_current_thread()
632 /// .enable_metrics_poll_count_histogram()
633 /// .build()
634 /// .unwrap()
635 /// .block_on(async {
636 /// let metrics = Handle::current().metrics();
637 /// let buckets = metrics.poll_count_histogram_num_buckets();
638 ///
639 /// println!("Histogram buckets: {:?}", buckets);
640 /// });
641 /// }
642 /// ```
643 ///
644 /// [`metrics_poll_count_histogram_buckets()`]:
645 /// crate::runtime::Builder::metrics_poll_count_histogram_buckets
646 pub fn poll_count_histogram_num_buckets(&self) -> usize {
647 self.handle
648 .inner
649 .worker_metrics(0)
650 .poll_count_histogram
651 .as_ref()
652 .map(|histogram| histogram.num_buckets())
653 .unwrap_or_default()
654 }
655
656 /// Returns the range of task poll times tracked by the given bucket.
657 ///
658 /// This value is configured by calling
659 /// [`metrics_poll_count_histogram_resolution()`] when building the runtime.
660 ///
661 /// # Panics
662 ///
663 /// The method panics if `bucket` represents an invalid bucket index, i.e.
664 /// is greater than or equal to `poll_count_histogram_num_buckets()`.
665 ///
666 /// # Examples
667 ///
668 /// ```
669 /// use tokio::runtime::{self, Handle};
670 ///
671 /// fn main() {
672 /// runtime::Builder::new_current_thread()
673 /// .enable_metrics_poll_count_histogram()
674 /// .build()
675 /// .unwrap()
676 /// .block_on(async {
677 /// let metrics = Handle::current().metrics();
678 /// let buckets = metrics.poll_count_histogram_num_buckets();
679 ///
680 /// for i in 0..buckets {
681 /// let range = metrics.poll_count_histogram_bucket_range(i);
682 /// println!("Histogram bucket {} range: {:?}", i, range);
683 /// }
684 /// });
685 /// }
686 /// ```
687 ///
688 /// [`metrics_poll_count_histogram_resolution()`]:
689 /// crate::runtime::Builder::metrics_poll_count_histogram_resolution
690 #[track_caller]
691 pub fn poll_count_histogram_bucket_range(&self, bucket: usize) -> Range<Duration> {
692 self.handle
693 .inner
694 .worker_metrics(0)
695 .poll_count_histogram
696 .as_ref()
697 .map(|histogram| {
698 let range = histogram.bucket_range(bucket);
699 std::ops::Range {
700 start: Duration::from_nanos(range.start),
701 end: Duration::from_nanos(range.end),
702 }
703 })
704 .unwrap_or_default()
705 }
706
707 /// Returns the number of times the given worker polled tasks with a poll
708 /// duration within the given bucket's range.
709 ///
710 /// Each worker maintains its own histogram and the counts for each bucket
711 /// starts at zero when the runtime is created. Each time the worker polls a
712 /// task, it tracks the duration the task poll time took and increments the
713 /// associated bucket by 1.
714 ///
715 /// Each bucket is a monotonically increasing counter. It is never
716 /// decremented or reset to zero.
717 ///
718 /// # Arguments
719 ///
720 /// `worker` is the index of the worker being queried. The given value must
721 /// be between 0 and `num_workers()`. The index uniquely identifies a single
722 /// worker and will continue to identify the worker throughout the lifetime
723 /// of the runtime instance.
724 ///
725 /// `bucket` is the index of the bucket being queried. The bucket is scoped
726 /// to the worker. The range represented by the bucket can be queried by
727 /// calling [`poll_count_histogram_bucket_range()`]. Each worker maintains
728 /// identical bucket ranges.
729 ///
730 /// # Panics
731 ///
732 /// The method panics when `worker` represents an invalid worker, i.e. is
733 /// greater than or equal to `num_workers()` or if `bucket` represents an
734 /// invalid bucket.
735 ///
736 /// # Examples
737 ///
738 /// ```
739 /// use tokio::runtime::{self, Handle};
740 ///
741 /// fn main() {
742 /// runtime::Builder::new_current_thread()
743 /// .enable_metrics_poll_count_histogram()
744 /// .build()
745 /// .unwrap()
746 /// .block_on(async {
747 /// let metrics = Handle::current().metrics();
748 /// let buckets = metrics.poll_count_histogram_num_buckets();
749 ///
750 /// for worker in 0..metrics.num_workers() {
751 /// for i in 0..buckets {
752 /// let count = metrics.poll_count_histogram_bucket_count(worker, i);
753 /// println!("Poll count {}", count);
754 /// }
755 /// }
756 /// });
757 /// }
758 /// ```
759 ///
760 /// [`poll_count_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::poll_count_histogram_bucket_range
761 #[track_caller]
762 pub fn poll_count_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 {
763 self.handle
764 .inner
765 .worker_metrics(worker)
766 .poll_count_histogram
767 .as_ref()
768 .map(|histogram| histogram.get(bucket))
769 .unwrap_or_default()
770 }
771
772 /// Returns the mean duration of task polls, in nanoseconds.
773 ///
774 /// This is an exponentially weighted moving average. Currently, this metric
775 /// is only provided by the multi-threaded runtime.
776 ///
777 /// # Arguments
778 ///
779 /// `worker` is the index of the worker being queried. The given value must
780 /// be between 0 and `num_workers()`. The index uniquely identifies a single
781 /// worker and will continue to identify the worker throughout the lifetime
782 /// of the runtime instance.
783 ///
784 /// # Panics
785 ///
786 /// The method panics when `worker` represents an invalid worker, i.e. is
787 /// greater than or equal to `num_workers()`.
788 ///
789 /// # Examples
790 ///
791 /// ```
792 /// use tokio::runtime::Handle;
793 ///
794 /// #[tokio::main]
795 /// async fn main() {
796 /// let metrics = Handle::current().metrics();
797 ///
798 /// let n = metrics.worker_mean_poll_time(0);
799 /// println!("worker 0 has a mean poll time of {:?}", n);
800 /// }
801 /// ```
802 #[track_caller]
803 pub fn worker_mean_poll_time(&self, worker: usize) -> Duration {
804 let nanos = self
805 .handle
806 .inner
807 .worker_metrics(worker)
808 .mean_poll_time
809 .load(Relaxed);
810 Duration::from_nanos(nanos)
811 }
812
813 /// Returns the number of tasks currently scheduled in the blocking
814 /// thread pool, spawned using `spawn_blocking`.
815 ///
816 /// This metric returns the **current** number of tasks pending in
817 /// blocking thread pool. As such, the returned value may increase
818 /// or decrease as new tasks are scheduled and processed.
819 ///
820 /// # Examples
821 ///
822 /// ```
823 /// use tokio::runtime::Handle;
824 ///
825 /// #[tokio::main]
826 /// async fn main() {
827 /// let metrics = Handle::current().metrics();
828 ///
829 /// let n = metrics.blocking_queue_depth();
830 /// println!("{} tasks currently pending in the blocking thread pool", n);
831 /// }
832 /// ```
833 pub fn blocking_queue_depth(&self) -> usize {
834 self.handle.inner.blocking_queue_depth()
835 }
836}
837
838cfg_net! {
839 impl RuntimeMetrics {
840 /// Returns the number of file descriptors that have been registered with the
841 /// runtime's I/O driver.
842 ///
843 /// # Examples
844 ///
845 /// ```
846 /// use tokio::runtime::Handle;
847 ///
848 /// #[tokio::main]
849 /// async fn main() {
850 /// let metrics = Handle::current().metrics();
851 ///
852 /// let registered_fds = metrics.io_driver_fd_registered_count();
853 /// println!("{} fds have been registered with the runtime's I/O driver.", registered_fds);
854 ///
855 /// let deregistered_fds = metrics.io_driver_fd_deregistered_count();
856 ///
857 /// let current_fd_count = registered_fds - deregistered_fds;
858 /// println!("{} fds are currently registered by the runtime's I/O driver.", current_fd_count);
859 /// }
860 /// ```
861 pub fn io_driver_fd_registered_count(&self) -> u64 {
862 self.with_io_driver_metrics(|m| {
863 m.fd_registered_count.load(Relaxed)
864 })
865 }
866
867 /// Returns the number of file descriptors that have been deregistered by the
868 /// runtime's I/O driver.
869 ///
870 /// # Examples
871 ///
872 /// ```
873 /// use tokio::runtime::Handle;
874 ///
875 /// #[tokio::main]
876 /// async fn main() {
877 /// let metrics = Handle::current().metrics();
878 ///
879 /// let n = metrics.io_driver_fd_deregistered_count();
880 /// println!("{} fds have been deregistered by the runtime's I/O driver.", n);
881 /// }
882 /// ```
883 pub fn io_driver_fd_deregistered_count(&self) -> u64 {
884 self.with_io_driver_metrics(|m| {
885 m.fd_deregistered_count.load(Relaxed)
886 })
887 }
888
889 /// Returns the number of ready events processed by the runtime's
890 /// I/O driver.
891 ///
892 /// # Examples
893 ///
894 /// ```
895 /// use tokio::runtime::Handle;
896 ///
897 /// #[tokio::main]
898 /// async fn main() {
899 /// let metrics = Handle::current().metrics();
900 ///
901 /// let n = metrics.io_driver_ready_count();
902 /// println!("{} ready events processed by the runtime's I/O driver.", n);
903 /// }
904 /// ```
905 pub fn io_driver_ready_count(&self) -> u64 {
906 self.with_io_driver_metrics(|m| m.ready_count.load(Relaxed))
907 }
908
909 fn with_io_driver_metrics<F>(&self, f: F) -> u64
910 where
911 F: Fn(&super::IoDriverMetrics) -> u64,
912 {
913 // TODO: Investigate if this should return 0, most of our metrics always increase
914 // thus this breaks that guarantee.
915 self.handle
916 .inner
917 .driver()
918 .io
919 .as_ref()
920 .map(|h| f(&h.metrics))
921 .unwrap_or(0)
922 }
923 }
924}