From a1f359094b15f8fbf33b27b197620d37ce3a23eb Mon Sep 17 00:00:00 2001 From: Janakarajan Natarajan Date: Mon, 3 Jun 2024 19:39:22 +0000 Subject: [PATCH] Add support for selecting CPU(s) This will allow the user to select the CPU(s) for all the graphs in CPU Utilization, Perf Stat and Interrupt tabs. --- src/html_files/configure.png | Bin 0 -> 5933 bytes src/html_files/configure.ts | 130 ++++++++++++++++++++++++++++++ src/html_files/cpu_utilization.ts | 13 ++- src/html_files/index.css | 14 +++- src/html_files/index.html | 10 ++- src/html_files/index.ts | 3 + src/html_files/interrupts.ts | 9 ++- src/html_files/perf_stat.ts | 18 ++++- src/html_files/utils.ts | 14 +++- src/report.rs | 9 ++- 10 files changed, 205 insertions(+), 15 deletions(-) create mode 100644 src/html_files/configure.png create mode 100644 src/html_files/configure.ts diff --git a/src/html_files/configure.png b/src/html_files/configure.png new file mode 100644 index 0000000000000000000000000000000000000000..749dcdef672f947e176879971239ef9f77fcf350 GIT binary patch literal 5933 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 Tsn#m^Cl7FU@o+A2T%Y)F+^IgU literal 0 HcmV?d00001 diff --git a/src/html_files/configure.ts b/src/html_files/configure.ts new file mode 100644 index 00000000..ad5e2116 --- /dev/null +++ b/src/html_files/configure.ts @@ -0,0 +1,130 @@ +class RunConfig { + run: string; + cpu_count: number; + cpu_list: Array; +} + +var run_config: Map = new Map(); +var init_done = false; + +function getCPUList(run) { + return run_config.get(run).cpu_list; +} + +function formGlobalConfig() { + for (let i = 0; i < cpu_utilization_raw_data['runs'].length; i++) { + let this_run_data = cpu_utilization_raw_data['runs'][i]; + let run_name = this_run_data['name']; + var config = new RunConfig(); + + /* Elemet 0 is aggregate. Don't use that. */ + let key = this_run_data['keys'][1]; + config.cpu_count = JSON.parse(this_run_data['key_values'][key]).length; + config.cpu_list = new Array(); + for (let i = 0; i < config.cpu_count; i++) { + config.cpu_list.push(i.toString()); + } + run_config.set(run_name, config); + } +} + +function isCPUListUnchanged(check_cpu_list, run) { + return check_cpu_list.sort().toString() === run_config.get(run).cpu_list.sort().toString(); +} + +function allSelect(elem: HTMLInputElement, run) { + let checkboxes = document.getElementsByName(`${run}-cpulist`); + for (let i = 0; i < checkboxes.length; i++) { + (checkboxes[i] as HTMLInputElement).checked = elem.checked; + toggleCheckbox(checkboxes[i] as HTMLInputElement, run, false); + } +} + +function allCPUsCheck(run) { + let checkboxes = document.getElementsByName(`${run}-cpulist`); + let allToggled = true; + for (let i = 0; i < checkboxes.length; i++) { + if (!((checkboxes[i] as HTMLInputElement).checked)) { + allToggled = false; + break; + } + } + (document.getElementById(`${run}-CPUAll`) as HTMLInputElement).checked = allToggled; +} + +function toggleCheckbox(elem: HTMLInputElement, run, check) { + let cpu_list = run_config.get(run).cpu_list; + if (elem.checked) { + if (cpu_list.indexOf(elem.value) == -1) { + cpu_list.push(elem.value); + } + } else { + let index = cpu_list.indexOf(elem.value); + if (index != -1) { + cpu_list.splice(index, 1); + } + } + if (check) { + allCPUsCheck(run); + } +} + +function createCheckbox(run, id, label_str, name, value, toggle_func) { + let elem = document.createElement("input"); + elem.setAttribute("type", "checkbox"); + elem.id = id; + elem.name = name; + elem.value = value; + elem.addEventListener("click", function (ev: Event) { + toggle_func(this, run, true); + }, false); + let label = document.createElement("label"); + label.htmlFor = id; + label.innerHTML = label_str; + + let cpu_div = document.createElement("div"); + cpu_div.appendChild(elem); + cpu_div.appendChild(label); + return cpu_div; +} + +function createCPUConfigure(container_id, run) { + let config = run_config.get(run); + + /* Add a Toggle All checkbox */ + let all_cpu = createCheckbox(run, `${run}-CPUAll`, 'Toggle All', '', -1, allSelect); + addElemToNode(container_id, all_cpu); + + let cpu_configure_list = document.createElement("div"); + cpu_configure_list.id = `${run}-cpus-configure-list`; + cpu_configure_list.style.display = "grid"; + addElemToNode(container_id, cpu_configure_list); + let max_cpus = config.cpu_count; + let max_columns = max_cpus / 16; + let cpus_per_column = Math.floor(max_cpus / max_columns); + for (let column = 0; column < max_columns; column++) { + let cpus_start = column * cpus_per_column; + let cpus_end = cpus_start + cpus_per_column; + if (cpus_end > max_cpus) { + cpus_end = max_cpus; + } + for (let i = cpus_start, row = 1; i < cpus_end; i++, row++) { + let cpu_div = createCheckbox(run, `${run}-CPU${i}`, `CPU ${i}`, `${run}-cpulist`, i, toggleCheckbox); + cpu_div.style.gridColumn = `${column + 1}`; + cpu_div.style.gridRow = `${row}`; + addElemToNode(`${run}-cpus-configure-list`, cpu_div); + } + } + document.getElementById(`${run}-CPUAll`).click(); +} + +function configure() { + if (init_done) { + return; + } + clear_and_create('configure'); + runs_raw.forEach(function (value, index, arr) { + createCPUConfigure(`${value}-configure-per-data`, value); + }); + init_done = true; +} diff --git a/src/html_files/cpu_utilization.ts b/src/html_files/cpu_utilization.ts index d7ebec99..6fc78365 100644 --- a/src/html_files/cpu_utilization.ts +++ b/src/html_files/cpu_utilization.ts @@ -1,4 +1,5 @@ let got_cpu_util_data = false; +let util_cpu_list: Map> = new Map>(); function getUtilizationType(run, elem, type, run_data) { var cpu_type_datas = []; @@ -9,18 +10,21 @@ function getUtilizationType(run, elem, type, run_data) { data.forEach(function (value, index, arr) { var x_time = []; var y_data = []; - cpu = value.cpu; + cpu = value.cpu.toString(); type_data = value.data; type_data.forEach(function (i_value, i_index, i_arr) { x_time.push(i_value.time.TimeDiff); y_data.push(i_value.value); }); - var cpu_type_data = { + var cpu_type_data: Partial = { name: `CPU ${cpu}`, x: x_time, y: y_data, type: 'scatter', - }; + } + if (util_cpu_list.get(run).indexOf(cpu) == -1) { + cpu_type_data.visible = 'legendonly'; + } cpu_type_datas.push(cpu_type_data); }); var TESTER = elem; @@ -134,12 +138,13 @@ function getCpuUtilization(elem, run, run_data) { Plotly.newPlot(TESTER, data_list, layout, { frameMargins: 0 }); } function cpuUtilization() { - if (got_cpu_util_data) { + if (got_cpu_util_data && allRunCPUListUnchanged(util_cpu_list)) { return; } clear_and_create('cpuutilization'); for (let i = 0; i < cpu_utilization_raw_data['runs'].length; i++) { let run_name = cpu_utilization_raw_data['runs'][i]['name']; + util_cpu_list.set(run_name, getCPUList(run_name).slice()); let elem_id = `${run_name}-cpuutilization-per-data`; let this_run_data = cpu_utilization_raw_data['runs'][i]; getCpuUtilization(document.getElementById(elem_id), run_name, this_run_data['key_values']['aggregate']); diff --git a/src/html_files/index.css b/src/html_files/index.css index db217bff..73dd6a94 100644 --- a/src/html_files/index.css +++ b/src/html_files/index.css @@ -38,7 +38,7 @@ body { /* Style the buttons inside the tab */ .tab button { - display: block; + display: grid; background-color: inherit; color: black; padding: 22px 16px; @@ -51,6 +51,18 @@ body { font-size: 17px; } +.icon-text { + grid-column: 1; +} + +.configure { + grid-column: 2; + background-image: url("images/configure.png"); + background-repeat: no-repeat; + background-position-x: center; + background-position-y: center; +} + .all-runs { grid-row: 1; position: sticky; diff --git a/src/html_files/index.html b/src/html_files/index.html index 184deb5f..556d2ee8 100644 --- a/src/html_files/index.html +++ b/src/html_files/index.html @@ -2,13 +2,15 @@ - + APerf
APerf
+ + @@ -98,6 +100,11 @@

