Skip to content

Commit 498faf9

Browse files
authored
Add utility to combine runs of lines in log files. (#317)
Hopefully satisfies #193. Change-Id: I427d763aeca2322b05ed88b42fd4a5f0446a654b
1 parent d18c2f2 commit 498faf9

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

tools/filter_openocd_log.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
5+
# This function is the only OpenOCD-specific part of this script.
6+
def make_canonical(line):
7+
# Remove the line number and time stamp.
8+
parts = line.split(None, 3)
9+
if len(parts) > 3:
10+
return "%s - - %s" % (parts[0], parts[3])
11+
else:
12+
return line
13+
14+
def buf_startswith(buf, sequence):
15+
if len(buf) < len(sequence):
16+
return False
17+
for i, entry in enumerate(sequence):
18+
if entry[1] != buf[i][1]:
19+
return False
20+
return True
21+
22+
def shorten_buffer(outfd, buf, current_repetition):
23+
"""Do something to the buffer to make it shorter. If we can't compress
24+
anything, then print out the first line and remove it."""
25+
length_before = len(buf)
26+
27+
if current_repetition:
28+
while buf_startswith(buf, current_repetition[0]):
29+
del buf[:len(current_repetition[0])]
30+
current_repetition[1] += 1
31+
if len(buf) < length_before:
32+
return current_repetition
33+
outfd.write("## The following %d lines repeat %d times:\n" % (
34+
len(current_repetition[0]), current_repetition[1]))
35+
for entry in current_repetition[0]:
36+
outfd.write("# %s" % entry[1])
37+
38+
# Look for repeated sequences...
39+
repetitions = []
40+
for length in range(1, len(buf)/2):
41+
# Is there a repeating sequence of `length` lines?
42+
matched_lines = 0
43+
for i, entry in enumerate(buf[length:]):
44+
if entry[1] == buf[i % length][1]:
45+
matched_lines += 1
46+
else:
47+
break
48+
if matched_lines >= length:
49+
repetitions.append((matched_lines + length, length))
50+
51+
if repetitions:
52+
repetitions.sort(key=lambda entry: (entry[0] * (entry[1] / entry[0]), -entry[1]))
53+
matched_lines, length = repetitions[-1]
54+
repeated = matched_lines / length
55+
if repeated * length >= 3:
56+
sequence = buf[:length]
57+
del buf[:repeated * length]
58+
59+
if matched_lines == length_before:
60+
# Could be continued...
61+
return [sequence, repeated]
62+
63+
else:
64+
outfd.write("## The following %d lines repeat %d times:\n" %
65+
(length, repeated))
66+
for entry in sequence:
67+
outfd.write("# %s" % entry[1])
68+
return None
69+
70+
if len(buf) >= length_before:
71+
line, _ = buf[0]
72+
outfd.write(line)
73+
buf.pop(0)
74+
75+
if length_before <= len(buf):
76+
print "Buffer:"
77+
for entry in buf:
78+
print "%r" % entry[0]
79+
assert False
80+
81+
return None
82+
83+
def compress_log(infd, outfd, window):
84+
"""Compress log by finding repeated runs of lines. Runs in O(lines *
85+
window**2), which can probably be improved."""
86+
# Contains line, canonical tuples
87+
buf = []
88+
current_repetition = None
89+
90+
for line in infd:
91+
buf.append((line, make_canonical(line)))
92+
if len(buf) > window:
93+
current_repetition = shorten_buffer(outfd, buf, current_repetition)
94+
95+
while len(buf) > 0:
96+
current_repetition = shorten_buffer(outfd, buf, current_repetition)
97+
98+
def main(args):
99+
import argparse
100+
parser = argparse.ArgumentParser(
101+
description='Combine repeated OpenOCD debug output lines. This is '
102+
'very helpful when looking at verbose log files where e.g. target '
103+
'polling is repeated over and over.',
104+
epilog='If no files are specified, read standard input.',
105+
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
106+
parser.add_argument('file', nargs='*', help='input file')
107+
parser.add_argument('-o', '--output', help='output file', default=sys.stdout)
108+
parser.add_argument('-w', '--window', type=int, default=100,
109+
help='number of lines to consider when looking for repetitions')
110+
args = parser.parse_args(args)
111+
112+
if args.file:
113+
for f in args.file:
114+
compress_log(open(f, "r"), args.output, args.window)
115+
else:
116+
compress_log(sys.stdin, args.output, args.window)
117+
118+
if __name__ == '__main__':
119+
sys.exit(main(sys.argv[1:]))

0 commit comments

Comments
 (0)