1use anyhow::Result;
2use anyhow::{anyhow, bail};
3use libc;
4use log::{info, warn};
5use scx_stats::prelude::*;
6use serde::Deserialize;
7use std::path::Path;
8use std::thread::sleep;
9use std::time::Duration;
10
11pub fn monitor_stats<T>(
12 stats_args: &[(String, String)],
13 intv: Duration,
14 mut should_exit: impl FnMut() -> bool,
15 mut output: impl FnMut(T) -> Result<()>,
16) -> Result<()>
17where
18 T: for<'a> Deserialize<'a>,
19{
20 let mut retry_cnt: u32 = 0;
21
22 const RETRYABLE_ERRORS: [std::io::ErrorKind; 2] = [
23 std::io::ErrorKind::NotFound,
24 std::io::ErrorKind::ConnectionRefused,
25 ];
26
27 while !should_exit() {
28 let mut client = match StatsClient::new().connect() {
29 Ok(v) => v,
30 Err(e) => match e.downcast_ref::<std::io::Error>() {
31 Some(ioe) if RETRYABLE_ERRORS.contains(&ioe.kind()) => {
32 if retry_cnt == 1 {
33 info!("Stats server not available, retrying...");
34 }
35 retry_cnt += 1;
36 sleep(Duration::from_secs(1));
37 continue;
38 }
39 _ => Err(e)?,
40 },
41 };
42 retry_cnt = 0;
43
44 while !should_exit() {
45 let stats = match client.request::<T>("stats", stats_args.to_owned()) {
46 Ok(v) => v,
47 Err(e) => match e.downcast_ref::<std::io::Error>() {
48 Some(ioe) => {
49 info!("Connection to stats_server failed ({})", ioe);
50 sleep(Duration::from_secs(1));
51 break;
52 }
53 None => {
54 warn!("error on handling stats_server result {}", e);
55 sleep(Duration::from_secs(1));
56 break;
57 }
58 },
59 };
60 output(stats)?;
61 sleep(intv);
62 }
63 }
64
65 Ok(())
66}
67
68pub fn set_rlimit_infinity() {
69 unsafe {
70 let new_rlimit = libc::rlimit {
72 rlim_cur: libc::RLIM_INFINITY,
73 rlim_max: libc::RLIM_INFINITY,
74 };
75 let res = libc::setrlimit(libc::RLIMIT_MEMLOCK, &new_rlimit);
76 if res != 0 {
77 panic!("setrlimit failed with error code: {}", res);
78 }
79 };
80}
81
82pub fn read_from_file<T>(path: &Path) -> Result<T>
89where
90 T: std::str::FromStr,
91 T::Err: std::error::Error + Send + Sync + 'static,
92{
93 let val = match std::fs::read_to_string(path) {
94 Ok(val) => val,
95 Err(_) => {
96 bail!("Failed to open or read file {:?}", path);
97 }
98 };
99 let val = val.trim_end_matches('\0');
100
101 match val.trim().parse::<T>() {
102 Ok(parsed) => Ok(parsed),
103 Err(_) => {
104 bail!("Failed to parse content '{}' from {:?}", val.trim(), path);
105 }
106 }
107}
108
109pub fn read_file_usize_vec(path: &Path, separator: char) -> Result<Vec<usize>> {
110 let val = std::fs::read_to_string(path)?;
111 let val = val.trim_end_matches('\0');
112
113 val.split(separator)
114 .map(|s| {
115 s.trim()
116 .parse::<usize>()
117 .map_err(|_| anyhow!("Failed to parse '{}' as usize", s))
118 })
119 .collect::<Result<Vec<usize>>>()
120}
121
122pub fn read_file_byte(path: &Path) -> Result<usize> {
123 let val = std::fs::read_to_string(path)?;
124 let val = val.trim_end_matches('\0');
125 let val = val.trim();
126
127 if let Some(sval) = val.strip_suffix("K") {
129 let byte = sval.parse::<usize>()?;
130 return Ok(byte * 1024);
131 }
132 if let Some(sval) = val.strip_suffix("M") {
133 let byte = sval.parse::<usize>()?;
134 return Ok(byte * 1024 * 1024);
135 }
136 if let Some(sval) = val.strip_suffix("G") {
137 let byte = sval.parse::<usize>()?;
138 return Ok(byte * 1024 * 1024 * 1024);
139 }
140
141 let byte = val.parse::<usize>()?;
142 Ok(byte)
143}
144
145pub fn normalize_load_metric(metric: f64) -> f64 {
164 metric / 100.0
165}