diff --git a/bottlecap/src/lifecycle/invocation/context.rs b/bottlecap/src/lifecycle/invocation/context.rs index 4cffb929..7010e245 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 9c72f453..a94164a1 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 f62fd67e..fcd83367 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 6e4f53c7..3d999062 100644 --- a/bottlecap/src/metrics/enhanced/lambda.rs +++ b/bottlecap/src/metrics/enhanced/lambda.rs @@ -429,6 +429,115 @@ 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); + } + + // 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), + 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); + } + + // 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), + 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_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_f64; + + let mut interval = interval(Duration::from_millis(1)); + 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 +612,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 +779,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 +940,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 fe06b908..452cdf4f 100644 --- a/bottlecap/src/proc/constants.rs +++ b/bottlecap/src/proc/constants.rs @@ -1,5 +1,8 @@ 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"; +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 3dfa1a67..2aaf1747 100644 --- a/bottlecap/src/proc/mod.rs +++ b/bottlecap/src/proc/mod.rs @@ -3,11 +3,43 @@ 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 regex::Regex; +use tracing::debug; + +#[must_use] +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 Ok(entries) = fs::read_dir(path) else { + 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,12 +212,126 @@ fn get_uptime_from_path(path: &str) -> Result { )) } +#[must_use] +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: &[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"); + let Ok(file) = File::open(&limits_path) else { + continue; + }; + + let reader = io::BufReader::new(file); + for line in reader.lines().map_while(Result::ok) { + 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; + } + } + } + + fd_max +} + +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: &[i64]) -> Result { + let mut fd_use = 0; + + 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( + io::ErrorKind::InvalidData, + "File descriptor use data not found", + )); + }; + let count = files.count(); + fd_use += count; + } + + Ok(fd_use as f64) +} + +#[must_use] +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: &[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"); + let Ok(file) = File::open(&limits_path) else { + continue; + }; + + let reader = io::BufReader::new(file); + for line in reader.lines().map_while(Result::ok) { + 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; + } + } + } + + threads_max +} + +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: &[i64]) -> Result { + let mut threads_use = 0; + + 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( + io::ErrorKind::InvalidData, + "Threads use data not found", + )); + }; + + threads_use += files + .flatten() + .filter_map(|dir_entry| dir_entry.file_type().ok()) + .filter(fs::FileType::is_dir) + .count(); + } + + Ok(threads_use as f64) +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { use super::*; use std::path::PathBuf; + const PRECISION: f64 = 1e-6; + fn path_from_root(file: &str) -> String { let mut safe_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); safe_path.push(file); @@ -193,14 +339,27 @@ 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); + 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] 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() < 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); @@ -220,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() < 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_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() + < PRECISION ); - 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() + < PRECISION ); let path = "./tests/proc/stat/invalid_stat_non_numerical_value_1"; @@ -271,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() < PRECISION); let path = "./tests/proc/uptime/invalid_data_uptime"; let uptime_data_result = get_uptime_from_path(path); @@ -291,4 +452,72 @@ mod tests { let uptime_data_result = get_uptime_from_path(path); assert!(uptime_data_result.is_err()); } + + #[test] + 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!((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() < 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() < PRECISION); + } + + #[test] + 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!((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); + assert!(fd_use_result.is_err()); + } + + #[test] + fn test_get_threads_max_data() { + 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() < 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() < 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() < PRECISION + ); + } + + #[test] + 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!((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); + 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 00000000..e69de29b diff --git a/bottlecap/tests/proc/142/.gitkeep b/bottlecap/tests/proc/142/.gitkeep new file mode 100644 index 00000000..e69de29b 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 00000000..2d25ac30 --- /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 00000000..2436ec08 --- /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 00000000..c7dc2c55 --- /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 00000000..07de49ec --- /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 00000000..e69de29b 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 00000000..e69de29b diff --git a/bottlecap/tests/proc/process/valid/31/limits b/bottlecap/tests/proc/process/valid/31/limits new file mode 100644 index 00000000..75d41eee --- /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 00000000..e69de29b 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 00000000..e69de29b 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 00000000..e69de29b 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 00000000..e69de29b 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 00000000..e69de29b 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 00000000..e69de29b diff --git a/bottlecap/tests/proc/process/valid/9/limits b/bottlecap/tests/proc/process/valid/9/limits new file mode 100644 index 00000000..664f04c8 --- /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 00000000..e69de29b 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 00000000..e69de29b 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 00000000..e69de29b