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