From bb60826b69a93d0aca30903debbea9ee4ea49670 Mon Sep 17 00:00:00 2001 From: shreyamalpani Date: Wed, 13 Nov 2024 18:19:33 -0500 Subject: [PATCH 1/4] add fd and threads enhanced metrics --- bottlecap/src/lifecycle/invocation/context.rs | 2 + .../src/lifecycle/invocation/processor.rs | 8 + bottlecap/src/metrics/enhanced/constants.rs | 4 + bottlecap/src/metrics/enhanced/lambda.rs | 200 +++++++++++++ bottlecap/src/proc/constants.rs | 1 + bottlecap/src/proc/mod.rs | 265 +++++++++++++++++- bottlecap/tests/proc/13/.gitkeep | 0 bottlecap/tests/proc/142/.gitkeep | 0 .../proc/process/invalid_malformed/31/limits | 17 ++ .../proc/process/invalid_malformed/9/limits | 17 ++ .../proc/process/invalid_missing/31/limits | 15 + .../proc/process/invalid_missing/9/limits | 15 + bottlecap/tests/proc/process/valid/31/fd/1 | 0 bottlecap/tests/proc/process/valid/31/fd/2 | 0 bottlecap/tests/proc/process/valid/31/limits | 17 ++ .../proc/process/valid/31/task/1/.gitkeep | 0 .../proc/process/valid/31/task/2/.gitkeep | 0 bottlecap/tests/proc/process/valid/31/task/3 | 0 bottlecap/tests/proc/process/valid/9/fd/1 | 0 bottlecap/tests/proc/process/valid/9/fd/2 | 0 bottlecap/tests/proc/process/valid/9/fd/3 | 0 bottlecap/tests/proc/process/valid/9/limits | 17 ++ .../proc/process/valid/9/task/1/.gitkeep | 0 .../proc/process/valid/9/task/2/.gitkeep | 0 .../proc/process/valid/9/task/3/.gitkeep | 0 25 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 bottlecap/tests/proc/13/.gitkeep create mode 100644 bottlecap/tests/proc/142/.gitkeep create mode 100644 bottlecap/tests/proc/process/invalid_malformed/31/limits create mode 100644 bottlecap/tests/proc/process/invalid_malformed/9/limits create mode 100644 bottlecap/tests/proc/process/invalid_missing/31/limits create mode 100644 bottlecap/tests/proc/process/invalid_missing/9/limits create mode 100644 bottlecap/tests/proc/process/valid/31/fd/1 create mode 100644 bottlecap/tests/proc/process/valid/31/fd/2 create mode 100644 bottlecap/tests/proc/process/valid/31/limits create mode 100644 bottlecap/tests/proc/process/valid/31/task/1/.gitkeep create mode 100644 bottlecap/tests/proc/process/valid/31/task/2/.gitkeep create mode 100644 bottlecap/tests/proc/process/valid/31/task/3 create mode 100644 bottlecap/tests/proc/process/valid/9/fd/1 create mode 100644 bottlecap/tests/proc/process/valid/9/fd/2 create mode 100644 bottlecap/tests/proc/process/valid/9/fd/3 create mode 100644 bottlecap/tests/proc/process/valid/9/limits create mode 100644 bottlecap/tests/proc/process/valid/9/task/1/.gitkeep create mode 100644 bottlecap/tests/proc/process/valid/9/task/2/.gitkeep create mode 100644 bottlecap/tests/proc/process/valid/9/task/3/.gitkeep diff --git a/bottlecap/src/lifecycle/invocation/context.rs b/bottlecap/src/lifecycle/invocation/context.rs index 4cffb929c..7010e245a 100644 --- a/bottlecap/src/lifecycle/invocation/context.rs +++ b/bottlecap/src/lifecycle/invocation/context.rs @@ -320,12 +320,14 @@ mod tests { let uptime_offset = Some(50.0); let (tmp_chan_tx, _) = watch::channel(()); + let (process_chan_tx, _) = watch::channel(()); let enhanced_metric_data = Some(EnhancedMetricData { network_offset, cpu_offset, uptime_offset, tmp_chan_tx, + process_chan_tx, }); buffer.add_enhanced_metric_data(&request_id, enhanced_metric_data.clone()); diff --git a/bottlecap/src/lifecycle/invocation/processor.rs b/bottlecap/src/lifecycle/invocation/processor.rs index 64a8582b1..de97d8c7d 100644 --- a/bottlecap/src/lifecycle/invocation/processor.rs +++ b/bottlecap/src/lifecycle/invocation/processor.rs @@ -112,11 +112,17 @@ impl Processor { let (tmp_chan_tx, tmp_chan_rx) = watch::channel(()); self.enhanced_metrics.set_tmp_enhanced_metrics(tmp_chan_rx); + // Start a channel for monitoring file descriptor and thread count + let (process_chan_tx, process_chan_rx) = watch::channel(()); + self.enhanced_metrics + .set_process_enhanced_metrics(process_chan_rx); + let enhanced_metric_offsets = Some(EnhancedMetricData { network_offset, cpu_offset, uptime_offset, tmp_chan_tx, + process_chan_tx, }); self.context_buffer .add_enhanced_metric_data(&request_id, enhanced_metric_offsets); @@ -196,6 +202,8 @@ impl Processor { ); // Send the signal to stop monitoring tmp _ = offsets.tmp_chan_tx.send(()); + // Send the signal to stop monitoring file descriptors and threads + _ = offsets.process_chan_tx.send(()); } } diff --git a/bottlecap/src/metrics/enhanced/constants.rs b/bottlecap/src/metrics/enhanced/constants.rs index f62fd67ea..fcd833676 100644 --- a/bottlecap/src/metrics/enhanced/constants.rs +++ b/bottlecap/src/metrics/enhanced/constants.rs @@ -38,5 +38,9 @@ pub const CPU_MIN_UTILIZATION_METRIC: &str = "aws.lambda.enhanced.cpu_min_utiliz pub const TMP_MAX_METRIC: &str = "aws.lambda.enhanced.tmp_max"; pub const TMP_USED_METRIC: &str = "aws.lambda.enhanced.tmp_used"; pub const TMP_FREE_METRIC: &str = "aws.lambda.enhanced.tmp_free"; +pub const FD_MAX_METRIC: &str = "aws.lambda.enhanced.fd_max"; +pub const FD_USE_METRIC: &str = "aws.lambda.enhanced.fd_use"; +pub const THREADS_MAX_METRIC: &str = "aws.lambda.enhanced.threads_max"; +pub const THREADS_USE_METRIC: &str = "aws.lambda.enhanced.threads_use"; //pub const ASM_INVOCATIONS_METRIC: &str = "aws.lambda.enhanced.asm.invocations"; pub const ENHANCED_METRICS_ENV_VAR: &str = "DD_ENHANCED_METRICS"; diff --git a/bottlecap/src/metrics/enhanced/lambda.rs b/bottlecap/src/metrics/enhanced/lambda.rs index 6e4f53c72..11de05f9a 100644 --- a/bottlecap/src/metrics/enhanced/lambda.rs +++ b/bottlecap/src/metrics/enhanced/lambda.rs @@ -429,6 +429,113 @@ impl Lambda { }); } + pub fn generate_fd_enhanced_metrics( + fd_max: f64, + fd_use: f64, + aggr: &mut std::sync::MutexGuard, + ) { + let metric = Metric::new( + constants::FD_MAX_METRIC.into(), + MetricValue::distribution(fd_max), + None, + ); + if let Err(e) = aggr.insert(metric) { + error!("Failed to insert fd_max metric: {}", e); + } + + if fd_use != -1.0 { + let metric = Metric::new( + constants::FD_USE_METRIC.into(), + MetricValue::distribution(fd_use), + None, + ); + if let Err(e) = aggr.insert(metric) { + error!("Failed to insert fd_use metric: {}", e); + } + } + } + + pub fn generate_threads_enhanced_metrics( + threads_max: f64, + threads_use: f64, + aggr: &mut std::sync::MutexGuard, + ) { + let metric = Metric::new( + constants::THREADS_MAX_METRIC.into(), + MetricValue::distribution(threads_max), + None, + ); + if let Err(e) = aggr.insert(metric) { + error!("Failed to insert threads_max metric: {}", e); + } + + if threads_use != -1.0 { + let metric = Metric::new( + constants::THREADS_USE_METRIC.into(), + MetricValue::distribution(threads_use), + None, + ); + if let Err(e) = aggr.insert(metric) { + error!("Failed to insert threads_use metric: {}", e); + } + } + } + + pub fn set_process_enhanced_metrics(&self, mut send_metrics: Receiver<()>) { + if !self.config.enhanced_metrics { + return; + } + + let aggr = Arc::clone(&self.aggregator); + + tokio::spawn(async move { + // get list of all process ids + let pids = proc::get_pid_list(); + + // Set fd_max and initial value for fd_use to -1 + let fd_max = proc::get_fd_max_data(&pids); + let mut fd_use = -1 as f64; + + // Set threads_max and initial value for threads_use to -1 + let threads_max = proc::get_threads_max_data(&pids); + let mut threads_use = -1 as f64; + + let mut interval = interval(Duration::from_millis(2)); + loop { + tokio::select! { + biased; + // When the stop signal is received, generate final metrics + _ = send_metrics.changed() => { + let mut aggr: std::sync::MutexGuard = + aggr.lock().expect("lock poisoned"); + Self::generate_fd_enhanced_metrics(fd_max, fd_use, &mut aggr); + Self::generate_threads_enhanced_metrics(threads_max, threads_use, &mut aggr); + return; + } + // Otherwise keep monitoring file descriptor and thread usage periodically + _ = interval.tick() => { + match proc::get_fd_use_data(&pids) { + Ok(fd_use_curr) => { + fd_use = fd_use.max(fd_use_curr); + }, + Err(_) => { + debug!("Could not update file descriptor use enhanced metric."); + } + }; + match proc::get_threads_use_data(&pids) { + Ok(threads_use_curr) => { + threads_use = threads_use.max(threads_use_curr); + }, + Err(_) => { + debug!("Could not update threads use enhanced metric."); + } + }; + } + } + } + }); + } + fn calculate_estimated_cost_usd(billed_duration_ms: u64, memory_size_mb: u64) -> f64 { let gb_seconds = (billed_duration_ms as f64 * constants::MS_TO_SEC) * (memory_size_mb as f64 / constants::MB_TO_GB); @@ -503,6 +610,7 @@ pub struct EnhancedMetricData { pub cpu_offset: Option, pub uptime_offset: Option, pub tmp_chan_tx: Sender<()>, + pub process_chan_tx: Sender<()>, } impl PartialEq for EnhancedMetricData { @@ -669,6 +777,18 @@ mod tests { assert!(aggr .get_entry_by_id(constants::TMP_FREE_METRIC.into(), &None) .is_none()); + assert!(aggr + .get_entry_by_id(constants::FD_MAX_METRIC.into(), &None) + .is_none()); + assert!(aggr + .get_entry_by_id(constants::FD_USE_METRIC.into(), &None) + .is_none()); + assert!(aggr + .get_entry_by_id(constants::THREADS_MAX_METRIC.into(), &None) + .is_none()); + assert!(aggr + .get_entry_by_id(constants::THREADS_USE_METRIC.into(), &None) + .is_none()); } #[test] @@ -818,4 +938,84 @@ mod tests { assert_sketch(&metrics_aggr, constants::TMP_USED_METRIC, 12165120.0); assert_sketch(&metrics_aggr, constants::TMP_FREE_METRIC, 538296320.0); } + + #[test] + fn test_set_fd_enhanced_metrics_valid_fd_use() { + let (metrics_aggr, my_config) = setup(); + let lambda = Lambda::new(metrics_aggr.clone(), my_config); + + let fd_max = 1024.0; + let fd_use = 175.0; + + Lambda::generate_fd_enhanced_metrics( + fd_max, + fd_use, + &mut lambda.aggregator.lock().expect("lock poisoned"), + ); + + assert_sketch(&metrics_aggr, constants::FD_MAX_METRIC, 1024.0); + assert_sketch(&metrics_aggr, constants::FD_USE_METRIC, 175.0); + } + + #[test] + fn test_set_fd_enhanced_metrics_invalid_fd_use() { + let (metrics_aggr, my_config) = setup(); + let lambda = Lambda::new(metrics_aggr.clone(), my_config); + + let fd_max = 1024.0; + let fd_use = -1.0; + + Lambda::generate_fd_enhanced_metrics( + fd_max, + fd_use, + &mut lambda.aggregator.lock().expect("lock poisoned"), + ); + + assert_sketch(&metrics_aggr, constants::FD_MAX_METRIC, 1024.0); + + let aggr = lambda.aggregator.lock().expect("lock poisoned"); + assert!(aggr + .get_entry_by_id(constants::FD_USE_METRIC.into(), &None) + .is_none()); + } + + #[test] + fn test_set_threads_enhanced_metrics_valid_threads_use() { + let (metrics_aggr, my_config) = setup(); + let lambda = Lambda::new(metrics_aggr.clone(), my_config); + + let threads_max = 1024.0; + let threads_use = 40.0; + + Lambda::generate_threads_enhanced_metrics( + threads_max, + threads_use, + &mut lambda.aggregator.lock().expect("lock poisoned"), + ); + + assert_sketch(&metrics_aggr, constants::THREADS_MAX_METRIC, 1024.0); + assert_sketch(&metrics_aggr, constants::THREADS_USE_METRIC, 40.0); + } + + #[test] + fn test_set_threads_enhanced_metrics_invalid_threads_use() { + let (metrics_aggr, my_config) = setup(); + let lambda = Lambda::new(metrics_aggr.clone(), my_config); + + let threads_max = 1024.0; + let threads_use = -1.0; + + Lambda::generate_threads_enhanced_metrics( + threads_max, + threads_use, + &mut lambda.aggregator.lock().expect("lock poisoned"), + ); + + assert_sketch(&metrics_aggr, constants::THREADS_MAX_METRIC, 1024.0); + + let aggr = lambda.aggregator.lock().expect("lock poisoned"); + assert!(aggr + .get_entry_by_id(constants::THREADS_USE_METRIC.into(), &None) + .is_none()); + } } diff --git a/bottlecap/src/proc/constants.rs b/bottlecap/src/proc/constants.rs index fe06b908d..b24b497f8 100644 --- a/bottlecap/src/proc/constants.rs +++ b/bottlecap/src/proc/constants.rs @@ -1,5 +1,6 @@ pub const PROC_NET_DEV_PATH: &str = "/proc/net/dev"; pub const PROC_STAT_PATH: &str = "/proc/stat"; pub const PROC_UPTIME_PATH: &str = "/proc/uptime"; +pub const PROC_PATH: &str = "/proc"; pub const LAMDBA_NETWORK_INTERFACE: &str = "vinternal_1"; diff --git a/bottlecap/src/proc/mod.rs b/bottlecap/src/proc/mod.rs index 3dfa1a67f..94b5dc31d 100644 --- a/bottlecap/src/proc/mod.rs +++ b/bottlecap/src/proc/mod.rs @@ -3,11 +3,44 @@ pub mod constants; use std::{ collections::HashMap, - fs::File, + fs::{self, File}, io::{self, BufRead}, }; -use constants::{LAMDBA_NETWORK_INTERFACE, PROC_NET_DEV_PATH, PROC_STAT_PATH, PROC_UPTIME_PATH}; +use constants::{ + LAMDBA_NETWORK_INTERFACE, PROC_NET_DEV_PATH, PROC_PATH, PROC_STAT_PATH, PROC_UPTIME_PATH, +}; +use tracing::debug; + +pub fn get_pid_list() -> Vec { + get_pid_list_from_path(PROC_PATH) +} + +pub fn get_pid_list_from_path(path: &str) -> Vec { + let mut pids = Vec::::new(); + + let entries = match fs::read_dir(path) { + Ok(entries) => entries, + Err(_) => { + debug!("Could not list /proc files"); + return pids; + } + }; + + pids.extend(entries.filter_map(|entry| { + entry.ok().and_then(|dir_entry| { + // Check if the entry is a directory + if dir_entry.file_type().ok()?.is_dir() { + // If the directory name can be parsed as an integer, it will be added to the list + dir_entry.file_name().to_str()?.parse::().ok() + } else { + None + } + }) + })); + + pids +} #[derive(Copy, Clone, Debug, PartialEq)] pub struct NetworkData { @@ -180,6 +213,155 @@ fn get_uptime_from_path(path: &str) -> Result { )) } +pub fn get_fd_max_data(pids: &[i32]) -> f64 { + get_fd_max_data_from_path(PROC_PATH, pids) +} + +fn get_fd_max_data_from_path(path: &str, pids: &[i32]) -> f64 { + let mut fd_max: f64 = 1024.0; // Default to hard limit set by AWS Lambda + + for &pid in pids { + let limits_path = format!("{}/{}/limits", path, pid); + + let file = match File::open(&limits_path) { + Ok(file) => file, + Err(_) => continue, + }; + + let reader = io::BufReader::new(file); + for line in reader.lines() { + if let Some(line) = line.ok() { + if line.starts_with("Max open files") { + let values: Vec<&str> = line.split_whitespace().collect(); + + // Line should have 6 items: Max open files [soft limit] [hard limit] [units] + if values.len() < 6 { + debug!("File descriptor max data not found in file {}", limits_path); + break; + } + + // Skip past the first three items ("Max open files") to get the soft limit + let fd_max_pid_str = values[3]; + let fd_max_pid: f64 = match fd_max_pid_str.parse() { + Ok(val) => val, + Err(_) => { + debug!("File descriptor max data not found in file {}", limits_path); + break; + } + }; + fd_max = fd_max.min(fd_max_pid); + break; + } + } + } + } + + fd_max +} + +pub fn get_fd_use_data(pids: &[i32]) -> Result { + get_fd_use_data_from_path(PROC_PATH, pids) +} + +fn get_fd_use_data_from_path(path: &str, pids: &[i32]) -> Result { + let mut fd_use = 0; + + for pid in pids { + let fd_path = format!("{}/{}/fd", path, pid); + let files = match fs::read_dir(fd_path) { + Ok(files) => files, + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "File descriptor use data not found", + )); + } + }; + fd_use += files.count(); + } + + Ok(fd_use as f64) +} + +pub fn get_threads_max_data(pids: &[i32]) -> f64 { + get_threads_max_data_from_path(PROC_PATH, pids) +} + +fn get_threads_max_data_from_path(path: &str, pids: &[i32]) -> f64 { + let mut threads_max: f64 = 1024.0; // Default to hard limit set by AWS Lambda + + for &pid in pids { + let limits_path = format!("{}/{}/limits", path, pid); + + let file = match File::open(&limits_path) { + Ok(file) => file, + Err(_) => continue, + }; + + let reader = io::BufReader::new(file); + for line in reader.lines() { + if let Some(line) = line.ok() { + if line.starts_with("Max processes") { + let values: Vec<&str> = line.split_whitespace().collect(); + + // Line should have 5 items: Max processes [soft limit] [hard limit] [units] + if values.len() < 5 { + debug!("Threads max data not found in file {}", limits_path); + break; + } + + // Skip past the first three items ("Max open files") to get the soft limit + let fd_max_pid_str = values[2]; + let fd_max_pid: f64 = match fd_max_pid_str.parse() { + Ok(val) => val, + Err(_) => { + debug!("Threads max data not found in file {}", limits_path); + break; + } + }; + threads_max = threads_max.min(fd_max_pid); + break; + } + } + } + } + + threads_max +} + +pub fn get_threads_use_data(pids: &[i32]) -> Result { + get_threads_use_data_from_path(PROC_PATH, pids) +} + +fn get_threads_use_data_from_path(path: &str, pids: &[i32]) -> Result { + let mut threads_use = 0; + + for pid in pids { + let task_path = format!("{}/{}/task", path, pid); + let files = match fs::read_dir(task_path) { + Ok(files) => files, + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Threads use data not found", + )); + } + }; + + for entry in files { + if let Ok(dir_entry) = entry { + if let Some(file_type) = dir_entry.file_type().ok() { + if file_type.is_dir() { + threads_use += 1; + } + } + } + } + } + + Ok(threads_use as f64) +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { @@ -192,6 +374,21 @@ mod tests { safe_path.to_str().unwrap().to_string() } + #[test] + #[allow(clippy::float_cmp)] + fn test_get_pid_list() { + let path = "./tests/proc"; + let mut pids = get_pid_list_from_path(path); + pids.sort(); + assert_eq!(pids.len(), 2); + assert_eq!(pids[0], 13); + assert_eq!(pids[1], 142); + + let path = "./tests/incorrect_folder"; + let pids = get_pid_list_from_path(path); + assert_eq!(pids.len(), 0); + } + #[test] #[allow(clippy::float_cmp)] fn test_get_network_data() { @@ -291,4 +488,68 @@ mod tests { let uptime_data_result = get_uptime_from_path(path); assert!(uptime_data_result.is_err()); } + + #[test] + #[allow(clippy::float_cmp)] + fn test_get_fd_max_data() { + let path = "./tests/proc/process/valid"; + let pids = get_pid_list_from_path(path); + let fd_max = get_fd_max_data_from_path(path, &pids); + assert_eq!(fd_max, 900.0); + + let path = "./tests/proc/process/invalid_malformed"; + let fd_max = get_fd_max_data_from_path(path, &pids); + assert_eq!(fd_max, 1024.0); // assert that fd_max is equal to AWS Lambda limit + + let path = "./tests/proc/process/invalid_missing"; + let fd_max = get_fd_max_data_from_path(path, &pids); + assert_eq!(fd_max, 1024.0); // assert that fd_max is equal to AWS Lambda limit + } + + #[test] + #[allow(clippy::float_cmp)] + fn test_get_fd_use_data() { + let path = "./tests/proc/process/valid"; + let pids = get_pid_list_from_path(path); + let fd_use_result = get_fd_use_data_from_path(path, &pids); + assert!(fd_use_result.is_ok()); + let fd_use = fd_use_result.unwrap(); + assert_eq!(fd_use, 5.0); + + let path = "./tests/proc/process/invalid_missing"; + let fd_use_result = get_fd_use_data_from_path(path, &pids); + assert!(fd_use_result.is_err()); + } + + #[test] + #[allow(clippy::float_cmp)] + fn test_get_threads_max_data() { + let path = "./tests/proc/process/valid"; + let pids = get_pid_list_from_path(path); + let fd_max = get_threads_max_data_from_path(path, &pids); + assert_eq!(fd_max, 1024.0); + + let path = "./tests/proc/process/invalid_malformed"; + let fd_max = get_threads_max_data_from_path(path, &pids); + assert_eq!(fd_max, 1024.0); // assert that threads_max is equal to AWS Lambda limit + + let path = "./tests/proc/process/invalid_missing"; + let fd_max = get_threads_max_data_from_path(path, &pids); + assert_eq!(fd_max, 1024.0); // assert that threads_max is equal to AWS Lambda limit + } + + #[test] + #[allow(clippy::float_cmp)] + fn test_get_threads_use_data() { + let path = "./tests/proc/process/valid"; + let pids = get_pid_list_from_path(path); + let threads_use_result = get_threads_use_data_from_path(path, &pids); + assert!(threads_use_result.is_ok()); + let threads_use = threads_use_result.unwrap(); + assert_eq!(threads_use, 5.0); + + let path = "./tests/proc/process/invalid_missing"; + let threads_use_result = get_threads_use_data_from_path(path, &pids); + assert!(threads_use_result.is_err()); + } } diff --git a/bottlecap/tests/proc/13/.gitkeep b/bottlecap/tests/proc/13/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/142/.gitkeep b/bottlecap/tests/proc/142/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/invalid_malformed/31/limits b/bottlecap/tests/proc/process/invalid_malformed/31/limits new file mode 100644 index 000000000..2d25ac301 --- /dev/null +++ b/bottlecap/tests/proc/process/invalid_malformed/31/limits @@ -0,0 +1,17 @@ +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size unlimited unlimited bytes +Max resident set unlimited unlimited bytes +Max processes 1024 1024 +Max open files 1024 +Max locked memory 65536 65536 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 4622 4622 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us diff --git a/bottlecap/tests/proc/process/invalid_malformed/9/limits b/bottlecap/tests/proc/process/invalid_malformed/9/limits new file mode 100644 index 000000000..2436ec085 --- /dev/null +++ b/bottlecap/tests/proc/process/invalid_malformed/9/limits @@ -0,0 +1,17 @@ +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size unlimited unlimited bytes +Max resident set unlimited unlimited bytes +Max processes 1024 +Max open files 1024 1024 +Max locked memory 65536 65536 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 4622 4622 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us diff --git a/bottlecap/tests/proc/process/invalid_missing/31/limits b/bottlecap/tests/proc/process/invalid_missing/31/limits new file mode 100644 index 000000000..c7dc2c55d --- /dev/null +++ b/bottlecap/tests/proc/process/invalid_missing/31/limits @@ -0,0 +1,15 @@ +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size unlimited unlimited bytes +Max resident set unlimited unlimited bytes +Max locked memory 65536 65536 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 4622 4622 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us diff --git a/bottlecap/tests/proc/process/invalid_missing/9/limits b/bottlecap/tests/proc/process/invalid_missing/9/limits new file mode 100644 index 000000000..07de49ec4 --- /dev/null +++ b/bottlecap/tests/proc/process/invalid_missing/9/limits @@ -0,0 +1,15 @@ +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size unlimited unlimited bytes +Max resident set unlimited unlimited bytes +Max locked memory 65536 65536 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 4622 4622 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us diff --git a/bottlecap/tests/proc/process/valid/31/fd/1 b/bottlecap/tests/proc/process/valid/31/fd/1 new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/31/fd/2 b/bottlecap/tests/proc/process/valid/31/fd/2 new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/31/limits b/bottlecap/tests/proc/process/valid/31/limits new file mode 100644 index 000000000..75d41eee4 --- /dev/null +++ b/bottlecap/tests/proc/process/valid/31/limits @@ -0,0 +1,17 @@ +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size unlimited unlimited bytes +Max resident set unlimited unlimited bytes +Max processes 1024 1024 processes +Max open files 900 1024 files +Max locked memory 65536 65536 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 4622 4622 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us diff --git a/bottlecap/tests/proc/process/valid/31/task/1/.gitkeep b/bottlecap/tests/proc/process/valid/31/task/1/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/31/task/2/.gitkeep b/bottlecap/tests/proc/process/valid/31/task/2/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/31/task/3 b/bottlecap/tests/proc/process/valid/31/task/3 new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/9/fd/1 b/bottlecap/tests/proc/process/valid/9/fd/1 new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/9/fd/2 b/bottlecap/tests/proc/process/valid/9/fd/2 new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/9/fd/3 b/bottlecap/tests/proc/process/valid/9/fd/3 new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/9/limits b/bottlecap/tests/proc/process/valid/9/limits new file mode 100644 index 000000000..664f04c88 --- /dev/null +++ b/bottlecap/tests/proc/process/valid/9/limits @@ -0,0 +1,17 @@ +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size unlimited unlimited bytes +Max resident set unlimited unlimited bytes +Max processes 1024 1024 processes +Max open files 1024 1024 files +Max locked memory 65536 65536 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 4622 4622 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us diff --git a/bottlecap/tests/proc/process/valid/9/task/1/.gitkeep b/bottlecap/tests/proc/process/valid/9/task/1/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/9/task/2/.gitkeep b/bottlecap/tests/proc/process/valid/9/task/2/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/bottlecap/tests/proc/process/valid/9/task/3/.gitkeep b/bottlecap/tests/proc/process/valid/9/task/3/.gitkeep new file mode 100644 index 000000000..e69de29bb From cbf4c08e7bc64d59e65d2594a25250f68ddc6bc4 Mon Sep 17 00:00:00 2001 From: shreyamalpani Date: Wed, 13 Nov 2024 19:18:09 -0500 Subject: [PATCH 2/4] clippy fixes --- bottlecap/src/metrics/enhanced/lambda.rs | 10 +- bottlecap/src/proc/mod.rs | 140 ++++++++++------------- 2 files changed, 65 insertions(+), 85 deletions(-) diff --git a/bottlecap/src/metrics/enhanced/lambda.rs b/bottlecap/src/metrics/enhanced/lambda.rs index 11de05f9a..d2a1e52a6 100644 --- a/bottlecap/src/metrics/enhanced/lambda.rs +++ b/bottlecap/src/metrics/enhanced/lambda.rs @@ -443,7 +443,8 @@ impl Lambda { error!("Failed to insert fd_max metric: {}", e); } - if fd_use != -1.0 { + // Check if fd_use value is valid before inserting metric + if fd_use > 0.0 { let metric = Metric::new( constants::FD_USE_METRIC.into(), MetricValue::distribution(fd_use), @@ -469,7 +470,8 @@ impl Lambda { error!("Failed to insert threads_max metric: {}", e); } - if threads_use != -1.0 { + // Check if threads_use value is valid before inserting metric + if threads_use > 0.0 { let metric = Metric::new( constants::THREADS_USE_METRIC.into(), MetricValue::distribution(threads_use), @@ -494,11 +496,11 @@ impl Lambda { // Set fd_max and initial value for fd_use to -1 let fd_max = proc::get_fd_max_data(&pids); - let mut fd_use = -1 as f64; + let mut fd_use = -1_f64; // Set threads_max and initial value for threads_use to -1 let threads_max = proc::get_threads_max_data(&pids); - let mut threads_use = -1 as f64; + let mut threads_use = -1_f64; let mut interval = interval(Duration::from_millis(2)); loop { diff --git a/bottlecap/src/proc/mod.rs b/bottlecap/src/proc/mod.rs index 94b5dc31d..842fbb94a 100644 --- a/bottlecap/src/proc/mod.rs +++ b/bottlecap/src/proc/mod.rs @@ -12,6 +12,7 @@ use constants::{ }; use tracing::debug; +#[must_use] pub fn get_pid_list() -> Vec { get_pid_list_from_path(PROC_PATH) } @@ -19,12 +20,9 @@ pub fn get_pid_list() -> Vec { pub fn get_pid_list_from_path(path: &str) -> Vec { let mut pids = Vec::::new(); - let entries = match fs::read_dir(path) { - Ok(entries) => entries, - Err(_) => { - debug!("Could not list /proc files"); - return pids; - } + let Ok(entries) = fs::read_dir(path) else { + debug!("Could not list /proc files"); + return pids; }; pids.extend(entries.filter_map(|entry| { @@ -213,6 +211,7 @@ fn get_uptime_from_path(path: &str) -> Result { )) } +#[must_use] pub fn get_fd_max_data(pids: &[i32]) -> f64 { get_fd_max_data_from_path(PROC_PATH, pids) } @@ -221,37 +220,30 @@ fn get_fd_max_data_from_path(path: &str, pids: &[i32]) -> f64 { let mut fd_max: f64 = 1024.0; // Default to hard limit set by AWS Lambda for &pid in pids { - let limits_path = format!("{}/{}/limits", path, pid); - - let file = match File::open(&limits_path) { - Ok(file) => file, - Err(_) => continue, + let limits_path = format!("{path}/{pid}/limits"); + let Ok(file) = File::open(&limits_path) else { + continue; }; let reader = io::BufReader::new(file); - for line in reader.lines() { - if let Some(line) = line.ok() { - if line.starts_with("Max open files") { - let values: Vec<&str> = line.split_whitespace().collect(); - - // Line should have 6 items: Max open files [soft limit] [hard limit] [units] - if values.len() < 6 { - debug!("File descriptor max data not found in file {}", limits_path); - break; - } + for line in reader.lines().map_while(Result::ok) { + if line.starts_with("Max open files") { + let values: Vec<&str> = line.split_whitespace().collect(); - // Skip past the first three items ("Max open files") to get the soft limit - let fd_max_pid_str = values[3]; - let fd_max_pid: f64 = match fd_max_pid_str.parse() { - Ok(val) => val, - Err(_) => { - debug!("File descriptor max data not found in file {}", limits_path); - break; - } - }; - fd_max = fd_max.min(fd_max_pid); + // Line should have 6 items: Max open files [soft limit] [hard limit] [units] + if values.len() < 6 { + debug!("File descriptor max data not found in file {}", limits_path); break; } + + // Skip past the first three items ("Max open files") to get the soft limit + let fd_max_pid_str = values[3]; + let Ok(fd_max_pid) = fd_max_pid_str.parse() else { + debug!("File descriptor max data not found in file {}", limits_path); + break; + }; + fd_max = fd_max.min(fd_max_pid); + break; } } } @@ -267,15 +259,12 @@ fn get_fd_use_data_from_path(path: &str, pids: &[i32]) -> Result let mut fd_use = 0; for pid in pids { - let fd_path = format!("{}/{}/fd", path, pid); - let files = match fs::read_dir(fd_path) { - Ok(files) => files, - Err(_) => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "File descriptor use data not found", - )); - } + let fd_path = format!("{path}/{pid}/fd"); + let Ok(files) = fs::read_dir(fd_path) else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "File descriptor use data not found", + )); }; fd_use += files.count(); } @@ -283,6 +272,7 @@ fn get_fd_use_data_from_path(path: &str, pids: &[i32]) -> Result Ok(fd_use as f64) } +#[must_use] pub fn get_threads_max_data(pids: &[i32]) -> f64 { get_threads_max_data_from_path(PROC_PATH, pids) } @@ -291,37 +281,30 @@ fn get_threads_max_data_from_path(path: &str, pids: &[i32]) -> f64 { let mut threads_max: f64 = 1024.0; // Default to hard limit set by AWS Lambda for &pid in pids { - let limits_path = format!("{}/{}/limits", path, pid); - - let file = match File::open(&limits_path) { - Ok(file) => file, - Err(_) => continue, + let limits_path = format!("{path}/{pid}/limits"); + let Ok(file) = File::open(&limits_path) else { + continue; }; let reader = io::BufReader::new(file); - for line in reader.lines() { - if let Some(line) = line.ok() { - if line.starts_with("Max processes") { - let values: Vec<&str> = line.split_whitespace().collect(); - - // Line should have 5 items: Max processes [soft limit] [hard limit] [units] - if values.len() < 5 { - debug!("Threads max data not found in file {}", limits_path); - break; - } + for line in reader.lines().map_while(Result::ok) { + if line.starts_with("Max processes") { + let values: Vec<&str> = line.split_whitespace().collect(); - // Skip past the first three items ("Max open files") to get the soft limit - let fd_max_pid_str = values[2]; - let fd_max_pid: f64 = match fd_max_pid_str.parse() { - Ok(val) => val, - Err(_) => { - debug!("Threads max data not found in file {}", limits_path); - break; - } - }; - threads_max = threads_max.min(fd_max_pid); + // Line should have 5 items: Max processes [soft limit] [hard limit] [units] + if values.len() < 5 { + debug!("Threads max data not found in file {}", limits_path); break; } + + // Skip past the first three items ("Max open files") to get the soft limit + let threads_max_pid_str = values[2]; + let Ok(threads_max_pid) = threads_max_pid_str.parse() else { + debug!("Threads max data not found in file {}", limits_path); + break; + }; + threads_max = threads_max.min(threads_max_pid); + break; } } } @@ -337,29 +320,24 @@ fn get_threads_use_data_from_path(path: &str, pids: &[i32]) -> Result files, - Err(_) => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "Threads use data not found", - )); - } + let task_path = format!("{path}/{pid}/task"); + let Ok(files) = fs::read_dir(task_path) else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Threads use data not found", + )); }; - for entry in files { - if let Ok(dir_entry) = entry { - if let Some(file_type) = dir_entry.file_type().ok() { - if file_type.is_dir() { - threads_use += 1; - } + for dir_entry in files.flatten() { + if let Ok(file_type) = dir_entry.file_type() { + if file_type.is_dir() { + threads_use += 1; } } } } - Ok(threads_use as f64) + Ok(f64::from(threads_use)) } #[cfg(test)] From 98ea23e84213b85a4f15b75a11ad8c21fd7f15ef Mon Sep 17 00:00:00 2001 From: shreyamalpani Date: Thu, 14 Nov 2024 16:28:05 -0500 Subject: [PATCH 3/4] fixes --- bottlecap/src/metrics/enhanced/lambda.rs | 2 +- bottlecap/src/proc/constants.rs | 2 + bottlecap/src/proc/mod.rs | 160 +++++++++++------------ 3 files changed, 76 insertions(+), 88 deletions(-) diff --git a/bottlecap/src/metrics/enhanced/lambda.rs b/bottlecap/src/metrics/enhanced/lambda.rs index d2a1e52a6..3d999062f 100644 --- a/bottlecap/src/metrics/enhanced/lambda.rs +++ b/bottlecap/src/metrics/enhanced/lambda.rs @@ -502,7 +502,7 @@ impl Lambda { let threads_max = proc::get_threads_max_data(&pids); let mut threads_use = -1_f64; - let mut interval = interval(Duration::from_millis(2)); + let mut interval = interval(Duration::from_millis(1)); loop { tokio::select! { biased; diff --git a/bottlecap/src/proc/constants.rs b/bottlecap/src/proc/constants.rs index b24b497f8..452cdf4fb 100644 --- a/bottlecap/src/proc/constants.rs +++ b/bottlecap/src/proc/constants.rs @@ -4,3 +4,5 @@ pub const PROC_UPTIME_PATH: &str = "/proc/uptime"; pub const PROC_PATH: &str = "/proc"; pub const LAMDBA_NETWORK_INTERFACE: &str = "vinternal_1"; +pub const LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT: f64 = 1024.0; +pub const LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT: f64 = 1024.0; diff --git a/bottlecap/src/proc/mod.rs b/bottlecap/src/proc/mod.rs index 842fbb94a..5be7262bf 100644 --- a/bottlecap/src/proc/mod.rs +++ b/bottlecap/src/proc/mod.rs @@ -10,15 +10,16 @@ use std::{ use constants::{ LAMDBA_NETWORK_INTERFACE, PROC_NET_DEV_PATH, PROC_PATH, PROC_STAT_PATH, PROC_UPTIME_PATH, }; +use regex::Regex; use tracing::debug; #[must_use] -pub fn get_pid_list() -> Vec { +pub fn get_pid_list() -> Vec { get_pid_list_from_path(PROC_PATH) } -pub fn get_pid_list_from_path(path: &str) -> Vec { - let mut pids = Vec::::new(); +pub fn get_pid_list_from_path(path: &str) -> Vec { + let mut pids = Vec::::new(); let Ok(entries) = fs::read_dir(path) else { debug!("Could not list /proc files"); @@ -30,7 +31,7 @@ pub fn get_pid_list_from_path(path: &str) -> Vec { // Check if the entry is a directory if dir_entry.file_type().ok()?.is_dir() { // If the directory name can be parsed as an integer, it will be added to the list - dir_entry.file_name().to_str()?.parse::().ok() + dir_entry.file_name().to_str()?.parse::().ok() } else { None } @@ -212,12 +213,14 @@ fn get_uptime_from_path(path: &str) -> Result { } #[must_use] -pub fn get_fd_max_data(pids: &[i32]) -> f64 { +pub fn get_fd_max_data(pids: &[i64]) -> f64 { get_fd_max_data_from_path(PROC_PATH, pids) } -fn get_fd_max_data_from_path(path: &str, pids: &[i32]) -> f64 { - let mut fd_max: f64 = 1024.0; // Default to hard limit set by AWS Lambda +fn get_fd_max_data_from_path(path: &str, pids: &[i64]) -> f64 { + let mut fd_max = constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT; + // regex to capture the soft limit value (first numeric value after the title) + let re = Regex::new(r"^Max open files\s+(\d+)").expect("Failed to create regex"); for &pid in pids { let limits_path = format!("{path}/{pid}/limits"); @@ -227,22 +230,12 @@ fn get_fd_max_data_from_path(path: &str, pids: &[i32]) -> f64 { let reader = io::BufReader::new(file); for line in reader.lines().map_while(Result::ok) { - if line.starts_with("Max open files") { - let values: Vec<&str> = line.split_whitespace().collect(); - - // Line should have 6 items: Max open files [soft limit] [hard limit] [units] - if values.len() < 6 { + if let Some(line_items) = re.captures(&line) { + if let Ok(fd_max_pid) = line_items[1].parse() { + fd_max = fd_max.min(fd_max_pid); + } else { debug!("File descriptor max data not found in file {}", limits_path); - break; } - - // Skip past the first three items ("Max open files") to get the soft limit - let fd_max_pid_str = values[3]; - let Ok(fd_max_pid) = fd_max_pid_str.parse() else { - debug!("File descriptor max data not found in file {}", limits_path); - break; - }; - fd_max = fd_max.min(fd_max_pid); break; } } @@ -251,14 +244,14 @@ fn get_fd_max_data_from_path(path: &str, pids: &[i32]) -> f64 { fd_max } -pub fn get_fd_use_data(pids: &[i32]) -> Result { +pub fn get_fd_use_data(pids: &[i64]) -> Result { get_fd_use_data_from_path(PROC_PATH, pids) } -fn get_fd_use_data_from_path(path: &str, pids: &[i32]) -> Result { +fn get_fd_use_data_from_path(path: &str, pids: &[i64]) -> Result { let mut fd_use = 0; - for pid in pids { + for &pid in pids { let fd_path = format!("{path}/{pid}/fd"); let Ok(files) = fs::read_dir(fd_path) else { return Err(io::Error::new( @@ -266,19 +259,22 @@ fn get_fd_use_data_from_path(path: &str, pids: &[i32]) -> Result "File descriptor use data not found", )); }; - fd_use += files.count(); + let count = files.count(); + fd_use += count; } Ok(fd_use as f64) } #[must_use] -pub fn get_threads_max_data(pids: &[i32]) -> f64 { +pub fn get_threads_max_data(pids: &[i64]) -> f64 { get_threads_max_data_from_path(PROC_PATH, pids) } -fn get_threads_max_data_from_path(path: &str, pids: &[i32]) -> f64 { - let mut threads_max: f64 = 1024.0; // Default to hard limit set by AWS Lambda +fn get_threads_max_data_from_path(path: &str, pids: &[i64]) -> f64 { + let mut threads_max = constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT; + // regex to capture the soft limit value (first numeric value after the title) + let re = Regex::new(r"^Max processes\s+(\d+)").expect("Failed to create regex"); for &pid in pids { let limits_path = format!("{path}/{pid}/limits"); @@ -288,22 +284,12 @@ fn get_threads_max_data_from_path(path: &str, pids: &[i32]) -> f64 { let reader = io::BufReader::new(file); for line in reader.lines().map_while(Result::ok) { - if line.starts_with("Max processes") { - let values: Vec<&str> = line.split_whitespace().collect(); - - // Line should have 5 items: Max processes [soft limit] [hard limit] [units] - if values.len() < 5 { + if let Some(line_items) = re.captures(&line) { + if let Ok(threads_max_pid) = line_items[1].parse() { + threads_max = threads_max.min(threads_max_pid); + } else { debug!("Threads max data not found in file {}", limits_path); - break; } - - // Skip past the first three items ("Max open files") to get the soft limit - let threads_max_pid_str = values[2]; - let Ok(threads_max_pid) = threads_max_pid_str.parse() else { - debug!("Threads max data not found in file {}", limits_path); - break; - }; - threads_max = threads_max.min(threads_max_pid); break; } } @@ -312,14 +298,14 @@ fn get_threads_max_data_from_path(path: &str, pids: &[i32]) -> f64 { threads_max } -pub fn get_threads_use_data(pids: &[i32]) -> Result { +pub fn get_threads_use_data(pids: &[i64]) -> Result { get_threads_use_data_from_path(PROC_PATH, pids) } -fn get_threads_use_data_from_path(path: &str, pids: &[i32]) -> Result { +fn get_threads_use_data_from_path(path: &str, pids: &[i64]) -> Result { let mut threads_use = 0; - for pid in pids { + for &pid in pids { let task_path = format!("{path}/{pid}/task"); let Ok(files) = fs::read_dir(task_path) else { return Err(io::Error::new( @@ -328,16 +314,14 @@ fn get_threads_use_data_from_path(path: &str, pids: &[i32]) -> Result String { let mut safe_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); safe_path.push(file); @@ -353,7 +339,6 @@ mod tests { } #[test] - #[allow(clippy::float_cmp)] fn test_get_pid_list() { let path = "./tests/proc"; let mut pids = get_pid_list_from_path(path); @@ -368,14 +353,13 @@ mod tests { } #[test] - #[allow(clippy::float_cmp)] fn test_get_network_data() { let path = "./tests/proc/net/valid_dev"; let network_data_result = get_network_data_from_path(path_from_root(path).as_str()); assert!(network_data_result.is_ok()); - let network_data_result = network_data_result.unwrap(); - assert_eq!(network_data_result.rx_bytes, 180.0); - assert_eq!(network_data_result.tx_bytes, 254.0); + let network_data = network_data_result.unwrap(); + assert!((network_data.rx_bytes - 180.0).abs() < DELTA); + assert!((network_data.tx_bytes - 254.0).abs() < DELTA); let path = "./tests/proc/net/invalid_dev_malformed"; let network_data_result = get_network_data_from_path(path); @@ -395,29 +379,32 @@ mod tests { } #[test] - #[allow(clippy::float_cmp)] fn test_get_cpu_data() { let path = "./tests/proc/stat/valid_stat"; let cpu_data_result = get_cpu_data_from_path(path_from_root(path).as_str()); assert!(cpu_data_result.is_ok()); let cpu_data = cpu_data_result.unwrap(); - assert_eq!(cpu_data.total_user_time_ms, 23370.0); - assert_eq!(cpu_data.total_system_time_ms, 1880.0); - assert_eq!(cpu_data.total_idle_time_ms, 178_380.0); + assert!((cpu_data.total_user_time_ms - 23370.0).abs() < DELTA); + assert!((cpu_data.total_system_time_ms - 1880.0).abs() < DELTA); + assert!((cpu_data.total_idle_time_ms - 178_380.0).abs() < DELTA); assert_eq!(cpu_data.individual_cpu_idle_times.len(), 2); - assert_eq!( - *cpu_data + assert!( + (*cpu_data .individual_cpu_idle_times .get("cpu0") - .expect("cpu0 not found"), - 91880.0 + .expect("cpu0 not found") + - 91880.0) + .abs() + < DELTA ); - assert_eq!( - *cpu_data + assert!( + (*cpu_data .individual_cpu_idle_times .get("cpu1") - .expect("cpu1 not found"), - 86490.0 + .expect("cpu1 not found") + - 86490.0) + .abs() + < DELTA ); let path = "./tests/proc/stat/invalid_stat_non_numerical_value_1"; @@ -446,13 +433,12 @@ mod tests { } #[test] - #[allow(clippy::float_cmp)] fn test_get_uptime_data() { let path = "./tests/proc/uptime/valid_uptime"; let uptime_data_result = get_uptime_from_path(path_from_root(path).as_str()); assert!(uptime_data_result.is_ok()); let uptime_data = uptime_data_result.unwrap(); - assert_eq!(uptime_data, 3_213_103_123_000.0); + assert!((uptime_data - 3_213_103_123_000.0).abs() < DELTA); let path = "./tests/proc/uptime/invalid_data_uptime"; let uptime_data_result = get_uptime_from_path(path); @@ -468,31 +454,31 @@ mod tests { } #[test] - #[allow(clippy::float_cmp)] fn test_get_fd_max_data() { let path = "./tests/proc/process/valid"; let pids = get_pid_list_from_path(path); let fd_max = get_fd_max_data_from_path(path, &pids); - assert_eq!(fd_max, 900.0); + assert!((fd_max - 900.0).abs() < DELTA); let path = "./tests/proc/process/invalid_malformed"; let fd_max = get_fd_max_data_from_path(path, &pids); - assert_eq!(fd_max, 1024.0); // assert that fd_max is equal to AWS Lambda limit + // assert that fd_max is equal to AWS Lambda limit + assert!((fd_max - constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT).abs() < DELTA); let path = "./tests/proc/process/invalid_missing"; let fd_max = get_fd_max_data_from_path(path, &pids); - assert_eq!(fd_max, 1024.0); // assert that fd_max is equal to AWS Lambda limit + // assert that fd_max is equal to AWS Lambda limit + assert!((fd_max - constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT).abs() < DELTA); } #[test] - #[allow(clippy::float_cmp)] fn test_get_fd_use_data() { let path = "./tests/proc/process/valid"; let pids = get_pid_list_from_path(path); let fd_use_result = get_fd_use_data_from_path(path, &pids); assert!(fd_use_result.is_ok()); let fd_use = fd_use_result.unwrap(); - assert_eq!(fd_use, 5.0); + assert!((fd_use - 5.0).abs() < DELTA); let path = "./tests/proc/process/invalid_missing"; let fd_use_result = get_fd_use_data_from_path(path, &pids); @@ -500,31 +486,31 @@ mod tests { } #[test] - #[allow(clippy::float_cmp)] fn test_get_threads_max_data() { let path = "./tests/proc/process/valid"; let pids = get_pid_list_from_path(path); - let fd_max = get_threads_max_data_from_path(path, &pids); - assert_eq!(fd_max, 1024.0); + let threads_max = get_threads_max_data_from_path(path, &pids); + assert!((threads_max - 1024.0).abs() < DELTA); let path = "./tests/proc/process/invalid_malformed"; - let fd_max = get_threads_max_data_from_path(path, &pids); - assert_eq!(fd_max, 1024.0); // assert that threads_max is equal to AWS Lambda limit + let threads_max = get_threads_max_data_from_path(path, &pids); + // assert that threads_max is equal to AWS Lambda limit + assert!((threads_max - constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT).abs() < DELTA); let path = "./tests/proc/process/invalid_missing"; - let fd_max = get_threads_max_data_from_path(path, &pids); - assert_eq!(fd_max, 1024.0); // assert that threads_max is equal to AWS Lambda limit + let threads_max = get_threads_max_data_from_path(path, &pids); + // assert that threads_max is equal to AWS Lambda limit + assert!((threads_max - constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT).abs() < DELTA); } #[test] - #[allow(clippy::float_cmp)] fn test_get_threads_use_data() { let path = "./tests/proc/process/valid"; let pids = get_pid_list_from_path(path); let threads_use_result = get_threads_use_data_from_path(path, &pids); assert!(threads_use_result.is_ok()); let threads_use = threads_use_result.unwrap(); - assert_eq!(threads_use, 5.0); + assert!((threads_use - 5.0).abs() < DELTA); let path = "./tests/proc/process/invalid_missing"; let threads_use_result = get_threads_use_data_from_path(path, &pids); From 9e33ca2d0ee92e0147888ad9fb760160f5bb02a5 Mon Sep 17 00:00:00 2001 From: shreyamalpani Date: Thu, 14 Nov 2024 16:51:13 -0500 Subject: [PATCH 4/4] rename var --- bottlecap/src/proc/mod.rs | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/bottlecap/src/proc/mod.rs b/bottlecap/src/proc/mod.rs index 5be7262bf..2aaf17474 100644 --- a/bottlecap/src/proc/mod.rs +++ b/bottlecap/src/proc/mod.rs @@ -330,7 +330,7 @@ mod tests { use super::*; use std::path::PathBuf; - const DELTA: f64 = 1e-6; + const PRECISION: f64 = 1e-6; fn path_from_root(file: &str) -> String { let mut safe_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -358,8 +358,8 @@ mod tests { let network_data_result = get_network_data_from_path(path_from_root(path).as_str()); assert!(network_data_result.is_ok()); let network_data = network_data_result.unwrap(); - assert!((network_data.rx_bytes - 180.0).abs() < DELTA); - assert!((network_data.tx_bytes - 254.0).abs() < DELTA); + assert!((network_data.rx_bytes - 180.0).abs() < PRECISION); + assert!((network_data.tx_bytes - 254.0).abs() < PRECISION); let path = "./tests/proc/net/invalid_dev_malformed"; let network_data_result = get_network_data_from_path(path); @@ -384,9 +384,9 @@ mod tests { let cpu_data_result = get_cpu_data_from_path(path_from_root(path).as_str()); assert!(cpu_data_result.is_ok()); let cpu_data = cpu_data_result.unwrap(); - assert!((cpu_data.total_user_time_ms - 23370.0).abs() < DELTA); - assert!((cpu_data.total_system_time_ms - 1880.0).abs() < DELTA); - assert!((cpu_data.total_idle_time_ms - 178_380.0).abs() < DELTA); + assert!((cpu_data.total_user_time_ms - 23370.0).abs() < PRECISION); + assert!((cpu_data.total_system_time_ms - 1880.0).abs() < PRECISION); + assert!((cpu_data.total_idle_time_ms - 178_380.0).abs() < PRECISION); assert_eq!(cpu_data.individual_cpu_idle_times.len(), 2); assert!( (*cpu_data @@ -395,7 +395,7 @@ mod tests { .expect("cpu0 not found") - 91880.0) .abs() - < DELTA + < PRECISION ); assert!( (*cpu_data @@ -404,7 +404,7 @@ mod tests { .expect("cpu1 not found") - 86490.0) .abs() - < DELTA + < PRECISION ); let path = "./tests/proc/stat/invalid_stat_non_numerical_value_1"; @@ -438,7 +438,7 @@ mod tests { let uptime_data_result = get_uptime_from_path(path_from_root(path).as_str()); assert!(uptime_data_result.is_ok()); let uptime_data = uptime_data_result.unwrap(); - assert!((uptime_data - 3_213_103_123_000.0).abs() < DELTA); + assert!((uptime_data - 3_213_103_123_000.0).abs() < PRECISION); let path = "./tests/proc/uptime/invalid_data_uptime"; let uptime_data_result = get_uptime_from_path(path); @@ -458,17 +458,17 @@ mod tests { let path = "./tests/proc/process/valid"; let pids = get_pid_list_from_path(path); let fd_max = get_fd_max_data_from_path(path, &pids); - assert!((fd_max - 900.0).abs() < DELTA); + assert!((fd_max - 900.0).abs() < PRECISION); let path = "./tests/proc/process/invalid_malformed"; let fd_max = get_fd_max_data_from_path(path, &pids); // assert that fd_max is equal to AWS Lambda limit - assert!((fd_max - constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT).abs() < DELTA); + assert!((fd_max - constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT).abs() < PRECISION); let path = "./tests/proc/process/invalid_missing"; let fd_max = get_fd_max_data_from_path(path, &pids); // assert that fd_max is equal to AWS Lambda limit - assert!((fd_max - constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT).abs() < DELTA); + assert!((fd_max - constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT).abs() < PRECISION); } #[test] @@ -478,7 +478,7 @@ mod tests { let fd_use_result = get_fd_use_data_from_path(path, &pids); assert!(fd_use_result.is_ok()); let fd_use = fd_use_result.unwrap(); - assert!((fd_use - 5.0).abs() < DELTA); + assert!((fd_use - 5.0).abs() < PRECISION); let path = "./tests/proc/process/invalid_missing"; let fd_use_result = get_fd_use_data_from_path(path, &pids); @@ -490,17 +490,21 @@ mod tests { let path = "./tests/proc/process/valid"; let pids = get_pid_list_from_path(path); let threads_max = get_threads_max_data_from_path(path, &pids); - assert!((threads_max - 1024.0).abs() < DELTA); + assert!((threads_max - 1024.0).abs() < PRECISION); let path = "./tests/proc/process/invalid_malformed"; let threads_max = get_threads_max_data_from_path(path, &pids); // assert that threads_max is equal to AWS Lambda limit - assert!((threads_max - constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT).abs() < DELTA); + assert!( + (threads_max - constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT).abs() < PRECISION + ); let path = "./tests/proc/process/invalid_missing"; let threads_max = get_threads_max_data_from_path(path, &pids); // assert that threads_max is equal to AWS Lambda limit - assert!((threads_max - constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT).abs() < DELTA); + assert!( + (threads_max - constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT).abs() < PRECISION + ); } #[test] @@ -510,7 +514,7 @@ mod tests { let threads_use_result = get_threads_use_data_from_path(path, &pids); assert!(threads_use_result.is_ok()); let threads_use = threads_use_result.unwrap(); - assert!((threads_use - 5.0).abs() < DELTA); + assert!((threads_use - 5.0).abs() < PRECISION); let path = "./tests/proc/process/invalid_missing"; let threads_use_result = get_threads_use_data_from_path(path, &pids);