blob: a8bf36f0afbf808329b342db27765c8a563cd397 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# 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.
"""
This script supports visualizing all histograms found in an inspect snapshot.
To use you need to have a snapshot.zip file either generated by `ffx snapshot` or from a crash
report. This also works by dumping the output of `ffx --machine json inspect` to a file.
Example usage:
```
$ unzip /path/to/snapshot.zip
$ fuchsia-vendored-python snapshot_histograms.py /path/to/unzipped/inspect.json
// open html in browser.
```
"""
import argparse
import json
import sys
def extract_buckets(
payload: dict[str, object], current_path: list[str] = []
) -> list[tuple[str, str]]:
result = []
for key, child in payload.items():
if type(child) is not dict:
continue
if "buckets" in child:
path = current_path + [key]
result.append(("/".join(path), child["buckets"]))
continue
result.extend(extract_buckets(child, current_path + [key]))
return result
HTML_DATA = """
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {packages: ['corechart', 'table']});
google.charts.setOnLoadCallback(onLoad);
const allData = {DATA};
let showTables = false;
function drawChart(charts, nodePath, buckets) {
let data = [['Range', 'Count']];
for (const bucket of buckets) {
if (bucket.ceiling === bucket.floor) { continue; }
data.push([
{ v: bucket.floor, f: `${bucket.floor}-${bucket.ceiling}`},
bucket.count
]);
}
let dataTable = google.visualization.arrayToDataTable(data);
let chartDiv = document.createElement('div');
chartDiv.classList.add('chart');
charts.appendChild(chartDiv);
let tableDiv = document.createElement('div');
tableDiv.classList.add('table');
tableDiv.hidden = !showTables;
charts.appendChild(tableDiv);
const chart = new google.visualization.ColumnChart(chartDiv);
chart.draw(dataTable, {
title: nodePath,
hAxis: {
title: 'Ranges',
logScale: true,
},
});
const table = new google.visualization.Table(tableDiv);
table.draw(dataTable, {showRowNumber: true, 'max-height': '100px'});
}
function render(moniker) {
if (moniker !== undefined) {
let charts = document.getElementById('charts');
charts.innerHTML = '';
for (const nodePath of Object.keys(allData[moniker])) {
drawChart(charts, nodePath, allData[moniker][nodePath]);
}
}
}
function onLoad() {
const select = document.querySelector('#monikers');
let selectedMoniker = undefined;
for (const moniker of Object.keys(allData)) {
const opt = document.createElement('option');
opt.innerText = moniker;
opt.setAttribute('value', moniker);
select.appendChild(opt);
}
select.addEventListener('change', (event) => {
render(event.target.value);
});
const showTablesCheckbox = document.querySelector('#show-tables');
showTablesCheckbox.addEventListener('change', (event) => {
showTables = event.target.checked;
console.log('got change', showTables);
for (const element of document.getElementsByClassName('table')) {
console.log(`Changing ${element} to ${!showTables}`);
element.hidden = !showTables;
}
});
}
</script>
</head>
<body>
<select id="monikers">
<option value="" disabled selected>Select moniker</option>
</select>
<input type="checkbox" id="show-tables"> Show tables
<div id="charts"></div>
</body>
</html>
"""
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Show histograms in a snapshot."
)
parser.add_argument(
"filepath", metavar="FILE", type=str, help="path to inspect.json file"
)
args = parser.parse_args(sys.argv[1:])
results = {}
with open(args.filepath) as f:
data = json.load(f)
for response in data:
if response["payload"]:
buckets = extract_buckets(response["payload"])
if buckets:
results[response["moniker"]] = dict(buckets)
html = HTML_DATA.replace("{DATA}", "{}".format(results))
print(html)