Skip to content

Commit

Permalink
Add Aperf Stats
Browse files Browse the repository at this point in the history
Collect the time it takes the various aperf data types to gather the
data and print them to a file. This also adds a tab in the report.
  • Loading branch information
janaknat committed Jun 20, 2024
1 parent 49b553b commit c045f15
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 9 deletions.
5 changes: 4 additions & 1 deletion src/data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod aperf_stats;
pub mod constants;
pub mod cpu_utilization;
pub mod diskstats;
Expand Down Expand Up @@ -26,6 +27,7 @@ pub mod vmstat;
use crate::visualizer::{GetData, ReportParams};
use crate::{InitParams, APERF_FILE_FORMAT};
use anyhow::Result;
use aperf_stats::AperfStat;
use chrono::prelude::*;
use cpu_utilization::{CpuUtilization, CpuUtilizationRaw};
use diskstats::{Diskstats, DiskstatsRaw};
Expand Down Expand Up @@ -300,7 +302,8 @@ processed_data!(
MeminfoData,
Netstat,
PerfProfile,
Flamegraph
Flamegraph,
AperfStat
);

macro_rules! noop {
Expand Down
158 changes: 158 additions & 0 deletions src/data/aperf_stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
extern crate ctor;

use crate::data::{ProcessedData, TimeEnum};
use crate::visualizer::{DataVisualizer, GetData, GraphLimitType, GraphMetadata, ReportParams};
use crate::VISUALIZATION_DATA;
use anyhow::Result;
use chrono::prelude::*;
use ctor::ctor;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;

pub static APERF_RUN_STATS_FILE_NAME: &str = "aperf_run_stats";

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AperfStat {
pub time: TimeEnum,
pub name: String,
pub data: HashMap<String, u64>,
}

impl AperfStat {
fn new() -> Self {
AperfStat {
time: TimeEnum::DateTime(Utc::now()),
name: String::new(),
data: HashMap::new(),
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PerDataTypeStat {
pub name: String,
pub collect: Vec<DataPoint>,
pub print: Vec<DataPoint>,
pub metadata: GraphMetadata,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DataPoint {
pub time: TimeEnum,
pub time_taken: u64,
}

fn get_key_data(values: Vec<AperfStat>, key: String) -> Result<String> {
let mut end_value = PerDataTypeStat {
name: key.clone(),
collect: Vec::new(),
print: Vec::new(),
metadata: GraphMetadata::new(),
};
let time_zero = &values[0].time;

for value in &values {
let time_now = value.time - *time_zero;
for (k, v) in &value.data {
if !k.contains(&key) {
continue;
}
let datapoint = DataPoint {
time: time_now,
time_taken: *v,
};
end_value.metadata.update_limits(GraphLimitType::UInt64(*v));
if k.contains(&key) {
if k.contains("print") {
end_value.print.push(datapoint);
} else {
end_value.collect.push(datapoint);
}
}
}
}

Ok(serde_json::to_string(&end_value)?)
}

impl GetData for AperfStat {
fn custom_raw_data_parser(&mut self, params: ReportParams) -> Result<Vec<ProcessedData>> {
let mut raw_data: Vec<ProcessedData> = Vec::new();

let file: Result<fs::File> = Ok(fs::OpenOptions::new()
.read(true)
.open(params.data_file_path)
.expect("Could not open APerf Stats file"));
loop {
match bincode::deserialize_from::<_, AperfStat>(file.as_ref().unwrap()) {
Ok(v) => raw_data.push(ProcessedData::AperfStat(v)),
Err(e) => match *e {
// EOF
bincode::ErrorKind::Io(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
break
}
e => panic!("Error when Deserializing APerf Stats data: {}", e),
},
};
}
Ok(raw_data)
}

fn get_calls(&mut self) -> Result<Vec<String>> {
Ok(vec!["keys".to_string(), "values".to_string()])
}

fn get_data(&mut self, buffer: Vec<ProcessedData>, query: String) -> Result<String> {
let mut values = Vec::new();
for data in buffer {
match data {
ProcessedData::AperfStat(ref value) => values.push(value.clone()),
_ => unreachable!(),
}
}
let param: Vec<(String, String)> = serde_urlencoded::from_str(&query).unwrap();
let (_, req_str) = &param[1];

match req_str.as_str() {
"keys" => {
let mut names = Vec::new();
names.push("aperf".to_string());
let keys = values[0].data.keys().clone();

for k in keys {
let datatype: Vec<&str> = k.split('-').collect();
if !names.contains(&datatype[0].to_string()) {
names.push(datatype[0].to_string());
}
}
Ok(serde_json::to_string(&names)?)
}
"values" => {
let (_, key) = &param[2];
get_key_data(values, key.to_string())
}
_ => panic!("Unsupported API"),
}
}
}

#[ctor]
fn init_aperf_stats() {
let file_name = APERF_RUN_STATS_FILE_NAME.to_string();
let js_file_name = file_name.clone() + ".js";
let aperf_stat = AperfStat::new();
let mut dv = DataVisualizer::new(
ProcessedData::AperfStat(aperf_stat.clone()),
file_name.clone(),
js_file_name,
include_str!(concat!(env!("JS_DIR"), "/aperf_run_stats.js")).to_string(),
file_name.clone(),
);
dv.has_custom_raw_data_parser();

VISUALIZATION_DATA
.lock()
.unwrap()
.add_visualizer(file_name.clone(), dv);
}
73 changes: 73 additions & 0 deletions src/html_files/aperf_run_stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
let got_aperfstat_data = false;

function getAperfEntry(elem, key, run_data) {
var value = JSON.parse(run_data);
let collect = value.collect;
let print = value.print;
let x_collect = [];
let y_collect = [];
let x_print = [];
let y_print = [];
for (var i = 0; i < collect.length; i++) {
x_collect.push(collect[i].time.TimeDiff);
y_collect.push(collect[i].time_taken);
}
for (var i = 0; i < print.length; i++) {
x_print.push(print[i].time.TimeDiff);
y_print.push(print[i].time_taken);
}
var TESTER = elem;
var aperfstat_collect_data: Partial<Plotly.PlotData> = {
name: `${key}-collect`,
x: x_collect,
y: y_collect,
type: 'scatter',
};
var aperfstat_print_data: Partial<Plotly.PlotData> = {
name: `${key}-print`,
x: x_print,
y: y_print,
type: 'scatter',
};
var layout = {
title: `${key}`,
xaxis: {
title: 'Time (s)',
},
yaxis: {
title: 'Time (us)',
},
}
Plotly.newPlot(TESTER, [aperfstat_collect_data, aperfstat_print_data], layout, { frameMargins: 0 });
}

function getAperfEntries(run, container_id, keys, run_data) {
for (let i = 0; i < all_run_keys.length; i++) {
let value = all_run_keys[i];
var elem = document.createElement('div');
elem.id = `aperfstat-${run}-${value}`;
elem.style.float = "none";
if (keys.length == 0) {
elem.innerHTML = "No data collected";
addElemToNode(container_id, elem);
return;
}
addElemToNode(container_id, elem);
emptyOrCallback(keys, false, getAperfEntry, elem, value, run_data);
}
}

function aperfStat() {
if (got_aperfstat_data) {
return;
}
clear_and_create('aperfstat');
form_graph_limits(aperf_run_stats_raw_data);
for (let i = 0; i < aperf_run_stats_raw_data['runs'].length; i++) {
let run_name = aperf_run_stats_raw_data['runs'][i]['name'];
let elem_id = `${run_name}-aperfstat-per-data`;
let this_run_data = aperf_run_stats_raw_data['runs'][i];
getAperfEntries(run_name, elem_id, this_run_data['keys'], this_run_data['key_values']);
}
got_aperfstat_data = true;
}
6 changes: 6 additions & 0 deletions src/html_files/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<button class="tablinks" name="interrupts">Interrupt Data</button>
<button class="tablinks" name="disk_stats">Disk Stats</button>
<button class="tablinks" name="netstat">Net Stats</button>
<button class="tablinks" name="aperfstat">Aperf Stats</button>
</div>
<div class="content">
<div id="header" class="all-runs"></div>
Expand Down Expand Up @@ -94,6 +95,9 @@ <h3>Hide N/A and all-zero graphs:</h3>
</div>
<div id="netstat-runs"></div>
</div>
<div id="aperfstat" class="tabcontent">
<div id="aperfstat-runs"></div>
</div>
</div>
<script type="text/javascript" src="data/js/runs.js"></script>
<script type="text/javascript" src="data/js/system_info.js"></script>
Expand All @@ -109,6 +113,7 @@ <h3>Hide N/A and all-zero graphs:</h3>
<script type="text/javascript" src="data/js/netstat.js"></script>
<script type="text/javascript" src="data/js/perf_profile.js"></script>
<script type="text/javascript" src="data/js/flamegraph.js"></script>
<script type="text/javascript" src="data/js/aperf_run_stats.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/plotly.js" charset="utf-8"></script>
<script type="text/javascript" src="js/system_info.js"></script>
Expand All @@ -124,6 +129,7 @@ <h3>Hide N/A and all-zero graphs:</h3>
<script type="text/javascript" src="js/disk_stats.js"></script>
<script type="text/javascript" src="js/perf_stat.js"></script>
<script type="text/javascript" src="js/netstat.js"></script>
<script type="text/javascript" src="js/aperf_run_stats.js"></script>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
1 change: 1 addition & 0 deletions src/html_files/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ DataTypes.set('flamegraphs', {name: 'flamegraphs', hideClass: '', trueId: '', ca
DataTypes.set('top_functions', {name: 'topfunctions', hideClass: '', trueId: '', callback: topFunctions});
DataTypes.set('processes', {name: 'processes', hideClass: '', trueId: '', callback: processes});
DataTypes.set('perfstat', {name: 'perfstat', hideClass: '', trueId: '', callback: perfStat});
DataTypes.set('aperfstat', {name: 'aperfstat', hideClass: '', trueId: '', callback: aperfStat});

function openData(evt: Event, elem: HTMLButtonElement) {
var tabName: string = elem.name;
Expand Down
1 change: 1 addition & 0 deletions src/html_files/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare let meminfo_raw_data;
declare let netstat_raw_data;
declare let perf_profile_raw_data;
declare let flamegraph_raw_data;
declare let aperf_run_stats_raw_data;

let all_run_keys: Array<string> = new Array<string>();
let key_limits: Map<string, Limits> = new Map<string, Limits>();
Expand Down
Loading

0 comments on commit c045f15

Please sign in to comment.