async_profiler_agent/metadata/
mod.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module includes ways to get metadata attached to profiling reports.
5
6use ordered_float::OrderedFloat;
7pub use std::time::Duration;
8
9/// An ordered float. Individual type to avoid public API dependencies.
10#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
11#[serde(transparent)]
12pub struct OrderedF64(OrderedFloat<f64>);
13
14impl OrderedF64 {
15    /// Create a new `OrderedF64`
16    ///
17    /// ```rust
18    /// # use async_profiler_agent::metadata::OrderedF64;
19    /// let _v = OrderedF64::new(0.0);
20    /// ```
21    pub const fn new(value: f64) -> Self {
22        Self(OrderedFloat(value))
23    }
24}
25
26impl From<f64> for OrderedF64 {
27    fn from(value: f64) -> Self {
28        Self(OrderedFloat(value))
29    }
30}
31
32impl From<OrderedF64> for f64 {
33    fn from(value: OrderedF64) -> Self {
34        value.0.0
35    }
36}
37
38/// Host Metadata, which describes a host that runs a profiling agent. The current set of supported agent metadata is
39/// AWS-specific. If you are not running on AWS, you can use [AgentMetadata::NoMetadata].
40#[derive(Debug, Clone, PartialEq, Eq)]
41#[non_exhaustive]
42pub enum AgentMetadata {
43    /// Metadata for an [EC2] instance running on AWS
44    ///
45    /// [EC2]: https://aws.amazon.com/ec2
46    #[cfg_attr(feature = "__unstable-fargate-cpu-count", non_exhaustive)]
47    Ec2AgentMetadata {
48        /// The AWS account id
49        aws_account_id: String,
50        /// The AWS region id
51        aws_region_id: String,
52        /// The EC2 instance id
53        ec2_instance_id: String,
54    },
55    /// Metadata for a [Fargate] task running on AWS.
56    ///
57    /// [Fargate]: https://aws.amazon.com/fargate
58    #[cfg_attr(feature = "__unstable-fargate-cpu-count", non_exhaustive)]
59    FargateAgentMetadata {
60        /// The AWS account id
61        aws_account_id: String,
62        /// The AWS region id
63        aws_region_id: String,
64        /// The ECS task ARN
65        ///
66        /// For example, `arn:aws:ecs:us-east-1:123456789012:task/profiler-metadata-cluster/5261e761e0e2a3d92da3f02c8e5bab1f`
67        ///
68        /// See the ECS documentation for more details
69        ecs_task_arn: String,
70        /// The ECS cluster ARN
71        ///
72        /// For example, `arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster`
73        ///
74        /// See the ECS documentation for more details
75        ecs_cluster_arn: String,
76        /// The CPU limit for the Fargate cluster
77        ///
78        /// For example, `Some(0.25)`. This will be `None` if the CPU limit is not specified.
79        ///
80        /// See the ECS documentation for more details
81        #[cfg(feature = "__unstable-fargate-cpu-count")]
82        cpu_limit: Option<OrderedF64>,
83        /// The memory limit for the Fargate cluster (in megabytes)
84        ///
85        /// For example, `Some(2048)`. This will be `None` if the memory limit is not specified.
86        ///
87        /// See the ECS documentation for more details
88        #[cfg(feature = "__unstable-fargate-cpu-count")]
89        memory_limit: Option<u64>,
90    },
91    /// Metadata for a host that is neither an EC2 nor a Fargate
92    #[deprecated = "Use AgentMetadata::NoMetadata"]
93    Other,
94    /// A placeholder when a host has no metadata, or when a reporter does not
95    /// use metadata.
96    NoMetadata,
97}
98
99impl AgentMetadata {
100    /// Create a builder for EC2 agent metadata
101    ///
102    /// Normally, for real-world use, you would get the metadata using
103    /// autodetection via
104    #[cfg_attr(
105        feature = "aws-metadata-no-defaults",
106        doc = "[aws::load_agent_metadata],"
107    )]
108    #[cfg_attr(
109        feature = "aws-metadata-no-defaults",
110        doc = "`aws::load_agent_metadata`,"
111    )]
112    /// this function is intended for use in tests.
113    ///
114    /// ## Example
115    ///
116    /// ```rust
117    /// # use async_profiler_agent::metadata::AgentMetadata;
118    /// let metadata = AgentMetadata::ec2_agent_metadata(
119    ///     "123456789012".to_string(),
120    ///     "us-east-1".to_string(),
121    ///     "i-1234567890abcdef0".to_string(),
122    /// ).build();
123    /// ```
124    pub fn ec2_agent_metadata(
125        aws_account_id: String,
126        aws_region_id: String,
127        ec2_instance_id: String,
128    ) -> Ec2AgentMetadataBuilder {
129        Ec2AgentMetadataBuilder {
130            aws_account_id,
131            aws_region_id,
132            ec2_instance_id,
133        }
134    }
135
136    /// Create a builder for Fargate agent metadata
137    ///
138    /// Normally, for real-world use, you would get the metadata using
139    /// autodetection via
140    #[cfg_attr(
141        feature = "aws-metadata-no-defaults",
142        doc = "[aws::load_agent_metadata],"
143    )]
144    #[cfg_attr(
145        feature = "aws-metadata-no-defaults",
146        doc = "`aws::load_agent_metadata`,"
147    )]
148    /// this function is intended for use in tests.
149    ///
150    /// ## Example
151    ///
152    /// ```rust
153    /// # use async_profiler_agent::metadata::AgentMetadata;
154    /// let metadata = AgentMetadata::fargate_agent_metadata(
155    ///     "123456789012".to_string(),
156    ///     "us-east-1".to_string(),
157    ///     "arn:aws:ecs:us-east-1:123456789012:task/cluster/5261e761e0e2a3d92da3f02c8e5bab1f".to_string(),
158    ///     "arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster".to_string(),
159    /// )
160    /// .with_cpu_limit(0.25)
161    /// .with_memory_limit(2048)
162    /// .build();
163    /// ```
164    pub fn fargate_agent_metadata(
165        aws_account_id: String,
166        aws_region_id: String,
167        ecs_task_arn: String,
168        ecs_cluster_arn: String,
169    ) -> FargateAgentMetadataBuilder {
170        FargateAgentMetadataBuilder {
171            aws_account_id,
172            aws_region_id,
173            ecs_task_arn,
174            ecs_cluster_arn,
175            cpu_limit: None,
176            memory_limit: None,
177        }
178    }
179}
180
181/// Builder for EC2 agent metadata
182#[derive(Debug, Clone)]
183pub struct Ec2AgentMetadataBuilder {
184    aws_account_id: String,
185    aws_region_id: String,
186    ec2_instance_id: String,
187}
188
189impl Ec2AgentMetadataBuilder {
190    /// Build the AgentMetadata
191    pub fn build(self) -> AgentMetadata {
192        AgentMetadata::Ec2AgentMetadata {
193            aws_account_id: self.aws_account_id,
194            aws_region_id: self.aws_region_id,
195            ec2_instance_id: self.ec2_instance_id,
196        }
197    }
198}
199
200/// Builder for Fargate agent metadata
201#[derive(Debug, Clone)]
202pub struct FargateAgentMetadataBuilder {
203    aws_account_id: String,
204    aws_region_id: String,
205    ecs_task_arn: String,
206    ecs_cluster_arn: String,
207    cpu_limit: Option<f64>,
208    memory_limit: Option<u64>,
209}
210
211impl FargateAgentMetadataBuilder {
212    /// Set the CPU limit (in vCPUs)
213    pub fn with_cpu_limit(mut self, cpu_limit: f64) -> Self {
214        self.cpu_limit = Some(cpu_limit);
215        self
216    }
217
218    /// Set the memory limit (in megabytes)
219    pub fn with_memory_limit(mut self, memory_limit: u64) -> Self {
220        self.memory_limit = Some(memory_limit);
221        self
222    }
223
224    /// Build the AgentMetadata
225    pub fn build(self) -> AgentMetadata {
226        AgentMetadata::FargateAgentMetadata {
227            aws_account_id: self.aws_account_id,
228            aws_region_id: self.aws_region_id,
229            ecs_task_arn: self.ecs_task_arn,
230            ecs_cluster_arn: self.ecs_cluster_arn,
231            #[cfg(feature = "__unstable-fargate-cpu-count")]
232            cpu_limit: self.cpu_limit.map(Into::into),
233            #[cfg(feature = "__unstable-fargate-cpu-count")]
234            memory_limit: self.memory_limit,
235        }
236    }
237}
238
239/// Metadata associated with a specific individual profiling report
240#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct ReportMetadata<'a> {
242    /// The host running the agent
243    pub instance: &'a AgentMetadata,
244    /// The start time of the profiling report, as a duration from the process start
245    pub start: Duration,
246    /// The end time of the profiling report, as a duration from the process start
247    pub end: Duration,
248    /// The desired reporting interval (on average, this should be
249    /// approximately the same as `self.end - self.start`).
250    pub reporting_interval: Duration,
251}
252
253#[cfg(feature = "aws-metadata-no-defaults")]
254pub mod aws;
255
256/// [private] dummy metadata to make testing easier
257#[cfg(test)]
258pub(crate) const DUMMY_METADATA: ReportMetadata<'static> = ReportMetadata {
259    instance: &AgentMetadata::NoMetadata,
260    start: Duration::from_secs(1),
261    end: Duration::from_secs(2),
262    reporting_interval: Duration::from_secs(1),
263};