diff --git a/src/data.rs b/src/data.rs index f5df7d6f..674f0e75 100644 --- a/src/data.rs +++ b/src/data.rs @@ -7,6 +7,7 @@ pub mod interrupts; pub mod sysctldata; pub mod perf_stat; pub mod processes; +pub mod meminfodata; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub mod intel_perf_events; #[cfg(target_arch = "aarch64")] @@ -31,6 +32,7 @@ use interrupts::{InterruptData, InterruptDataRaw}; use sysctldata::SysctlData; use perf_stat::{PerfStatRaw, PerfStat}; use processes::{ProcessesRaw, Processes}; +use meminfodata::{MeminfoDataRaw, MeminfoData}; pub struct DataType { pub data: Data, @@ -202,7 +204,8 @@ data!( InterruptDataRaw, SysctlData, PerfStatRaw, - ProcessesRaw + ProcessesRaw, + MeminfoDataRaw ); processed_data!( @@ -214,7 +217,8 @@ processed_data!( InterruptData, SysctlData, PerfStat, - Processes + Processes, + MeminfoData ); macro_rules! noop { () => (); } diff --git a/src/data/meminfodata.rs b/src/data/meminfodata.rs new file mode 100644 index 00000000..ae08cdfb --- /dev/null +++ b/src/data/meminfodata.rs @@ -0,0 +1,419 @@ +extern crate ctor; + +use anyhow::Result; +use crate::data::{CollectData, Data, ProcessedData, DataType, TimeEnum}; +use crate::{PDError, PERFORMANCE_DATA, VISUALIZATION_DATA}; +use crate::visualizer::{DataVisualizer, GetData}; +use chrono::prelude::*; +use ctor::ctor; +use log::{trace}; +use serde::{Deserialize, Serialize}; +use procfs::Meminfo; +use std::collections::HashMap; +use std::io::BufReader; +use strum::IntoEnumIterator; +use strum_macros::{Display, EnumString, EnumIter}; + +pub static MEMINFO_FILE_NAME: &str = "meminfo"; + +/// Gather Meminfo raw data. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct MeminfoDataRaw { + pub time: TimeEnum, + pub data: String, +} + +impl MeminfoDataRaw { + pub fn new() -> Self { + MeminfoDataRaw { + time: TimeEnum::DateTime(Utc::now()), + data: String::new(), + } + } +} + +impl CollectData for MeminfoDataRaw { + fn collect_data(&mut self) -> Result<()> { + self.time = TimeEnum::DateTime(Utc::now()); + self.data = String::new(); + self.data = std::fs::read_to_string("/proc/meminfo")?; + trace!("{:#?}", self.data); + Ok(()) + } +} + +#[derive(Debug, Display, EnumString, EnumIter)] +pub enum MeminfoKeys { + #[strum(serialize = "Mem Total")] + MemTotal, + #[strum(serialize = "Mem Free")] + MemFree, + #[strum(serialize = "Mem Available")] + MemAvailable, + Buffers, + Cached, + #[strum(serialize = "Swap Cached")] + SwapCached, + Active, + Inactive, + #[strum(serialize = "Active Anon")] + ActiveAnon, + #[strum(serialize = "Inactive Anon")] + InactiveAnon, + #[strum(serialize = "Active File")] + ActiveFile, + #[strum(serialize = "Inactive File")] + InactiveFile, + Unevictable, + Mlocked, + #[strum(serialize = "High Total")] + HighTotal, + #[strum(serialize = "High Free")] + HighFree, + #[strum(serialize = "Low Total")] + LowTotal, + #[strum(serialize = "Low Free")] + LowFree, + #[strum(serialize = "Mmap Copy")] + MmapCopy, + #[strum(serialize = "Swap Total")] + SwapTotal, + #[strum(serialize = "Swap Free")] + SwapFree, + Dirty, + Writeback, + #[strum(serialize = "Anon Pages")] + AnonPages, + Mapped, + Shmem, + Slab, + #[strum(serialize = "S Reclaimable")] + SReclaimable, + #[strum(serialize = "S Unreclaim")] + SUnreclaim, + #[strum(serialize = "Kernel Stack")] + KernelStack, + #[strum(serialize = "Page Tables")] + PageTables, + Quicklists, + #[strum(serialize = "NFS Unstable")] + NfsUnstable, + Bounce, + #[strum(serialize = "Writeback Tmp")] + WritebackTmp, + #[strum(serialize = "Commit Limit")] + CommitLimit, + #[strum(serialize = "Committed As")] + CommittedAs, + #[strum(serialize = "Vmalloc Total")] + VmallocTotal, + #[strum(serialize = "Vmalloc Used")] + VmallocUsed, + #[strum(serialize = "Vmalloc Chunk")] + VmallocChunk, + #[strum(serialize = "Hardware Corrupted")] + HardwareCorrupted, + #[strum(serialize = "Anon HugePages")] + AnonHugepages, + #[strum(serialize = "Shmem HugePages")] + ShmemHugepages, + #[strum(serialize = "Shmem Pmd Mapped")] + ShmemPmdMapped, + #[strum(serialize = "Cma Total")] + CmaTotal, + #[strum(serialize = "Cma Free")] + CmaFree, + #[strum(serialize = "HugePages_Total")] + HugepagesTotal, + #[strum(serialize = "HugePages_Free")] + HugepagesFree, + #[strum(serialize = "HugePages_Rsvd")] + HugepagesRsvd, + #[strum(serialize = "HugePages_Surp")] + HugepagesSurp, + Hugepagesize, + #[strum(serialize = "Direct Map 4K")] + DirectMap4k, + #[strum(serialize = "Direct Map 4M")] + DirectMap4M, + #[strum(serialize = "Direct Map 2M")] + DirectMap2M, + #[strum(serialize = "Direct Map 1G")] + DirectMap1G, + Hugetlb, + #[strum(serialize = "Per CPU")] + PerCpu, + #[strum(serialize = "K Reclaimable")] + KReclaimable, + #[strum(serialize = "File Pmd Mapped")] + FilePmdMapped, + #[strum(serialize = "File Huge Pages")] + FileHugePages, +} + +fn get_keys() -> Result { + let mut end_values: Vec = Vec::new(); + for key in MeminfoKeys::iter() { + end_values.push(key.to_string()); + } + return Ok(serde_json::to_string(&end_values)?) +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct MemData { + pub name: String, + pub values: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct MemEntry { + pub time: TimeEnum, + pub value: u64, +} + +fn get_values(values: Vec, key: String) -> Result { + let time_zero = values[0].time; + let mut end_value = MemData {name: key.clone(), values: Vec::new()}; + for v in values { + let value = v.data + .get(&key) + .ok_or(PDError::VisualizerMeminfoValueGetError(key.to_string()))?; + let mementry = MemEntry {time: v.time - time_zero, value: *value}; + end_value.values.push(mementry); + } + return Ok(serde_json::to_string(&end_value)?) +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct MeminfoData { + pub time: TimeEnum, + pub data: HashMap, +} + +impl MeminfoData { + fn new() -> Self { + MeminfoData { + time: TimeEnum::DateTime(Utc::now()), + data: HashMap::new(), + } + } + + fn add(&mut self, key: String, value: u64) { + self.data.insert(key, value); + } + + fn set_time(&mut self, time: TimeEnum) { + self.time = time; + } +} + +impl GetData for MeminfoData { + fn process_raw_data(&mut self, buffer: Data) -> Result { + let raw_value = match buffer { + Data::MeminfoDataRaw(ref value) => value, + _ => panic!("Invalid Data type in raw file"), + }; + let reader = BufReader::new(raw_value.data.as_bytes()); + let meminfo = Meminfo::from_reader(reader)?; + let mut meminfo_data = MeminfoData::new(); + meminfo_data.add(MeminfoKeys::MemTotal.to_string(), meminfo.mem_total); + meminfo_data.add(MeminfoKeys::MemFree.to_string(), meminfo.mem_free); + meminfo_data.add(MeminfoKeys::MemAvailable.to_string(), meminfo.mem_available.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Buffers.to_string(), meminfo.buffers); + meminfo_data.add(MeminfoKeys::Cached.to_string(), meminfo.cached); + meminfo_data.add(MeminfoKeys::SwapCached.to_string(), meminfo.swap_cached); + meminfo_data.add(MeminfoKeys::Active.to_string(), meminfo.active); + meminfo_data.add(MeminfoKeys::Inactive.to_string(), meminfo.inactive); + meminfo_data.add(MeminfoKeys::ActiveAnon.to_string(), meminfo.active_anon.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::InactiveAnon.to_string(), meminfo.inactive_anon.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::ActiveFile.to_string(), meminfo.active_file.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::InactiveFile.to_string(), meminfo.inactive_file.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Unevictable.to_string(), meminfo.unevictable.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Mlocked.to_string(), meminfo.mlocked.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::HighTotal.to_string(), meminfo.high_total.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::HighFree.to_string(), meminfo.high_free.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::LowTotal.to_string(), meminfo.low_total.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::LowFree.to_string(), meminfo.low_free.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::MmapCopy.to_string(), meminfo.mmap_copy.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::SwapTotal.to_string(), meminfo.swap_total); + meminfo_data.add(MeminfoKeys::SwapFree.to_string(), meminfo.swap_free); + meminfo_data.add(MeminfoKeys::Dirty.to_string(), meminfo.dirty); + meminfo_data.add(MeminfoKeys::Writeback.to_string(), meminfo.writeback); + meminfo_data.add(MeminfoKeys::AnonPages.to_string(), meminfo.anon_pages.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Mapped.to_string(), meminfo.mapped); + meminfo_data.add(MeminfoKeys::Shmem.to_string(), meminfo.shmem.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Slab.to_string(), meminfo.slab); + meminfo_data.add(MeminfoKeys::SReclaimable.to_string(), meminfo.s_reclaimable.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::SUnreclaim.to_string(), meminfo.s_unreclaim.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::KernelStack.to_string(), meminfo.kernel_stack.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::PageTables.to_string(), meminfo.page_tables.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Quicklists.to_string(), meminfo.quicklists.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::NfsUnstable.to_string(), meminfo.nfs_unstable.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Bounce.to_string(), meminfo.bounce.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::WritebackTmp.to_string(), meminfo.writeback_tmp.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::CommitLimit.to_string(), meminfo.commit_limit.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::CommittedAs.to_string(), meminfo.committed_as); + meminfo_data.add(MeminfoKeys::VmallocTotal.to_string(), meminfo.vmalloc_total); + meminfo_data.add(MeminfoKeys::VmallocUsed.to_string(), meminfo.vmalloc_used); + meminfo_data.add(MeminfoKeys::VmallocChunk.to_string(), meminfo.vmalloc_chunk); + meminfo_data.add(MeminfoKeys::HardwareCorrupted.to_string(), meminfo.hardware_corrupted.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::AnonHugepages.to_string(), meminfo.anon_hugepages.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::ShmemHugepages.to_string(), meminfo.shmem_hugepages.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::ShmemPmdMapped.to_string(), meminfo.shmem_pmd_mapped.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::CmaTotal.to_string(), meminfo.cma_total.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::CmaFree.to_string(), meminfo.cma_free.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::HugepagesTotal.to_string(), meminfo.hugepages_total.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::HugepagesFree.to_string(), meminfo.hugepages_free.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::HugepagesRsvd.to_string(), meminfo.hugepages_rsvd.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::HugepagesSurp.to_string(), meminfo.hugepages_surp.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Hugepagesize.to_string(), meminfo.hugepagesize.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::DirectMap4k.to_string(), meminfo.direct_map_4k.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::DirectMap4M.to_string(), meminfo.direct_map_4M.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::DirectMap2M.to_string(), meminfo.direct_map_2M.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::DirectMap1G.to_string(), meminfo.direct_map_1G.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::Hugetlb.to_string(), meminfo.hugetlb.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::PerCpu.to_string(), meminfo.per_cpu.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::KReclaimable.to_string(), meminfo.k_reclaimable.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::FilePmdMapped.to_string(), meminfo.file_pmd_mapped.unwrap_or_default()); + meminfo_data.add(MeminfoKeys::FileHugePages.to_string(), meminfo.file_huge_pages.unwrap_or_default()); + meminfo_data.set_time(raw_value.time); + let processed_data = ProcessedData::MeminfoData(meminfo_data); + Ok(processed_data) + } + + fn get_calls(&mut self) -> Result> { + let mut end_values = Vec::new(); + end_values.push("keys".to_string()); + end_values.push("values".to_string()); + Ok(end_values) + } + + fn get_data(&mut self, buffer: Vec, query: String) -> Result { + let mut values = Vec::new(); + for data in buffer { + match data { + ProcessedData::MeminfoData(ref value) => values.push(value.clone()), + _ => panic!("Invalid Data type in file"), + } + } + let param: Vec<(String, String)> = serde_urlencoded::from_str(&query).unwrap(); + let (_, req_str) = ¶m[1]; + + match req_str.as_str() { + "keys" => { + return get_keys(); + }, + "values" => { + let (_, key) = ¶m[2]; + return get_values(values, key.to_string()); + }, + _ => panic!("Unsupported API"), + } + } +} + +#[ctor] +fn init_meminfo() { + let meminfo_data_raw = MeminfoDataRaw::new(); + let file_name = MEMINFO_FILE_NAME.to_string(); + let dt = DataType::new( + Data::MeminfoDataRaw(meminfo_data_raw.clone()), + file_name.clone(), + false + ); + let js_file_name = file_name.clone() + &".js".to_string(); + let meminfo_data = MeminfoData::new(); + let dv = DataVisualizer::new( + ProcessedData::MeminfoData(meminfo_data), + file_name.clone(), + js_file_name, + include_str!(concat!(env!("JS_DIR"), "/meminfo.js")).to_string(), + file_name.clone(), + ); + + PERFORMANCE_DATA + .lock() + .unwrap() + .add_datatype(file_name.clone(), dt); + + VISUALIZATION_DATA + .lock() + .unwrap() + .add_visualizer(file_name.clone(), dv); +} + +#[cfg(test)] +mod tests { + use super::{MeminfoDataRaw, MeminfoData, MeminfoKeys, MemData}; + use crate::data::{CollectData, Data, ProcessedData}; + use crate::visualizer::GetData; + use std::collections::HashMap; + use strum::IntoEnumIterator; + + #[test] + fn test_collect_data() { + let mut meminfodata_raw = MeminfoDataRaw::new(); + + assert!(meminfodata_raw.collect_data().unwrap() == ()); + assert!(!meminfodata_raw.data.is_empty()); + } + + #[test] + fn test_keys() { + let mut meminfodata_raw = MeminfoDataRaw::new(); + let mut key_map = HashMap::new(); + for key in MeminfoKeys::iter() { + key_map.insert(key.to_string(), 0); + } + meminfodata_raw.collect_data().unwrap(); + let processed_data = MeminfoData::new().process_raw_data(Data::MeminfoDataRaw(meminfodata_raw)).unwrap(); + let mut meminfodata = MeminfoData::new(); + match processed_data { + ProcessedData::MeminfoData(value) => meminfodata = value, + _ => assert!(false, "Invalid data type in processed data"), + }; + let keys: Vec = meminfodata.data.clone().into_keys().collect(); + for key in keys { + assert!(key_map.contains_key(&key)); + let value = key_map.get(&key).unwrap() + 1; + key_map.insert(key, value); + } + let mut values: Vec = key_map.into_values().collect(); + values.dedup(); + assert!(values.len() == 1); + } + + #[test] + fn test_get_data_keys() { + let mut buffer: Vec = Vec::new(); + let mut meminfodata_raw = MeminfoDataRaw::new(); + let mut processed_buffer: Vec = Vec::new(); + + meminfodata_raw.collect_data().unwrap(); + buffer.push(Data::MeminfoDataRaw(meminfodata_raw)); + processed_buffer.push(MeminfoData::new().process_raw_data(buffer[0].clone()).unwrap()); + let json = MeminfoData::new().get_data(processed_buffer, "run=test&get=keys".to_string()).unwrap(); + let values: Vec = serde_json::from_str(&json).unwrap(); + assert!(values.len() > 0); + } + + #[test] + fn test_get_data_values() { + let mut buffer: Vec = Vec::new(); + let mut meminfodata_raw_zero = MeminfoDataRaw::new(); + let mut meminfodata_raw_one = MeminfoDataRaw::new(); + let mut processed_buffer: Vec = Vec::new(); + + meminfodata_raw_zero.collect_data().unwrap(); + meminfodata_raw_one.collect_data().unwrap(); + buffer.push(Data::MeminfoDataRaw(meminfodata_raw_zero)); + buffer.push(Data::MeminfoDataRaw(meminfodata_raw_one)); + for buf in buffer { + processed_buffer.push(MeminfoData::new().process_raw_data(buf).unwrap()); + } + let json = MeminfoData::new().get_data(processed_buffer, "run=test&get=values&key=Mem Total".to_string()).unwrap(); + let memdata: MemData = serde_json::from_str(&json).unwrap(); + assert!(memdata.name == "Mem Total"); + assert!(memdata.values.len() > 0); + } +} diff --git a/src/html_files/index.html b/src/html_files/index.html index ac694007..360ab701 100644 --- a/src/html_files/index.html +++ b/src/html_files/index.html @@ -13,6 +13,7 @@

