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        #[cfg(feature = "__unstable-fargate-cpu-count")]
55        /// The EC2 instance type
56        ec2_instance_type: String,
57    },
58    /// Metadata for a [Fargate] task running on AWS.
59    ///
60    /// [Fargate]: https://aws.amazon.com/fargate
61    #[cfg_attr(feature = "__unstable-fargate-cpu-count", non_exhaustive)]
62    FargateAgentMetadata {
63        /// The AWS account id
64        aws_account_id: String,
65        /// The AWS region id
66        aws_region_id: String,
67        /// The ECS task ARN
68        ///
69        /// For example, `arn:aws:ecs:us-east-1:123456789012:task/profiler-metadata-cluster/5261e761e0e2a3d92da3f02c8e5bab1f`
70        ///
71        /// See the ECS documentation for more details
72        ecs_task_arn: String,
73        /// The ECS cluster ARN
74        ///
75        /// For example, `arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster`
76        ///
77        /// See the ECS documentation for more details
78        ecs_cluster_arn: String,
79        /// The CPU limit for the Fargate cluster
80        ///
81        /// For example, `Some(0.25)`. This will be `None` if the CPU limit is not specified.
82        ///
83        /// See the ECS documentation for more details
84        #[cfg(feature = "__unstable-fargate-cpu-count")]
85        cpu_limit: Option<OrderedF64>,
86        /// The memory limit for the Fargate cluster (in megabytes)
87        ///
88        /// For example, `Some(2048)`. This will be `None` if the memory limit is not specified.
89        ///
90        /// See the ECS documentation for more details
91        #[cfg(feature = "__unstable-fargate-cpu-count")]
92        memory_limit: Option<u64>,
93    },
94    /// Metadata for a host that is neither an EC2 nor a Fargate
95    #[deprecated = "Use AgentMetadata::NoMetadata"]
96    Other,
97    /// A placeholder when a host has no metadata, or when a reporter does not
98    /// use metadata.
99    NoMetadata,
100}
101
102impl AgentMetadata {
103    /// Create a builder for EC2 agent metadata
104    ///
105    /// Normally, for real-world use, you would get the metadata using
106    /// autodetection via
107    #[cfg_attr(
108        feature = "aws-metadata-no-defaults",
109        doc = "[aws::load_agent_metadata],"
110    )]
111    #[cfg_attr(
112        feature = "aws-metadata-no-defaults",
113        doc = "`aws::load_agent_metadata`,"
114    )]
115    /// this function is intended for use in tests.
116    ///
117    /// ## Example
118    ///
119    /// ```rust
120    /// # use async_profiler_agent::metadata::AgentMetadata;
121    /// let metadata = AgentMetadata::ec2_agent_metadata(
122    ///     "123456789012".to_string(),
123    ///     "us-east-1".to_string(),
124    ///     "i-1234567890abcdef0".to_string(),
125    /// ).build();
126    /// ```
127    pub fn ec2_agent_metadata(
128        aws_account_id: String,
129        aws_region_id: String,
130        ec2_instance_id: String,
131    ) -> Ec2AgentMetadataBuilder {
132        Ec2AgentMetadataBuilder {
133            aws_account_id,
134            aws_region_id,
135            ec2_instance_id,
136            ec2_instance_type: None,
137        }
138    }
139
140    /// Create a builder for Fargate agent metadata
141    ///
142    /// Normally, for real-world use, you would get the metadata using
143    /// autodetection via
144    #[cfg_attr(
145        feature = "aws-metadata-no-defaults",
146        doc = "[aws::load_agent_metadata],"
147    )]
148    #[cfg_attr(
149        feature = "aws-metadata-no-defaults",
150        doc = "`aws::load_agent_metadata`,"
151    )]
152    /// this function is intended for use in tests.
153    ///
154    /// ## Example
155    ///
156    /// ```rust
157    /// # use async_profiler_agent::metadata::AgentMetadata;
158    /// let metadata = AgentMetadata::fargate_agent_metadata(
159    ///     "123456789012".to_string(),
160    ///     "us-east-1".to_string(),
161    ///     "arn:aws:ecs:us-east-1:123456789012:task/cluster/5261e761e0e2a3d92da3f02c8e5bab1f".to_string(),
162    ///     "arn:aws:ecs:us-east-1:123456789012:cluster/profiler-metadata-cluster".to_string(),
163    /// )
164    /// .with_cpu_limit(0.25)
165    /// .with_memory_limit(2048)
166    /// .build();
167    /// ```
168    pub fn fargate_agent_metadata(
169        aws_account_id: String,
170        aws_region_id: String,
171        ecs_task_arn: String,
172        ecs_cluster_arn: String,
173    ) -> FargateAgentMetadataBuilder {
174        FargateAgentMetadataBuilder {
175            aws_account_id,
176            aws_region_id,
177            ecs_task_arn,
178            ecs_cluster_arn,
179            cpu_limit: None,
180            memory_limit: None,
181        }
182    }
183}
184
185/// Builder for EC2 agent metadata
186#[derive(Debug, Clone)]
187pub struct Ec2AgentMetadataBuilder {
188    aws_account_id: String,
189    aws_region_id: String,
190    ec2_instance_id: String,
191    ec2_instance_type: Option<String>,
192}
193
194impl Ec2AgentMetadataBuilder {
195    /// Set the EC2 instance type
196    pub fn with_ec2_instance_type(mut self, ec2_instance_type: String) -> Self {
197        self.ec2_instance_type = Some(ec2_instance_type);
198        self
199    }
200
201    /// Build the AgentMetadata
202    pub fn build(self) -> AgentMetadata {
203        AgentMetadata::Ec2AgentMetadata {
204            aws_account_id: self.aws_account_id,
205            aws_region_id: self.aws_region_id,
206            ec2_instance_id: self.ec2_instance_id,
207            #[cfg(feature = "__unstable-fargate-cpu-count")]
208            ec2_instance_type: self.ec2_instance_type.unwrap_or_default(),
209        }
210    }
211}
212
213/// Builder for Fargate agent metadata
214#[derive(Debug, Clone)]
215pub struct FargateAgentMetadataBuilder {
216    aws_account_id: String,
217    aws_region_id: String,
218    ecs_task_arn: String,
219    ecs_cluster_arn: String,
220    cpu_limit: Option<f64>,
221    memory_limit: Option<u64>,
222}
223
224impl FargateAgentMetadataBuilder {
225    /// Set the CPU limit (in vCPUs)
226    pub fn with_cpu_limit(mut self, cpu_limit: f64) -> Self {
227        self.cpu_limit = Some(cpu_limit);
228        self
229    }
230
231    /// Set the memory limit (in megabytes)
232    pub fn with_memory_limit(mut self, memory_limit: u64) -> Self {
233        self.memory_limit = Some(memory_limit);
234        self
235    }
236
237    /// Build the AgentMetadata
238    pub fn build(self) -> AgentMetadata {
239        AgentMetadata::FargateAgentMetadata {
240            aws_account_id: self.aws_account_id,
241            aws_region_id: self.aws_region_id,
242            ecs_task_arn: self.ecs_task_arn,
243            ecs_cluster_arn: self.ecs_cluster_arn,
244            #[cfg(feature = "__unstable-fargate-cpu-count")]
245            cpu_limit: self.cpu_limit.map(Into::into),
246            #[cfg(feature = "__unstable-fargate-cpu-count")]
247            memory_limit: self.memory_limit,
248        }
249    }
250}
251
252/// Metadata associated with a specific individual profiling report
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub struct ReportMetadata<'a> {
255    /// The host running the agent
256    pub instance: &'a AgentMetadata,
257    /// The start time of the profiling report, as a duration from the process start
258    pub start: Duration,
259    /// The end time of the profiling report, as a duration from the process start
260    pub end: Duration,
261    /// The desired reporting interval (on average, this should be
262    /// approximately the same as `self.end - self.start`).
263    pub reporting_interval: Duration,
264}
265
266#[cfg(feature = "aws-metadata-no-defaults")]
267pub mod aws;
268
269/// [private] dummy metadata to make testing easier
270#[cfg(test)]
271pub(crate) const DUMMY_METADATA: ReportMetadata<'static> = ReportMetadata {
272    instance: &AgentMetadata::NoMetadata,
273    start: Duration::from_secs(1),
274    end: Duration::from_secs(2),
275    reporting_interval: Duration::from_secs(1),
276};