blob: e3c01424c8b61de5aa11605c2105459b1cdc77ee [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.
import argparse
import re
import sys
from snapshot import Snapshot
group_specs = [
["[scudo]", "scudo:.*"],
["[relro]", "relro:.*"],
["[stacks]", "thrd_t:0x.*|initial-thread|pthread_t:0x.*"],
["[data]", "data[0-9]*:.*"],
["[blobs]", "blob-[0-9a-f]+"],
["[inactive-blobs]", "inactive-blob-[0-9a-f]+"],
["[mrkls]", "mrkl-[0-9a-f]+"],
["[uncompressed-bootfs]", "uncompressed-bootfs"],
]
for gs in group_specs:
gs[1] = re.compile(gs[1])
def main(args):
snapshot = Snapshot.FromJSONFile(
sys.stdin) if args.snapshot is None else Snapshot.FromJSONFilename(
args.snapshot)
vmo_to_count = {}
for p in snapshot.processes.values():
for v in p.vmos:
if v.koid in vmo_to_count:
vmo_to_count[v.koid] += 1
else:
vmo_to_count[v.koid] = 1
nodes = []
snaphot_name = 'Snapshot'
kernel_name = 'Kernel'
nodes.append(["Orphaned", snaphot_name, 0])
nodes.append(["Orphaned VMOs", "Orphaned", snapshot.orphaned])
nodes.append([kernel_name, snaphot_name, 0])
nodes.append(["Wired", kernel_name, snapshot.kernel.wired])
nodes.append(["Heap", kernel_name, snapshot.kernel.total_heap])
nodes.append(["IPC", kernel_name, snapshot.kernel.ipc])
nodes.append(["Other", kernel_name, snapshot.kernel.other])
nodes.append(["MMU", kernel_name, snapshot.kernel.mmu])
for p in snapshot.processes.values():
process_name = "%s<%d>" % (p.name, p.koid)
nodes.append([process_name, snaphot_name, 0])
groups = {}
for v in p.vmos:
group_name = None
for gs in group_specs:
if gs[1].match(v.name):
group_name = "%s<%d>" % (gs[0], p.koid)
if gs[0] not in groups:
nodes.append([group_name, process_name, 0])
groups[gs[0]] = True
break
vmo_name = "%s<%d:%d>" % (v.name, p.koid, v.koid)
nodes.append([
vmo_name,
process_name if group_name is None else group_name,
float(v.committed_bytes) / vmo_to_count[v.koid]
])
template = '''\
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {'packages':['treemap']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Process','Parent','Size (MB)'],
['Snapshot',null,0],
%s
]);
tree = new google.visualization.TreeMap(document.getElementById('chart_div'));
tree.draw(data, {
headerHeight: 15,
fontColor: 'black',
generateTooltip: showTooltip,
maxDepth: 2,
enableHighlight: true,
minHighlightColor: '#8c6bb1',
midHighlightColor: '#9ebcda',
maxHighlightColor: '#edf8fb',
minColor: '#009688',
midColor: '#f7f7f7',
maxColor: '#ee8100'
});
function showTooltip(row, size, value) {
return '<div style="background:#fd9; padding:10px; border-style:solid">' + data.getValue(row, 0) + ' ' + size.toFixed(2) + 'MB</div>';
}
}
</script>
</head>
<body>
<div id="chart_div" style="width: 100%%; height: 100%%;"></div>
</body>
</html>
'''
node_strings = '\n'.join([
"[\'%s\',\'%s\',%.2g]," % (n[0], n[1], round(n[2] / (1024.0 * 1024), 2))
for n in nodes
])
print(template % node_strings)
def get_arg_parser():
parser = argparse.ArgumentParser(description='Convert snapshot to tree.')
parser.add_argument('-s', '--snapshot')
return parser
if __name__ == '__main__':
main(get_arg_parser().parse_args())