APerf

+ @@ -36,6 +37,9 @@

Diff:

+
+
+
Loading...
@@ -62,6 +66,7 @@

Diff:

+ @@ -72,6 +77,7 @@

Diff:

+ diff --git a/src/html_files/index.ts b/src/html_files/index.ts index 9b6d4c3b..8b548687 100644 --- a/src/html_files/index.ts +++ b/src/html_files/index.ts @@ -20,6 +20,9 @@ function openData(evt: Event, elem: HTMLButtonElement) { if (tabName == "processes") { processes(); } + if (tabName == "meminfo") { + meminfo(); + } if (tabName == "vmstat") { vmStat(); } diff --git a/src/html_files/meminfo.ts b/src/html_files/meminfo.ts new file mode 100644 index 00000000..9e794c0f --- /dev/null +++ b/src/html_files/meminfo.ts @@ -0,0 +1,128 @@ +let got_meminfo_data = false; +let TB = 1073741824; +let GB = 1048576; +function get_divisor_unit(values) { + var total = 0; + for (i = 0; i < values.length; i++) { + total += values[i]; + } + let average = total / values.length; + if (average > TB) { + return { + divisor: TB, + unit: "TB", + }; + } + if (average > GB) { + return { + divisor: GB, + unit: "GB", + }; + } + return { + divisor: 1, + unit: "KB", + }; +} + +function getMeminfo(elem, key, run_data) { + var data = JSON.parse(run_data); + var x_data = []; + var y_data = []; + data.values.forEach(function (value, index, arr) { + x_data.push(value.time.TimeDiff); + + /* Bytes => kB */ + y_data.push(value.value / 1024); + }) + + var { divisor, unit } = get_divisor_unit(y_data); + if (key.includes("Mem Total") || + key.includes("Vmalloc Total") || + key.includes("Hugepagesize")) { + var mem_elem = document.createElement('h3'); + if (divisor == 1) { + mem_elem.innerHTML = `${key}: ${y_data[0]}KB`; + } else { + mem_elem.innerHTML = `${key}: ${(y_data[0] / divisor).toLocaleString(undefined, { minimumFractionDigits: 0 })}${unit} (${(y_data[0]).toLocaleString(undefined, { minimumFractionDigits: 0 })}KB)`; + } + addElemToNode(elem.id, mem_elem); + return; + } + if (key.includes("HugePages_")) { + divisor = 1; + unit = 'Count'; + } + for (i = 0; i < y_data.length; i++) { + y_data[i] /= divisor; + } + var meminfodata: Partial = { + x: x_data, + y: y_data, + type: 'scatter', + }; + var TESTER = elem; + var layout = { + title: key, + xaxis: { + title: 'Time (s)', + }, + yaxis: { + title: `${unit}`, + } + }; + Plotly.newPlot(TESTER, [meminfodata], layout, { frameMargins: 0 }); +} + +function getMeminfoKeys(run, container_id, keys, run_data) { + var data = keys; + data.forEach(function (value, index, arr) { + var elem = document.createElement('div'); + elem.id = `disk-stat-${run}-${value.name}`; + elem.style.float = "none"; + addElemToNode(container_id, elem); + setTimeout(() => { + getMeminfo(elem, value, run_data[value]); + }, 0); + }) +} + +function meminfo() { + if (got_meminfo_data) { + return; + } + var data = runs_raw; + var float_style = "none"; + if (data.length > 1) { + float_style = "left"; + } + var run_width = 100 / data.length; + clearElements('meminfo-runs'); + data.forEach(function (value, index, arr) { + // Run div + var run_div = document.createElement('div'); + let this_run_data; + run_div.id = `${value}-meminfo`; + run_div.style.float = float_style; + run_div.style.width = `${run_width}%`; + addElemToNode('meminfo-runs', run_div); + var run_node_id = run_div.id; + + // Run name + var h3_run_name = document.createElement('h3'); + h3_run_name.innerHTML = value; + h3_run_name.style.textAlign = "center"; + addElemToNode(run_node_id, h3_run_name); + + // Show data + var per_value_div = document.createElement('div'); + per_value_div.id = `${value}-meminfo-per-data`; + addElemToNode(run_node_id, per_value_div); + for (let i = 0; i < meminfo_raw_data['runs'].length; i++) { + if (meminfo_raw_data['runs'][i]['name'] == value) { + this_run_data = meminfo_raw_data['runs'][i]; + getMeminfoKeys(value, per_value_div.id, this_run_data['keys'], this_run_data['key_values']); + } + } + }) +} \ No newline at end of file diff --git a/src/html_files/utils.ts b/src/html_files/utils.ts index 15185984..59fd4bc0 100644 --- a/src/html_files/utils.ts +++ b/src/html_files/utils.ts @@ -8,6 +8,7 @@ declare let interrupts_raw_data; declare let disk_stats_raw_data; declare let perf_stat_raw_data; declare let processes_raw_data; +declare let meminfo_raw_data; class RunEntry { run: string; diff --git a/src/lib.rs b/src/lib.rs index bce15881..14f20610 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,9 @@ pub enum PDError { #[error("Run data not available")] InvalidRunData, + + #[error("Error getting Meminfo values for {}", .0)] + VisualizerMeminfoValueGetError(String), } lazy_static! {