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