| #!/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()) |