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