| #!/usr/bin/env python3 |
| |
| # 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 |
| $ python3 snapshot_histograms.py /path/to/unzipped/inspect.json |
| // open html in browser. |
| ``` |
| """ |
| |
| import argparse |
| import json |
| import sys |
| |
| |
| def extract_buckets(payload, current_path=[]): |
| 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) |