Last active
January 28, 2023 22:15
-
-
Save chapmanjacobd/32d1f92ac389a17341f4861ef38ae7fd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
from multiprocessing import Pool | |
import btrfs | |
parser = argparse.ArgumentParser() | |
parser.add_argument("btrfs_fs_mountpoint") | |
args = parser.parse_args() | |
def sizeof_fmt(num, suffix="B"): | |
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: | |
if abs(num) < 1024.0: | |
return f"{num:3.1f}{unit}{suffix}" | |
num /= 1024.0 | |
return f"{num:.1f}Yi{suffix}" | |
def safe_get_block_group(vaddr): | |
try: | |
return fs.block_group(vaddr) | |
except IndexError: | |
return None | |
with btrfs.FileSystem(args.btrfs_fs_mountpoint) as fs: | |
devices = fs.devices() | |
devids = [d.devid for d in devices] | |
fs_chunks = list(fs.chunks()) | |
print(args.btrfs_fs_mountpoint, '\t', fs.usage().virtual_used_str, '\t', len(fs_chunks), 'fs chunks') | |
for devid in devids: | |
dev_info = fs.dev_info(devid) | |
dev_vaddrs = [d.vaddr for d in fs.dev_extents(devid)] | |
print( | |
dev_info.path, | |
'\t', | |
dev_info.bytes_used_str, | |
'\t', | |
len(dev_vaddrs), | |
'chunk extents', | |
"({:.0%})".format(len(dev_vaddrs) / len(fs_chunks)), | |
) | |
print('\nCalculating chunk use (may take a few minutes)...\n') | |
for devid in devids: | |
print(fs.dev_info(devid).path) | |
dev_vaddrs = [d.vaddr for d in fs.dev_extents(devid)] | |
chunk_stats = {} | |
with Pool() as pool: | |
results = pool.map(safe_get_block_group, dev_vaddrs) | |
for bg in [r for r in results if r is not None]: | |
if bg.flags_str in chunk_stats: | |
chunk_stats[bg.flags_str]['count'] += 1 | |
chunk_stats[bg.flags_str]['size_used'] += bg.used | |
chunk_stats[bg.flags_str]['percent_used'].append(bg.used_pct) | |
else: | |
chunk_stats[bg.flags_str] = {'count': 1, 'size_used': bg.used, 'percent_used': [bg.used_pct]} | |
for data_flag in sorted(chunk_stats): | |
stats = chunk_stats[data_flag] | |
print('\t', data_flag) | |
print('\t' * 2, 'Chunks at least partially on disk:', stats['count']) | |
print('\t' * 2, 'Data-device dependance:', sizeof_fmt(stats['size_used'])) | |
print('\t' * 2, 'Approx. chunk percentage used:') | |
grouped_stats = {} | |
for percent in stats['percent_used']: | |
group = (percent // 10) * 10 | |
if group in grouped_stats: | |
grouped_stats[group] += 1 | |
else: | |
grouped_stats[group] = 1 | |
min_count = min(count for _group, count in grouped_stats.items()) | |
max_count = max(count for _group, count in grouped_stats.items()) | |
for group, count in sorted(grouped_stats.items()): | |
try: | |
scaled_count = int(60 * (count - min_count) / (max_count - min_count)) | |
except ZeroDivisionError: | |
scaled_count = 1 | |
print('\t' * 2, f' {str(group).rjust(3)}% packed', '*' * max(scaled_count, 1), f"({count} chunks)") | |
print('\n') |
Author
chapmanjacobd
commented
Jan 28, 2023
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment