OILS / test / syscall.py View on Github | oilshell.org

268 lines, 170 significant
1#!/usr/bin/env python2
2"""test/syscall.py
3
4Print a results table.
5
6Input looks like
7
801-dash
901-dash
1001-osh
1101-osh
1201-bash-4.4
13...
14"""
15from __future__ import print_function
16
17import collections
18import optparse
19import os
20import re
21import sys
22
23
24def log(msg, *args):
25 if args:
26 msg = msg % args
27 print(msg, file=sys.stderr)
28
29
30def Cell(i):
31 """Visually show number of processes.
32
33 ^ ^^ ^^^ etc.
34 """
35 s = '^' * i
36 return '%6s' % s
37
38
39# lines look like this:
40#
41# 554 01-osh.1234
42# 553 01-osh.1235
43
44WC_LINE = re.compile(
45 r'''
46\s*
47(\d+) # number of lines
48\s+
49([a-z0-9.-]+) # shell name, could be bash-4.4
50__
51(\d{2}) # case ID
52''', re.VERBOSE)
53
54assert WC_LINE.match(' 68 osh-cpp__01.19610')
55# This is unfortunate
56assert WC_LINE.match(' 68 bash-4.4__01.19610')
57
58
59def WriteHeader(f, shells, col=''):
60 f.write("ID\t")
61 for sh in shells:
62 # abbreviate
63 if sh.startswith('bash-4'):
64 sh = 'bash-4'
65 elif sh.startswith('bash-5'):
66 sh = 'bash-5'
67 f.write("%6s\t" % sh)
68 f.write('%s\t' % col)
69 f.write('Description')
70 f.write("\n")
71
72
73def WriteProcessReport(f, cases, code_strs, proc_sh, num_procs,
74 procs_by_shell):
75 not_minimum = 0
76 more_than_bash = 0
77 fewer_than_bash = 0
78
79 minimum = {} # case -> number of procses
80 for case_id in sorted(cases):
81 min_procs = 20
82 for sh in proc_sh:
83 n = num_procs[case_id, sh]
84 min_procs = min(n, min_procs)
85 minimum[case_id] = min_procs
86
87 osh_count = num_procs[case_id, 'osh']
88 if osh_count != min_procs:
89 not_minimum += 1
90
91 bash_count = num_procs[case_id, 'bash-5.2.21']
92 if osh_count > bash_count:
93 more_than_bash += 1
94 if osh_count < bash_count:
95 fewer_than_bash += 1
96
97 f.write('Number of Processes Started, by shell and test case\n')
98 f.write('\n')
99 f.write("Cases where ...\n")
100 f.write(" OSH isn't the minimum: %d\n" % not_minimum)
101 f.write(" OSH starts more than bash 5: %d\n" % more_than_bash)
102 f.write(" OSH starts fewer than bash 5: %d\n\n" % fewer_than_bash)
103 f.write('\n')
104 WriteHeader(f, proc_sh, col='osh>min')
105 f.write('\n')
106
107 f.write("TOTAL\t")
108 for sh in proc_sh:
109 f.write('%6d\t' % procs_by_shell[sh])
110 f.write('\n')
111 f.write('\n')
112
113 for case_id in sorted(cases):
114 f.write(case_id + "\t")
115 for sh in proc_sh:
116 n = num_procs[case_id, sh]
117 f.write(Cell(n) + "\t")
118
119 osh_count = num_procs[case_id, 'osh']
120 min_procs = minimum[case_id]
121 if osh_count != min_procs:
122 f.write('%d>%d\t' % (osh_count, min_procs))
123 else:
124 f.write('\t')
125
126 f.write(code_strs[case_id])
127 f.write("\n")
128
129 return not_minimum, more_than_bash, fewer_than_bash
130
131
132def WriteSyscallReport(f, cases, code_strs, syscall_sh, num_syscalls,
133 syscalls_by_shell):
134 f.write('Number of Syscalls\n\n')
135
136 WriteHeader(f, syscall_sh)
137
138 for case_id in sorted(cases):
139 f.write(case_id + "\t")
140 #min_procs = 20
141 for sh in syscall_sh:
142 n = num_syscalls[case_id, sh]
143 f.write('%6d\t' % n)
144 #min_procs = min(n, min_procs)
145
146 f.write('\t')
147
148 f.write(code_strs[case_id])
149 f.write("\n")
150
151 f.write("TOTAL\t")
152 for sh in syscall_sh:
153 f.write('%6d\t' % syscalls_by_shell[sh])
154 f.write('\n\n')
155
156
157def Options():
158 """Returns an option parser instance."""
159 p = optparse.OptionParser()
160 p.add_option('--suite',
161 dest='suite',
162 default='SUITE',
163 help='Test suite name')
164 p.add_option(
165 '--not-minimum',
166 dest='not_minimum',
167 type=int,
168 default=0,
169 help=
170 "Expected number of cases where OSH doesn't start the minimum number of"
171 "processes")
172 p.add_option(
173 '--more-than-bash',
174 dest='more_than_bash',
175 type=int,
176 default=0,
177 help=
178 'Expected number of cases where OSH starts more processes than bash')
179 return p
180
181
182def main(argv):
183 o = Options()
184 opts, argv = o.parse_args(argv[1:])
185
186 cases_path = argv[0]
187 out_dir = argv[1]
188
189 code_strs = {}
190 with open(cases_path) as f:
191 for line in f:
192 case_id, code_str = line.split(None, 1) # whitespace
193 code_strs[case_id] = code_str
194
195 cases = set()
196 shells = set()
197
198 num_procs = collections.defaultdict(int)
199 procs_by_shell = collections.defaultdict(int)
200
201 num_syscalls = collections.defaultdict(int)
202 syscalls_by_shell = collections.defaultdict(int)
203
204 #
205 # Summarize Data
206 #
207
208 for line in sys.stdin:
209 m = WC_LINE.match(line)
210 if not m:
211 raise RuntimeError('Invalid line %r' % line)
212 num_sys, sh, case = m.groups()
213 num_sys = int(num_sys)
214
215 cases.add(case)
216 shells.add(sh)
217
218 num_procs[case, sh] += 1
219 num_syscalls[case, sh] += num_sys
220
221 procs_by_shell[sh] += 1
222 syscalls_by_shell[sh] += num_sys
223
224 # Orders columns by how good the results are, then shell name.
225 proc_sh = sorted(procs_by_shell, key=lambda sh: (procs_by_shell[sh], sh))
226 syscall_sh = sorted(syscalls_by_shell,
227 key=lambda sh: (syscalls_by_shell[sh], sh))
228
229 #
230 # Print Tables
231 #
232
233 out_path = os.path.join(out_dir, 'processes.%s.txt' % opts.suite)
234 with open(out_path, 'w') as f:
235 not_minimum, more_than_bash, fewer_than_bash = WriteProcessReport(
236 f, cases, code_strs, proc_sh, num_procs, procs_by_shell)
237 log('Wrote %s', out_path)
238
239 #
240 # Print Table of Syscall Counts
241 #
242
243 out_path = os.path.join(out_dir, 'syscalls.%s.txt' % opts.suite)
244 with open(out_path, 'w') as f:
245 WriteSyscallReport(f, cases, code_strs, syscall_sh, num_syscalls,
246 syscalls_by_shell)
247 log('Wrote %s', out_path)
248
249 ok = True
250 if more_than_bash != opts.more_than_bash:
251 log('Expected %d more than bash, got %d', opts.more_than_bash,
252 more_than_bash)
253 ok = False
254
255 if not_minimum != opts.not_minimum:
256 log('Expected %d that are not minimal, got %d', opts.not_minimum,
257 not_minimum)
258 ok = False
259
260 return 0 if ok else 1
261
262
263if __name__ == '__main__':
264 try:
265 sys.exit(main(sys.argv))
266 except RuntimeError as e:
267 print('FATAL: %s' % e, file=sys.stderr)
268 sys.exit(1)