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