Hide N/A and all-zero graphs:

+
+
+
+
+
@@ -130,6 +137,7 @@

Hide N/A and all-zero graphs:

+ diff --git a/src/html_files/index.ts b/src/html_files/index.ts index 9bc35775..d528536e 100644 --- a/src/html_files/index.ts +++ b/src/html_files/index.ts @@ -20,6 +20,7 @@ DataTypes.set('top_functions', {name: 'topfunctions', hideClass: '', trueId: '', 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}); +DataTypes.set('configure', {name: 'configure', hideClass: '', trueId: '', callback: configure}); function openData(evt: Event, elem: HTMLButtonElement) { var tabName: string = elem.name; @@ -118,5 +119,7 @@ function create_runs_header() { // Set Runs header create_runs_header(); +formGlobalConfig(); + // Show landing page document.getElementById("default").click(); diff --git a/src/html_files/interrupts.ts b/src/html_files/interrupts.ts index b854f971..eb23b08d 100644 --- a/src/html_files/interrupts.ts +++ b/src/html_files/interrupts.ts @@ -1,4 +1,5 @@ let got_interrupt_data = false; +let interrupt_cpu_list: Map> = new Map>(); function getLine(run, elem, key, run_data) { var data = JSON.parse(run_data); @@ -15,12 +16,15 @@ function getLine(run, elem, key, run_data) { } }) }) - var interrupt_cpu_data = { + var interrupt_cpu_data: Partial = { name: `CPU ${cpu}`, x: x_time, y: y_data, type: 'scatter', }; + if (interrupt_cpu_list.get(run).indexOf(cpu.toString()) == -1) { + interrupt_cpu_data.visible = 'legendonly'; + } interrupt_type_datas.push(interrupt_cpu_data); } var title; @@ -56,12 +60,13 @@ function getLines(run, container_id, keys, run_data) { } function interrupts() { - if (got_interrupt_data) { + if (got_interrupt_data && allRunCPUListUnchanged(interrupt_cpu_list)) { return; } clear_and_create('interrupts'); for (let i = 0; i < interrupts_raw_data['runs'].length; i++) { let run_name = interrupts_raw_data['runs'][i]['name']; + interrupt_cpu_list.set(run_name, getCPUList(run_name).slice()); let elem_id = `${run_name}-interrupts-per-data`; let this_run_data = interrupts_raw_data['runs'][i]; getLines(run_name, elem_id, this_run_data['keys'], this_run_data['key_values']); diff --git a/src/html_files/perf_stat.ts b/src/html_files/perf_stat.ts index 16cb5c5a..da45ad4c 100644 --- a/src/html_files/perf_stat.ts +++ b/src/html_files/perf_stat.ts @@ -1,4 +1,5 @@ let got_perf_stat_data = false; +let perf_cpu_list: Map> = new Map>(); function getEvents(run, container_id, keys, run_data) { if (keys.length == 0) { @@ -13,7 +14,7 @@ function getEvents(run, container_id, keys, run_data) { elem.id = `perfstat-${run}-${value}`; elem.style.float = "none"; addElemToNode(container_id, elem); - emptyOrCallback(keys, false, getEvent, elem, value, run_data); + emptyOrCallback(keys, false, getEvent, elem, value, run_data, run); } } } @@ -32,7 +33,7 @@ function addData(perfstat_data, stat, timediff) { } }) } -function getEvent(elem, key, run_data) { +function getEvent(elem, key, run_data, run) { var data = JSON.parse(run_data); var perfstat_datas = []; data.data[0].cpus.forEach(function (value, index, arr) { @@ -51,6 +52,7 @@ function getEvent(elem, key, run_data) { var end_datas = []; perfstat_datas.forEach(function (value, index, arr) { var cpu_string = ""; + let cpu = value.cpu.toString(); if (value.cpu > -1) { cpu_string = `CPU ${value.cpu}`; } @@ -63,7 +65,14 @@ function getEvent(elem, key, run_data) { y: value.y_data, type: 'scatter', }; - end_datas.push(perfstat_line); + if (cpu_string != 'Aggregate' && perf_cpu_list.get(run).indexOf(cpu) == -1) { + perfstat_line.visible = 'legendonly'; + } + if (cpu_string == 'Aggregate') { + end_datas.unshift(perfstat_line); + } else { + end_datas.push(perfstat_line); + } }) let limits = key_limits.get(key); var layout = { @@ -80,13 +89,14 @@ function getEvent(elem, key, run_data) { } function perfStat() { - if (got_perf_stat_data) { + if (got_perf_stat_data && allRunCPUListUnchanged(perf_cpu_list)) { return; } clear_and_create('perfstat'); form_graph_limits(perf_stat_raw_data); for (let i = 0; i < perf_stat_raw_data['runs'].length; i++) { let run_name = perf_stat_raw_data['runs'][i]['name']; + perf_cpu_list.set(run_name, getCPUList(run_name).slice()); let elem_id = `${run_name}-perfstat-per-data`; let this_run_data = perf_stat_raw_data['runs'][i]; getEvents(run_name, elem_id, this_run_data['keys'], this_run_data['key_values']); diff --git a/src/html_files/utils.ts b/src/html_files/utils.ts index 1a7e56a8..7cbbd107 100644 --- a/src/html_files/utils.ts +++ b/src/html_files/utils.ts @@ -70,7 +70,7 @@ function canHide(hide, keys, key) { } return false; } -function emptyOrCallback(keys, hide, callback, elem, key, run_data) { +function emptyOrCallback(keys, hide, callback, elem, key, run_data, run="") { if (canHide(hide, keys, key)) { return; } @@ -80,7 +80,7 @@ function emptyOrCallback(keys, hide, callback, elem, key, run_data) { }, 0); } else { setTimeout(() => { - callback(elem, key, run_data[key]); + callback(elem, key, run_data[key], run); }, 0); } } @@ -159,3 +159,13 @@ function split_keys(check_runs, check_common_keys) { } } } + +function allRunCPUListUnchanged(cpu_list) { + for (let i = 0; i < runs_raw.length; i++) { + let run_name = runs_raw[i]; + if (!isCPUListUnchanged(cpu_list.get(run_name), run_name)) { + return false; + } + } + return true; +} diff --git a/src/report.rs b/src/report.rs index c59c145f..c8f2f8c0 100644 --- a/src/report.rs +++ b/src/report.rs @@ -199,6 +199,7 @@ pub fn report(report: &Report) -> Result<()> { info!("Creating APerf report..."); let ico = include_bytes!("html_files/favicon.ico"); + let configure = include_bytes!("html_files/configure.png"); let index_html = include_str!("html_files/index.html"); let index_css = include_str!("html_files/index.css"); let index_js = include_str!(concat!(env!("JS_DIR"), "/index.js")); @@ -207,8 +208,10 @@ pub fn report(report: &Report) -> Result<()> { env!("CARGO_MANIFEST_DIR"), "/node_modules/plotly.js/dist/plotly.min.js" )); + let configure_js = include_str!(concat!(env!("JS_DIR"), "/configure.js")); let run_names = dir_stems.clone(); + fs::create_dir_all(report_name.join("images"))?; fs::create_dir_all(report_name.join("js"))?; fs::create_dir_all(report_name.join("data/archive"))?; fs::create_dir_all(report_name.join("data/js"))?; @@ -218,18 +221,22 @@ pub fn report(report: &Report) -> Result<()> { form_and_copy_archive(dir.to_path_buf(), &report_name)?; } /* Generate base HTML, JS files */ - let mut ico_file = File::create(report_name.join("favicon.ico"))?; + let mut ico_file = File::create(report_name.join("images/favicon.ico"))?; + let mut configure_file = File::create(report_name.join("images/configure.png"))?; let mut index_html_file = File::create(report_name.join("index.html"))?; let mut index_css_file = File::create(report_name.join("index.css"))?; let mut index_js_file = File::create(report_name.join("index.js"))?; let mut utils_js_file = File::create(report_name.join("js/utils.js"))?; let mut plotly_js_file = File::create(report_name.join("js/plotly.js"))?; + let mut configure_js_file = File::create(report_name.join("js/configure.js"))?; ico_file.write_all(ico)?; + configure_file.write_all(configure)?; write!(index_html_file, "{}", index_html)?; write!(index_css_file, "{}", index_css)?; write!(index_js_file, "{}", index_js)?; write!(utils_js_file, "{}", utils_js)?; write!(plotly_js_file, "{}", plotly_js)?; + write!(configure_js_file, "{}", configure_js)?; let mut visualizer = VISUALIZATION_DATA.lock().unwrap(); /* Init visualizers */