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};