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
100 match val.trim().parse::<T>() {
101 Ok(parsed) => Ok(parsed),
102 Err(_) => {
103 bail!("Failed to parse content '{}' from {:?}", val.trim(), path);
104 }
105 }
106}
107
108pub fn read_file_usize_vec(path: &Path, separator: char) -> Result<Vec<usize>> {
109 let val = std::fs::read_to_string(path)?;
110
111 val.split(separator)
112 .map(|s| {
113 s.trim()
114 .parse::<usize>()
115 .map_err(|_| anyhow!("Failed to parse '{}' as usize", s))
116 })
117 .collect::<Result<Vec<usize>>>()
118}
119
120pub fn read_file_byte(path: &Path) -> Result<usize> {
121 let val = std::fs::read_to_string(path)?;
122 let val = val.trim();
123
124 if let Some(sval) = val.strip_suffix("K") {
126 let byte = sval.parse::<usize>()?;
127 return Ok(byte * 1024);
128 }
129 if let Some(sval) = val.strip_suffix("M") {
130 let byte = sval.parse::<usize>()?;
131 return Ok(byte * 1024 * 1024);
132 }
133 if let Some(sval) = val.strip_suffix("G") {
134 let byte = sval.parse::<usize>()?;
135 return Ok(byte * 1024 * 1024 * 1024);
136 }
137
138 let byte = val.parse::<usize>()?;
139 Ok(byte)
140}
141
142pub fn normalize_load_metric(metric: f64) -> f64 {
161 metric / 100.0
162}