blob: 204998cdb230c6d195989f4fbf07c18be61887ea [file]
#!/usr/bin/env python3
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Plots the distribution of allocated and unallocated slots in SlotSpans.
To run this:
1. Build pa_tcache_inspect at a "close-enough" revision to the Chrome instance
you're interested in.
2. Run pa_tcache_inspect --pid=PID_OF_RUNNING_CHROME_PROCESS --json=output.json
3. Run plot_bucket_stats.py --json=output.json --output=output.png
"""
import argparse
import json
import matplotlib
from matplotlib import pylab as plt
import numpy as np
def ParseJson(filename):
with open(filename, 'r') as f:
data = json.load(f)
return data
def PlotData(data, output_filename):
rows, cols = 6, 5
fig, axs = plt.subplots(rows, cols, figsize=(30, 30))
fig.suptitle('Active Slot Spans fill-in - %s' % output_filename, fontsize=16)
for index, bucket in enumerate(data['buckets'][: rows * cols]):
ax = axs[int(index / cols), index % cols]
active_slot_spans = bucket['active_slot_spans']
freelist_sizes = np.array(
[
s['freelist_size'] if not s['is_decommitted'] else 0
for s in active_slot_spans
]
)
allocated_slots = np.array(
[s['num_allocated_slots'] for s in active_slot_spans]
)
unprovisioned_slots = np.array(
[s['num_unprovisioned_slots'] for s in active_slot_spans]
)
hatch = ['//' if s['freelist_is_sorted'] else '' for s in active_slot_spans]
slot_size = bucket['slot_size']
allocated_size = bucket['allocated_slots'] * slot_size
free_size = bucket['freelist_size'] * slot_size
ax.set_title(
'Slot span = %d\nAllocated size = %dkiB, Free size = %dkiB'
% (slot_size, allocated_size // 1024, free_size // 1024)
)
indices = range(len(active_slot_spans))
bottom = np.zeros(len(indices))
b1 = ax.bar(indices, allocated_slots, bottom=bottom, color='green')
bottom += allocated_slots
b2 = ax.bar(
indices,
freelist_sizes,
bottom=bottom,
color=[
'darkgrey' if s['is_decommitted'] else 'red' for s in active_slot_spans
],
hatch=hatch,
)
bottom += freelist_sizes
ax.bar(indices, unprovisioned_slots, bottom=bottom, color='lightgrey')
ax.set_xlim(left=-0.5, right=len(indices) - 0.5)
fig.tight_layout()
fig.subplots_adjust(top=0.95)
handles = [
matplotlib.patches.Patch(facecolor='green', label='Allocated'),
matplotlib.patches.Patch(
facecolor='red', hatch='//', label='Sorted freelist'
),
matplotlib.patches.Patch(facecolor='red', label='Free'),
matplotlib.patches.Patch(facecolor='grey', label='Unprovisioned'),
matplotlib.patches.Patch(facecolor='darkgrey', label='Decommitted'),
]
fig.legend(handles=handles, loc='lower right', fontsize=20)
plt.savefig(output_filename)
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--json', help='JSON dump from pa_tcache_inspect', required=True
)
parser.add_argument(
'--output_prefix', help='Output file prefix', required=True
)
args = parser.parse_args()
data = ParseJson(args.json)
PlotData(data, args.output_prefix)
if __name__ == '__main__':
main()