Skip to content

Commit 19c7955

Browse files
committed
tools/analyze-dump-sort: a helper to compare two 'systemd-analyze dump' outputs
Lines in the dumps are ordered by some pseudo-random hashmap entry order, which makes it hard to diff two outputs. This sort the entries alphabetically, and also sorts items within the entries, and supresses timestamps and other fields which always vary. We could sort the output inside of systemd itself, but it'd make things more complex, and we probably don't need output to be sorted in most cases. It also wouldn't be enough, because timestamps and such would still need to be ignored to do a nice diff. So I think doing the sorting and suppression in a python helper is a better approach.
1 parent 771bdb6 commit 19c7955

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

tools/analyze-dump-sort.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/python
2+
# SPDX-License-Identifier: LGPL-2.1-or-later
3+
4+
"""
5+
A helper to compare 'systemd-analyze dump' outputs.
6+
7+
systemd-analyze dump >/var/tmp/dump1
8+
(reboot)
9+
tools/analyze-dump-sort.py /var/tmp/dump1 → this does a diff from dump1 to current
10+
11+
systemd-analyze dump >/var/tmp/dump2
12+
tools/analyze-dump-sort.py /var/tmp/{dump1,dump2} → this does a diff from dump1 to dump2
13+
"""
14+
15+
import argparse
16+
import tempfile
17+
import subprocess
18+
19+
def sort_dump(sourcefile, destfile=None):
20+
if destfile is None:
21+
destfile = tempfile.NamedTemporaryFile('wt')
22+
23+
units = {}
24+
unit = []
25+
26+
same = []
27+
28+
for line in sourcefile:
29+
line = line.rstrip()
30+
31+
header = line.split(':')[0]
32+
if 'Timestamp' in header or 'Invocation ID' in header or 'PID' in header:
33+
line = header + ': …'
34+
35+
if line.startswith('->'):
36+
if unit:
37+
units[unit[0]] = unit
38+
unit = [line]
39+
elif line.startswith('\t'):
40+
assert unit
41+
42+
if same and same[0].startswith(header):
43+
same.append(line)
44+
else:
45+
unit.extend(sorted(same, key=str.lower))
46+
same = [line]
47+
else:
48+
print(line, file=destfile)
49+
50+
if unit:
51+
units[unit[0]] = unit
52+
53+
for unit in sorted(units.values()):
54+
print('\n'.join(unit), file=destfile)
55+
56+
destfile.flush()
57+
return destfile
58+
59+
def parse_args():
60+
p = argparse.ArgumentParser(description=__doc__)
61+
p.add_argument('one')
62+
p.add_argument('two', nargs='?')
63+
p.add_argument('--user', action='store_true')
64+
return p.parse_args()
65+
66+
if __name__ == '__main__':
67+
opts = parse_args()
68+
69+
one = sort_dump(open(opts.one))
70+
if opts.two:
71+
two = sort_dump(open(opts.two))
72+
else:
73+
user = ['--user'] if opts.user else []
74+
two = subprocess.run(['systemd-analyze', 'dump', *user],
75+
capture_output=True, text=True, check=True)
76+
two = sort_dump(two.stdout.splitlines())
77+
with subprocess.Popen(['diff', '-U10', one.name, two.name], stdout=subprocess.PIPE) as diff:
78+
subprocess.Popen(['less'], stdin=diff.stdout)

0 commit comments

Comments
 (0)