mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-05-06 23:47:31 +00:00
commit
2068801b80
src
agent
libs
mem-agent
.gitignoreCargo.lockCargo.tomlMakefile
example
src
runtime-rs/config
tools/agent-ctl/src
tests
tools/packaging/kernel
26
src/agent/Cargo.lock
generated
26
src/agent/Cargo.lock
generated
@ -3026,6 +3026,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"logging",
|
||||
"mem-agent",
|
||||
"netlink-packet-utils",
|
||||
"netlink-sys",
|
||||
"nix 0.24.3",
|
||||
@ -3483,6 +3484,21 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mem-agent"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
"nix 0.23.2",
|
||||
"page_size",
|
||||
"slog",
|
||||
"slog-scope",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
@ -4108,6 +4124,16 @@ dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "page_size"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.0"
|
||||
|
@ -7,6 +7,7 @@ license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
runtime-spec = { path = "../libs/runtime-spec" }
|
||||
mem-agent = { path = "../mem-agent" }
|
||||
oci-spec = { version = "0.6.8", features = ["runtime"] }
|
||||
rustjail = { path = "rustjail" }
|
||||
protocols = { path = "../libs/protocols", features = ["async", "with-serde"] }
|
||||
|
@ -45,6 +45,27 @@ const IMAGE_POLICY_FILE: &str = "agent.image_policy_file";
|
||||
const HTTPS_PROXY: &str = "agent.https_proxy";
|
||||
const NO_PROXY: &str = "agent.no_proxy";
|
||||
|
||||
const MEM_AGENT_ENABLE: &str = "agent.mem_agent_enable";
|
||||
const MEM_AGENT_MEMCG_DISABLE: &str = "agent.mem_agent_memcg_disable";
|
||||
const MEM_AGENT_MEMCG_SWAP: &str = "agent.mem_agent_memcg_swap";
|
||||
const MEM_AGENT_MEMCG_SWAPPINESS_MAX: &str = "agent.mem_agent_memcg_swappiness_max";
|
||||
const MEM_AGENT_MEMCG_PERIOD_SECS: &str = "agent.mem_agent_memcg_period_secs";
|
||||
const MEM_AGENT_MEMCG_PERIOD_PSI_PERCENT_LIMIT: &str =
|
||||
"agent.mem_agent_memcg_period_psi_percent_limit";
|
||||
const MEM_AGENT_MEMCG_EVICTION_PSI_PERCENT_LIMIT: &str =
|
||||
"agent.mem_agent_memcg_eviction_psi_percent_limit";
|
||||
const MEM_AGENT_MEMCG_EVICTION_RUN_AGING_COUNT_MIN: &str =
|
||||
"agent.mem_agent_memcg_eviction_run_aging_count_min";
|
||||
const MEM_AGENT_COMPACT_DISABLE: &str = "agent.mem_agent_compact_disable";
|
||||
const MEM_AGENT_COMPACT_PERIOD_SECS: &str = "agent.mem_agent_compact_period_secs";
|
||||
const MEM_AGENT_COMPACT_PERIOD_PSI_PERCENT_LIMIT: &str =
|
||||
"agent.mem_agent_compact_period_psi_percent_limit";
|
||||
const MEM_AGENT_COMPACT_PSI_PERCENT_LIMIT: &str = "agent.mem_agent_compact_psi_percent_limit";
|
||||
const MEM_AGENT_COMPACT_SEC_MAX: &str = "agent.mem_agent_compact_sec_max";
|
||||
const MEM_AGENT_COMPACT_ORDER: &str = "agent.mem_agent_compact_order";
|
||||
const MEM_AGENT_COMPACT_THRESHOLD: &str = "agent.mem_agent_compact_threshold";
|
||||
const MEM_AGENT_COMPACT_FORCE_TIMES: &str = "agent.mem_agent_compact_force_times";
|
||||
|
||||
const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info;
|
||||
const DEFAULT_HOTPLUG_TIMEOUT: time::Duration = time::Duration::from_secs(3);
|
||||
const DEFAULT_CDH_API_TIMEOUT: time::Duration = time::Duration::from_secs(50);
|
||||
@ -131,6 +152,13 @@ pub struct AgentConfig {
|
||||
pub image_policy_file: String,
|
||||
#[cfg(feature = "agent-policy")]
|
||||
pub policy_file: String,
|
||||
pub mem_agent: Option<MemAgentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct MemAgentConfig {
|
||||
pub memcg_config: mem_agent::memcg::Config,
|
||||
pub compact_config: mem_agent::compact::Config,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -160,6 +188,22 @@ pub struct AgentConfigBuilder {
|
||||
pub image_policy_file: Option<String>,
|
||||
#[cfg(feature = "agent-policy")]
|
||||
pub policy_file: Option<String>,
|
||||
pub mem_agent_enable: Option<bool>,
|
||||
pub mem_agent_memcg_disable: Option<bool>,
|
||||
pub mem_agent_memcg_swap: Option<bool>,
|
||||
pub mem_agent_memcg_swappiness_max: Option<u8>,
|
||||
pub mem_agent_memcg_period_secs: Option<u64>,
|
||||
pub mem_agent_memcg_period_psi_percent_limit: Option<u8>,
|
||||
pub mem_agent_memcg_eviction_psi_percent_limit: Option<u8>,
|
||||
pub mem_agent_memcg_eviction_run_aging_count_min: Option<u64>,
|
||||
pub mem_agent_compact_disable: Option<bool>,
|
||||
pub mem_agent_compact_period_secs: Option<u64>,
|
||||
pub mem_agent_compact_period_psi_percent_limit: Option<u8>,
|
||||
pub mem_agent_compact_psi_percent_limit: Option<u8>,
|
||||
pub mem_agent_compact_sec_max: Option<i64>,
|
||||
pub mem_agent_compact_order: Option<u8>,
|
||||
pub mem_agent_compact_threshold: Option<u64>,
|
||||
pub mem_agent_compact_force_times: Option<u64>,
|
||||
}
|
||||
|
||||
macro_rules! config_override {
|
||||
@ -176,6 +220,14 @@ macro_rules! config_override {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mem_agent_config_override {
|
||||
($builder_v:expr, $mac_v:expr) => {
|
||||
if let Some(v) = $builder_v {
|
||||
$mac_v = v;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// parse_cmdline_param parse commandline parameters.
|
||||
macro_rules! parse_cmdline_param {
|
||||
// commandline flags, without func to parse the option values
|
||||
@ -235,6 +287,7 @@ impl Default for AgentConfig {
|
||||
image_policy_file: String::from(""),
|
||||
#[cfg(feature = "agent-policy")]
|
||||
policy_file: String::from(""),
|
||||
mem_agent: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -287,6 +340,75 @@ impl FromStr for AgentConfig {
|
||||
|
||||
#[cfg(feature = "agent-policy")]
|
||||
config_override!(agent_config_builder, agent_config, policy_file);
|
||||
|
||||
if agent_config_builder.mem_agent_enable.unwrap_or(false) {
|
||||
let mut mac = MemAgentConfig::default();
|
||||
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_memcg_disable,
|
||||
mac.memcg_config.disabled
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_memcg_swap,
|
||||
mac.memcg_config.swap
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_memcg_swappiness_max,
|
||||
mac.memcg_config.swappiness_max
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_memcg_period_secs,
|
||||
mac.memcg_config.period_secs
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_memcg_period_psi_percent_limit,
|
||||
mac.memcg_config.period_psi_percent_limit
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_memcg_eviction_psi_percent_limit,
|
||||
mac.memcg_config.eviction_psi_percent_limit
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_memcg_eviction_run_aging_count_min,
|
||||
mac.memcg_config.eviction_run_aging_count_min
|
||||
);
|
||||
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_disable,
|
||||
mac.compact_config.disabled
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_period_secs,
|
||||
mac.compact_config.period_secs
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_period_psi_percent_limit,
|
||||
mac.compact_config.period_psi_percent_limit
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_psi_percent_limit,
|
||||
mac.compact_config.compact_psi_percent_limit
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_sec_max,
|
||||
mac.compact_config.compact_sec_max
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_order,
|
||||
mac.compact_config.compact_order
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_threshold,
|
||||
mac.compact_config.compact_threshold
|
||||
);
|
||||
mem_agent_config_override!(
|
||||
agent_config_builder.mem_agent_compact_force_times,
|
||||
mac.compact_config.compact_force_times
|
||||
);
|
||||
|
||||
agent_config.mem_agent = Some(mac);
|
||||
}
|
||||
|
||||
Ok(agent_config)
|
||||
}
|
||||
}
|
||||
@ -311,6 +433,8 @@ impl AgentConfig {
|
||||
let mut config: AgentConfig = Default::default();
|
||||
let cmdline = fs::read_to_string(file)?;
|
||||
let params: Vec<&str> = cmdline.split_ascii_whitespace().collect();
|
||||
let mut mem_agent_enable = false;
|
||||
let mut mac = MemAgentConfig::default();
|
||||
for param in params.iter() {
|
||||
// If we get a configuration file path from the command line, we
|
||||
// generate our config from it.
|
||||
@ -367,21 +491,21 @@ impl AgentConfig {
|
||||
param,
|
||||
DEBUG_CONSOLE_VPORT_OPTION,
|
||||
config.debug_console_vport,
|
||||
get_vsock_port,
|
||||
get_number_value,
|
||||
|port| port > 0
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
LOG_VPORT_OPTION,
|
||||
config.log_vport,
|
||||
get_vsock_port,
|
||||
get_number_value,
|
||||
|port| port > 0
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
PASSFD_LISTENER_PORT,
|
||||
config.passfd_listener_port,
|
||||
get_vsock_port,
|
||||
get_number_value,
|
||||
|port| port > 0
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
@ -437,6 +561,105 @@ impl AgentConfig {
|
||||
config.secure_storage_integrity,
|
||||
get_bool_value
|
||||
);
|
||||
|
||||
parse_cmdline_param!(param, MEM_AGENT_ENABLE, mem_agent_enable, get_bool_value);
|
||||
|
||||
if mem_agent_enable {
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_MEMCG_DISABLE,
|
||||
mac.memcg_config.disabled,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_MEMCG_SWAP,
|
||||
mac.memcg_config.swap,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_MEMCG_SWAPPINESS_MAX,
|
||||
mac.memcg_config.swappiness_max,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_MEMCG_PERIOD_SECS,
|
||||
mac.memcg_config.period_secs,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_MEMCG_PERIOD_PSI_PERCENT_LIMIT,
|
||||
mac.memcg_config.period_psi_percent_limit,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_MEMCG_EVICTION_PSI_PERCENT_LIMIT,
|
||||
mac.memcg_config.eviction_psi_percent_limit,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_MEMCG_EVICTION_RUN_AGING_COUNT_MIN,
|
||||
mac.memcg_config.eviction_run_aging_count_min,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_DISABLE,
|
||||
mac.compact_config.disabled,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_PERIOD_SECS,
|
||||
mac.compact_config.period_secs,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_PERIOD_PSI_PERCENT_LIMIT,
|
||||
mac.compact_config.period_psi_percent_limit,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_PSI_PERCENT_LIMIT,
|
||||
mac.compact_config.compact_psi_percent_limit,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_SEC_MAX,
|
||||
mac.compact_config.compact_sec_max,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_ORDER,
|
||||
mac.compact_config.compact_order,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_THRESHOLD,
|
||||
mac.compact_config.compact_threshold,
|
||||
get_number_value
|
||||
);
|
||||
parse_cmdline_param!(
|
||||
param,
|
||||
MEM_AGENT_COMPACT_FORCE_TIMES,
|
||||
mac.compact_config.compact_force_times,
|
||||
get_number_value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if mem_agent_enable {
|
||||
config.mem_agent = Some(mac);
|
||||
}
|
||||
|
||||
config.override_config_from_envs();
|
||||
@ -477,11 +700,19 @@ impl AgentConfig {
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
fn get_vsock_port(p: &str) -> Result<i32> {
|
||||
fn get_number_value<T>(p: &str) -> Result<T>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
let fields: Vec<&str> = p.split('=').collect();
|
||||
ensure!(fields.len() == 2, "invalid port parameter");
|
||||
if fields.len() != 2 {
|
||||
return Err(anyhow!("format of {} is invalid", p));
|
||||
}
|
||||
|
||||
Ok(fields[1].parse::<i32>()?)
|
||||
fields[1]
|
||||
.parse::<T>()
|
||||
.map_err(|e| anyhow!("parse from {} failed: {:?}", &fields[1], e))
|
||||
}
|
||||
|
||||
// Map logrus (https://godoc.org/github.com/sirupsen/logrus)
|
||||
@ -682,6 +913,7 @@ mod tests {
|
||||
image_policy_file: &'a str,
|
||||
#[cfg(feature = "agent-policy")]
|
||||
policy_file: &'a str,
|
||||
mem_agent: Option<MemAgentConfig>,
|
||||
}
|
||||
|
||||
impl Default for TestData<'_> {
|
||||
@ -710,6 +942,7 @@ mod tests {
|
||||
image_policy_file: "",
|
||||
#[cfg(feature = "agent-policy")]
|
||||
policy_file: "",
|
||||
mem_agent: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1204,6 +1437,40 @@ mod tests {
|
||||
policy_file: "/tmp/policy.rego",
|
||||
..Default::default()
|
||||
},
|
||||
TestData {
|
||||
contents: "",
|
||||
..Default::default()
|
||||
},
|
||||
TestData {
|
||||
contents: "agent.mem_agent_enable=1",
|
||||
mem_agent: Some(MemAgentConfig::default()),
|
||||
..Default::default()
|
||||
},
|
||||
TestData {
|
||||
contents: "agent.mem_agent_enable=1\nagent.mem_agent_memcg_period_secs=300",
|
||||
mem_agent: Some(MemAgentConfig {
|
||||
memcg_config: mem_agent::memcg::Config {
|
||||
period_secs: 300,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
TestData {
|
||||
contents: "agent.mem_agent_enable=1\nagent.mem_agent_memcg_period_secs=300\nagent.mem_agent_compact_order=6",
|
||||
mem_agent: Some(MemAgentConfig {
|
||||
memcg_config: mem_agent::memcg::Config {
|
||||
period_secs: 300,
|
||||
..Default::default()
|
||||
},
|
||||
compact_config: mem_agent::compact::Config {
|
||||
compact_order: 6,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
|
||||
let dir = tempdir().expect("failed to create tmpdir");
|
||||
@ -1281,6 +1548,8 @@ mod tests {
|
||||
#[cfg(feature = "agent-policy")]
|
||||
assert_eq!(d.policy_file, config.policy_file, "{}", msg);
|
||||
|
||||
assert_eq!(d.mem_agent, config.mem_agent, "{}", msg);
|
||||
|
||||
for v in vars_to_unset {
|
||||
env::remove_var(v);
|
||||
}
|
||||
@ -1525,6 +1794,7 @@ Caused by:
|
||||
server_addr = 'vsock://8:2048'
|
||||
guest_components_procs = "api-server-rest"
|
||||
guest_components_rest_api = "all"
|
||||
mem_agent_enable = true
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
@ -1543,5 +1813,7 @@ Caused by:
|
||||
|
||||
// Verify that the default values are valid
|
||||
assert_eq!(config.hotplug_timeout, DEFAULT_HOTPLUG_TIMEOUT);
|
||||
|
||||
assert_eq!(config.mem_agent, Some(MemAgentConfig::default()),);
|
||||
}
|
||||
}
|
||||
|
@ -428,8 +428,23 @@ async fn start_sandbox(
|
||||
init_attestation_components(logger, config).await?;
|
||||
}
|
||||
|
||||
let mut oma = None;
|
||||
let mut _ort = None;
|
||||
if let Some(c) = &config.mem_agent {
|
||||
let (ma, rt) =
|
||||
mem_agent::agent::MemAgent::new(c.memcg_config.clone(), c.compact_config.clone())
|
||||
.map_err(|e| {
|
||||
error!(logger, "MemAgent::new fail: {}", e);
|
||||
e
|
||||
})
|
||||
.context("start mem-agent")?;
|
||||
oma = Some(ma);
|
||||
_ort = Some(rt);
|
||||
}
|
||||
|
||||
// vsock:///dev/vsock, port
|
||||
let mut server = rpc::start(sandbox.clone(), config.server_addr.as_str(), init_mode).await?;
|
||||
let mut server =
|
||||
rpc::start(sandbox.clone(), config.server_addr.as_str(), init_mode, oma).await?;
|
||||
|
||||
server.start().await?;
|
||||
|
||||
|
@ -181,6 +181,7 @@ impl<T> OptionToTtrpcResult<T> for Option<T> {
|
||||
pub struct AgentService {
|
||||
sandbox: Arc<Mutex<Sandbox>>,
|
||||
init_mode: bool,
|
||||
oma: Option<mem_agent::agent::MemAgent>,
|
||||
}
|
||||
|
||||
impl AgentService {
|
||||
@ -698,6 +699,37 @@ impl AgentService {
|
||||
}
|
||||
}
|
||||
|
||||
fn mem_agent_memcgconfig_to_memcg_optionconfig(
|
||||
mc: &protocols::agent::MemAgentMemcgConfig,
|
||||
) -> mem_agent::memcg::OptionConfig {
|
||||
mem_agent::memcg::OptionConfig {
|
||||
disabled: mc.disabled,
|
||||
swap: mc.swap,
|
||||
swappiness_max: mc.swappiness_max.map(|x| x as u8),
|
||||
period_secs: mc.period_secs,
|
||||
period_psi_percent_limit: mc.period_psi_percent_limit.map(|x| x as u8),
|
||||
eviction_psi_percent_limit: mc.eviction_psi_percent_limit.map(|x| x as u8),
|
||||
eviction_run_aging_count_min: mc.eviction_run_aging_count_min,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn mem_agent_compactconfig_to_compact_optionconfig(
|
||||
cc: &protocols::agent::MemAgentCompactConfig,
|
||||
) -> mem_agent::compact::OptionConfig {
|
||||
mem_agent::compact::OptionConfig {
|
||||
disabled: cc.disabled,
|
||||
period_secs: cc.period_secs,
|
||||
period_psi_percent_limit: cc.period_psi_percent_limit.map(|x| x as u8),
|
||||
compact_psi_percent_limit: cc.compact_psi_percent_limit.map(|x| x as u8),
|
||||
compact_sec_max: cc.compact_sec_max,
|
||||
compact_order: cc.compact_order.map(|x| x as u8),
|
||||
compact_threshold: cc.compact_threshold,
|
||||
compact_force_times: cc.compact_force_times,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl agent_ttrpc::AgentService for AgentService {
|
||||
async fn create_container(
|
||||
@ -1513,6 +1545,54 @@ impl agent_ttrpc::AgentService for AgentService {
|
||||
|
||||
Ok(Empty::new())
|
||||
}
|
||||
|
||||
async fn mem_agent_memcg_set(
|
||||
&self,
|
||||
_ctx: &::ttrpc::r#async::TtrpcContext,
|
||||
config: protocols::agent::MemAgentMemcgConfig,
|
||||
) -> ::ttrpc::Result<Empty> {
|
||||
if let Some(ma) = &self.oma {
|
||||
ma.memcg_set_config_async(mem_agent_memcgconfig_to_memcg_optionconfig(&config))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let estr = format!("ma.memcg_set_config_async fail: {}", e);
|
||||
error!(sl(), "{}", estr);
|
||||
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::INTERNAL, estr))
|
||||
})?;
|
||||
} else {
|
||||
let estr = "mem-agent is disabled";
|
||||
error!(sl(), "{}", estr);
|
||||
return Err(ttrpc::Error::RpcStatus(ttrpc::get_status(
|
||||
ttrpc::Code::INTERNAL,
|
||||
estr,
|
||||
)));
|
||||
}
|
||||
Ok(Empty::new())
|
||||
}
|
||||
|
||||
async fn mem_agent_compact_set(
|
||||
&self,
|
||||
_ctx: &::ttrpc::r#async::TtrpcContext,
|
||||
config: protocols::agent::MemAgentCompactConfig,
|
||||
) -> ::ttrpc::Result<Empty> {
|
||||
if let Some(ma) = &self.oma {
|
||||
ma.compact_set_config_async(mem_agent_compactconfig_to_compact_optionconfig(&config))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let estr = format!("ma.compact_set_config_async fail: {}", e);
|
||||
error!(sl(), "{}", estr);
|
||||
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::INTERNAL, estr))
|
||||
})?;
|
||||
} else {
|
||||
let estr = "mem-agent is disabled";
|
||||
error!(sl(), "{}", estr);
|
||||
return Err(ttrpc::Error::RpcStatus(ttrpc::get_status(
|
||||
ttrpc::Code::INTERNAL,
|
||||
estr,
|
||||
)));
|
||||
}
|
||||
Ok(Empty::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -1656,10 +1736,12 @@ pub async fn start(
|
||||
s: Arc<Mutex<Sandbox>>,
|
||||
server_address: &str,
|
||||
init_mode: bool,
|
||||
oma: Option<mem_agent::agent::MemAgent>,
|
||||
) -> Result<TtrpcServer> {
|
||||
let agent_service = Box::new(AgentService {
|
||||
sandbox: s,
|
||||
init_mode,
|
||||
oma,
|
||||
}) as Box<dyn agent_ttrpc::AgentService + Send + Sync>;
|
||||
let aservice = agent_ttrpc::create_agent_service(Arc::new(agent_service));
|
||||
|
||||
@ -2294,6 +2376,7 @@ mod tests {
|
||||
let agent_service = Box::new(AgentService {
|
||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||
init_mode: true,
|
||||
oma: None,
|
||||
});
|
||||
|
||||
let req = protocols::agent::UpdateInterfaceRequest::default();
|
||||
@ -2311,6 +2394,7 @@ mod tests {
|
||||
let agent_service = Box::new(AgentService {
|
||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||
init_mode: true,
|
||||
oma: None,
|
||||
});
|
||||
|
||||
let req = protocols::agent::UpdateRoutesRequest::default();
|
||||
@ -2328,6 +2412,7 @@ mod tests {
|
||||
let agent_service = Box::new(AgentService {
|
||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||
init_mode: true,
|
||||
oma: None,
|
||||
});
|
||||
|
||||
let req = protocols::agent::AddARPNeighborsRequest::default();
|
||||
@ -2466,6 +2551,7 @@ mod tests {
|
||||
let agent_service = Box::new(AgentService {
|
||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||
init_mode: true,
|
||||
oma: None,
|
||||
});
|
||||
|
||||
let result = agent_service
|
||||
@ -2956,6 +3042,7 @@ OtherField:other
|
||||
let agent_service = Box::new(AgentService {
|
||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||
init_mode: true,
|
||||
oma: None,
|
||||
});
|
||||
|
||||
let ctx = mk_ttrpc_context();
|
||||
|
@ -18,6 +18,44 @@ use crate::eother;
|
||||
/// agent name of Kata agent.
|
||||
pub const AGENT_NAME_KATA: &str = "kata";
|
||||
|
||||
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct MemAgent {
|
||||
#[serde(default, alias = "mem_agent_enable")]
|
||||
pub enable: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub memcg_disable: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub memcg_swap: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub memcg_swappiness_max: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub memcg_period_secs: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub memcg_period_psi_percent_limit: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub memcg_eviction_psi_percent_limit: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub memcg_eviction_run_aging_count_min: Option<u64>,
|
||||
|
||||
#[serde(default)]
|
||||
pub compact_disable: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub compact_period_secs: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub compact_period_psi_percent_limit: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub compact_psi_percent_limit: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub compact_sec_max: Option<i64>,
|
||||
#[serde(default)]
|
||||
pub compact_order: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub compact_threshold: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub compact_force_times: Option<u64>,
|
||||
}
|
||||
|
||||
/// Kata agent configuration information.
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct Agent {
|
||||
@ -98,6 +136,10 @@ pub struct Agent {
|
||||
/// container pipe size
|
||||
#[serde(default)]
|
||||
pub container_pipe_size: u32,
|
||||
|
||||
/// Memory agent configuration
|
||||
#[serde(default)]
|
||||
pub mem_agent: MemAgent,
|
||||
}
|
||||
|
||||
impl std::default::Default for Agent {
|
||||
@ -116,6 +158,7 @@ impl std::default::Default for Agent {
|
||||
health_check_request_timeout_ms: 90_000,
|
||||
kernel_modules: Default::default(),
|
||||
container_pipe_size: 0,
|
||||
mem_agent: MemAgent::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +115,14 @@ pub struct TomlConfig {
|
||||
pub runtime: Runtime,
|
||||
}
|
||||
|
||||
macro_rules! mem_agent_kv_insert {
|
||||
($ma_cfg:expr, $key:expr, $map:expr) => {
|
||||
if let Some(n) = $ma_cfg {
|
||||
$map.insert($key.to_string(), n.to_string());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl TomlConfig {
|
||||
/// Load Kata configuration information from configuration files.
|
||||
///
|
||||
@ -204,6 +212,83 @@ impl TomlConfig {
|
||||
DEFAULT_AGENT_DBG_CONSOLE_PORT.to_string(),
|
||||
);
|
||||
}
|
||||
if cfg.mem_agent.enable {
|
||||
kv.insert("psi".to_string(), "1".to_string());
|
||||
kv.insert("agent.mem_agent_enable".to_string(), "1".to_string());
|
||||
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.memcg_disable,
|
||||
"agent.mem_agent_memcg_disable",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(cfg.mem_agent.memcg_swap, "agent.mem_agent_memcg_swap", kv);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.memcg_swappiness_max,
|
||||
"agent.mem_agent_memcg_swappiness_max",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.memcg_period_secs,
|
||||
"agent.mem_agent_memcg_period_secs",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.memcg_period_psi_percent_limit,
|
||||
"agent.mem_agent_memcg_period_psi_percent_limit",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.memcg_eviction_psi_percent_limit,
|
||||
"agent.mem_agent_memcg_eviction_psi_percent_limit",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.memcg_eviction_run_aging_count_min,
|
||||
"agent.mem_agent_memcg_eviction_run_aging_count_min",
|
||||
kv
|
||||
);
|
||||
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_disable,
|
||||
"agent.mem_agent_compact_disable",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_period_secs,
|
||||
"agent.mem_agent_compact_period_secs",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_period_psi_percent_limit,
|
||||
"agent.mem_agent_compact_period_psi_percent_limit",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_psi_percent_limit,
|
||||
"agent.mem_agent_compact_psi_percent_limit",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_sec_max,
|
||||
"agent.mem_agent_compact_sec_max",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_order,
|
||||
"agent.mem_agent_compact_order",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_threshold,
|
||||
"agent.mem_agent_compact_threshold",
|
||||
kv
|
||||
);
|
||||
mem_agent_kv_insert!(
|
||||
cfg.mem_agent.compact_force_times,
|
||||
"agent.mem_agent_compact_force_times",
|
||||
kv
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(kv)
|
||||
}
|
||||
|
@ -59,6 +59,10 @@ service AgentService {
|
||||
// observability
|
||||
rpc GetMetrics(GetMetricsRequest) returns (Metrics);
|
||||
|
||||
// mem-agent
|
||||
rpc MemAgentMemcgSet(MemAgentMemcgConfig) returns (google.protobuf.Empty);
|
||||
rpc MemAgentCompactSet(MemAgentCompactConfig) returns (google.protobuf.Empty);
|
||||
|
||||
// misc (TODO: some rpcs can be replaced by hyperstart-exec)
|
||||
rpc CreateSandbox(CreateSandboxRequest) returns (google.protobuf.Empty);
|
||||
rpc DestroySandbox(DestroySandboxRequest) returns (google.protobuf.Empty);
|
||||
@ -611,3 +615,24 @@ message ResizeVolumeRequest {
|
||||
message SetPolicyRequest {
|
||||
string policy = 1;
|
||||
}
|
||||
|
||||
message MemAgentMemcgConfig {
|
||||
optional bool disabled = 1;
|
||||
optional bool swap = 2;
|
||||
optional uint32 swappiness_max = 3;
|
||||
optional uint64 period_secs = 4;
|
||||
optional uint32 period_psi_percent_limit = 5;
|
||||
optional uint32 eviction_psi_percent_limit = 6;
|
||||
optional uint64 eviction_run_aging_count_min = 7;
|
||||
}
|
||||
|
||||
message MemAgentCompactConfig {
|
||||
optional bool disabled = 1;
|
||||
optional uint64 period_secs = 2;
|
||||
optional uint32 period_psi_percent_limit = 3;
|
||||
optional uint32 compact_psi_percent_limit = 4;
|
||||
optional int64 compact_sec_max = 5;
|
||||
optional uint32 compact_order = 6;
|
||||
optional uint64 compact_threshold = 7;
|
||||
optional uint64 compact_force_times = 8;
|
||||
}
|
||||
|
5
src/mem-agent/.gitignore
vendored
Normal file
5
src/mem-agent/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/target
|
||||
/example/target
|
||||
/.vscode
|
||||
.vscode-ctags
|
||||
|
943
src/mem-agent/Cargo.lock
generated
Normal file
943
src/mem-agent/Cargo.lock
generated
Normal file
@ -0,0 +1,943 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.4.0",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "mem-agent"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
"maplit",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"page_size",
|
||||
"slog",
|
||||
"slog-async",
|
||||
"slog-scope",
|
||||
"slog-term",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "page_size"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slog"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06"
|
||||
|
||||
[[package]]
|
||||
name = "slog-async"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c8038f898a2c79507940990f05386455b3a317d8f18d4caea7cbc3d5096b84"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"slog",
|
||||
"take_mut",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slog-scope"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f95a4b4c3274cd2869549da82b57ccc930859bdbf5bcea0424bc5f140b3c786"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"lazy_static",
|
||||
"slog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slog-term"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
"slog",
|
||||
"term",
|
||||
"thread_local",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take_mut"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.4",
|
||||
"windows_aarch64_msvc 0.52.4",
|
||||
"windows_i686_gnu 0.52.4",
|
||||
"windows_i686_msvc 0.52.4",
|
||||
"windows_x86_64_gnu 0.52.4",
|
||||
"windows_x86_64_gnullvm 0.52.4",
|
||||
"windows_x86_64_msvc 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
22
src/mem-agent/Cargo.toml
Normal file
22
src/mem-agent/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "mem-agent"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
slog = "2.5.2"
|
||||
slog-scope = "4.1.2"
|
||||
anyhow = "1.0"
|
||||
page_size = "0.6"
|
||||
chrono = "0.4"
|
||||
tokio = { version = "1.33", features = ["full"] }
|
||||
async-trait = "0.1"
|
||||
lazy_static = "1.4"
|
||||
nix = "0.23.2"
|
||||
|
||||
[dev-dependencies]
|
||||
maplit = "1.0"
|
||||
slog-term = "2.9.0"
|
||||
slog-async = "2.7"
|
||||
once_cell = "1.9.0"
|
||||
|
6
src/mem-agent/Makefile
Normal file
6
src/mem-agent/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# Copyright (C) 2024 Ant group. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
default:
|
||||
cd example; cargo build --examples --target x86_64-unknown-linux-musl
|
1658
src/mem-agent/example/Cargo.lock
generated
Normal file
1658
src/mem-agent/example/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
src/mem-agent/example/Cargo.toml
Normal file
36
src/mem-agent/example/Cargo.toml
Normal file
@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "mem-agent-bin"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
slog = "2.5.2"
|
||||
slog-scope = "4.1.2"
|
||||
slog-term = "2.9.0"
|
||||
slog-async = "2.7"
|
||||
structopt = "0.3"
|
||||
anyhow = "1.0"
|
||||
libc = "0.2"
|
||||
page_size = "0.6"
|
||||
chrono = "0.4"
|
||||
maplit = "1.0"
|
||||
ttrpc = { version = "0.8", features = ["async"] }
|
||||
tokio = { version = "1.33", features = ["full"] }
|
||||
async-trait = "0.1"
|
||||
byteorder = "1.5"
|
||||
protobuf = "3.1"
|
||||
lazy_static = "1.4"
|
||||
# Rust 1.68 doesn't support 0.5.9
|
||||
home = "=0.5.5"
|
||||
mem-agent = { path = "../" }
|
||||
|
||||
[[example]]
|
||||
name = "mem-agent-srv"
|
||||
path = "./srv.rs"
|
||||
|
||||
[[example]]
|
||||
name = "mem-agent-ctl"
|
||||
path = "./ctl.rs"
|
||||
|
||||
[build-dependencies]
|
||||
ttrpc-codegen = "0.4"
|
29
src/mem-agent/example/build.rs
Normal file
29
src/mem-agent/example/build.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use ttrpc_codegen::{Codegen, Customize, ProtobufCustomize};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let protos = vec![
|
||||
"protocols/protos/mem-agent.proto",
|
||||
"protocols/protos/google/protobuf/empty.proto",
|
||||
"protocols/protos/google/protobuf/timestamp.proto",
|
||||
];
|
||||
|
||||
let protobuf_customized = ProtobufCustomize::default().gen_mod_rs(false);
|
||||
|
||||
Codegen::new()
|
||||
.out_dir("protocols/")
|
||||
.inputs(&protos)
|
||||
.include("protocols/protos/")
|
||||
.rust_protobuf()
|
||||
.customize(Customize {
|
||||
async_all: true,
|
||||
..Default::default()
|
||||
})
|
||||
.rust_protobuf_customize(protobuf_customized.clone())
|
||||
.run()?;
|
||||
|
||||
Ok(())
|
||||
}
|
79
src/mem-agent/example/ctl.rs
Normal file
79
src/mem-agent/example/ctl.rs
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod protocols;
|
||||
mod share;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use protocols::empty;
|
||||
use protocols::mem_agent_ttrpc;
|
||||
use share::option::{CompactSetOption, MemcgSetOption};
|
||||
use structopt::StructOpt;
|
||||
use ttrpc::r#async::Client;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
enum Command {
|
||||
#[structopt(name = "memcgstatus", about = "get memory cgroup status")]
|
||||
MemcgStatus,
|
||||
|
||||
#[structopt(name = "memcgset", about = "set memory cgroup")]
|
||||
MemcgSet(MemcgSetOption),
|
||||
|
||||
#[structopt(name = "compactset", about = "set compact")]
|
||||
CompactSet(CompactSetOption),
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "mem-agent-ctl", about = "Memory agent controler")]
|
||||
struct Opt {
|
||||
#[structopt(long, default_value = "unix:///var/run/mem-agent.sock")]
|
||||
addr: String,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let opt = Opt::from_args();
|
||||
|
||||
// setup client
|
||||
let c = Client::connect(&opt.addr).unwrap();
|
||||
let client = mem_agent_ttrpc::ControlClient::new(c.clone());
|
||||
|
||||
match opt.command {
|
||||
Command::MemcgStatus => {
|
||||
let mss = client
|
||||
.memcg_status(ttrpc::context::with_timeout(0), &empty::Empty::new())
|
||||
.await
|
||||
.map_err(|e| anyhow!("client.memcg_status fail: {}", e))?;
|
||||
for mcg in mss.mem_cgroups {
|
||||
println!("{:?}", mcg);
|
||||
for (numa_id, n) in mcg.numa {
|
||||
if let Some(t) = n.last_inc_time.into_option() {
|
||||
println!("{} {:?}", numa_id, share::misc::timestamp_to_datetime(t)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Command::MemcgSet(c) => {
|
||||
let config = c.to_rpc_memcg_config();
|
||||
client
|
||||
.memcg_set(ttrpc::context::with_timeout(0), &config)
|
||||
.await
|
||||
.map_err(|e| anyhow!("client.memcg_status fail: {}", e))?;
|
||||
}
|
||||
|
||||
Command::CompactSet(c) => {
|
||||
let config = c.to_rpc_compact_config();
|
||||
client
|
||||
.compact_set(ttrpc::context::with_timeout(0), &config)
|
||||
.await
|
||||
.map_err(|e| anyhow!("client.memcg_status fail: {}", e))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
8
src/mem-agent/example/protocols/mod.rs
Normal file
8
src/mem-agent/example/protocols/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod empty;
|
||||
pub mod mem_agent;
|
||||
pub mod mem_agent_ttrpc;
|
||||
pub mod timestamp;
|
@ -0,0 +1,52 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package google.protobuf;
|
||||
|
||||
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
|
||||
option go_package = "types";
|
||||
option java_package = "com.google.protobuf";
|
||||
option java_outer_classname = "EmptyProto";
|
||||
option java_multiple_files = true;
|
||||
option objc_class_prefix = "GPB";
|
||||
option cc_enable_arenas = true;
|
||||
|
||||
// A generic empty message that you can re-use to avoid defining duplicated
|
||||
// empty messages in your APIs. A typical example is to use it as the request
|
||||
// or the response type of an API method. For instance:
|
||||
//
|
||||
// service Foo {
|
||||
// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||
// }
|
||||
//
|
||||
// The JSON representation for `Empty` is empty JSON object `{}`.
|
||||
message Empty {}
|
@ -0,0 +1,138 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package google.protobuf;
|
||||
|
||||
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
|
||||
option cc_enable_arenas = true;
|
||||
option go_package = "github.com/golang/protobuf/ptypes/timestamp";
|
||||
option java_package = "com.google.protobuf";
|
||||
option java_outer_classname = "TimestampProto";
|
||||
option java_multiple_files = true;
|
||||
option objc_class_prefix = "GPB";
|
||||
|
||||
// A Timestamp represents a point in time independent of any time zone or local
|
||||
// calendar, encoded as a count of seconds and fractions of seconds at
|
||||
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
|
||||
// January 1, 1970, in the proleptic Gregorian calendar which extends the
|
||||
// Gregorian calendar backwards to year one.
|
||||
//
|
||||
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
|
||||
// second table is needed for interpretation, using a [24-hour linear
|
||||
// smear](https://developers.google.com/time/smear).
|
||||
//
|
||||
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
|
||||
// restricting to that range, we ensure that we can convert to and from [RFC
|
||||
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
|
||||
//
|
||||
// # Examples
|
||||
//
|
||||
// Example 1: Compute Timestamp from POSIX `time()`.
|
||||
//
|
||||
// Timestamp timestamp;
|
||||
// timestamp.set_seconds(time(NULL));
|
||||
// timestamp.set_nanos(0);
|
||||
//
|
||||
// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
|
||||
//
|
||||
// struct timeval tv;
|
||||
// gettimeofday(&tv, NULL);
|
||||
//
|
||||
// Timestamp timestamp;
|
||||
// timestamp.set_seconds(tv.tv_sec);
|
||||
// timestamp.set_nanos(tv.tv_usec * 1000);
|
||||
//
|
||||
// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
|
||||
//
|
||||
// FILETIME ft;
|
||||
// GetSystemTimeAsFileTime(&ft);
|
||||
// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||
//
|
||||
// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
|
||||
// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
|
||||
// Timestamp timestamp;
|
||||
// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
|
||||
// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
|
||||
//
|
||||
// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
|
||||
//
|
||||
// long millis = System.currentTimeMillis();
|
||||
//
|
||||
// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
|
||||
// .setNanos((int) ((millis % 1000) * 1000000)).build();
|
||||
//
|
||||
//
|
||||
// Example 5: Compute Timestamp from current time in Python.
|
||||
//
|
||||
// timestamp = Timestamp()
|
||||
// timestamp.GetCurrentTime()
|
||||
//
|
||||
// # JSON Mapping
|
||||
//
|
||||
// In JSON format, the Timestamp type is encoded as a string in the
|
||||
// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
|
||||
// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
|
||||
// where {year} is always expressed using four digits while {month}, {day},
|
||||
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
|
||||
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
||||
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
||||
// is required. A proto3 JSON serializer should always use UTC (as indicated by
|
||||
// "Z") when printing the Timestamp type and a proto3 JSON parser should be
|
||||
// able to accept both UTC and other timezones (as indicated by an offset).
|
||||
//
|
||||
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
|
||||
// 01:30 UTC on January 15, 2017.
|
||||
//
|
||||
// In JavaScript, one can convert a Date object to this format using the
|
||||
// standard
|
||||
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
|
||||
// method. In Python, a standard `datetime.datetime` object can be converted
|
||||
// to this format using
|
||||
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
|
||||
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
|
||||
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
|
||||
// ) to obtain a formatter capable of generating timestamps in this format.
|
||||
//
|
||||
//
|
||||
message Timestamp {
|
||||
// Represents seconds of UTC time since Unix epoch
|
||||
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
||||
// 9999-12-31T23:59:59Z inclusive.
|
||||
int64 seconds = 1;
|
||||
|
||||
// Non-negative fractions of a second at nanosecond resolution. Negative
|
||||
// second values with fractions must still have non-negative nanos values
|
||||
// that count forward in time. Must be from 0 to 999,999,999
|
||||
// inclusive.
|
||||
int32 nanos = 2;
|
||||
}
|
66
src/mem-agent/example/protocols/protos/mem-agent.proto
Normal file
66
src/mem-agent/example/protocols/protos/mem-agent.proto
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package MemAgent;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
service Control {
|
||||
rpc MemcgStatus(google.protobuf.Empty) returns (MemcgStatusReply);
|
||||
rpc MemcgSet(MemcgConfig) returns (google.protobuf.Empty);
|
||||
rpc CompactSet(CompactConfig) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message EvictionCount {
|
||||
uint64 page = 1;
|
||||
uint64 no_min_lru_file = 2;
|
||||
uint64 min_lru_inc = 3;
|
||||
uint64 other_error = 4;
|
||||
uint64 error = 5;
|
||||
uint64 psi_exceeds_limit = 6;
|
||||
}
|
||||
|
||||
message StatusNuma {
|
||||
google.protobuf.Timestamp last_inc_time = 1;
|
||||
uint64 max_seq = 2;
|
||||
uint64 min_seq = 3;
|
||||
uint64 run_aging_count = 4;
|
||||
EvictionCount eviction_count = 5;
|
||||
}
|
||||
|
||||
message MemCgroup {
|
||||
uint32 id = 1;
|
||||
uint64 ino = 2;
|
||||
string path = 3;
|
||||
uint64 sleep_psi_exceeds_limit = 4;
|
||||
map<uint32, StatusNuma> numa = 5;
|
||||
}
|
||||
|
||||
message MemcgStatusReply {
|
||||
repeated MemCgroup mem_cgroups = 1;
|
||||
}
|
||||
|
||||
message MemcgConfig {
|
||||
optional bool disabled = 1;
|
||||
optional bool swap = 2;
|
||||
optional uint32 swappiness_max = 3;
|
||||
optional uint64 period_secs = 4;
|
||||
optional uint32 period_psi_percent_limit = 5;
|
||||
optional uint32 eviction_psi_percent_limit = 6;
|
||||
optional uint64 eviction_run_aging_count_min = 7;
|
||||
}
|
||||
|
||||
message CompactConfig {
|
||||
optional bool disabled = 1;
|
||||
optional uint64 period_secs = 2;
|
||||
optional uint32 period_psi_percent_limit = 3;
|
||||
optional uint32 compact_psi_percent_limit = 4;
|
||||
optional int64 compact_sec_max = 5;
|
||||
optional uint32 compact_order = 6;
|
||||
optional uint64 compact_threshold = 7;
|
||||
optional uint64 compact_force_times = 8;
|
||||
}
|
29
src/mem-agent/example/share/misc.rs
Normal file
29
src/mem-agent/example/share/misc.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, LocalResult, TimeZone, Utc};
|
||||
use protobuf::well_known_types::timestamp::Timestamp;
|
||||
|
||||
pub fn datatime_to_timestamp(dt: DateTime<Utc>) -> Timestamp {
|
||||
let seconds = dt.timestamp();
|
||||
let nanos = dt.timestamp_subsec_nanos();
|
||||
|
||||
Timestamp {
|
||||
seconds,
|
||||
nanos: nanos as i32,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn timestamp_to_datetime(timestamp: Timestamp) -> Result<DateTime<Utc>> {
|
||||
let seconds = timestamp.seconds;
|
||||
let nanos = timestamp.nanos;
|
||||
|
||||
match Utc.timestamp_opt(seconds, nanos as u32) {
|
||||
LocalResult::Single(t) => Ok(t),
|
||||
_ => Err(anyhow!("Utc.timestamp_opt {} fail", timestamp)),
|
||||
}
|
||||
}
|
7
src/mem-agent/example/share/mod.rs
Normal file
7
src/mem-agent/example/share/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod misc;
|
||||
pub mod option;
|
||||
pub mod rpc;
|
146
src/mem-agent/example/share/option.rs
Normal file
146
src/mem-agent/example/share/option.rs
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::protocols::mem_agent as rpc;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct MemcgSetOption {
|
||||
#[structopt(long)]
|
||||
memcg_disabled: Option<bool>,
|
||||
#[structopt(long)]
|
||||
memcg_swap: Option<bool>,
|
||||
#[structopt(long)]
|
||||
memcg_swappiness_max: Option<u8>,
|
||||
#[structopt(long)]
|
||||
memcg_period_secs: Option<u64>,
|
||||
#[structopt(long)]
|
||||
memcg_period_psi_percent_limit: Option<u8>,
|
||||
#[structopt(long)]
|
||||
memcg_eviction_psi_percent_limit: Option<u8>,
|
||||
#[structopt(long)]
|
||||
memcg_eviction_run_aging_count_min: Option<u64>,
|
||||
}
|
||||
|
||||
impl MemcgSetOption {
|
||||
#[allow(dead_code)]
|
||||
pub fn to_rpc_memcg_config(&self) -> rpc::MemcgConfig {
|
||||
let config = rpc::MemcgConfig {
|
||||
disabled: self.memcg_disabled,
|
||||
swap: self.memcg_swap,
|
||||
swappiness_max: self.memcg_swappiness_max.map(|v| v as u32),
|
||||
period_secs: self.memcg_period_secs,
|
||||
period_psi_percent_limit: self.memcg_period_psi_percent_limit.map(|v| v as u32),
|
||||
eviction_psi_percent_limit: self.memcg_eviction_psi_percent_limit.map(|v| v as u32),
|
||||
eviction_run_aging_count_min: self.memcg_eviction_run_aging_count_min,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn to_mem_agent_memcg_config(&self) -> mem_agent::memcg::Config {
|
||||
let mut config = mem_agent::memcg::Config {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(v) = self.memcg_disabled {
|
||||
config.disabled = v;
|
||||
}
|
||||
if let Some(v) = self.memcg_swap {
|
||||
config.swap = v;
|
||||
}
|
||||
if let Some(v) = self.memcg_swappiness_max {
|
||||
config.swappiness_max = v;
|
||||
}
|
||||
if let Some(v) = self.memcg_period_secs {
|
||||
config.period_secs = v;
|
||||
}
|
||||
if let Some(v) = self.memcg_period_psi_percent_limit {
|
||||
config.period_psi_percent_limit = v;
|
||||
}
|
||||
if let Some(v) = self.memcg_eviction_psi_percent_limit {
|
||||
config.eviction_psi_percent_limit = v;
|
||||
}
|
||||
if let Some(v) = self.memcg_eviction_run_aging_count_min {
|
||||
config.eviction_run_aging_count_min = v;
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct CompactSetOption {
|
||||
#[structopt(long)]
|
||||
compact_disabled: Option<bool>,
|
||||
#[structopt(long)]
|
||||
compact_period_secs: Option<u64>,
|
||||
#[structopt(long)]
|
||||
compact_period_psi_percent_limit: Option<u8>,
|
||||
#[structopt(long)]
|
||||
compact_psi_percent_limit: Option<u8>,
|
||||
#[structopt(long)]
|
||||
compact_sec_max: Option<i64>,
|
||||
#[structopt(long)]
|
||||
compact_order: Option<u8>,
|
||||
#[structopt(long)]
|
||||
compact_threshold: Option<u64>,
|
||||
#[structopt(long)]
|
||||
compact_force_times: Option<u64>,
|
||||
}
|
||||
|
||||
impl CompactSetOption {
|
||||
#[allow(dead_code)]
|
||||
pub fn to_rpc_compact_config(&self) -> rpc::CompactConfig {
|
||||
let config = rpc::CompactConfig {
|
||||
disabled: self.compact_disabled,
|
||||
period_secs: self.compact_period_secs,
|
||||
period_psi_percent_limit: self.compact_period_psi_percent_limit.map(|v| v as u32),
|
||||
compact_psi_percent_limit: self.compact_psi_percent_limit.map(|v| v as u32),
|
||||
compact_sec_max: self.compact_sec_max,
|
||||
compact_order: self.compact_order.map(|v| v as u32),
|
||||
compact_threshold: self.compact_threshold,
|
||||
compact_force_times: self.compact_force_times,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn to_mem_agent_compact_config(&self) -> mem_agent::compact::Config {
|
||||
let mut config = mem_agent::compact::Config {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(v) = self.compact_disabled {
|
||||
config.disabled = v;
|
||||
}
|
||||
if let Some(v) = self.compact_period_secs {
|
||||
config.period_secs = v;
|
||||
}
|
||||
if let Some(v) = self.compact_period_psi_percent_limit {
|
||||
config.period_psi_percent_limit = v;
|
||||
}
|
||||
if let Some(v) = self.compact_psi_percent_limit {
|
||||
config.compact_psi_percent_limit = v;
|
||||
}
|
||||
if let Some(v) = self.compact_sec_max {
|
||||
config.compact_sec_max = v;
|
||||
}
|
||||
if let Some(v) = self.compact_order {
|
||||
config.compact_order = v;
|
||||
}
|
||||
if let Some(v) = self.compact_threshold {
|
||||
config.compact_threshold = v;
|
||||
}
|
||||
if let Some(v) = self.compact_force_times {
|
||||
config.compact_force_times = v;
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
}
|
221
src/mem-agent/example/share/rpc.rs
Normal file
221
src/mem-agent/example/share/rpc.rs
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::protocols::mem_agent as rpc_mem_agent;
|
||||
use crate::protocols::{empty, mem_agent_ttrpc};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use mem_agent::{agent, compact, memcg};
|
||||
use slog_scope::{error, info};
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
use ttrpc::asynchronous::Server;
|
||||
use ttrpc::error::Error;
|
||||
use ttrpc::proto::Code;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MyControl {
|
||||
agent: agent::MemAgent,
|
||||
}
|
||||
|
||||
impl MyControl {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(agent: agent::MemAgent) -> Self {
|
||||
Self { agent }
|
||||
}
|
||||
}
|
||||
|
||||
fn mem_cgroup_to_mem_cgroup_rpc(mcg: &memcg::MemCgroup) -> rpc_mem_agent::MemCgroup {
|
||||
rpc_mem_agent::MemCgroup {
|
||||
id: mcg.id as u32,
|
||||
ino: mcg.ino as u64,
|
||||
path: mcg.path.clone(),
|
||||
sleep_psi_exceeds_limit: mcg.sleep_psi_exceeds_limit,
|
||||
numa: mcg
|
||||
.numa
|
||||
.iter()
|
||||
.map(|(numa_id, n)| {
|
||||
(
|
||||
*numa_id,
|
||||
rpc_mem_agent::StatusNuma {
|
||||
last_inc_time: protobuf::MessageField::some(
|
||||
crate::share::misc::datatime_to_timestamp(n.last_inc_time),
|
||||
),
|
||||
max_seq: n.max_seq,
|
||||
min_seq: n.min_seq,
|
||||
run_aging_count: n.run_aging_count,
|
||||
eviction_count: protobuf::MessageField::some(
|
||||
rpc_mem_agent::EvictionCount {
|
||||
page: n.eviction_count.page,
|
||||
no_min_lru_file: n.eviction_count.no_min_lru_file,
|
||||
min_lru_inc: n.eviction_count.min_lru_inc,
|
||||
other_error: n.eviction_count.other_error,
|
||||
error: n.eviction_count.error,
|
||||
psi_exceeds_limit: n.eviction_count.psi_exceeds_limit,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn mem_cgroups_to_memcg_status_reply(
|
||||
mgs: Vec<memcg::MemCgroup>,
|
||||
) -> rpc_mem_agent::MemcgStatusReply {
|
||||
let mem_cgroups: Vec<rpc_mem_agent::MemCgroup> = mgs
|
||||
.iter()
|
||||
.map(|x| mem_cgroup_to_mem_cgroup_rpc(&x))
|
||||
.collect();
|
||||
|
||||
rpc_mem_agent::MemcgStatusReply {
|
||||
mem_cgroups,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn memcgconfig_to_memcg_optionconfig(mc: &rpc_mem_agent::MemcgConfig) -> memcg::OptionConfig {
|
||||
let moc = memcg::OptionConfig {
|
||||
disabled: mc.disabled,
|
||||
swap: mc.swap,
|
||||
swappiness_max: mc.swappiness_max.map(|val| val as u8),
|
||||
period_secs: mc.period_secs,
|
||||
period_psi_percent_limit: mc.period_psi_percent_limit.map(|val| val as u8),
|
||||
eviction_psi_percent_limit: mc.eviction_psi_percent_limit.map(|val| val as u8),
|
||||
eviction_run_aging_count_min: mc.eviction_run_aging_count_min,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
moc
|
||||
}
|
||||
|
||||
fn compactconfig_to_compact_optionconfig(
|
||||
cc: &rpc_mem_agent::CompactConfig,
|
||||
) -> compact::OptionConfig {
|
||||
let coc = compact::OptionConfig {
|
||||
disabled: cc.disabled,
|
||||
period_secs: cc.period_secs,
|
||||
period_psi_percent_limit: cc.period_psi_percent_limit.map(|val| val as u8),
|
||||
compact_psi_percent_limit: cc.compact_psi_percent_limit.map(|val| val as u8),
|
||||
compact_sec_max: cc.compact_sec_max,
|
||||
compact_order: cc.compact_order.map(|val| val as u8),
|
||||
compact_threshold: cc.compact_threshold,
|
||||
compact_force_times: cc.compact_force_times,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
coc
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl mem_agent_ttrpc::Control for MyControl {
|
||||
async fn memcg_status(
|
||||
&self,
|
||||
_ctx: &::ttrpc::r#async::TtrpcContext,
|
||||
_: empty::Empty,
|
||||
) -> ::ttrpc::Result<rpc_mem_agent::MemcgStatusReply> {
|
||||
Ok(mem_cgroups_to_memcg_status_reply(
|
||||
self.agent.memcg_status_async().await.map_err(|e| {
|
||||
let estr = format!("agent.memcg_status_async fail: {}", e);
|
||||
error!("{}", estr);
|
||||
Error::RpcStatus(ttrpc::get_status(Code::INTERNAL, estr))
|
||||
})?,
|
||||
))
|
||||
}
|
||||
|
||||
async fn memcg_set(
|
||||
&self,
|
||||
_ctx: &::ttrpc::r#async::TtrpcContext,
|
||||
mc: rpc_mem_agent::MemcgConfig,
|
||||
) -> ::ttrpc::Result<empty::Empty> {
|
||||
self.agent
|
||||
.memcg_set_config_async(memcgconfig_to_memcg_optionconfig(&mc))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let estr = format!("agent.memcg_set_config_async fail: {}", e);
|
||||
error!("{}", estr);
|
||||
Error::RpcStatus(ttrpc::get_status(Code::INTERNAL, estr))
|
||||
})?;
|
||||
Ok(empty::Empty::new())
|
||||
}
|
||||
|
||||
async fn compact_set(
|
||||
&self,
|
||||
_ctx: &::ttrpc::r#async::TtrpcContext,
|
||||
cc: rpc_mem_agent::CompactConfig,
|
||||
) -> ::ttrpc::Result<empty::Empty> {
|
||||
self.agent
|
||||
.compact_set_config_async(compactconfig_to_compact_optionconfig(&cc))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let estr = format!("agent.compact_set_config_async fail: {}", e);
|
||||
error!("{}", estr);
|
||||
Error::RpcStatus(ttrpc::get_status(Code::INTERNAL, estr))
|
||||
})?;
|
||||
Ok(empty::Empty::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[tokio::main]
|
||||
pub async fn rpc_loop(agent: agent::MemAgent, addr: String) -> Result<()> {
|
||||
let path = addr
|
||||
.strip_prefix("unix://")
|
||||
.ok_or(anyhow!("format of addr {} is not right", addr))?;
|
||||
if std::path::Path::new(path).exists() {
|
||||
return Err(anyhow!("addr {} is exist", addr));
|
||||
}
|
||||
|
||||
let control = MyControl::new(agent);
|
||||
let c = Box::new(control) as Box<dyn mem_agent_ttrpc::Control + Send + Sync>;
|
||||
let c = Arc::new(c);
|
||||
let service = mem_agent_ttrpc::create_control(c);
|
||||
|
||||
let mut server = Server::new().bind(&addr).unwrap().register_service(service);
|
||||
|
||||
let metadata = fs::metadata(path).map_err(|e| anyhow!("fs::metadata {} fail: {}", path, e))?;
|
||||
let mut permissions = metadata.permissions();
|
||||
permissions.set_mode(0o600);
|
||||
fs::set_permissions(path, permissions)
|
||||
.map_err(|e| anyhow!("fs::set_permissions {} fail: {}", path, e))?;
|
||||
|
||||
let mut interrupt = signal(SignalKind::interrupt())
|
||||
.map_err(|e| anyhow!("signal(SignalKind::interrupt()) fail: {}", e))?;
|
||||
let mut quit = signal(SignalKind::quit())
|
||||
.map_err(|e| anyhow!("signal(SignalKind::quit()) fail: {}", e))?;
|
||||
let mut terminate = signal(SignalKind::terminate())
|
||||
.map_err(|e| anyhow!("signal(SignalKind::terminate()) fail: {}", e))?;
|
||||
server
|
||||
.start()
|
||||
.await
|
||||
.map_err(|e| anyhow!("server.start() fail: {}", e))?;
|
||||
|
||||
tokio::select! {
|
||||
_ = interrupt.recv() => {
|
||||
info!("mem-agent: interrupt shutdown");
|
||||
}
|
||||
|
||||
_ = quit.recv() => {
|
||||
info!("mem-agent: quit shutdown");
|
||||
}
|
||||
|
||||
_ = terminate.recv() => {
|
||||
info!("mem-agent: terminate shutdown");
|
||||
}
|
||||
};
|
||||
|
||||
server
|
||||
.shutdown()
|
||||
.await
|
||||
.map_err(|e| anyhow!("server.shutdown() fail: {}", e))?;
|
||||
fs::remove_file(&path).map_err(|e| anyhow!("fs::remove_file {} fail: {}", path, e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
95
src/mem-agent/example/srv.rs
Normal file
95
src/mem-agent/example/srv.rs
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use share::option::{CompactSetOption, MemcgSetOption};
|
||||
use slog::{Drain, Level, Logger};
|
||||
use slog_async;
|
||||
use slog_scope::set_global_logger;
|
||||
use slog_scope::{error, info};
|
||||
use slog_term;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::BufWriter;
|
||||
use structopt::StructOpt;
|
||||
|
||||
mod protocols;
|
||||
mod share;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "mem-agent", about = "Memory agent")]
|
||||
struct Opt {
|
||||
#[structopt(long, default_value = "unix:///var/run/mem-agent.sock")]
|
||||
addr: String,
|
||||
#[structopt(long)]
|
||||
log_file: Option<String>,
|
||||
#[structopt(long, default_value = "trace", parse(try_from_str = parse_slog_level))]
|
||||
log_level: Level,
|
||||
#[structopt(flatten)]
|
||||
memcg: MemcgSetOption,
|
||||
#[structopt(flatten)]
|
||||
compact: CompactSetOption,
|
||||
}
|
||||
|
||||
fn parse_slog_level(src: &str) -> Result<Level, String> {
|
||||
match src.to_lowercase().as_str() {
|
||||
"trace" => Ok(Level::Trace),
|
||||
"debug" => Ok(Level::Debug),
|
||||
"info" => Ok(Level::Info),
|
||||
"warning" => Ok(Level::Warning),
|
||||
"warn" => Ok(Level::Warning),
|
||||
"error" => Ok(Level::Error),
|
||||
_ => Err(format!("Invalid log level: {}", src)),
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_logging(opt: &Opt) -> Result<slog_scope::GlobalLoggerGuard> {
|
||||
let drain = if let Some(f) = &opt.log_file {
|
||||
let log_file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(f)
|
||||
.map_err(|e| anyhow!("Open log file {} fail: {}", f, e))?;
|
||||
let buffered = BufWriter::new(log_file);
|
||||
let decorator = slog_term::PlainDecorator::new(buffered);
|
||||
let drain = slog_term::CompactFormat::new(decorator)
|
||||
.build()
|
||||
.filter_level(opt.log_level)
|
||||
.fuse();
|
||||
slog_async::Async::new(drain).build().fuse()
|
||||
} else {
|
||||
let decorator = slog_term::TermDecorator::new().stderr().build();
|
||||
let drain = slog_term::CompactFormat::new(decorator)
|
||||
.build()
|
||||
.filter_level(opt.log_level)
|
||||
.fuse();
|
||||
slog_async::Async::new(drain).build().fuse()
|
||||
};
|
||||
|
||||
let logger = Logger::root(drain, slog::o!());
|
||||
Ok(set_global_logger(logger.clone()))
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Check opt
|
||||
let opt = Opt::from_args();
|
||||
|
||||
let _ = setup_logging(&opt).map_err(|e| anyhow!("setup_logging fail: {}", e))?;
|
||||
|
||||
let memcg_config = opt.memcg.to_mem_agent_memcg_config();
|
||||
let compact_config = opt.compact.to_mem_agent_compact_config();
|
||||
|
||||
let (ma, _rt) = mem_agent::agent::MemAgent::new(memcg_config, compact_config)
|
||||
.map_err(|e| anyhow!("MemAgent::new fail: {}", e))?;
|
||||
|
||||
info!("MemAgent started");
|
||||
|
||||
share::rpc::rpc_loop(ma, opt.addr).map_err(|e| {
|
||||
let estr = format!("rpc::rpc_loop fail: {}", e);
|
||||
error!("{}", estr);
|
||||
anyhow!("{}", estr)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
378
src/mem-agent/src/agent.rs
Normal file
378
src/mem-agent/src/agent.rs
Normal file
@ -0,0 +1,378 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::compact;
|
||||
use crate::memcg::{self, MemCgroup};
|
||||
use crate::{error, info};
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::thread;
|
||||
use tokio::runtime::{Builder, Runtime};
|
||||
use tokio::select;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::time::{sleep, Duration, Instant};
|
||||
|
||||
const AGENT_WORK_ERROR_SLEEP_SECS: u64 = 5 * 60;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AgentCmd {
|
||||
MemcgStatus,
|
||||
MemcgSet(memcg::OptionConfig),
|
||||
CompactSet(compact::OptionConfig),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum AgentReturn {
|
||||
Ok,
|
||||
Err(anyhow::Error),
|
||||
MemcgStatus(Vec<memcg::MemCgroup>),
|
||||
}
|
||||
|
||||
async fn handle_agent_cmd(
|
||||
cmd: AgentCmd,
|
||||
ret_tx: oneshot::Sender<AgentReturn>,
|
||||
memcg: &mut memcg::MemCG,
|
||||
comp: &mut compact::Compact,
|
||||
) -> Result<bool> {
|
||||
#[allow(unused_assignments)]
|
||||
let mut ret_msg = AgentReturn::Ok;
|
||||
|
||||
let need_reset_mas = match cmd {
|
||||
AgentCmd::MemcgStatus => {
|
||||
ret_msg = AgentReturn::MemcgStatus(memcg.get_status().await);
|
||||
false
|
||||
}
|
||||
AgentCmd::MemcgSet(opt) => memcg.set_config(opt).await,
|
||||
AgentCmd::CompactSet(opt) => comp.set_config(opt).await,
|
||||
};
|
||||
|
||||
ret_tx
|
||||
.send(ret_msg)
|
||||
.map_err(|e| anyhow!("ret_tx.send failed: {:?}", e))?;
|
||||
|
||||
Ok(need_reset_mas)
|
||||
}
|
||||
|
||||
fn get_remaining_tokio_duration(memcg: &memcg::MemCG, comp: &compact::Compact) -> Duration {
|
||||
let memcg_d = memcg.get_remaining_tokio_duration();
|
||||
let comp_d = comp.get_remaining_tokio_duration();
|
||||
|
||||
if memcg_d > comp_d {
|
||||
comp_d
|
||||
} else {
|
||||
memcg_d
|
||||
}
|
||||
}
|
||||
|
||||
async fn async_get_remaining_tokio_duration(
|
||||
memcg: &memcg::MemCG,
|
||||
comp: &compact::Compact,
|
||||
) -> Duration {
|
||||
let memcg_f = memcg.async_get_remaining_tokio_duration();
|
||||
let comp_f = comp.async_get_remaining_tokio_duration();
|
||||
|
||||
let memcg_d = memcg_f.await;
|
||||
let comp_d = comp_f.await;
|
||||
|
||||
if memcg_d > comp_d {
|
||||
comp_d
|
||||
} else {
|
||||
memcg_d
|
||||
}
|
||||
}
|
||||
|
||||
fn agent_work(mut memcg: memcg::MemCG, mut comp: compact::Compact) -> Result<Duration> {
|
||||
let memcg_need_reset = if memcg.need_work() {
|
||||
info!("memcg.work start");
|
||||
memcg
|
||||
.work()
|
||||
.map_err(|e| anyhow!("memcg.work failed: {}", e))?;
|
||||
info!("memcg.work stop");
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let compact_need_reset = if comp.need_work() {
|
||||
info!("compact.work start");
|
||||
comp.work()
|
||||
.map_err(|e| anyhow!("comp.work failed: {}", e))?;
|
||||
info!("compact.work stop");
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if memcg_need_reset {
|
||||
memcg.reset_timer();
|
||||
}
|
||||
if compact_need_reset {
|
||||
comp.reset_timer();
|
||||
}
|
||||
|
||||
Ok(get_remaining_tokio_duration(&memcg, &comp))
|
||||
}
|
||||
|
||||
struct MemAgentSleep {
|
||||
duration: Duration,
|
||||
start_wait_time: Instant,
|
||||
timeout: bool,
|
||||
}
|
||||
|
||||
impl MemAgentSleep {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
duration: Duration::MAX,
|
||||
start_wait_time: Instant::now(),
|
||||
timeout: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_timeout(&mut self) {
|
||||
self.duration = Duration::MAX;
|
||||
self.timeout = true;
|
||||
}
|
||||
|
||||
fn set_sleep(&mut self, d: Duration) {
|
||||
self.duration = d;
|
||||
self.start_wait_time = Instant::now();
|
||||
}
|
||||
|
||||
/* Return true if timeout */
|
||||
fn refresh(&mut self) -> bool {
|
||||
if self.duration != Duration::MAX {
|
||||
let elapsed = self.start_wait_time.elapsed();
|
||||
if self.duration > elapsed {
|
||||
self.duration -= elapsed;
|
||||
} else {
|
||||
/* timeout */
|
||||
self.set_timeout();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
async fn mem_agent_loop(
|
||||
mut cmd_rx: mpsc::Receiver<(AgentCmd, oneshot::Sender<AgentReturn>)>,
|
||||
mut memcg: memcg::MemCG,
|
||||
mut comp: compact::Compact,
|
||||
) -> Result<()> {
|
||||
let (work_ret_tx, mut work_ret_rx) = mpsc::channel(2);
|
||||
// the time that wait to next.
|
||||
let mut mas = MemAgentSleep::new();
|
||||
|
||||
loop {
|
||||
if mas.timeout {
|
||||
let thread_memcg = memcg.clone();
|
||||
let thread_comp = comp.clone();
|
||||
let thread_work_ret_tx = work_ret_tx.clone();
|
||||
thread::spawn(move || {
|
||||
info!("agent work thread start");
|
||||
let d = agent_work(thread_memcg, thread_comp).unwrap_or_else(|err| {
|
||||
error!("agent work thread fail {}", err);
|
||||
Duration::from_secs(AGENT_WORK_ERROR_SLEEP_SECS)
|
||||
});
|
||||
if let Err(e) = thread_work_ret_tx.blocking_send(d) {
|
||||
error!("work_ret_tx.blocking_send failed: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
mas.timeout = false;
|
||||
} else {
|
||||
if mas.refresh() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
info!("mem_agent_loop wait timeout {:?}", mas.duration);
|
||||
select! {
|
||||
Some((cmd, ret_tx)) = cmd_rx.recv() => {
|
||||
if handle_agent_cmd(cmd, ret_tx, &mut memcg, &mut comp).await.map_err(|e| anyhow!("handle_agent_cmd failed: {}", e))? && !mas.timeout{
|
||||
mas.set_sleep(async_get_remaining_tokio_duration(&memcg, &comp).await);
|
||||
}
|
||||
}
|
||||
d = work_ret_rx.recv() => {
|
||||
info!("agent work thread stop");
|
||||
mas.set_sleep(d.unwrap_or(Duration::from_secs(AGENT_WORK_ERROR_SLEEP_SECS)));
|
||||
}
|
||||
_ = async {
|
||||
sleep(mas.duration).await;
|
||||
} => {
|
||||
mas.set_timeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MemAgent {
|
||||
cmd_tx: mpsc::Sender<(AgentCmd, oneshot::Sender<AgentReturn>)>,
|
||||
}
|
||||
|
||||
impl MemAgent {
|
||||
pub fn new(
|
||||
memcg_config: memcg::Config,
|
||||
compact_config: compact::Config,
|
||||
) -> Result<(Self, Runtime)> {
|
||||
let mg = memcg::MemCG::new(memcg_config)
|
||||
.map_err(|e| anyhow!("memcg::MemCG::new fail: {}", e))?;
|
||||
|
||||
let comp = compact::Compact::new(compact_config)
|
||||
.map_err(|e| anyhow!("compact::Compact::new fail: {}", e))?;
|
||||
|
||||
let (cmd_tx, cmd_rx) = mpsc::channel(10);
|
||||
|
||||
let runtime = Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.enable_all()
|
||||
.build()
|
||||
.map_err(|e| anyhow!("Builder::new_multi_threa failed: {}", e))?;
|
||||
|
||||
runtime.spawn(async move {
|
||||
info!("mem-agent start");
|
||||
match mem_agent_loop(cmd_rx, mg, comp).await {
|
||||
Err(e) => error!("mem-agent error {}", e),
|
||||
Ok(()) => info!("mem-agent stop"),
|
||||
}
|
||||
});
|
||||
|
||||
Ok((Self { cmd_tx }, runtime))
|
||||
}
|
||||
|
||||
async fn send_cmd_async(&self, cmd: AgentCmd) -> Result<AgentReturn> {
|
||||
let (ret_tx, ret_rx) = oneshot::channel();
|
||||
|
||||
self.cmd_tx
|
||||
.send((cmd, ret_tx))
|
||||
.await
|
||||
.map_err(|e| anyhow!("cmd_tx.send cmd failed: {}", e))?;
|
||||
|
||||
let ret = ret_rx
|
||||
.await
|
||||
.map_err(|e| anyhow!("ret_rx.recv failed: {}", e))?;
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn memcg_set_config_async(&self, opt: memcg::OptionConfig) -> Result<()> {
|
||||
let ret = self
|
||||
.send_cmd_async(AgentCmd::MemcgSet(opt))
|
||||
.await
|
||||
.map_err(|e| anyhow!("send_cmd failed: {}", e))?;
|
||||
|
||||
match ret {
|
||||
AgentReturn::Err(e) => Err(anyhow!(
|
||||
"mem_agent thread memcg_set_config_async failed: {}",
|
||||
e
|
||||
)),
|
||||
AgentReturn::Ok => Ok(()),
|
||||
_ => Err(anyhow!(
|
||||
"mem_agent thread memcg_set_config_async return wrong value"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn compact_set_config_async(&self, opt: compact::OptionConfig) -> Result<()> {
|
||||
let ret = self
|
||||
.send_cmd_async(AgentCmd::CompactSet(opt))
|
||||
.await
|
||||
.map_err(|e| anyhow!("send_cmd failed: {}", e))?;
|
||||
|
||||
match ret {
|
||||
AgentReturn::Err(e) => Err(anyhow!(
|
||||
"mem_agent thread compact_set_config_async failed: {}",
|
||||
e
|
||||
)),
|
||||
AgentReturn::Ok => Ok(()),
|
||||
_ => Err(anyhow!(
|
||||
"mem_agent thread compact_set_config_async return wrong value"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn memcg_status_async(&self) -> Result<Vec<MemCgroup>> {
|
||||
let ret = self
|
||||
.send_cmd_async(AgentCmd::MemcgStatus)
|
||||
.await
|
||||
.map_err(|e| anyhow!("send_cmd failed: {}", e))?;
|
||||
|
||||
let status = match ret {
|
||||
AgentReturn::Err(e) => {
|
||||
return Err(anyhow!("mem_agent thread memcg_status_async failed: {}", e))
|
||||
}
|
||||
AgentReturn::Ok => {
|
||||
return Err(anyhow!(
|
||||
"mem_agent thread memcg_status_async return wrong value"
|
||||
))
|
||||
}
|
||||
AgentReturn::MemcgStatus(s) => s,
|
||||
};
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_agent() {
|
||||
let memcg_config = memcg::Config {
|
||||
disabled: true,
|
||||
..Default::default()
|
||||
};
|
||||
let compact_config = compact::Config {
|
||||
disabled: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (ma, _rt) = MemAgent::new(memcg_config, compact_config).unwrap();
|
||||
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on({
|
||||
let memcg_config = memcg::OptionConfig {
|
||||
period_secs: Some(120),
|
||||
..Default::default()
|
||||
};
|
||||
ma.memcg_set_config_async(memcg_config)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on({
|
||||
let compact_config = compact::OptionConfig {
|
||||
period_secs: Some(280),
|
||||
..Default::default()
|
||||
};
|
||||
ma.compact_set_config_async(compact_config)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_agent_memcg_status() {
|
||||
let memcg_config = memcg::Config {
|
||||
disabled: true,
|
||||
..Default::default()
|
||||
};
|
||||
let compact_config = compact::Config {
|
||||
disabled: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (ma, _rt) = MemAgent::new(memcg_config, compact_config).unwrap();
|
||||
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(ma.memcg_status_async())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
446
src/mem-agent/src/compact.rs
Normal file
446
src/mem-agent/src/compact.rs
Normal file
@ -0,0 +1,446 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::proc;
|
||||
use crate::psi;
|
||||
use crate::timer::Timeout;
|
||||
use crate::{debug, error, info, trace};
|
||||
use anyhow::{anyhow, Result};
|
||||
use nix::sched::sched_yield;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::Duration as TokioDuration;
|
||||
|
||||
const PAGE_REPORTING_MIN_ORDER: u8 = 9;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Config {
|
||||
pub disabled: bool,
|
||||
pub psi_path: PathBuf,
|
||||
pub period_secs: u64,
|
||||
pub period_psi_percent_limit: u8,
|
||||
pub compact_psi_percent_limit: u8,
|
||||
pub compact_sec_max: i64,
|
||||
|
||||
// the order that want to get from compaction
|
||||
pub compact_order: u8,
|
||||
|
||||
// compact_threshold is the pages number.
|
||||
// When examining the /proc/pagetypeinfo, if there's an increase in the
|
||||
// number of movable pages of orders smaller than the compact_order
|
||||
// compared to the amount following the previous compaction,
|
||||
// and this increase surpasses a certain threshold—specifically,
|
||||
// more than 'compact_threshold' number of pages.
|
||||
// Or the number of free pages has decreased by 'compact_threshold'
|
||||
// since the previous compaction.
|
||||
// then the system should initiate another round of memory compaction.
|
||||
pub compact_threshold: u64,
|
||||
|
||||
// After one compaction, if there has not been a compaction within
|
||||
// the next compact_force_times times, a compaction will be forced
|
||||
// regardless of the system's memory situation.
|
||||
// If compact_force_times is set to 0, will do force compaction each time.
|
||||
// If compact_force_times is set to std::u64::MAX, will never do force compaction.
|
||||
pub compact_force_times: u64,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
disabled: false,
|
||||
psi_path: PathBuf::from(""),
|
||||
period_secs: 10 * 60,
|
||||
period_psi_percent_limit: 1,
|
||||
compact_psi_percent_limit: 5,
|
||||
compact_sec_max: 30 * 60,
|
||||
compact_order: PAGE_REPORTING_MIN_ORDER,
|
||||
compact_threshold: 2 << PAGE_REPORTING_MIN_ORDER,
|
||||
compact_force_times: std::u64::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OptionConfig {
|
||||
pub disabled: Option<bool>,
|
||||
pub psi_path: Option<PathBuf>,
|
||||
pub period_secs: Option<u64>,
|
||||
pub period_psi_percent_limit: Option<u8>,
|
||||
pub compact_psi_percent_limit: Option<u8>,
|
||||
pub compact_sec_max: Option<i64>,
|
||||
|
||||
pub compact_order: Option<u8>,
|
||||
|
||||
pub compact_threshold: Option<u64>,
|
||||
|
||||
pub compact_force_times: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct CompactCore {
|
||||
timeout: Timeout,
|
||||
config: Config,
|
||||
psi: psi::Period,
|
||||
force_counter: u64,
|
||||
prev_free_movable_pages_after_compact: u64,
|
||||
prev_memfree_kb: u64,
|
||||
}
|
||||
|
||||
impl CompactCore {
|
||||
fn new(config: Config) -> Self {
|
||||
Self {
|
||||
timeout: Timeout::new(config.period_secs),
|
||||
psi: psi::Period::new(&config.psi_path, true),
|
||||
force_counter: 0,
|
||||
prev_free_movable_pages_after_compact: 0,
|
||||
prev_memfree_kb: 0,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
fn psi_ok(&mut self) -> bool {
|
||||
if crate::misc::is_test_environment() {
|
||||
return false;
|
||||
}
|
||||
let percent = match self.psi.get_percent() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
debug!("psi.get_percent failed: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if percent > self.config.period_psi_percent_limit as u64 {
|
||||
info!(
|
||||
"compact will not work because period psi {}% exceeds limit",
|
||||
percent
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn need_force_compact(&self) -> bool {
|
||||
if self.config.compact_force_times == std::u64::MAX {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.force_counter >= self.config.compact_force_times
|
||||
}
|
||||
|
||||
fn check_compact_threshold(&self, memfree_kb: u64, free_movable_pages: u64) -> bool {
|
||||
if self.prev_memfree_kb > memfree_kb + (self.config.compact_threshold << 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let threshold = self.config.compact_threshold + self.prev_free_movable_pages_after_compact;
|
||||
if free_movable_pages > threshold {
|
||||
true
|
||||
} else {
|
||||
info!(
|
||||
"compact will not work because free movable pages {} less than threshold {} and prev_free {}kB current_free {}kB",
|
||||
free_movable_pages, threshold, self.prev_memfree_kb, memfree_kb
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn get_special_psi(&self) -> psi::Period {
|
||||
psi::Period::new(&self.config.psi_path, true)
|
||||
}
|
||||
|
||||
fn set_prev(&mut self, memfree_kb: u64, free_movable_pages: u64) {
|
||||
self.prev_memfree_kb = memfree_kb;
|
||||
self.prev_free_movable_pages_after_compact = free_movable_pages;
|
||||
}
|
||||
|
||||
fn set_disabled(&mut self, disabled: bool) {
|
||||
if !disabled {
|
||||
self.timeout.reset();
|
||||
}
|
||||
|
||||
self.config.disabled = disabled;
|
||||
}
|
||||
|
||||
// return if MemAgentSleep need be reset
|
||||
fn set_config(&mut self, new_config: OptionConfig) -> bool {
|
||||
let mut need_reset_mas = false;
|
||||
|
||||
if let Some(d) = new_config.disabled {
|
||||
if self.config.disabled != d {
|
||||
self.set_disabled(d);
|
||||
need_reset_mas = true;
|
||||
}
|
||||
}
|
||||
if let Some(p) = new_config.psi_path {
|
||||
self.config.psi_path = p.clone();
|
||||
}
|
||||
if let Some(p) = new_config.period_psi_percent_limit {
|
||||
self.config.period_psi_percent_limit = p;
|
||||
}
|
||||
if let Some(p) = new_config.compact_psi_percent_limit {
|
||||
self.config.compact_psi_percent_limit = p;
|
||||
}
|
||||
if let Some(p) = new_config.compact_sec_max {
|
||||
self.config.compact_sec_max = p;
|
||||
}
|
||||
if let Some(p) = new_config.compact_order {
|
||||
self.config.compact_order = p;
|
||||
}
|
||||
if let Some(p) = new_config.compact_threshold {
|
||||
self.config.compact_threshold = p;
|
||||
}
|
||||
if let Some(p) = new_config.compact_force_times {
|
||||
self.config.compact_force_times = p;
|
||||
}
|
||||
if let Some(p) = new_config.period_secs {
|
||||
self.config.period_secs = p;
|
||||
self.timeout.set_sleep_duration(p);
|
||||
if !self.config.disabled {
|
||||
need_reset_mas = true;
|
||||
}
|
||||
}
|
||||
|
||||
info!("new compact config: {:#?}", self.config);
|
||||
if need_reset_mas {
|
||||
info!("need reset mem-agent sleep");
|
||||
}
|
||||
|
||||
need_reset_mas
|
||||
}
|
||||
|
||||
fn need_work(&self) -> bool {
|
||||
if self.config.disabled {
|
||||
return false;
|
||||
}
|
||||
self.timeout.is_timeout()
|
||||
}
|
||||
|
||||
pub fn get_remaining_tokio_duration(&self) -> TokioDuration {
|
||||
if self.config.disabled {
|
||||
return TokioDuration::MAX;
|
||||
}
|
||||
|
||||
self.timeout.remaining_tokio_duration()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Compact {
|
||||
core: Arc<RwLock<CompactCore>>,
|
||||
}
|
||||
|
||||
impl Compact {
|
||||
pub fn new(mut config: Config) -> Result<Self> {
|
||||
config.psi_path =
|
||||
psi::check(&config.psi_path).map_err(|e| anyhow!("psi::check failed: {}", e))?;
|
||||
|
||||
let c = Self {
|
||||
core: Arc::new(RwLock::new(CompactCore::new(config))),
|
||||
};
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
fn calculate_free_movable_pages(&self) -> Result<u64> {
|
||||
let file = File::open("/proc/pagetypeinfo")?;
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
let order_limit = self.core.blocking_read().config.compact_order as usize;
|
||||
|
||||
let mut total_free_movable_pages = 0;
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
if line.contains("Movable") {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if let Some(index) = parts.iter().position(|&element| element == "Movable") {
|
||||
for (order, &count_str) in parts[(index + 1)..].iter().enumerate() {
|
||||
if order < order_limit {
|
||||
if let Ok(count) = count_str.parse::<u64>() {
|
||||
total_free_movable_pages += count * 1 << order;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(total_free_movable_pages)
|
||||
}
|
||||
|
||||
fn check_compact_threshold(&self) -> bool {
|
||||
let memfree_kb = match proc::get_memfree_kb() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("get_memfree_kb failed: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let free_movable_pages = match self.calculate_free_movable_pages() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("calculate_free_movable_pages failed: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
self.core
|
||||
.blocking_read()
|
||||
.check_compact_threshold(memfree_kb, free_movable_pages)
|
||||
}
|
||||
|
||||
fn set_prev(&mut self) -> Result<()> {
|
||||
let memfree_kb =
|
||||
proc::get_memfree_kb().map_err(|e| anyhow!("get_memfree_kb failed: {}", e))?;
|
||||
let free_movable_pages = self
|
||||
.calculate_free_movable_pages()
|
||||
.map_err(|e| anyhow!("calculate_free_movable_pages failed: {}", e))?;
|
||||
|
||||
self.core
|
||||
.blocking_write()
|
||||
.set_prev(memfree_kb, free_movable_pages);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_compact(&self) -> Result<()> {
|
||||
let compact_psi_percent_limit = self.core.blocking_read().config.compact_psi_percent_limit;
|
||||
let mut compact_psi = self.core.blocking_read().get_special_psi();
|
||||
let mut rest_sec = self.core.blocking_read().config.compact_sec_max;
|
||||
|
||||
if let Err(e) = sched_yield() {
|
||||
error!("sched_yield failed: {:?}", e);
|
||||
}
|
||||
|
||||
info!("compact start");
|
||||
|
||||
let mut child = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg("echo 1 > /proc/sys/vm/compact_memory")
|
||||
.spawn()
|
||||
.map_err(|e| anyhow!("Command::new failed: {}", e))?;
|
||||
|
||||
debug!("compact pid {}", child.id());
|
||||
|
||||
let mut killed = false;
|
||||
loop {
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
debug!("compact done with status {}", status);
|
||||
break;
|
||||
}
|
||||
Ok(None) => {
|
||||
if killed {
|
||||
if rest_sec <= 0 {
|
||||
error!("compact killed but not quit");
|
||||
break;
|
||||
} else {
|
||||
debug!("compact killed and keep wait");
|
||||
}
|
||||
} else {
|
||||
if rest_sec <= 0 {
|
||||
debug!("compact timeout");
|
||||
child
|
||||
.kill()
|
||||
.map_err(|e| anyhow!("child.kill failed: {}", e))?;
|
||||
killed = true;
|
||||
}
|
||||
}
|
||||
|
||||
let percent = compact_psi
|
||||
.get_percent()
|
||||
.map_err(|e| anyhow!("compact_psi.get_percent failed: {}", e))?;
|
||||
if percent > compact_psi_percent_limit as u64 {
|
||||
info!(
|
||||
"compaction need stop because period psi {}% exceeds limit",
|
||||
percent
|
||||
);
|
||||
child
|
||||
.kill()
|
||||
.map_err(|e| anyhow!("child.kill failed: {}", e))?;
|
||||
killed = true;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
// try_wait will fail with code 10 because some task will
|
||||
// wait compact task before try_wait.
|
||||
debug!("compact try_wait fail: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
rest_sec -= 1;
|
||||
}
|
||||
|
||||
info!("compact stop");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn need_work(&self) -> bool {
|
||||
self.core.blocking_read().need_work()
|
||||
}
|
||||
|
||||
pub fn reset_timer(&mut self) {
|
||||
self.core.blocking_write().timeout.reset();
|
||||
}
|
||||
|
||||
pub fn get_remaining_tokio_duration(&self) -> TokioDuration {
|
||||
self.core.blocking_read().get_remaining_tokio_duration()
|
||||
}
|
||||
|
||||
pub async fn async_get_remaining_tokio_duration(&self) -> TokioDuration {
|
||||
self.core.read().await.get_remaining_tokio_duration()
|
||||
}
|
||||
|
||||
pub fn work(&mut self) -> Result<()> {
|
||||
let mut can_work = self.core.blocking_write().psi_ok();
|
||||
if can_work {
|
||||
if !self.core.blocking_read().need_force_compact() {
|
||||
if !self.check_compact_threshold() {
|
||||
trace!("not enough free movable pages");
|
||||
can_work = false;
|
||||
}
|
||||
} else {
|
||||
trace!("force compact");
|
||||
}
|
||||
}
|
||||
|
||||
if can_work {
|
||||
self.do_compact()
|
||||
.map_err(|e| anyhow!("do_compact failed: {}", e))?;
|
||||
|
||||
self.set_prev()?;
|
||||
|
||||
self.core.blocking_write().force_counter = 0;
|
||||
} else {
|
||||
self.core.blocking_write().force_counter += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_config(&mut self, new_config: OptionConfig) -> bool {
|
||||
self.core.write().await.set_config(new_config)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_compact() {
|
||||
let mut c = Compact::new(Config::default()).unwrap();
|
||||
assert!(c.work().is_ok());
|
||||
}
|
||||
}
|
12
src/mem-agent/src/lib.rs
Normal file
12
src/mem-agent/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod agent;
|
||||
pub mod compact;
|
||||
pub mod memcg;
|
||||
mod mglru;
|
||||
mod misc;
|
||||
mod proc;
|
||||
mod psi;
|
||||
mod timer;
|
843
src/mem-agent/src/memcg.rs
Normal file
843
src/mem-agent/src/memcg.rs
Normal file
@ -0,0 +1,843 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mglru::{self, MGenLRU};
|
||||
use crate::timer::Timeout;
|
||||
use crate::{debug, error, info, trace};
|
||||
use crate::{proc, psi};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use nix::sched::sched_yield;
|
||||
use page_size;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::Duration as TokioDuration;
|
||||
|
||||
/* If last_inc_time to current_time small than IDLE_FRESH_IGNORE_SECS,
|
||||
not do idle_fresh for this memcg. */
|
||||
const IDLE_FRESH_IGNORE_SECS: i64 = 60;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Config {
|
||||
pub disabled: bool,
|
||||
pub swap: bool,
|
||||
pub swappiness_max: u8,
|
||||
pub psi_path: PathBuf,
|
||||
pub period_secs: u64,
|
||||
pub period_psi_percent_limit: u8,
|
||||
pub eviction_psi_percent_limit: u8,
|
||||
pub eviction_run_aging_count_min: u64,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
disabled: false,
|
||||
swap: false,
|
||||
swappiness_max: 50,
|
||||
psi_path: PathBuf::from(""),
|
||||
period_secs: 10 * 60,
|
||||
period_psi_percent_limit: 1,
|
||||
eviction_psi_percent_limit: 1,
|
||||
eviction_run_aging_count_min: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OptionConfig {
|
||||
pub disabled: Option<bool>,
|
||||
pub swap: Option<bool>,
|
||||
pub swappiness_max: Option<u8>,
|
||||
pub psi_path: Option<PathBuf>,
|
||||
pub period_secs: Option<u64>,
|
||||
pub period_psi_percent_limit: Option<u8>,
|
||||
pub eviction_psi_percent_limit: Option<u8>,
|
||||
pub eviction_run_aging_count_min: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EvictionCount {
|
||||
pub page: u64,
|
||||
pub no_min_lru_file: u64,
|
||||
pub min_lru_inc: u64,
|
||||
pub other_error: u64,
|
||||
pub error: u64,
|
||||
pub psi_exceeds_limit: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Numa {
|
||||
pub max_seq: u64,
|
||||
pub min_seq: u64,
|
||||
pub last_inc_time: DateTime<Utc>,
|
||||
pub min_lru_file: u64,
|
||||
pub min_lru_anon: u64,
|
||||
|
||||
pub run_aging_count: u64,
|
||||
pub eviction_count: EvictionCount,
|
||||
}
|
||||
|
||||
impl Numa {
|
||||
fn new(mglru: &MGenLRU) -> Self {
|
||||
Self {
|
||||
max_seq: mglru.max_seq,
|
||||
min_seq: mglru.min_seq,
|
||||
last_inc_time: mglru.last_birth,
|
||||
min_lru_file: mglru.lru[mglru.min_lru_index].file,
|
||||
min_lru_anon: mglru.lru[mglru.min_lru_index].anon,
|
||||
run_aging_count: 0,
|
||||
eviction_count: EvictionCount {
|
||||
page: 0,
|
||||
no_min_lru_file: 0,
|
||||
min_lru_inc: 0,
|
||||
other_error: 0,
|
||||
error: 0,
|
||||
psi_exceeds_limit: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, mglru: &MGenLRU) {
|
||||
self.max_seq = mglru.max_seq;
|
||||
self.min_seq = mglru.min_seq;
|
||||
self.last_inc_time = mglru.last_birth;
|
||||
self.min_lru_file = mglru.lru[mglru.min_lru_index].file;
|
||||
self.min_lru_anon = mglru.lru[mglru.min_lru_index].anon;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemCgroup {
|
||||
/* get from Linux kernel static inline unsigned short mem_cgroup_id(struct mem_cgroup *memcg) */
|
||||
pub id: u16,
|
||||
pub ino: usize,
|
||||
pub path: String,
|
||||
pub numa: HashMap<u32, Numa>,
|
||||
psi: psi::Period,
|
||||
|
||||
pub sleep_psi_exceeds_limit: u64,
|
||||
}
|
||||
|
||||
impl MemCgroup {
|
||||
fn new(
|
||||
id: &usize,
|
||||
ino: &usize,
|
||||
path: &String,
|
||||
hmg: &HashMap<usize, MGenLRU>,
|
||||
psi_path: &PathBuf,
|
||||
) -> Self {
|
||||
let s = Self {
|
||||
id: *id as u16,
|
||||
ino: *ino,
|
||||
path: path.to_string(),
|
||||
numa: hmg
|
||||
.iter()
|
||||
.map(|(numa_id, mglru)| (*numa_id as u32, Numa::new(mglru)))
|
||||
.collect(),
|
||||
psi: psi::Period::new(&psi_path.join(path.trim_start_matches('/')), false),
|
||||
sleep_psi_exceeds_limit: 0,
|
||||
};
|
||||
info!("MemCgroup::new {:?}", s);
|
||||
s
|
||||
}
|
||||
|
||||
fn update_from_hostmemcg(&mut self, hmg: &HashMap<usize, MGenLRU>) {
|
||||
self.numa
|
||||
.retain(|numa_id, _| hmg.contains_key(&(*numa_id as usize)));
|
||||
|
||||
for (numa_id, mglru) in hmg {
|
||||
self.numa
|
||||
.entry(*numa_id as u32)
|
||||
.and_modify(|e| e.update(mglru))
|
||||
.or_insert(Numa::new(mglru));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum EvictionStopReason {
|
||||
None,
|
||||
NoMinLru,
|
||||
MinLruInc,
|
||||
GetError,
|
||||
PsiExceedsLimit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct EvictionInfo {
|
||||
psi: psi::Period,
|
||||
|
||||
// the min_lru_file and min_lru_anon before last time mglru::run_eviction
|
||||
last_min_lru_file: u64,
|
||||
last_min_lru_anon: u64,
|
||||
|
||||
// the evicted page count
|
||||
file_page_count: u64,
|
||||
anon_page_count: u64,
|
||||
|
||||
only_swap_mode: bool,
|
||||
anon_eviction_max: u64,
|
||||
|
||||
stop_reason: EvictionStopReason,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Info {
|
||||
memcg_id: usize,
|
||||
numa_id: usize,
|
||||
path: String,
|
||||
max_seq: u64,
|
||||
min_seq: u64,
|
||||
last_inc_time: DateTime<Utc>,
|
||||
min_lru_file: u64,
|
||||
min_lru_anon: u64,
|
||||
|
||||
eviction: Option<EvictionInfo>,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
fn new(mcg: &MemCgroup, numa_id: usize, numa: &Numa) -> Self {
|
||||
Self {
|
||||
memcg_id: mcg.id as usize,
|
||||
numa_id: numa_id,
|
||||
path: mcg.path.clone(),
|
||||
min_seq: numa.min_seq,
|
||||
max_seq: numa.max_seq,
|
||||
last_inc_time: numa.last_inc_time,
|
||||
min_lru_file: numa.min_lru_file,
|
||||
min_lru_anon: numa.min_lru_anon,
|
||||
eviction: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, numa: &Numa) {
|
||||
self.min_seq = numa.min_seq;
|
||||
self.max_seq = numa.max_seq;
|
||||
self.last_inc_time = numa.last_inc_time;
|
||||
self.min_lru_file = numa.min_lru_file;
|
||||
self.min_lru_anon = numa.min_lru_anon;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MemCgroups {
|
||||
timeout: Timeout,
|
||||
config: Config,
|
||||
id_map: HashMap<u16, MemCgroup>,
|
||||
ino2id: HashMap<usize, u16>,
|
||||
path2id: HashMap<String, u16>,
|
||||
}
|
||||
|
||||
impl MemCgroups {
|
||||
fn new(config: Config) -> Self {
|
||||
Self {
|
||||
timeout: Timeout::new(config.period_secs),
|
||||
config,
|
||||
id_map: HashMap::new(),
|
||||
ino2id: HashMap::new(),
|
||||
path2id: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove not exist in host or id, ino changed memcgroup */
|
||||
fn remove_changed(
|
||||
&mut self,
|
||||
mg_hash: &HashMap<String, (usize, usize, HashMap<usize, MGenLRU>)>,
|
||||
) {
|
||||
/* Remove not exist in host or id, ino changed memcgroup */
|
||||
let mut remove_target = Vec::new();
|
||||
for (_, mg) in &self.id_map {
|
||||
let mut should_remove = true;
|
||||
|
||||
if let Some((id, ino, _)) = mg_hash.get(&mg.path) {
|
||||
if mg.id as usize == *id && mg.ino == *ino {
|
||||
should_remove = false;
|
||||
}
|
||||
}
|
||||
|
||||
if should_remove {
|
||||
remove_target.push((mg.id, mg.ino, mg.path.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
for (id, ino, path) in remove_target {
|
||||
self.id_map.remove(&id);
|
||||
self.ino2id.remove(&ino);
|
||||
self.path2id.remove(&path);
|
||||
info!("Remove memcg {} {} {} because host changed.", id, ino, path)
|
||||
}
|
||||
}
|
||||
|
||||
fn update_and_add(
|
||||
&mut self,
|
||||
mg_hash: &HashMap<String, (usize, usize, HashMap<usize, MGenLRU>)>,
|
||||
) {
|
||||
for (path, (id, ino, hmg)) in mg_hash {
|
||||
if *id == 0 {
|
||||
info!(
|
||||
"Not add {} {} {} because it is disabled.",
|
||||
*id,
|
||||
*ino,
|
||||
path.to_string()
|
||||
)
|
||||
}
|
||||
if let Some(mg) = self.id_map.get_mut(&(*id as u16)) {
|
||||
mg.update_from_hostmemcg(&hmg);
|
||||
} else {
|
||||
self.id_map.insert(
|
||||
*id as u16,
|
||||
MemCgroup::new(id, ino, path, hmg, &self.config.psi_path),
|
||||
);
|
||||
self.ino2id.insert(*ino, *id as u16);
|
||||
self.path2id.insert(path.to_string(), *id as u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_psi_get_info(&mut self) -> Vec<Info> {
|
||||
let mut info_ret = Vec::new();
|
||||
|
||||
for (_, mcg) in self.id_map.iter_mut() {
|
||||
let percent = match mcg.psi.get_percent() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
debug!("mcg.psi.get_percent {} failed: {}", mcg.path, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if percent > self.config.period_psi_percent_limit as u64 {
|
||||
mcg.sleep_psi_exceeds_limit += 1;
|
||||
info!("{} period psi {}% exceeds limit", mcg.path, percent);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (numa_id, numa) in &mcg.numa {
|
||||
info_ret.push(Info::new(&mcg, *numa_id as usize, numa));
|
||||
}
|
||||
}
|
||||
|
||||
info_ret
|
||||
}
|
||||
|
||||
fn update_info(&self, infov: &mut Vec<Info>) {
|
||||
let mut i = 0;
|
||||
while i < infov.len() {
|
||||
if let Some(mg) = self.id_map.get(&(infov[i].memcg_id as u16)) {
|
||||
if let Some(numa) = mg.numa.get(&(infov[i].numa_id as u32)) {
|
||||
infov[i].update(numa);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
infov.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
fn inc_run_aging_count(&mut self, infov: &mut Vec<Info>) {
|
||||
let mut i = 0;
|
||||
while i < infov.len() {
|
||||
if let Some(mg) = self.id_map.get_mut(&(infov[i].memcg_id as u16)) {
|
||||
if let Some(numa) = mg.numa.get_mut(&(infov[i].numa_id as u32)) {
|
||||
numa.run_aging_count += 1;
|
||||
if numa.run_aging_count >= self.config.eviction_run_aging_count_min {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infov.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
fn record_eviction(&mut self, infov: &Vec<Info>) {
|
||||
for info in infov {
|
||||
if let Some(mg) = self.id_map.get_mut(&(info.memcg_id as u16)) {
|
||||
if let Some(numa) = mg.numa.get_mut(&(info.numa_id as u32)) {
|
||||
if let Some(ei) = &info.eviction {
|
||||
numa.eviction_count.page += ei.file_page_count + ei.anon_page_count;
|
||||
match ei.stop_reason {
|
||||
EvictionStopReason::None => numa.eviction_count.other_error += 1,
|
||||
EvictionStopReason::NoMinLru => {
|
||||
numa.eviction_count.no_min_lru_file += 1
|
||||
}
|
||||
EvictionStopReason::MinLruInc => numa.eviction_count.min_lru_inc += 1,
|
||||
EvictionStopReason::GetError => numa.eviction_count.error += 1,
|
||||
EvictionStopReason::PsiExceedsLimit => {
|
||||
numa.eviction_count.psi_exceeds_limit += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_disabled(&mut self, disabled: bool) {
|
||||
if !disabled {
|
||||
self.timeout.reset();
|
||||
}
|
||||
|
||||
self.config.disabled = disabled;
|
||||
}
|
||||
|
||||
// return if MemAgentSleep need be reset
|
||||
fn set_config(&mut self, new_config: OptionConfig) -> bool {
|
||||
let mut need_reset_mas = false;
|
||||
|
||||
if let Some(d) = new_config.disabled {
|
||||
if self.config.disabled != d {
|
||||
self.set_disabled(d);
|
||||
need_reset_mas = true;
|
||||
}
|
||||
}
|
||||
if let Some(s) = new_config.swap {
|
||||
self.config.swap = s;
|
||||
}
|
||||
if let Some(s) = new_config.swappiness_max {
|
||||
self.config.swappiness_max = s;
|
||||
}
|
||||
if let Some(p) = new_config.psi_path {
|
||||
self.config.psi_path = p.clone();
|
||||
}
|
||||
if let Some(p) = new_config.period_psi_percent_limit {
|
||||
self.config.period_psi_percent_limit = p;
|
||||
}
|
||||
if let Some(p) = new_config.eviction_psi_percent_limit {
|
||||
self.config.eviction_psi_percent_limit = p;
|
||||
}
|
||||
if let Some(p) = new_config.eviction_run_aging_count_min {
|
||||
self.config.eviction_run_aging_count_min = p;
|
||||
}
|
||||
if let Some(p) = new_config.period_secs {
|
||||
self.config.period_secs = p;
|
||||
self.timeout.set_sleep_duration(p);
|
||||
if !self.config.disabled {
|
||||
need_reset_mas = true;
|
||||
}
|
||||
}
|
||||
|
||||
info!("new memcg config: {:#?}", self.config);
|
||||
if need_reset_mas {
|
||||
info!("need reset mem-agent sleep");
|
||||
}
|
||||
|
||||
need_reset_mas
|
||||
}
|
||||
|
||||
fn need_work(&self) -> bool {
|
||||
if self.config.disabled {
|
||||
return false;
|
||||
}
|
||||
self.timeout.is_timeout()
|
||||
}
|
||||
|
||||
pub fn get_remaining_tokio_duration(&self) -> TokioDuration {
|
||||
if self.config.disabled {
|
||||
return TokioDuration::MAX;
|
||||
}
|
||||
|
||||
self.timeout.remaining_tokio_duration()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemCG {
|
||||
memcgs: Arc<RwLock<MemCgroups>>,
|
||||
}
|
||||
|
||||
impl MemCG {
|
||||
pub fn new(mut config: Config) -> Result<Self> {
|
||||
mglru::check().map_err(|e| anyhow!("mglru::check failed: {}", e))?;
|
||||
config.psi_path =
|
||||
psi::check(&config.psi_path).map_err(|e| anyhow!("psi::check failed: {}", e))?;
|
||||
|
||||
let memcg = Self {
|
||||
memcgs: Arc::new(RwLock::new(MemCgroups::new(config))),
|
||||
};
|
||||
|
||||
Ok(memcg)
|
||||
}
|
||||
|
||||
/*
|
||||
* If target_paths.len == 0,
|
||||
* will remove the updated or not exist cgroup in the host from MemCgroups.
|
||||
* If target_paths.len > 0, will not do that.
|
||||
*/
|
||||
fn refresh(&mut self, target_paths: &HashSet<String>) -> Result<()> {
|
||||
let mg_hash = mglru::host_memcgs_get(target_paths, true)
|
||||
.map_err(|e| anyhow!("lru_gen_parse::file_parse failed: {}", e))?;
|
||||
|
||||
let mut mgs = self.memcgs.blocking_write();
|
||||
|
||||
if target_paths.len() == 0 {
|
||||
mgs.remove_changed(&mg_hash);
|
||||
}
|
||||
mgs.update_and_add(&mg_hash);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_aging(&mut self, infov: &mut Vec<Info>, swap: bool) {
|
||||
infov.retain(|info| {
|
||||
let now = Utc::now();
|
||||
if now.signed_duration_since(info.last_inc_time).num_seconds() < IDLE_FRESH_IGNORE_SECS
|
||||
{
|
||||
info!(
|
||||
"{} not run aging because last_inc_time {}",
|
||||
info.path, info.last_inc_time,
|
||||
);
|
||||
true
|
||||
} else {
|
||||
let res = if let Err(e) =
|
||||
mglru::run_aging(info.memcg_id, info.numa_id, info.max_seq, swap, true)
|
||||
{
|
||||
error!(
|
||||
"mglru::run_aging {} {} {} failed: {}",
|
||||
info.path, info.memcg_id, info.numa_id, e
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if let Err(e) = sched_yield() {
|
||||
error!("sched_yield failed: {:?}", e);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
});
|
||||
|
||||
self.memcgs.blocking_write().inc_run_aging_count(infov);
|
||||
}
|
||||
|
||||
fn swap_not_available(&self) -> Result<bool> {
|
||||
let freeswap_kb = proc::get_freeswap_kb().context("proc::get_freeswap_kb")?;
|
||||
|
||||
if freeswap_kb > (256 * page_size::get() as u64 / 1024) {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_swappiness(&self, anon_count: u64, file_count: u64) -> u8 {
|
||||
assert!(
|
||||
anon_count != 0 && file_count != 0,
|
||||
"anon and file must be non-zero"
|
||||
);
|
||||
|
||||
let total = anon_count + file_count;
|
||||
let c = 200 * anon_count / total;
|
||||
|
||||
c as u8
|
||||
}
|
||||
|
||||
fn run_eviction(&mut self, infov: &mut Vec<Info>, mut swap: bool) -> Result<()> {
|
||||
if self
|
||||
.swap_not_available()
|
||||
.context("self.swap_not_available")?
|
||||
{
|
||||
swap = false;
|
||||
}
|
||||
|
||||
let psi_path = self.memcgs.blocking_read().config.psi_path.clone();
|
||||
for info in infov.into_iter() {
|
||||
info.eviction = Some(EvictionInfo {
|
||||
psi: psi::Period::new(&psi_path.join(info.path.trim_start_matches('/')), false),
|
||||
last_min_lru_file: 0,
|
||||
last_min_lru_anon: 0,
|
||||
file_page_count: 0,
|
||||
anon_page_count: 0,
|
||||
only_swap_mode: false,
|
||||
anon_eviction_max: 0,
|
||||
stop_reason: EvictionStopReason::None,
|
||||
});
|
||||
}
|
||||
|
||||
let mut removed_infov = Vec::new();
|
||||
|
||||
let mut ret = Ok(());
|
||||
let eviction_psi_percent_limit = self
|
||||
.memcgs
|
||||
.blocking_read()
|
||||
.config
|
||||
.eviction_psi_percent_limit as u64;
|
||||
|
||||
let swappiness_max = self.memcgs.blocking_read().config.swappiness_max;
|
||||
|
||||
'main_loop: while infov.len() != 0 {
|
||||
let mut i = 0;
|
||||
while i < infov.len() {
|
||||
let path_set: HashSet<String> =
|
||||
infov.iter().map(|info| info.path.clone()).collect();
|
||||
match self.refresh(&path_set) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
ret = Err(anyhow!("refresh failed: {}", e));
|
||||
break 'main_loop;
|
||||
}
|
||||
};
|
||||
self.update_info(infov);
|
||||
|
||||
let ci = infov[i].clone();
|
||||
|
||||
trace!("{} {} run_eviction single loop start", ci.path, ci.numa_id);
|
||||
|
||||
if let Some(ref mut ei) = infov[i].eviction {
|
||||
if ei.last_min_lru_file == 0 && ei.last_min_lru_anon == 0 {
|
||||
// First loop
|
||||
trace!("{} {} run_eviction begin", ci.path, ci.numa_id,);
|
||||
if ci.min_lru_file == 0 {
|
||||
if !swap || ci.min_lru_anon == 0 {
|
||||
info!(
|
||||
"{} {} run_eviction stop because min_lru_file is 0 or min_lru_anon is 0, release {} {} pages",
|
||||
ci.path, ci.numa_id, ei.anon_page_count, ei.file_page_count,
|
||||
);
|
||||
ei.stop_reason = EvictionStopReason::NoMinLru;
|
||||
removed_infov.push(infov.remove(i));
|
||||
continue;
|
||||
} else {
|
||||
ei.only_swap_mode = true;
|
||||
ei.anon_eviction_max =
|
||||
ci.min_lru_anon * swappiness_max as u64 / 200;
|
||||
trace!(
|
||||
"{} {} run_eviction only swap mode anon_eviction_max {}",
|
||||
ci.path,
|
||||
ci.numa_id,
|
||||
ei.anon_eviction_max
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ei.last_min_lru_file = ci.min_lru_file;
|
||||
ei.last_min_lru_anon = ci.min_lru_anon;
|
||||
} else {
|
||||
if (!ei.only_swap_mode && ci.min_lru_file >= ei.last_min_lru_file)
|
||||
|| (ei.only_swap_mode && ci.min_lru_file > 0)
|
||||
|| (swap && ci.min_lru_anon > ei.last_min_lru_anon)
|
||||
{
|
||||
info!(
|
||||
"{} {} run_eviction stop because min_lru_file {} last_min_lru_file {} min_lru_anon {} last_min_lru_anon {}, release {} {} pages",
|
||||
ci.path, ci.numa_id, ci.min_lru_file, ei.last_min_lru_file, ci.min_lru_anon, ei.last_min_lru_anon, ei.anon_page_count, ei.file_page_count,
|
||||
);
|
||||
|
||||
ei.stop_reason = EvictionStopReason::MinLruInc;
|
||||
removed_infov.push(infov.remove(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
let released = ei.last_min_lru_anon - ci.min_lru_anon;
|
||||
trace!(
|
||||
"{} {} run_eviction anon {} pages",
|
||||
ci.path,
|
||||
ci.numa_id,
|
||||
released
|
||||
);
|
||||
ei.anon_page_count += released;
|
||||
|
||||
let released = ei.last_min_lru_file - ci.min_lru_file;
|
||||
trace!(
|
||||
"{} {} run_eviction file {} pages",
|
||||
ci.path,
|
||||
ci.numa_id,
|
||||
released
|
||||
);
|
||||
ei.file_page_count += released;
|
||||
|
||||
if !ei.only_swap_mode {
|
||||
if ci.min_lru_file == 0 {
|
||||
info!(
|
||||
"{} {} run_eviction stop because min_lru_file is 0, release {} {} pages",
|
||||
ci.path, ci.numa_id, ei.anon_page_count, ei.file_page_count,
|
||||
);
|
||||
ei.stop_reason = EvictionStopReason::NoMinLru;
|
||||
removed_infov.push(infov.remove(i));
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ei.anon_page_count >= ei.anon_eviction_max {
|
||||
info!(
|
||||
"{} {} run_eviction stop because anon_page_count is bigger than anon_eviction_max {}, release {} {} pages",
|
||||
ci.path, ci.numa_id, ei.anon_eviction_max, ei.anon_page_count, ei.file_page_count,
|
||||
);
|
||||
ei.stop_reason = EvictionStopReason::NoMinLru;
|
||||
removed_infov.push(infov.remove(i));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let percent = match ei.psi.get_percent() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"{} {} ei.psi.get_percent failed: {}, release {} {} pages",
|
||||
ci.path, ci.numa_id, e, ei.anon_page_count, ei.file_page_count,
|
||||
);
|
||||
ei.stop_reason = EvictionStopReason::GetError;
|
||||
removed_infov.push(infov.remove(i));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if percent > eviction_psi_percent_limit {
|
||||
info!(
|
||||
"{} {} run_eviction stop because period psi {}% exceeds limit, release {} {} pages",
|
||||
ci.path, ci.numa_id, percent, ei.anon_page_count, ei.file_page_count,
|
||||
);
|
||||
ei.stop_reason = EvictionStopReason::PsiExceedsLimit;
|
||||
removed_infov.push(infov.remove(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
ei.last_min_lru_file = ci.min_lru_file;
|
||||
ei.last_min_lru_anon = ci.min_lru_anon;
|
||||
}
|
||||
|
||||
// get swapiness
|
||||
let swappiness = if ei.only_swap_mode {
|
||||
200
|
||||
} else if !swap
|
||||
|| ci.min_lru_anon == 0
|
||||
|| match self.swap_not_available() {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
ret = Err(anyhow!("swap_not_available failed: {:?}", e));
|
||||
break 'main_loop;
|
||||
}
|
||||
}
|
||||
{
|
||||
0
|
||||
} else {
|
||||
let s = self.get_swappiness(ci.min_lru_anon, ci.min_lru_file);
|
||||
if s > swappiness_max {
|
||||
swappiness_max
|
||||
} else {
|
||||
s
|
||||
}
|
||||
};
|
||||
|
||||
trace!(
|
||||
"{} {} run_eviction min_seq {} swappiness {}",
|
||||
ci.path,
|
||||
ci.numa_id,
|
||||
ci.min_seq,
|
||||
swappiness
|
||||
);
|
||||
|
||||
match mglru::run_eviction(ci.memcg_id, ci.numa_id, ci.min_seq, swappiness, 1) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"{} {} mglru::run_eviction failed: {}, release {} {} pages",
|
||||
ci.path, ci.numa_id, e, ei.anon_page_count, ei.file_page_count,
|
||||
);
|
||||
ei.stop_reason = EvictionStopReason::GetError;
|
||||
removed_infov.push(infov.remove(i));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = sched_yield() {
|
||||
error!("sched_yield failed: {:?}", e);
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut mgs = self.memcgs.blocking_write();
|
||||
mgs.record_eviction(&infov);
|
||||
mgs.record_eviction(&removed_infov);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn check_psi_get_info(&mut self) -> Vec<Info> {
|
||||
self.memcgs.blocking_write().check_psi_get_info()
|
||||
}
|
||||
|
||||
fn update_info(&self, infov: &mut Vec<Info>) {
|
||||
self.memcgs.blocking_read().update_info(infov);
|
||||
}
|
||||
|
||||
pub fn need_work(&self) -> bool {
|
||||
self.memcgs.blocking_read().need_work()
|
||||
}
|
||||
|
||||
pub fn reset_timer(&mut self) {
|
||||
self.memcgs.blocking_write().timeout.reset();
|
||||
}
|
||||
|
||||
pub fn get_remaining_tokio_duration(&self) -> TokioDuration {
|
||||
self.memcgs.blocking_read().get_remaining_tokio_duration()
|
||||
}
|
||||
|
||||
pub async fn async_get_remaining_tokio_duration(&self) -> TokioDuration {
|
||||
self.memcgs.read().await.get_remaining_tokio_duration()
|
||||
}
|
||||
|
||||
pub fn work(&mut self) -> Result<()> {
|
||||
/* Refresh memcgroups info from host and store it to infov. */
|
||||
self.refresh(&HashSet::new())
|
||||
.map_err(|e| anyhow!("first refresh failed: {}", e))?;
|
||||
let mut infov = self.check_psi_get_info();
|
||||
|
||||
let swap = self.memcgs.blocking_read().config.swap;
|
||||
|
||||
/* Run aging with infov. */
|
||||
self.run_aging(&mut infov, swap);
|
||||
|
||||
self.run_eviction(&mut infov, swap)
|
||||
.map_err(|e| anyhow!("run_eviction failed: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_config(&mut self, new_config: OptionConfig) -> bool {
|
||||
self.memcgs.write().await.set_config(new_config)
|
||||
}
|
||||
|
||||
pub async fn get_status(&self) -> Vec<MemCgroup> {
|
||||
let mut mcgs = Vec::new();
|
||||
|
||||
let mgs = self.memcgs.read().await;
|
||||
for (_, m) in mgs.id_map.iter() {
|
||||
mcgs.push((*m).clone());
|
||||
}
|
||||
|
||||
mcgs
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_memcg_swap_not_available() {
|
||||
let m = MemCG::new(Config::default()).unwrap();
|
||||
assert!(m.swap_not_available().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memcg_get_swappiness() {
|
||||
let m = MemCG::new(Config::default()).unwrap();
|
||||
assert_eq!(m.get_swappiness(100, 50), 133);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memcg_need_work() {
|
||||
let m = MemCG::new(Config::default()).unwrap();
|
||||
assert_eq!(m.need_work(), true);
|
||||
}
|
||||
}
|
924
src/mem-agent/src/mglru.rs
Normal file
924
src/mem-agent/src/mglru.rs
Normal file
@ -0,0 +1,924 @@
|
||||
// Copyright (C) 2023 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::debug;
|
||||
use crate::warn;
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const WORKINGSET_ANON: usize = 0;
|
||||
const WORKINGSET_FILE: usize = 1;
|
||||
const LRU_GEN_ENABLED_PATH: &str = "/sys/kernel/mm/lru_gen/enabled";
|
||||
const LRU_GEN_PATH: &str = "/sys/kernel/debug/lru_gen";
|
||||
const MEMCGS_PATH: &str = "/sys/fs/cgroup/memory";
|
||||
|
||||
fn lru_gen_head_parse(line: &str) -> Result<(usize, String)> {
|
||||
let words: Vec<&str> = line.split_whitespace().map(|word| word.trim()).collect();
|
||||
if words.len() != 3 || words[0] != "memcg" {
|
||||
return Err(anyhow!("line {} format is not right", line));
|
||||
}
|
||||
|
||||
let id = usize::from_str_radix(words[1], 10)
|
||||
.map_err(|e| anyhow!("parse line {} failed: {}", line, e))?;
|
||||
|
||||
Ok((id, words[2].to_string()))
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct GenLRU {
|
||||
pub seq: u64,
|
||||
pub anon: u64,
|
||||
pub file: u64,
|
||||
pub birth: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl GenLRU {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
seq: 0,
|
||||
anon: 0,
|
||||
file: 0,
|
||||
birth: Utc::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct MGenLRU {
|
||||
pub min_seq: u64,
|
||||
pub max_seq: u64,
|
||||
pub last_birth: DateTime<Utc>,
|
||||
pub min_lru_index: usize,
|
||||
pub lru: Vec<GenLRU>,
|
||||
}
|
||||
|
||||
impl MGenLRU {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
min_seq: 0,
|
||||
max_seq: 0,
|
||||
last_birth: Utc::now(),
|
||||
min_lru_index: 0,
|
||||
lru: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//result:
|
||||
// last_line, HashMap<node_id, MGenLRU>
|
||||
fn lru_gen_lines_parse(reader: &mut BufReader<File>) -> Result<(String, HashMap<usize, MGenLRU>)> {
|
||||
let mut line = String::new();
|
||||
let mut ret_hash = HashMap::new();
|
||||
while line.len() > 0
|
||||
|| reader
|
||||
.read_line(&mut line)
|
||||
.map_err(|e| anyhow!("read file {} failed: {}", LRU_GEN_PATH, e))?
|
||||
> 0
|
||||
{
|
||||
let words: Vec<&str> = line.split_whitespace().map(|word| word.trim()).collect();
|
||||
if words.len() == 2 && words[0] == "node" {
|
||||
// Got a new node
|
||||
let node_id = usize::from_str_radix(words[1], 10)
|
||||
.map_err(|e| anyhow!("parse line {} failed: {}", line, e))?;
|
||||
let (ret_line, node_size) = lru_gen_seq_lines_parse(reader)
|
||||
.map_err(|e| anyhow!("lru_gen_seq_lines_parse failed: {}", e))?;
|
||||
if let Some(size) = node_size {
|
||||
ret_hash.insert(node_id, size);
|
||||
}
|
||||
line = ret_line;
|
||||
} else {
|
||||
// Cannot get node, return the line let caller handle it.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((line, ret_hash))
|
||||
}
|
||||
|
||||
fn str_to_u64(str: &str) -> Result<u64> {
|
||||
if str.starts_with("-") {
|
||||
warn!("{} format {} is not right", LRU_GEN_PATH, str);
|
||||
return Ok(0);
|
||||
}
|
||||
Ok(u64::from_str_radix(str, 10)?)
|
||||
}
|
||||
|
||||
//result:
|
||||
// last_line, Option<MGenLRU>
|
||||
fn lru_gen_seq_lines_parse(reader: &mut BufReader<File>) -> Result<(String, Option<MGenLRU>)> {
|
||||
let mut line = String::new();
|
||||
let mut ret = MGenLRU::new();
|
||||
let mut got = false;
|
||||
|
||||
while reader
|
||||
.read_line(&mut line)
|
||||
.map_err(|e| anyhow!("read file {} failed: {}", LRU_GEN_PATH, e))?
|
||||
> 0
|
||||
{
|
||||
let words: Vec<&str> = line.split_whitespace().map(|word| word.trim()).collect();
|
||||
if words.len() != 4 {
|
||||
//line is not format of seq line
|
||||
break;
|
||||
}
|
||||
|
||||
let msecs = i64::from_str_radix(words[1], 10)
|
||||
.map_err(|e| anyhow!("parse line {} failed: {}", line, e))?;
|
||||
// Use milliseconds because will got build error with try_milliseconds.
|
||||
#[allow(deprecated)]
|
||||
let birth = Utc::now() - Duration::milliseconds(msecs);
|
||||
|
||||
let mut gen = GenLRU::new();
|
||||
gen.birth = birth;
|
||||
|
||||
gen.seq = u64::from_str_radix(words[0], 10)
|
||||
.map_err(|e| anyhow!("parse line {} failed: {}", line, e))?;
|
||||
gen.anon = str_to_u64(&words[2 + WORKINGSET_ANON])
|
||||
.map_err(|e| anyhow!("parse line {} failed: {}", line, e))?;
|
||||
gen.file = str_to_u64(&words[2 + WORKINGSET_FILE])
|
||||
.map_err(|e| anyhow!("parse line {} failed: {}", line, e))?;
|
||||
|
||||
if !got {
|
||||
ret.min_seq = gen.seq;
|
||||
ret.max_seq = gen.seq;
|
||||
ret.last_birth = birth;
|
||||
got = true;
|
||||
} else {
|
||||
ret.min_seq = std::cmp::min(ret.min_seq, gen.seq);
|
||||
ret.max_seq = std::cmp::max(ret.max_seq, gen.seq);
|
||||
if ret.last_birth < birth {
|
||||
ret.last_birth = birth;
|
||||
}
|
||||
}
|
||||
if gen.seq == ret.min_seq {
|
||||
ret.min_lru_index = ret.lru.len();
|
||||
}
|
||||
ret.lru.push(gen);
|
||||
|
||||
line.clear();
|
||||
}
|
||||
|
||||
Ok((line, if got { Some(ret) } else { None }))
|
||||
}
|
||||
|
||||
// Just handle the path in the target_patchs. But if len of target_patchs is 0, will handle all paths.
|
||||
// if parse_line is false
|
||||
// HashMap<node_id, MGenLRU> will be empty.
|
||||
//result:
|
||||
// HashMap<path, (id, HashMap<node_id, MGenLRU>)>
|
||||
fn lru_gen_file_parse(
|
||||
mut reader: &mut BufReader<File>,
|
||||
target_patchs: &HashSet<String>,
|
||||
parse_line: bool,
|
||||
) -> Result<HashMap<String, (usize, HashMap<usize, MGenLRU>)>> {
|
||||
let mut line = String::new();
|
||||
let mut ret_hash = HashMap::new();
|
||||
while line.len() > 0
|
||||
|| reader
|
||||
.read_line(&mut line)
|
||||
.map_err(|e| anyhow!("read file {} failed: {}", LRU_GEN_PATH, e))?
|
||||
> 0
|
||||
{
|
||||
let mut clear_line = true;
|
||||
// Not handle the Err of lru_gen_head_parse because all lines of file will be checked.
|
||||
if let Ok((id, path)) = lru_gen_head_parse(&line) {
|
||||
if target_patchs.len() == 0 || target_patchs.contains(&path) {
|
||||
let seq_data = if parse_line {
|
||||
let (ret_line, data) = lru_gen_lines_parse(&mut reader).map_err(|e| {
|
||||
anyhow!(
|
||||
"lru_gen_seq_lines_parse file {} failed: {}",
|
||||
LRU_GEN_PATH,
|
||||
e
|
||||
)
|
||||
})?;
|
||||
line = ret_line;
|
||||
clear_line = false;
|
||||
data
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
|
||||
/*trace!(
|
||||
"lru_gen_file_parse path {} id {} seq_data {:#?}",
|
||||
path,
|
||||
id,
|
||||
seq_data
|
||||
);*/
|
||||
|
||||
ret_hash.insert(path.clone(), (id, seq_data));
|
||||
}
|
||||
}
|
||||
if clear_line {
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
Ok(ret_hash)
|
||||
}
|
||||
|
||||
fn file_parse(
|
||||
target_patchs: &HashSet<String>,
|
||||
parse_line: bool,
|
||||
) -> Result<HashMap<String, (usize, HashMap<usize, MGenLRU>)>> {
|
||||
let file = File::open(LRU_GEN_PATH)
|
||||
.map_err(|e| anyhow!("open file {} failed: {}", LRU_GEN_PATH, e))?;
|
||||
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
lru_gen_file_parse(&mut reader, target_patchs, parse_line)
|
||||
}
|
||||
|
||||
//result:
|
||||
// HashMap<path, (id, ino, HashMap<node_id, MGenLRU>)>
|
||||
pub fn host_memcgs_get(
|
||||
target_patchs: &HashSet<String>,
|
||||
parse_line: bool,
|
||||
) -> Result<HashMap<String, (usize, usize, HashMap<usize, MGenLRU>)>> {
|
||||
let mgs = file_parse(target_patchs, parse_line)
|
||||
.map_err(|e| anyhow!("mglru file_parse failed: {}", e))?;
|
||||
|
||||
let mut host_mgs = HashMap::new();
|
||||
for (path, (id, mglru)) in mgs {
|
||||
let host_path = PathBuf::from(MEMCGS_PATH).join(path.trim_start_matches('/'));
|
||||
|
||||
let metadata = match fs::metadata(host_path.clone()) {
|
||||
Err(e) => {
|
||||
if id != 0 {
|
||||
debug!("fs::metadata {:?} fail: {}", host_path, e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Ok(m) => m,
|
||||
};
|
||||
|
||||
host_mgs.insert(path, (id, metadata.ino() as usize, mglru));
|
||||
}
|
||||
|
||||
Ok(host_mgs)
|
||||
}
|
||||
|
||||
pub fn check() -> Result<()> {
|
||||
if crate::misc::is_test_environment() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let content = fs::read_to_string(LRU_GEN_ENABLED_PATH)
|
||||
.map_err(|e| anyhow!("open file {} failed: {}", LRU_GEN_ENABLED_PATH, e))?;
|
||||
let content = content.trim();
|
||||
let r = if content.starts_with("0x") {
|
||||
u32::from_str_radix(&content[2..], 16)
|
||||
} else {
|
||||
content.parse()
|
||||
};
|
||||
let enabled = r.map_err(|e| anyhow!("parse file {} failed: {}", LRU_GEN_ENABLED_PATH, e))?;
|
||||
if enabled != 7 {
|
||||
fs::write(LRU_GEN_ENABLED_PATH, "7")
|
||||
.map_err(|e| anyhow!("write file {} failed: {}", LRU_GEN_ENABLED_PATH, e))?;
|
||||
}
|
||||
|
||||
let _ = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(LRU_GEN_PATH)
|
||||
.map_err(|e| anyhow!("open file {} failed: {}", LRU_GEN_PATH, e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_aging(
|
||||
memcg_id: usize,
|
||||
numa_id: usize,
|
||||
max_seq: u64,
|
||||
can_swap: bool,
|
||||
force_scan: bool,
|
||||
) -> Result<()> {
|
||||
let cmd = format!(
|
||||
"+ {} {} {} {} {}",
|
||||
memcg_id, numa_id, max_seq, can_swap as i32, force_scan as i32
|
||||
);
|
||||
//trace!("send cmd {} to {}", cmd, LRU_GEN_PATH);
|
||||
fs::write(LRU_GEN_PATH, &cmd)
|
||||
.map_err(|e| anyhow!("write file {} cmd {} failed: {}", LRU_GEN_PATH, cmd, e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_eviction(
|
||||
memcg_id: usize,
|
||||
numa_id: usize,
|
||||
min_seq: u64,
|
||||
swappiness: u8,
|
||||
nr_to_reclaim: usize,
|
||||
) -> Result<()> {
|
||||
let cmd = format!(
|
||||
"- {} {} {} {} {}",
|
||||
memcg_id, numa_id, min_seq, swappiness, nr_to_reclaim
|
||||
);
|
||||
//trace!("send cmd {} to {}", cmd, LRU_GEN_PATH);
|
||||
fs::write(LRU_GEN_PATH, &cmd)
|
||||
.map_err(|e| anyhow!("write file {} cmd {} failed: {}", LRU_GEN_PATH, cmd, e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use maplit::hashmap;
|
||||
use once_cell::sync::OnceCell;
|
||||
use slog::{Drain, Level, Logger};
|
||||
use slog_async;
|
||||
use slog_scope::set_global_logger;
|
||||
use slog_term;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::io::Write;
|
||||
use std::sync::Mutex;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref TEST_MUTEX: Mutex<()> = Mutex::new(());
|
||||
}
|
||||
static LOGGER: OnceCell<slog_scope::GlobalLoggerGuard> = OnceCell::new();
|
||||
|
||||
impl GenLRU {
|
||||
pub fn new_from_data(seq: u64, anon: u64, file: u64, birth: DateTime<Utc>) -> Self {
|
||||
Self {
|
||||
seq,
|
||||
anon,
|
||||
file,
|
||||
birth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_logger() -> &'static slog_scope::GlobalLoggerGuard {
|
||||
LOGGER.get_or_init(|| {
|
||||
let decorator = slog_term::TermDecorator::new().stderr().build();
|
||||
let drain = slog_term::CompactFormat::new(decorator)
|
||||
.build()
|
||||
.filter_level(Level::Trace)
|
||||
.fuse();
|
||||
let drain = slog_async::Async::new(drain).build().fuse();
|
||||
let logger = Logger::root(drain, slog::o!());
|
||||
set_global_logger(logger.clone())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lru_gen_file_parse_single_no_parse() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let _logger = init_logger();
|
||||
|
||||
let mut reader = setup_test_file();
|
||||
let paths = ["/justto.slice/boot.mount".to_string()]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let ret = lru_gen_file_parse(&mut reader, &paths, false).unwrap();
|
||||
assert_eq!(ret.len(), 1);
|
||||
assert_eq!(
|
||||
ret.get("/justto.slice/boot.mount"),
|
||||
Some(&(16, HashMap::new()))
|
||||
);
|
||||
remove_test_file();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lru_gen_file_parse_multi_no_parse() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let _logger = init_logger();
|
||||
|
||||
let mut reader = setup_test_file();
|
||||
let paths = [
|
||||
"/aabbc/tea-loglogl".to_string(),
|
||||
"/aabbc/staraabbc".to_string(),
|
||||
"/aabbc/TEAE-iaabbc".to_string(),
|
||||
"/justto.slice/cpupower.service".to_string(),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let ret = lru_gen_file_parse(&mut reader, &paths, false).unwrap();
|
||||
assert_eq!(ret.len(), 4);
|
||||
assert_eq!(ret.get("/justto.slice/boot.mount"), None);
|
||||
assert_eq!(ret.get("/aabbc/tea-loglogl"), Some(&(30, hashmap![])));
|
||||
assert_eq!(ret.get("/aabbc/staraabbc"), Some(&(22, hashmap![])));
|
||||
assert_eq!(ret.get("/aabbc/TEAE-iaabbc"), Some(&(21, hashmap![])));
|
||||
assert_eq!(
|
||||
ret.get("/justto.slice/cpupower.service"),
|
||||
Some(&(0, hashmap![]))
|
||||
);
|
||||
remove_test_file();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lru_gen_file_parse_multi_parse() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let _logger = init_logger();
|
||||
|
||||
let mut reader = setup_test_file();
|
||||
let paths = [
|
||||
"/aabbc/tea-loglogl".to_string(),
|
||||
"/aabbc/staraabbc".to_string(),
|
||||
"/aabbc/TEAE-iaabbc".to_string(),
|
||||
"/justto.slice/cpupower.service".to_string(),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let ret = lru_gen_file_parse(&mut reader, &paths, true).unwrap();
|
||||
assert_eq!(ret.len(), 4);
|
||||
assert_eq!(ret.get("/justto.slice/boot.mount"), None);
|
||||
let birth_vec: Vec<DateTime<Utc>> = ret["/aabbc/tea-loglogl"].1[&0]
|
||||
.lru
|
||||
.iter()
|
||||
.map(|g| g.birth)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
ret.get("/aabbc/tea-loglogl"),
|
||||
Some(&(
|
||||
30,
|
||||
hashmap![0 => MGenLRU{min_seq: 0, max_seq: 3, last_birth: birth_vec[3], min_lru_index: 0,
|
||||
lru: vec![GenLRU::new_from_data(0, 20, 23, birth_vec[0]),
|
||||
GenLRU::new_from_data(1, 9, 23, birth_vec[1]),
|
||||
GenLRU::new_from_data(2, 20, 19, birth_vec[2]),
|
||||
GenLRU::new_from_data(3, 3, 8, birth_vec[3])]}]
|
||||
))
|
||||
);
|
||||
let birth_vec: Vec<DateTime<Utc>> = ret["/aabbc/staraabbc"].1[&1]
|
||||
.lru
|
||||
.iter()
|
||||
.map(|g| g.birth)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
ret.get("/aabbc/staraabbc"),
|
||||
Some(&(
|
||||
22,
|
||||
hashmap![1 => MGenLRU{min_seq: 2, max_seq: 5, last_birth: birth_vec[3], min_lru_index: 0,
|
||||
lru: vec![GenLRU::new_from_data(2, 0, 86201, birth_vec[0]),
|
||||
GenLRU::new_from_data(3, 253, 0, birth_vec[1]),
|
||||
GenLRU::new_from_data(4, 0, 0, birth_vec[2]),
|
||||
GenLRU::new_from_data(5, 2976, 41252, birth_vec[3])]}]
|
||||
))
|
||||
);
|
||||
let birth_vec: Vec<DateTime<Utc>> = ret["/aabbc/TEAE-iaabbc"].1[&0]
|
||||
.lru
|
||||
.iter()
|
||||
.map(|g| g.birth)
|
||||
.collect();
|
||||
let birth1_vec: Vec<DateTime<Utc>> = ret["/aabbc/TEAE-iaabbc"].1[&1]
|
||||
.lru
|
||||
.iter()
|
||||
.map(|g| g.birth)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
ret.get("/aabbc/TEAE-iaabbc"),
|
||||
Some(&(
|
||||
21,
|
||||
hashmap![0 => MGenLRU{min_seq: 0, max_seq: 3, last_birth: birth_vec[3], min_lru_index: 0, lru: vec![GenLRU::new_from_data(0, 0, 1, birth_vec[0]),
|
||||
GenLRU::new_from_data(1, 2, 3, birth_vec[1]),
|
||||
GenLRU::new_from_data(2, 6, 7, birth_vec[2]),
|
||||
GenLRU::new_from_data(3, 8, 9, birth_vec[3])]},
|
||||
1 => MGenLRU{min_seq: 3, max_seq: 6, last_birth: birth1_vec[3], min_lru_index: 0, lru: vec![GenLRU::new_from_data(3, 10, 11, birth1_vec[0]),
|
||||
GenLRU::new_from_data(4, 12, 16, birth1_vec[1]),
|
||||
GenLRU::new_from_data(5, 17, 18, birth1_vec[2]),
|
||||
GenLRU::new_from_data(6, 19, 20, birth1_vec[3])]}]
|
||||
))
|
||||
);
|
||||
let birth_vec: Vec<DateTime<Utc>> = ret["/justto.slice/cpupower.service"].1[&0]
|
||||
.lru
|
||||
.iter()
|
||||
.map(|g| g.birth)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
ret.get("/justto.slice/cpupower.service"),
|
||||
Some(&(
|
||||
0,
|
||||
hashmap![0 => MGenLRU{min_seq: 0, max_seq: 3, last_birth: birth_vec[3], min_lru_index: 0, lru: vec![GenLRU::new_from_data(0, 0, 33, birth_vec[0]),
|
||||
GenLRU::new_from_data(1, 0, 0, birth_vec[1]),
|
||||
GenLRU::new_from_data(2, 0, 0, birth_vec[2]),
|
||||
GenLRU::new_from_data(3, 0, 115, birth_vec[3])]}]
|
||||
))
|
||||
);
|
||||
remove_test_file();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lru_gen_file_parse_no_target_no_parse() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let _logger = init_logger();
|
||||
|
||||
let mut reader = setup_test_file();
|
||||
let paths = [].iter().cloned().collect();
|
||||
let ret = lru_gen_file_parse(&mut reader, &paths, false).unwrap();
|
||||
assert_eq!(ret.len(), 55);
|
||||
assert_eq!(ret.get("/justto.slice/boot.mount"), Some(&(16, hashmap![])));
|
||||
assert_eq!(ret.get("/aabbc/tea-loglogl"), Some(&(30, hashmap![])));
|
||||
assert_eq!(ret.get("/aabbc/staraabbc"), Some(&(22, hashmap![])));
|
||||
assert_eq!(ret.get("/aabbc/TEAE-iaabbc"), Some(&(21, hashmap![])));
|
||||
assert_eq!(
|
||||
ret.get("/justto.slice/cpupower.service"),
|
||||
Some(&(0, hashmap![]))
|
||||
);
|
||||
remove_test_file();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lru_gen_file_parse_no_target_parse() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let _logger = init_logger();
|
||||
|
||||
let mut reader = setup_test_file();
|
||||
let paths = [].iter().cloned().collect();
|
||||
let ret = lru_gen_file_parse(&mut reader, &paths, true).unwrap();
|
||||
assert_eq!(ret.len(), 55);
|
||||
let birth_vec: Vec<DateTime<Utc>> = ret["/aabbc/tea-loglogl"].1[&0]
|
||||
.lru
|
||||
.iter()
|
||||
.map(|g| g.birth)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
ret.get("/aabbc/tea-loglogl"),
|
||||
Some(&(
|
||||
30,
|
||||
hashmap![0 => MGenLRU{min_seq: 0, max_seq: 3, last_birth: birth_vec[3], min_lru_index: 0,
|
||||
lru: vec![GenLRU::new_from_data(0, 20, 23, birth_vec[0]),
|
||||
GenLRU::new_from_data(1, 9, 23, birth_vec[1]),
|
||||
GenLRU::new_from_data(2, 20, 19, birth_vec[2]),
|
||||
GenLRU::new_from_data(3, 3, 8, birth_vec[3])]}]
|
||||
))
|
||||
);
|
||||
let birth_vec: Vec<DateTime<Utc>> = ret["/aabbc/staraabbc"].1[&1]
|
||||
.lru
|
||||
.iter()
|
||||
.map(|g| g.birth)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
ret.get("/aabbc/staraabbc"),
|
||||
Some(&(
|
||||
22,
|
||||
hashmap![1 => MGenLRU{min_seq: 2, max_seq: 5, last_birth: birth_vec[3], min_lru_index: 0,
|
||||
lru: vec![GenLRU::new_from_data(2, 0, 86201, birth_vec[0]),
|
||||
GenLRU::new_from_data(3, 253, 0, birth_vec[1]),
|
||||
GenLRU::new_from_data(4, 0, 0, birth_vec[2]),
|
||||
GenLRU::new_from_data(5, 2976, 41252, birth_vec[3])]}]
|
||||
))
|
||||
);
|
||||
remove_test_file();
|
||||
}
|
||||
|
||||
fn setup_test_file() -> BufReader<File> {
|
||||
let data = r#"
|
||||
memcg 1 /
|
||||
node 0
|
||||
0 589359037 0 -0
|
||||
1 589359037 12 0
|
||||
2 589359037 0 0
|
||||
3 589359037 1265 2471
|
||||
memcg 2 /justto.slice
|
||||
node 0
|
||||
0 589334424 0 0
|
||||
1 589334424 0 0
|
||||
2 589334424 0 0
|
||||
3 589334424 0 0
|
||||
memcg 3 /justto.slice/justtod-teawated.service
|
||||
node 0
|
||||
0 589334423 0 217
|
||||
1 589334423 8 0
|
||||
2 589334423 0 0
|
||||
3 589334423 225 40293
|
||||
memcg 0 /justto.slice/justtod-readahead-replay.service
|
||||
node 0
|
||||
0 589334411 0 266694
|
||||
1 589334411 0 0
|
||||
2 589334411 0 0
|
||||
3 589334411 1 21
|
||||
memcg 0 /justto.slice/tea0-domainname.service
|
||||
node 0
|
||||
0 589334410 0 6
|
||||
1 589334410 0 0
|
||||
2 589334410 0 0
|
||||
3 589334410 0 198
|
||||
memcg 6 /justto.slice/justto-serial\x2dgetty.slice
|
||||
node 0
|
||||
0 589334408 0 1
|
||||
1 589334408 1 0
|
||||
2 589334408 0 0
|
||||
3 589334408 32 0
|
||||
memcg 7 /justto.slice/justto-getty.slice
|
||||
node 0
|
||||
0 589334408 0 0
|
||||
1 589334408 1 0
|
||||
2 589334408 0 0
|
||||
3 589334408 31 0
|
||||
memcg 8 /justto.slice/sys-kernel-debug.mount
|
||||
node 0
|
||||
0 589334407 0 6
|
||||
1 589334408 0 0
|
||||
2 589334408 0 0
|
||||
3 589334408 0 7
|
||||
memcg 10 /justto.slice/dev-hugepages.mount
|
||||
node 0
|
||||
0 589334406 0 1
|
||||
1 589334406 0 0
|
||||
2 589334406 0 0
|
||||
3 589334406 0 0
|
||||
memcg 0 /justto.slice/justtod-readahead-collect.service
|
||||
node 0
|
||||
0 589334405 0 96
|
||||
1 589334405 0 0
|
||||
2 589334405 0 0
|
||||
3 589334405 0 0
|
||||
memcg 12 /justto.slice/justto-justtod\x2dfsck.slice
|
||||
node 0
|
||||
0 589334403 0 25
|
||||
1 589334403 0 0
|
||||
2 589334403 0 0
|
||||
3 589334403 0 239
|
||||
memcg 13 /justto.slice/justto-selinux\x2dpolicy\x2dmigrate\x2dlocal\x2dchanges.slice
|
||||
node 0
|
||||
0 589334403 0 0
|
||||
1 589334403 0 0
|
||||
2 589334403 0 0
|
||||
3 589334403 0 0
|
||||
memcg 14 /justto.slice/dev-mqueue.mount
|
||||
node 0
|
||||
0 589334402 0 0
|
||||
1 589334402 0 0
|
||||
2 589334402 0 0
|
||||
3 589334402 0 0
|
||||
memcg 0 /justto.slice/tea2-monitor.service
|
||||
node 0
|
||||
0 589334401 0 9
|
||||
1 589334401 0 0
|
||||
2 589334401 0 0
|
||||
3 589334401 0 582
|
||||
memcg 0 /justto.slice/kmod-static-nodes.service
|
||||
node 0
|
||||
0 589334399 0 4
|
||||
1 589334399 1 0
|
||||
2 589334399 0 0
|
||||
3 589334399 0 33
|
||||
memcg 0 /justto.slice/plymouth-start.service
|
||||
node 0
|
||||
0 589334397 0 1
|
||||
1 589334397 0 0
|
||||
2 589334397 0 0
|
||||
3 589334397 0 0
|
||||
memcg 18 /justto.slice/sys-kernel-config.mount
|
||||
node 0
|
||||
0 589334396 0 0
|
||||
1 589334396 0 0
|
||||
2 589334396 0 0
|
||||
3 589334396 0 0
|
||||
memcg 5 /justto.slice/tea2-teaetad.service
|
||||
node 0
|
||||
0 589334383 0 4
|
||||
1 589334383 2 0
|
||||
2 589334383 0 0
|
||||
3 589334383 587 14
|
||||
memcg 0 /justto.slice/justtod-remount-fs.service
|
||||
node 0
|
||||
0 589334381 0 4
|
||||
1 589334381 0 0
|
||||
2 589334381 0 0
|
||||
3 589334381 0 11
|
||||
memcg 0 /justto.slice/justtod-tmpfiles-setup-dev.service
|
||||
node 0
|
||||
0 589334380 0 28
|
||||
1 589334380 0 0
|
||||
2 589334380 0 0
|
||||
3 589334380 0 32
|
||||
memcg 0 /justto.slice/justtod-sysctl.service
|
||||
node 0
|
||||
0 589334378 0 42
|
||||
1 589334378 0 0
|
||||
2 589334378 0 0
|
||||
3 589334378 0 13
|
||||
memcg 0 /justto.slice/justtod-teawate-flush.service
|
||||
node 0
|
||||
0 589334367 0 8
|
||||
1 589334367 0 0
|
||||
2 589334367 0 0
|
||||
3 589334367 0 141
|
||||
memcg 0 /justto.slice/justtod-udev-trigger.service
|
||||
node 0
|
||||
0 589334365 0 5
|
||||
1 589334365 0 0
|
||||
2 589334365 0 0
|
||||
3 589334365 0 103
|
||||
memcg 0 /justto.slice/tea0-readonly.service
|
||||
node 0
|
||||
0 589334364 0 163
|
||||
1 589334364 0 0
|
||||
2 589334364 0 0
|
||||
3 589334364 0 35
|
||||
memcg 0 /justto.slice/justtod-random-seed.service
|
||||
node 0
|
||||
0 589334363 0 38
|
||||
1 589334363 0 0
|
||||
2 589334363 0 0
|
||||
3 589334363 0 9
|
||||
memcg 25 /justto.slice/justtod-udevd.service
|
||||
node 0
|
||||
0 589334362 0 12553
|
||||
1 589334362 249 0
|
||||
2 589334362 0 0
|
||||
3 589334362 124 1415
|
||||
memcg 15 /justto.slice/dev-disk-by\x2dlabel-SWAP.swap
|
||||
node 0
|
||||
0 589334085 0 5
|
||||
1 589334085 0 0
|
||||
2 589334085 0 0
|
||||
3 589334085 0 10
|
||||
memcg 16 /justto.slice/boot.mount
|
||||
node 0
|
||||
0 589334035 0 26
|
||||
1 589334035 0 0
|
||||
2 589334035 0 0
|
||||
3 589334035 0 0
|
||||
memcg 0 /justto.slice/plymouth-read-write.service
|
||||
node 0
|
||||
0 589334011 0 9
|
||||
1 589334011 0 0
|
||||
2 589334011 0 0
|
||||
3 589334011 0 27
|
||||
memcg 0 /justto.slice/tea0-import-state.service
|
||||
node 0
|
||||
0 589334008 0 5
|
||||
1 589334008 0 0
|
||||
2 589334008 0 0
|
||||
3 589334008 0 45
|
||||
memcg 0 /justto.slice/justtod-tmpfiles-setup.service
|
||||
node 0
|
||||
0 589333868 0 8
|
||||
1 589333868 0 0
|
||||
2 589333868 0 0
|
||||
3 589333868 0 0
|
||||
memcg 0 /justto.slice/justtod-update-utmp.service
|
||||
node 0
|
||||
0 589333772 0 5
|
||||
1 589333772 1 0
|
||||
2 589333772 0 0
|
||||
3 589333772 0 74
|
||||
memcg 0 /justto.slice/network.service
|
||||
node 0
|
||||
0 589333758 0 2480
|
||||
1 589333758 0 0
|
||||
2 589333758 0 0
|
||||
3 589333758 0 542
|
||||
memcg 0 /justto.slice/tea0-dmesg.service
|
||||
node 0
|
||||
0 589333757 0 35
|
||||
1 589333757 0 0
|
||||
2 589333757 0 0
|
||||
3 589333757 0 37
|
||||
memcg 0 /justto.slice/cpupower.service
|
||||
node 0
|
||||
0 589333755 0 33
|
||||
1 589333755 0 0
|
||||
2 589333755 0 0
|
||||
3 589333755 0 115
|
||||
memcg 0 /justto.slice/justtod-user-sessions.service
|
||||
node 0
|
||||
0 589333749 0 4
|
||||
1 589333749 0 0
|
||||
2 589333749 0 0
|
||||
3 589333749 0 8
|
||||
memcg 0 /justto.slice/sysstat.service
|
||||
node 0
|
||||
0 589333747 0 17
|
||||
1 589333747 0 0
|
||||
2 589333747 0 0
|
||||
3 589333747 0 41
|
||||
memcg 26 /justto.slice/mcelog.service
|
||||
node 0
|
||||
0 589333745 0 41
|
||||
1 589333745 2 0
|
||||
2 589333745 0 0
|
||||
3 589333745 554 37
|
||||
memcg 27 /justto.slice/dbus.service
|
||||
node 0
|
||||
0 589333743 0 82
|
||||
1 589333743 1 0
|
||||
2 589333743 0 0
|
||||
3 589333743 119 216
|
||||
memcg 20 /justto.slice/syslog-ng.service
|
||||
node 0
|
||||
0 589333722 0 5889
|
||||
1 589333722 2 0
|
||||
2 589333722 0 0
|
||||
3 589333722 418 6488
|
||||
memcg 0 /justto.slice/cpunoturbo.service
|
||||
node 0
|
||||
0 589333596 0 1
|
||||
1 589333596 0 0
|
||||
2 589333596 0 0
|
||||
3 589333596 0 0
|
||||
memcg 4 /justto.slice/staraabbcctl.service
|
||||
node 0
|
||||
0 589327556 0 7
|
||||
1 589327556 2 0
|
||||
2 589327556 0 0
|
||||
3 589327556 69 11
|
||||
memcg 19 /justto.slice/sshd.service
|
||||
node 0
|
||||
0 589327547 0 9670
|
||||
1 589327547 11 0
|
||||
2 589327547 0 0
|
||||
3 589327547 2304 421
|
||||
memcg 0 /justto.slice/vmcore-collect.service
|
||||
node 0
|
||||
0 589327544 0 3
|
||||
1 589327544 0 0
|
||||
2 589327544 0 0
|
||||
3 589327544 0 0
|
||||
memcg 0 /justto.slice/kdump.service
|
||||
node 0
|
||||
0 589327543 0 417259
|
||||
1 589327543 2 0
|
||||
2 589327543 0 0
|
||||
3 589327543 0 0
|
||||
memcg 31 /justto.slice/proc-sys-fs-binfmt_misc.mount
|
||||
node 0
|
||||
0 589311768 0 0
|
||||
1 589311768 0 0
|
||||
2 589311768 0 0
|
||||
3 589311768 0 0
|
||||
memcg 32 /justto.slice/ntpd.service
|
||||
node 0
|
||||
0 589297199 0 14
|
||||
1 589297199 2 0
|
||||
2 589297199 0 0
|
||||
3 589297199 120 198
|
||||
memcg 29 /justto.slice/crond.service
|
||||
node 0
|
||||
0 589297184 0 115157
|
||||
1 589297184 2 0
|
||||
2 589297184 0 0
|
||||
3 589297184 195 324
|
||||
memcg 0 /justto.slice/justtod-tmpfiles-clean.service
|
||||
node 0
|
||||
0 588459896 0 8
|
||||
1 588459896 0 0
|
||||
2 588459896 0 0
|
||||
3 588459896 0 0
|
||||
memcg 9 /docker.slice
|
||||
node 0
|
||||
0 589334407 0 13919
|
||||
1 589334407 7 0
|
||||
2 589334407 0 0
|
||||
3 589334407 7254 146884
|
||||
memcg 17 /aabbc
|
||||
node 0
|
||||
0 589327431 0 0
|
||||
1 589327431 0 0
|
||||
2 589327431 0 0
|
||||
3 589327431 0 0
|
||||
memcg 22 /aabbc/staraabbc
|
||||
node 1
|
||||
2 589327430 0 86201
|
||||
3 589327430 253 0
|
||||
4 589327430 0 0
|
||||
5 589327430 2976 41252
|
||||
memcg 21 /aabbc/TEAE-iaabbc
|
||||
node 0
|
||||
0 589324388 0 1
|
||||
1 589324388 2 3
|
||||
2 589324388 6 7
|
||||
3 589324388 8 9
|
||||
node 1
|
||||
3 589324388 10 11
|
||||
4 589324388 12 16
|
||||
5 589324388 17 18
|
||||
6 589324388 19 20
|
||||
memcg 28 /aabbc/teawa_tea
|
||||
node 0
|
||||
0 589324387 0 69337
|
||||
1 589324387 2 0
|
||||
2 589324387 0 0
|
||||
3 589324387 1892 6103
|
||||
memcg 30 /aabbc/tea-loglogl
|
||||
node 0
|
||||
0 589324385 20 23
|
||||
1 589324385 9 23
|
||||
2 589324385 20 19
|
||||
3 589324380 3 8
|
||||
|
||||
"#;
|
||||
let mut file = File::create("test_lru_gen").unwrap();
|
||||
file.write_all(data.as_bytes()).unwrap();
|
||||
|
||||
let file = File::open("test_lru_gen").unwrap();
|
||||
BufReader::new(file)
|
||||
}
|
||||
|
||||
fn remove_test_file() {
|
||||
fs::remove_file("test_lru_gen").unwrap();
|
||||
}
|
||||
}
|
71
src/mem-agent/src/misc.rs
Normal file
71
src/mem-agent/src/misc.rs
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub fn sl() -> slog::Logger {
|
||||
slog_scope::logger().new(slog::o!("subsystem" => "mem-agent"))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
slog::info!(crate::misc::sl(), "{}", format_args!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($arg:tt)*) => {
|
||||
slog::info!(crate::misc::sl(), "{}", format_args!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => {
|
||||
slog::info!(crate::misc::sl(), "{}", format_args!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace {
|
||||
($($arg:tt)*) => {
|
||||
slog::info!(crate::misc::sl(), "{}", format_args!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)*) => {
|
||||
slog::info!(crate::misc::sl(), "{}", format_args!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn is_test_environment() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub fn is_test_environment() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_environment_check() {
|
||||
assert!(is_test_environment());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_log_macro() {
|
||||
error!("error");
|
||||
warn!("warn");
|
||||
info!("info");
|
||||
trace!("trace");
|
||||
debug!("debug");
|
||||
}
|
||||
}
|
44
src/mem-agent/src/proc.rs
Normal file
44
src/mem-agent/src/proc.rs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
fn get_meminfo(opt: &str) -> Result<u64> {
|
||||
let file = File::open("/proc/meminfo")?;
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
if line.starts_with(opt) {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() >= 2 {
|
||||
let kb = parts[1].parse::<u64>()?;
|
||||
return Ok(kb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("no {} found", opt))
|
||||
}
|
||||
|
||||
pub fn get_memfree_kb() -> Result<u64> {
|
||||
get_meminfo("MemFree:")
|
||||
}
|
||||
|
||||
pub fn get_freeswap_kb() -> Result<u64> {
|
||||
get_meminfo("SwapFree:")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_memfree_kb() {
|
||||
let memfree_kb = get_memfree_kb().unwrap();
|
||||
assert!(memfree_kb > 0);
|
||||
}
|
||||
}
|
260
src/mem-agent/src/psi.rs
Normal file
260
src/mem-agent/src/psi.rs
Normal file
@ -0,0 +1,260 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::info;
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
|
||||
const CGROUP_PATH: &str = "/sys/fs/cgroup/";
|
||||
const MEM_PSI: &str = "memory.pressure";
|
||||
const IO_PSI: &str = "io.pressure";
|
||||
|
||||
fn find_psi_subdirs() -> Result<PathBuf> {
|
||||
if PathBuf::from(CGROUP_PATH).is_dir() {
|
||||
for entry in fs::read_dir(CGROUP_PATH)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
if path.join(MEM_PSI).is_file() && path.join(IO_PSI).is_file() {
|
||||
return Ok(path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("cannot find cpuacct dir in {}", CGROUP_PATH))
|
||||
} else {
|
||||
Err(anyhow!("{} is not a directory", CGROUP_PATH))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(psi_path: &PathBuf) -> Result<PathBuf> {
|
||||
if crate::misc::is_test_environment() {
|
||||
return Ok(psi_path.clone());
|
||||
}
|
||||
|
||||
let p = if psi_path.as_os_str().is_empty() {
|
||||
find_psi_subdirs().map_err(|e| anyhow!("find_psi_subdirs failed: {}", e))?
|
||||
} else {
|
||||
psi_path.clone()
|
||||
};
|
||||
|
||||
let mem_psi_path = p.join(MEM_PSI);
|
||||
let _ = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(mem_psi_path.clone())
|
||||
.map_err(|e| anyhow!("open file {:?} failed: {}", mem_psi_path, e))?;
|
||||
|
||||
info!("psi is available at {:?}", p);
|
||||
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
fn read_pressure_some_total(file_path: PathBuf) -> Result<u64> {
|
||||
let file = File::open(file_path).map_err(|e| anyhow!("File::open failed: {}", e))?;
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
let mut first_line = String::new();
|
||||
if reader
|
||||
.read_line(&mut first_line)
|
||||
.map_err(|e| anyhow!("reader.read_line failed: {}", e))?
|
||||
<= 0
|
||||
{
|
||||
return Err(anyhow!("File is empty"));
|
||||
}
|
||||
|
||||
let parts: Vec<&str> = first_line.split_whitespace().collect();
|
||||
let total_str = parts.get(4).ok_or_else(|| anyhow!("format is not right"))?;
|
||||
let val = total_str
|
||||
.split('=')
|
||||
.nth(1)
|
||||
.ok_or_else(|| anyhow!("format is not right"))?;
|
||||
|
||||
let total_value = val
|
||||
.parse::<u64>()
|
||||
.map_err(|e| anyhow!("parse {} failed: {}", total_str, e))?;
|
||||
|
||||
Ok(total_value)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Period {
|
||||
path: PathBuf,
|
||||
last_psi: u64,
|
||||
last_update_time: DateTime<Utc>,
|
||||
include_child: bool,
|
||||
}
|
||||
|
||||
impl Period {
|
||||
pub fn new(path: &PathBuf, include_child: bool) -> Self {
|
||||
Self {
|
||||
path: path.to_owned(),
|
||||
last_psi: 0,
|
||||
last_update_time: Utc::now(),
|
||||
include_child,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path_pressure_us(&self, psi_name: &str) -> Result<u64> {
|
||||
let cur_path = self.path.join(psi_name);
|
||||
let mut parent_val = read_pressure_some_total(cur_path.clone())
|
||||
.map_err(|e| anyhow!("read_pressure_some_total {:?} failed: {}", cur_path, e))?;
|
||||
|
||||
if !self.include_child {
|
||||
let mut child_val = 0;
|
||||
let entries = fs::read_dir(self.path.clone())
|
||||
.map_err(|e| anyhow!("fs::read_dir failed: {}", e))?;
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|e| anyhow!("get path failed: {}", e))?;
|
||||
let epath = entry.path();
|
||||
|
||||
if epath.is_dir() {
|
||||
let full_path = self.path.join(entry.file_name()).join(psi_name);
|
||||
|
||||
child_val += read_pressure_some_total(full_path.clone()).map_err(|e| {
|
||||
anyhow!("read_pressure_some_total {:?} failed: {}", full_path, e)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
if parent_val < child_val {
|
||||
parent_val = 0;
|
||||
} else {
|
||||
parent_val -= child_val;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(parent_val)
|
||||
}
|
||||
|
||||
pub fn get_percent(&mut self) -> Result<u64> {
|
||||
let now = Utc::now();
|
||||
let mut psi = self
|
||||
.get_path_pressure_us(MEM_PSI)
|
||||
.map_err(|e| anyhow!("get_path_pressure_us MEM_PSI {:?} failed: {}", self.path, e))?;
|
||||
psi += self
|
||||
.get_path_pressure_us(IO_PSI)
|
||||
.map_err(|e| anyhow!("get_path_pressure_us IO_PSI {:?} failed: {}", self.path, e))?;
|
||||
|
||||
let mut percent = 0;
|
||||
|
||||
if self.last_psi != 0 && self.last_psi < psi && self.last_update_time < now {
|
||||
let us = (now - self.last_update_time).num_milliseconds() as u64 * 1000;
|
||||
|
||||
if us != 0 {
|
||||
percent = (psi - self.last_psi) * 100 / us;
|
||||
}
|
||||
}
|
||||
|
||||
self.last_psi = psi;
|
||||
self.last_update_time = now;
|
||||
|
||||
Ok(percent)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
fn test_read_pressure_some_total() {
|
||||
remove_fake_file();
|
||||
let val = read_pressure_some_total(PathBuf::from(setup_fake_file())).unwrap();
|
||||
assert_eq!(val, 37820);
|
||||
remove_fake_file();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_period() {
|
||||
remove_fake_cgroup_dir();
|
||||
|
||||
let dir = setup_fake_cgroup_dir();
|
||||
|
||||
let period = Period::new(&dir, true);
|
||||
let us = period.get_path_pressure_us(MEM_PSI).unwrap();
|
||||
assert_eq!(us, 37820);
|
||||
let us = period.get_path_pressure_us(IO_PSI).unwrap();
|
||||
assert_eq!(us, 82345);
|
||||
|
||||
let period = Period::new(&dir, false);
|
||||
let us = period.get_path_pressure_us(MEM_PSI).unwrap();
|
||||
assert_eq!(us, 26688);
|
||||
let us = period.get_path_pressure_us(IO_PSI).unwrap();
|
||||
assert_eq!(us, 66879);
|
||||
|
||||
remove_fake_cgroup_dir();
|
||||
}
|
||||
|
||||
fn write_fake_file(path: &PathBuf, data: &str) {
|
||||
let mut file = File::create(path).unwrap();
|
||||
file.write_all(data.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn setup_fake_file() -> String {
|
||||
let data = r#"some avg10=0.00 avg60=0.00 avg300=0.00 total=37820
|
||||
full avg10=0.00 avg60=0.00 avg300=0.00 total=28881
|
||||
"#;
|
||||
|
||||
write_fake_file(&PathBuf::from("test_psi"), data);
|
||||
|
||||
"test_psi".to_string()
|
||||
}
|
||||
|
||||
fn remove_fake_file() {
|
||||
let _ = fs::remove_file("test_psi");
|
||||
}
|
||||
|
||||
fn setup_fake_cgroup_dir() -> PathBuf {
|
||||
let dir = PathBuf::from("fake_cgroup");
|
||||
fs::create_dir(&dir).unwrap();
|
||||
let mem_psi = dir.join(MEM_PSI);
|
||||
let io_psi = dir.join(IO_PSI);
|
||||
let data = r#"some avg10=0.00 avg60=0.00 avg300=0.00 total=37820
|
||||
full avg10=0.00 avg60=0.00 avg300=0.00 total=28881
|
||||
"#;
|
||||
write_fake_file(&mem_psi, data);
|
||||
let data = r#"some avg10=0.00 avg60=0.00 avg300=0.00 total=82345
|
||||
full avg10=0.00 avg60=0.00 avg300=0.00 total=67890
|
||||
"#;
|
||||
write_fake_file(&io_psi, data);
|
||||
|
||||
let child_dir = dir.join("c1");
|
||||
fs::create_dir(&child_dir).unwrap();
|
||||
let child_mem_psi = child_dir.join(MEM_PSI);
|
||||
let child_io_psi = child_dir.join(IO_PSI);
|
||||
let data = r#"some avg10=0.00 avg60=0.00 avg300=0.00 total=3344
|
||||
full avg10=0.00 avg60=0.00 avg300=0.00 total=1234
|
||||
"#;
|
||||
write_fake_file(&child_mem_psi, data);
|
||||
let data = r#"some avg10=0.00 avg60=0.00 avg300=0.00 total=5566
|
||||
full avg10=0.00 avg60=0.00 avg300=0.00 total=5678
|
||||
"#;
|
||||
write_fake_file(&child_io_psi, data);
|
||||
|
||||
let child_dir = dir.join("c2");
|
||||
fs::create_dir(&child_dir).unwrap();
|
||||
let child_mem_psi = child_dir.join(MEM_PSI);
|
||||
let child_io_psi = child_dir.join(IO_PSI);
|
||||
let data = r#"some avg10=0.00 avg60=0.00 avg300=0.00 total=7788
|
||||
full avg10=0.00 avg60=0.00 avg300=0.00 total=4321
|
||||
"#;
|
||||
write_fake_file(&child_mem_psi, data);
|
||||
let data = r#"some avg10=0.00 avg60=0.00 avg300=0.00 total=9900
|
||||
full avg10=0.00 avg60=0.00 avg300=0.00 total=8765
|
||||
"#;
|
||||
write_fake_file(&child_io_psi, data);
|
||||
|
||||
dir
|
||||
}
|
||||
|
||||
fn remove_fake_cgroup_dir() {
|
||||
let _ = fs::remove_dir_all("fake_cgroup");
|
||||
}
|
||||
}
|
88
src/mem-agent/src/timer.rs
Normal file
88
src/mem-agent/src/timer.rs
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright (C) 2024 Ant group. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use chrono::Duration as ChronoDuration;
|
||||
use chrono::{DateTime, Utc};
|
||||
use tokio::time::Duration as TokioDuration;
|
||||
|
||||
fn chrono_to_tokio_duration(chrono_duration: ChronoDuration) -> TokioDuration {
|
||||
if chrono_duration.num_nanoseconds().unwrap_or(0) >= 0 {
|
||||
TokioDuration::new(
|
||||
chrono_duration.num_seconds() as u64,
|
||||
(chrono_duration.num_nanoseconds().unwrap_or(0) % 1_000_000_000) as u32,
|
||||
)
|
||||
} else {
|
||||
TokioDuration::new(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Timeout {
|
||||
sleep_duration: ChronoDuration,
|
||||
start_wait_time: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Timeout {
|
||||
pub fn new(secs: u64) -> Self {
|
||||
Self {
|
||||
sleep_duration: ChronoDuration::microseconds(secs as i64 * 1000000),
|
||||
/* Make sure the first time to timeout */
|
||||
start_wait_time: Utc::now() - ChronoDuration::microseconds(secs as i64 * 1000000) * 2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
let now = Utc::now();
|
||||
now >= self.start_wait_time + self.sleep_duration
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.start_wait_time = Utc::now();
|
||||
}
|
||||
|
||||
pub fn remaining_tokio_duration(&self) -> TokioDuration {
|
||||
let now = Utc::now();
|
||||
|
||||
if now >= self.start_wait_time + self.sleep_duration {
|
||||
return TokioDuration::ZERO;
|
||||
}
|
||||
|
||||
chrono_to_tokio_duration(self.start_wait_time + self.sleep_duration - now)
|
||||
}
|
||||
|
||||
pub fn set_sleep_duration(&mut self, secs: u64) {
|
||||
self.sleep_duration = ChronoDuration::microseconds(secs as i64 * 1000000);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn test_timeout() {
|
||||
let mut timeout = Timeout::new(1);
|
||||
|
||||
// timeout should be timeout at once.
|
||||
assert_eq!(timeout.is_timeout(), true);
|
||||
|
||||
timeout.reset();
|
||||
|
||||
assert_eq!(timeout.is_timeout(), false);
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
assert_eq!(timeout.is_timeout(), true);
|
||||
|
||||
timeout.set_sleep_duration(2);
|
||||
timeout.reset();
|
||||
|
||||
assert_eq!(timeout.is_timeout(), false);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
assert_eq!(timeout.is_timeout(), false);
|
||||
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
assert_eq!(timeout.is_timeout(), true);
|
||||
}
|
||||
}
|
@ -259,6 +259,102 @@ container_pipe_size=@PIPESIZE@
|
||||
# (default: 3000)
|
||||
#reconnect_timeout_ms = 3000
|
||||
|
||||
[agent.@PROJECT_TYPE@.mem_agent]
|
||||
# Control the mem-agent function enable or disable.
|
||||
# Default to false
|
||||
#mem_agent_enable = true
|
||||
|
||||
# Control the mem-agent memcg function disable or enable
|
||||
# Default to false
|
||||
#memcg_disable = false
|
||||
|
||||
# Control the mem-agent function swap enable or disable.
|
||||
# Default to false
|
||||
#memcg_swap = false
|
||||
|
||||
# Control the mem-agent function swappiness max number.
|
||||
# Default to 50
|
||||
#memcg_swappiness_max = 50
|
||||
|
||||
# Control the mem-agent memcg function wait period seconds
|
||||
# Default to 600
|
||||
#memcg_period_secs = 600
|
||||
|
||||
# Control the mem-agent memcg wait period PSI percent limit.
|
||||
# If the percentage of memory and IO PSI stall time within
|
||||
# the memcg waiting period for a cgroup exceeds this value,
|
||||
# then the aging and eviction for this cgroup will not be
|
||||
# executed after this waiting period.
|
||||
# Default to 1
|
||||
#memcg_period_psi_percent_limit = 1
|
||||
|
||||
# Control the mem-agent memcg eviction PSI percent limit.
|
||||
# If the percentage of memory and IO PSI stall time for a cgroup
|
||||
# exceeds this value during an eviction cycle, the eviction for
|
||||
# this cgroup will immediately stop and will not resume until
|
||||
# the next memcg waiting period.
|
||||
# Default to 1
|
||||
#memcg_eviction_psi_percent_limit = 1
|
||||
|
||||
# Control the mem-agent memcg eviction run aging count min.
|
||||
# A cgroup will only perform eviction when the number of aging cycles
|
||||
# in memcg is greater than or equal to memcg_eviction_run_aging_count_min.
|
||||
# Default to 3
|
||||
#memcg_eviction_run_aging_count_min = 3
|
||||
|
||||
# Control the mem-agent compact function disable or enable
|
||||
# Default to false
|
||||
#compact_disable = false
|
||||
|
||||
# Control the mem-agent compaction function wait period seconds
|
||||
# Default to 600
|
||||
#compact_period_secs = 600
|
||||
|
||||
# Control the mem-agent compaction function wait period PSI percent limit.
|
||||
# If the percentage of memory and IO PSI stall time within
|
||||
# the compaction waiting period exceeds this value,
|
||||
# then the compaction will not be executed after this waiting period.
|
||||
# Default to 1
|
||||
#compact_period_psi_percent_limit = 1
|
||||
|
||||
# Control the mem-agent compaction function compact PSI percent limit.
|
||||
# During compaction, the percentage of memory and IO PSI stall time
|
||||
# is checked every second. If this percentage exceeds
|
||||
# compact_psi_percent_limit, the compaction process will stop.
|
||||
# Default to 5
|
||||
#compact_psi_percent_limit = 5
|
||||
|
||||
# Control the maximum number of seconds for each compaction of mem-agent compact function.
|
||||
# Default to 180
|
||||
#compact_sec_max = 180
|
||||
|
||||
# Control the mem-agent compaction function compact order.
|
||||
# compact_order is use with compact_threshold.
|
||||
# Default to 9
|
||||
#compact_order = 9
|
||||
|
||||
# Control the mem-agent compaction function compact threshold.
|
||||
# compact_threshold is the pages number.
|
||||
# When examining the /proc/pagetypeinfo, if there's an increase in the
|
||||
# number of movable pages of orders smaller than the compact_order
|
||||
# compared to the amount following the previous compaction,
|
||||
# and this increase surpasses a certain threshold—specifically,
|
||||
# more than 'compact_threshold' number of pages.
|
||||
# Or the number of free pages has decreased by 'compact_threshold'
|
||||
# since the previous compaction.
|
||||
# then the system should initiate another round of memory compaction.
|
||||
# Default to 1024
|
||||
#compact_threshold = 1024
|
||||
|
||||
# Control the mem-agent compaction function force compact times.
|
||||
# After one compaction, if there has not been a compaction within
|
||||
# the next compact_force_times times, a compaction will be forced
|
||||
# regardless of the system's memory situation.
|
||||
# If compact_force_times is set to 0, will do force compaction each time.
|
||||
# If compact_force_times is set to 18446744073709551615, will never do force compaction.
|
||||
# Default to 18446744073709551615
|
||||
#compact_force_times = 18446744073709551615
|
||||
|
||||
[runtime]
|
||||
# If enabled, the runtime will log additional debug messages to the
|
||||
# system log
|
||||
|
@ -293,6 +293,16 @@ static AGENT_CMDS: &[AgentCmd] = &[
|
||||
st: ServiceType::Agent,
|
||||
fp: agent_cmd_sandbox_set_policy,
|
||||
},
|
||||
AgentCmd {
|
||||
name: "MemAgentMemcgSet",
|
||||
st: ServiceType::Agent,
|
||||
fp: agent_cmd_mem_agent_memcg_set,
|
||||
},
|
||||
AgentCmd {
|
||||
name: "MemAgentCompactSet",
|
||||
st: ServiceType::Agent,
|
||||
fp: agent_cmd_mem_agent_compact_set,
|
||||
},
|
||||
];
|
||||
|
||||
static BUILTIN_CMDS: & [BuiltinCmd] = &[
|
||||
@ -2126,3 +2136,50 @@ fn agent_cmd_sandbox_set_policy(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn agent_cmd_mem_agent_memcg_set(
|
||||
ctx: &Context,
|
||||
client: &AgentServiceClient,
|
||||
_health: &HealthClient,
|
||||
_options: &mut Options,
|
||||
args: &str,
|
||||
) -> Result<()> {
|
||||
//let req = MemAgentMemcgConfig::default();
|
||||
let req: MemAgentMemcgConfig = utils::make_request(args)?;
|
||||
|
||||
let ctx = clone_context(ctx);
|
||||
|
||||
info!(sl!(), "sending request"; "request" => format!("{:?}", req));
|
||||
|
||||
let reply = client
|
||||
.mem_agent_memcg_set(ctx, &req)
|
||||
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
|
||||
|
||||
info!(sl!(), "response received";
|
||||
"response" => format!("{:?}", reply));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn agent_cmd_mem_agent_compact_set(
|
||||
ctx: &Context,
|
||||
client: &AgentServiceClient,
|
||||
_health: &HealthClient,
|
||||
_options: &mut Options,
|
||||
args: &str,
|
||||
) -> Result<()> {
|
||||
let req: MemAgentCompactConfig = utils::make_request(args)?;
|
||||
|
||||
let ctx = clone_context(ctx);
|
||||
|
||||
info!(sl!(), "sending request"; "request" => format!("{:?}", req));
|
||||
|
||||
let reply = client
|
||||
.mem_agent_compact_set(ctx, &req)
|
||||
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
|
||||
|
||||
info!(sl!(), "response received";
|
||||
"response" => format!("{:?}", reply));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -472,6 +472,7 @@ static_check_license_headers()
|
||||
--exclude="tools/packaging/qemu/default-configs/*" \
|
||||
--exclude="src/libs/protocols/protos/gogo/*.proto" \
|
||||
--exclude="src/libs/protocols/protos/google/*.proto" \
|
||||
--exclude="src/mem-agent/example/protocols/protos/google/protobuf/*.proto" \
|
||||
--exclude="src/libs/*/test/texture/*" \
|
||||
--exclude="*.dic" \
|
||||
-EL $extra_args "\<${pattern}\>" \
|
||||
|
@ -0,0 +1,4 @@
|
||||
CONFIG_LRU_GEN=y
|
||||
CONFIG_DEBUG_FS=y
|
||||
CONFIG_PSI=y
|
||||
CONFIG_PSI_DEFAULT_DISABLED=y
|
@ -1 +1 @@
|
||||
140
|
||||
141
|
||||
|
Loading…
Reference in New Issue
Block a user