blob: 9507d69586c38cf62b592a3eda95ce3bee343ec1 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use camino::Utf8PathBuf;
use schemars::JsonSchema;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Diagnostics configuration options for the diagnostics area.
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct DiagnosticsConfig {
#[serde(default)]
pub archivist: Option<ArchivistConfig>,
/// The set of pipeline config files to supply to archivist.
#[serde(default)]
pub archivist_pipelines: Vec<ArchivistPipeline>,
#[serde(default)]
pub additional_serial_log_components: Vec<String>,
#[serde(default)]
pub sampler: SamplerConfig,
#[serde(default)]
pub memory_monitor: MemoryMonitorConfig,
}
/// Diagnostics configuration options for the archivist configuration area.
#[derive(Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub enum ArchivistConfig {
Default,
LowMem,
}
/// A single archivist pipeline config.
#[derive(Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ArchivistPipeline {
/// The name of the pipeline.
pub name: PipelineType,
/// The files to add to the pipeline.
/// Zero files is not valid.
#[schemars(schema_with = "crate::vec_path_schema")]
pub files: Vec<Utf8PathBuf>,
}
#[derive(Debug, PartialEq, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
#[serde(into = "String")]
pub enum PipelineType {
/// A pipeline for feedback data.
Feedback,
/// A custom pipeline.
Custom(String),
}
impl Serialize for PipelineType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Feedback => serializer.serialize_str("feedback"),
Self::Custom(s) => serializer.serialize_str(&s),
}
}
}
impl<'de> Deserialize<'de> for PipelineType {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let variant = String::deserialize(de)?.to_lowercase();
match variant.as_str() {
"feedback" => Ok(PipelineType::Feedback),
"lowpan" => Ok(PipelineType::Custom("lowpan".into())),
"legacy_metrics" => Ok(PipelineType::Custom("legacy_metrics".into())),
other => {
Err(D::Error::unknown_variant(other, &["feedback", "lowpan", "legacy_metrics"]))
}
}
}
}
impl std::fmt::Display for PipelineType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Feedback => "feedback",
Self::Custom(name) => &name,
}
)
}
}
impl From<PipelineType> for String {
fn from(t: PipelineType) -> Self {
t.to_string()
}
}
/// Diagnostics configuration options for the sampler configuration area.
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SamplerConfig {
/// The metrics configs to pass to sampler.
#[serde(default)]
#[schemars(schema_with = "crate::vec_path_schema")]
pub metrics_configs: Vec<Utf8PathBuf>,
/// The fire configs to pass to sampler.
#[serde(default)]
#[schemars(schema_with = "crate::vec_path_schema")]
pub fire_configs: Vec<Utf8PathBuf>,
}
/// Diagnostics configuration options for the memory monitor configuration area.
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MemoryMonitorConfig {
/// The memory buckets config file to provide to memory monitor.
#[serde(default)]
#[schemars(schema_with = "crate::option_path_schema")]
pub buckets: Option<Utf8PathBuf>,
/// Control whether a pressure change should trigger a capture.
#[serde(default)]
pub capture_on_pressure_change: Option<bool>,
/// Expected delay between scheduled captures upon imminent OOM, in
/// seconds.
#[serde(default)]
pub imminent_oom_capture_delay_s: Option<u32>,
/// Expected delay between scheduled captures when the memory
/// pressure is critical, in seconds.
#[serde(default)]
pub critical_capture_delay_s: Option<u32>,
/// Expected delay between scheduled captures when the memory
/// pressure is abnormal, in seconds.
#[serde(default)]
pub warning_capture_delay_s: Option<u32>,
/// Expected delay between scheduled captures when the memory
/// pressure is normal, in seconds.
#[serde(default)]
pub normal_capture_delay_s: Option<u32>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::platform_config::PlatformConfig;
use assembly_util as util;
#[test]
fn test_diagnostics_archivist_default_service() {
let json5 = r#""default""#;
let mut cursor = std::io::Cursor::new(json5);
let archivist: ArchivistConfig = util::from_reader(&mut cursor).unwrap();
assert_eq!(archivist, ArchivistConfig::Default);
}
#[test]
fn test_diagnostics_archivist_low_mem() {
let json5 = r#""low-mem""#;
let mut cursor = std::io::Cursor::new(json5);
let archivist: ArchivistConfig = util::from_reader(&mut cursor).unwrap();
assert_eq!(archivist, ArchivistConfig::LowMem);
}
#[test]
fn test_diagnostics_missing() {
let json5 = r#"
{
build_type: "eng",
}
"#;
let mut cursor = std::io::Cursor::new(json5);
let config: PlatformConfig = util::from_reader(&mut cursor).unwrap();
assert_eq!(
config.diagnostics,
DiagnosticsConfig {
archivist: None,
archivist_pipelines: Vec::new(),
additional_serial_log_components: Vec::new(),
sampler: SamplerConfig::default(),
memory_monitor: MemoryMonitorConfig::default(),
}
);
}
#[test]
fn test_diagnostics_empty() {
let json5 = r#"
{
build_type: "eng",
diagnostics: {}
}
"#;
let mut cursor = std::io::Cursor::new(json5);
let config: PlatformConfig = util::from_reader(&mut cursor).unwrap();
assert_eq!(
config.diagnostics,
DiagnosticsConfig {
archivist: None,
archivist_pipelines: Vec::new(),
additional_serial_log_components: Vec::new(),
sampler: SamplerConfig::default(),
memory_monitor: MemoryMonitorConfig::default(),
}
);
}
#[test]
fn test_diagnostics_with_additional_log_tags() {
let json5 = r#"
{
build_type: "eng",
diagnostics: {
additional_serial_log_components: [
"/foo",
]
}
}
"#;
let mut cursor = std::io::Cursor::new(json5);
let config: PlatformConfig = util::from_reader(&mut cursor).unwrap();
assert_eq!(
config.diagnostics,
DiagnosticsConfig {
archivist: None,
archivist_pipelines: Vec::new(),
additional_serial_log_components: vec!["/foo".to_string()],
sampler: SamplerConfig::default(),
memory_monitor: MemoryMonitorConfig::default(),
}
);
}
}