| 1 | use std::{fmt, num::NonZeroU64}; |
| 2 | |
| 3 | use crate::time::FineDuration; |
| 4 | |
| 5 | #[cfg (target_arch = "aarch64" )] |
| 6 | #[path = "aarch64.rs" ] |
| 7 | mod arch; |
| 8 | |
| 9 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 10 | #[path = "x86.rs" ] |
| 11 | mod arch; |
| 12 | |
| 13 | /// [CPU timestamp counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter). |
| 14 | #[derive (Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
| 15 | #[repr (transparent)] |
| 16 | pub(crate) struct TscTimestamp { |
| 17 | pub value: u64, |
| 18 | } |
| 19 | |
| 20 | impl TscTimestamp { |
| 21 | /// Gets the timestamp frequency. |
| 22 | /// |
| 23 | /// On AArch64, this simply reads `cntfrq_el0`. On x86, this measures the |
| 24 | /// TSC frequency. |
| 25 | #[inline ] |
| 26 | #[allow (unreachable_code)] |
| 27 | pub fn frequency() -> Result<NonZeroU64, TscUnavailable> { |
| 28 | // Miri does not support inline assembly. |
| 29 | #[cfg (miri)] |
| 30 | return Err(TscUnavailable::Unimplemented); |
| 31 | |
| 32 | #[cfg (any(target_arch = "aarch64" , target_arch = "x86" , target_arch = "x86_64" ))] |
| 33 | return NonZeroU64::new(arch::frequency()?).ok_or(TscUnavailable::ZeroFrequency); |
| 34 | |
| 35 | Err(TscUnavailable::Unimplemented) |
| 36 | } |
| 37 | |
| 38 | /// Reads the timestamp counter. |
| 39 | #[inline (always)] |
| 40 | pub fn start() -> Self { |
| 41 | #[allow (unused)] |
| 42 | let value = 0; |
| 43 | |
| 44 | #[cfg (target_arch = "aarch64" )] |
| 45 | let value = arch::timestamp(); |
| 46 | |
| 47 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 48 | let value = arch::start_timestamp(); |
| 49 | |
| 50 | Self { value } |
| 51 | } |
| 52 | |
| 53 | /// Reads the timestamp counter. |
| 54 | #[inline (always)] |
| 55 | pub fn end() -> Self { |
| 56 | #[allow (unused)] |
| 57 | let value = 0; |
| 58 | |
| 59 | #[cfg (target_arch = "aarch64" )] |
| 60 | let value = arch::timestamp(); |
| 61 | |
| 62 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 63 | let value = arch::end_timestamp(); |
| 64 | |
| 65 | Self { value } |
| 66 | } |
| 67 | |
| 68 | pub fn duration_since(self, earlier: Self, frequency: NonZeroU64) -> FineDuration { |
| 69 | const PICOS: u128 = 1_000_000_000_000; |
| 70 | |
| 71 | let Some(diff) = self.value.checked_sub(earlier.value) else { |
| 72 | return Default::default(); |
| 73 | }; |
| 74 | |
| 75 | FineDuration { picos: (diff as u128 * PICOS) / frequency.get() as u128 } |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | /// Reason for why the timestamp counter cannot be used. |
| 80 | #[derive (Clone, Copy)] |
| 81 | pub(crate) enum TscUnavailable { |
| 82 | /// Not yet implemented for this platform. |
| 83 | Unimplemented, |
| 84 | |
| 85 | /// Got a frequency of 0. |
| 86 | ZeroFrequency, |
| 87 | |
| 88 | /// Missing the appropriate instructions. |
| 89 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 90 | MissingInstructions, |
| 91 | |
| 92 | /// The timestamp counter is not guaranteed to be constant. |
| 93 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 94 | VariableFrequency, |
| 95 | } |
| 96 | |
| 97 | impl fmt::Display for TscUnavailable { |
| 98 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 99 | let reason: &'static str = match self { |
| 100 | Self::Unimplemented => "unimplemented" , |
| 101 | Self::ZeroFrequency => "zero TSC frequency" , |
| 102 | |
| 103 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 104 | Self::MissingInstructions => "missing instructions" , |
| 105 | |
| 106 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 107 | Self::VariableFrequency => "variable TSC frequency" , |
| 108 | }; |
| 109 | |
| 110 | f.write_str(data:reason) |
| 111 | } |
| 112 | } |
| 113 | |