xref: /xnu-11215.1.10/tools/format_vm_parameter_validation.py (revision 8d741a5de7ff4191bf97d57b9f54c2f6d4a15585)
1*8d741a5dSApple OSS Distributions#!/usr/bin/python3
2*8d741a5dSApple OSS Distributions
3*8d741a5dSApple OSS Distributions# format_vm_parameter_validation.py
4*8d741a5dSApple OSS Distributions# Pretty-print the output of tests/vm/vm_parameter_validation.c
5*8d741a5dSApple OSS Distributions#
6*8d741a5dSApple OSS Distributions# usage:
7*8d741a5dSApple OSS Distributions#     vm_parameter_validation | format_vm_parameter_validation.py
8*8d741a5dSApple OSS Distributions
9*8d741a5dSApple OSS Distributionsimport re
10*8d741a5dSApple OSS Distributionsimport sys
11*8d741a5dSApple OSS Distributionsimport copy
12*8d741a5dSApple OSS Distributionsimport itertools
13*8d741a5dSApple OSS Distributions
14*8d741a5dSApple OSS Distributions# magic return values used for in-band signalling
15*8d741a5dSApple OSS Distributions# fixme duplicated in vm_parameter_validation.c
16*8d741a5dSApple OSS Distributions# fixme also duplicated in other_return_values below
17*8d741a5dSApple OSS DistributionsRESULT_SUCCESS  = 0
18*8d741a5dSApple OSS DistributionsRESULT_BUSTED   = -99
19*8d741a5dSApple OSS DistributionsRESULT_IGNORED  = -98
20*8d741a5dSApple OSS DistributionsRESULT_ZEROSIZE = -97
21*8d741a5dSApple OSS DistributionsRESULT_PANIC    = -96
22*8d741a5dSApple OSS DistributionsRESULT_GUARD    = -95
23*8d741a5dSApple OSS DistributionsRESULT_MISMATCH = -94
24*8d741a5dSApple OSS DistributionsRESULT_OUT_PARAM_BAD = -93
25*8d741a5dSApple OSS DistributionsRESULT_MACH_SEND_INVALID_MEMORY = 0x1000000c
26*8d741a5dSApple OSS Distributions
27*8d741a5dSApple OSS Distributions# output formatting
28*8d741a5dSApple OSS Distributionsformat_result = {
29*8d741a5dSApple OSS Distributions    RESULT_SUCCESS  : '  .',
30*8d741a5dSApple OSS Distributions    RESULT_BUSTED   : ' **',
31*8d741a5dSApple OSS Distributions    RESULT_MISMATCH : ' ##',
32*8d741a5dSApple OSS Distributions    RESULT_IGNORED  : '   ',
33*8d741a5dSApple OSS Distributions    RESULT_ZEROSIZE : '  o',
34*8d741a5dSApple OSS Distributions    RESULT_PANIC    : ' pp',
35*8d741a5dSApple OSS Distributions    RESULT_GUARD    : ' gg',
36*8d741a5dSApple OSS Distributions    RESULT_OUT_PARAM_BAD: ' ot',
37*8d741a5dSApple OSS Distributions    RESULT_MACH_SEND_INVALID_MEMORY : ' mi',
38*8d741a5dSApple OSS Distributions}
39*8d741a5dSApple OSS Distributions
40*8d741a5dSApple OSS Distributionsformat_default = '%3d'
41*8d741a5dSApple OSS Distributionsformat_col_width = 3
42*8d741a5dSApple OSS Distributionsformat_empty_col = format_col_width * ' '
43*8d741a5dSApple OSS Distributionsformat_indent_width = 4
44*8d741a5dSApple OSS Distributionsformat_indent = format_indent_width * ' '
45*8d741a5dSApple OSS Distributions
46*8d741a5dSApple OSS Distributions
47*8d741a5dSApple OSS Distributions# record the result of one trial:
48*8d741a5dSApple OSS Distributions# ret: the return value from the tested function
49*8d741a5dSApple OSS Distributions# parameters: array of the input parameter names for that trial
50*8d741a5dSApple OSS Distributions#   (for example ["start PGSZ-2", "size -1"])
51*8d741a5dSApple OSS Distributionsclass Result:
52*8d741a5dSApple OSS Distributions    def __init__(self, new_ret, new_parameters):
53*8d741a5dSApple OSS Distributions        self.ret = new_ret
54*8d741a5dSApple OSS Distributions        self.parameters = new_parameters
55*8d741a5dSApple OSS Distributions    def __repr__(self):
56*8d741a5dSApple OSS Distributions        return str(self.ret) + " = " + str(self.parameters)
57*8d741a5dSApple OSS Distributions
58*8d741a5dSApple OSS Distributions# record the results of all trials in one test
59*8d741a5dSApple OSS Distributions# testname: the name of the test (including the function being tested)
60*8d741a5dSApple OSS Distributions# config: a string describing OS, CPU, etc
61*8d741a5dSApple OSS Distributions# compat: code for error compatibility
62*8d741a5dSApple OSS Distributions# results: an array of Result, one per trial
63*8d741a5dSApple OSS Distributionsclass Test:
64*8d741a5dSApple OSS Distributions    def __init__(self, new_name, new_config, new_compat, new_results = []):
65*8d741a5dSApple OSS Distributions        self.testname = new_name
66*8d741a5dSApple OSS Distributions        self.config = new_config
67*8d741a5dSApple OSS Distributions        self.compat = new_compat
68*8d741a5dSApple OSS Distributions        self.results = new_results
69*8d741a5dSApple OSS Distributions
70*8d741a5dSApple OSS Distributions# print column labels under some output
71*8d741a5dSApple OSS Distributions# example output given indent=2 col_width=4 labels=[foo,bar,baz,qux]:
72*8d741a5dSApple OSS Distributions#  |   |   |   |
73*8d741a5dSApple OSS Distributions#  |   |   |   qux
74*8d741a5dSApple OSS Distributions#  |   |   baz
75*8d741a5dSApple OSS Distributions#  |   bar
76*8d741a5dSApple OSS Distributions#  foo
77*8d741a5dSApple OSS Distributionsdef print_column_labels(labels, indent_width, col_width):
78*8d741a5dSApple OSS Distributions    indent = indent_width * ' '
79*8d741a5dSApple OSS Distributions    empty_column = '|' + (col_width-1) * ' '
80*8d741a5dSApple OSS Distributions
81*8d741a5dSApple OSS Distributions    unprinted = len(labels)
82*8d741a5dSApple OSS Distributions    print(indent + unprinted*empty_column)
83*8d741a5dSApple OSS Distributions
84*8d741a5dSApple OSS Distributions    for label in reversed(labels):
85*8d741a5dSApple OSS Distributions        unprinted -= 1
86*8d741a5dSApple OSS Distributions        print(indent + unprinted*empty_column + label)
87*8d741a5dSApple OSS Distributions
88*8d741a5dSApple OSS Distributions# pretty-print one function return code
89*8d741a5dSApple OSS Distributionsdef print_one_result(ret):
90*8d741a5dSApple OSS Distributions    if ret in format_result:
91*8d741a5dSApple OSS Distributions        print(format_result[ret], end='')
92*8d741a5dSApple OSS Distributions    else:
93*8d741a5dSApple OSS Distributions        print(format_default % (ret), end='')
94*8d741a5dSApple OSS Distributions
95*8d741a5dSApple OSS Distributions# choose the appropriate error code table for a test
96*8d741a5dSApple OSS Distributions# (either errno_return_values or kern_return_values)
97*8d741a5dSApple OSS Distributionsdef error_code_values_for_test(test):
98*8d741a5dSApple OSS Distributions    errno_fns = ['mprotect', 'msync', 'minherit', 'mincore', 'mlock', 'munlock',
99*8d741a5dSApple OSS Distributions                 'mmap', 'munmap', 'mremap_encrypted', 'vslock', 'vsunlock',
100*8d741a5dSApple OSS Distributions                 'madvise', 'useracc']
101*8d741a5dSApple OSS Distributions    for fn in errno_fns:
102*8d741a5dSApple OSS Distributions        if test.testname.startswith(fn):
103*8d741a5dSApple OSS Distributions            return errno_return_values
104*8d741a5dSApple OSS Distributions    else:
105*8d741a5dSApple OSS Distributions        return kern_return_values
106*8d741a5dSApple OSS Distributions
107*8d741a5dSApple OSS Distributions# print a helpful description of the return values seen in results
108*8d741a5dSApple OSS Distributions# fixme these won't include RESULT_MISMATCH
109*8d741a5dSApple OSS Distributionsdef print_legend(test):
110*8d741a5dSApple OSS Distributions    # find all error codes represented in the results
111*8d741a5dSApple OSS Distributions    codes = {}
112*8d741a5dSApple OSS Distributions    for result in test.results:
113*8d741a5dSApple OSS Distributions        codes[result.ret] = True
114*8d741a5dSApple OSS Distributions
115*8d741a5dSApple OSS Distributions    known_return_values = error_code_values_for_test(test)
116*8d741a5dSApple OSS Distributions
117*8d741a5dSApple OSS Distributions    # print the names of the detected error codes
118*8d741a5dSApple OSS Distributions    output = []
119*8d741a5dSApple OSS Distributions    for code in sorted(codes.keys()):
120*8d741a5dSApple OSS Distributions        if code in known_return_values:
121*8d741a5dSApple OSS Distributions            output.append(str(code) + ': ' + known_return_values[code])
122*8d741a5dSApple OSS Distributions        elif code in other_return_values:
123*8d741a5dSApple OSS Distributions            output.append(other_return_values[code])
124*8d741a5dSApple OSS Distributions        elif code != 0:
125*8d741a5dSApple OSS Distributions            output.append(str(code) + ': ????')
126*8d741a5dSApple OSS Distributions
127*8d741a5dSApple OSS Distributions    print(format_indent + '(' + ', '.join(output) + ')')
128*8d741a5dSApple OSS Distributions
129*8d741a5dSApple OSS Distributions# display names for error codes returned in errno
130*8d741a5dSApple OSS Distributionserrno_return_values = {
131*8d741a5dSApple OSS Distributions    1: 'EPERM',
132*8d741a5dSApple OSS Distributions    9: 'EBADF',
133*8d741a5dSApple OSS Distributions    12: 'ENOMEM',
134*8d741a5dSApple OSS Distributions    13: 'EACCES',
135*8d741a5dSApple OSS Distributions    14: 'EFAULT',
136*8d741a5dSApple OSS Distributions    22: 'EINVAL',
137*8d741a5dSApple OSS Distributions}
138*8d741a5dSApple OSS Distributions
139*8d741a5dSApple OSS Distributions# display names for error codes returned in kern_return_t
140*8d741a5dSApple OSS Distributionskern_return_values = {
141*8d741a5dSApple OSS Distributions    1: 'KERN_INVALID_ADDRESS',
142*8d741a5dSApple OSS Distributions    2: 'KERN_PROTECTION_FAILURE',
143*8d741a5dSApple OSS Distributions    3: 'KERN_NO_SPACE',
144*8d741a5dSApple OSS Distributions    4: 'KERN_INVALID_ARGUMENT',
145*8d741a5dSApple OSS Distributions    5: 'KERN_FAILURE',
146*8d741a5dSApple OSS Distributions    6: 'KERN_RESOURCE_SHORTAGE',
147*8d741a5dSApple OSS Distributions    7: 'KERN_NOT_RECEIVER',
148*8d741a5dSApple OSS Distributions    8: 'KERN_NO_ACCESS',
149*8d741a5dSApple OSS Distributions    9: 'KERN_MEMORY_FAILURE',
150*8d741a5dSApple OSS Distributions    10: 'KERN_MEMORY_ERROR',
151*8d741a5dSApple OSS Distributions    11: 'KERN_ALREADY_IN_SET',
152*8d741a5dSApple OSS Distributions    12: 'KERN_NOT_IN_SET',
153*8d741a5dSApple OSS Distributions    13: 'KERN_NAME_EXISTS',
154*8d741a5dSApple OSS Distributions    14: 'KERN_ABORTED',
155*8d741a5dSApple OSS Distributions    15: 'KERN_INVALID_NAME',
156*8d741a5dSApple OSS Distributions    16: 'KERN_INVALID_TASK',
157*8d741a5dSApple OSS Distributions    17: 'KERN_INVALID_RIGHT',
158*8d741a5dSApple OSS Distributions    18: 'KERN_INVALID_VALUE',
159*8d741a5dSApple OSS Distributions    19: 'KERN_UREFS_OVERFLOW',
160*8d741a5dSApple OSS Distributions    20: 'KERN_INVALID_CAPABILITY',
161*8d741a5dSApple OSS Distributions    21: 'KERN_RIGHT_EXISTS',
162*8d741a5dSApple OSS Distributions    22: 'KERN_INVALID_HOST',
163*8d741a5dSApple OSS Distributions    23: 'KERN_MEMORY_PRESENT',
164*8d741a5dSApple OSS Distributions    24: 'KERN_MEMORY_DATA_MOVED',
165*8d741a5dSApple OSS Distributions    25: 'KERN_MEMORY_RESTART_COPY',
166*8d741a5dSApple OSS Distributions    26: 'KERN_INVALID_PROCESSOR_SET',
167*8d741a5dSApple OSS Distributions    27: 'KERN_POLICY_LIMIT',
168*8d741a5dSApple OSS Distributions    28: 'KERN_INVALID_POLICY',
169*8d741a5dSApple OSS Distributions    29: 'KERN_INVALID_OBJECT',
170*8d741a5dSApple OSS Distributions    30: 'KERN_ALREADY_WAITING',
171*8d741a5dSApple OSS Distributions    31: 'KERN_DEFAULT_SET',
172*8d741a5dSApple OSS Distributions    32: 'KERN_EXCEPTION_PROTECTED',
173*8d741a5dSApple OSS Distributions    33: 'KERN_INVALID_LEDGER',
174*8d741a5dSApple OSS Distributions    34: 'KERN_INVALID_MEMORY_CONTROL',
175*8d741a5dSApple OSS Distributions    35: 'KERN_INVALID_SECURITY',
176*8d741a5dSApple OSS Distributions    36: 'KERN_NOT_DEPRESSED',
177*8d741a5dSApple OSS Distributions    37: 'KERN_TERMINATED',
178*8d741a5dSApple OSS Distributions    38: 'KERN_LOCK_SET_DESTROYED',
179*8d741a5dSApple OSS Distributions    39: 'KERN_LOCK_UNSTABLE',
180*8d741a5dSApple OSS Distributions    40: 'KERN_LOCK_OWNED',
181*8d741a5dSApple OSS Distributions    41: 'KERN_LOCK_OWNED_SELF',
182*8d741a5dSApple OSS Distributions    42: 'KERN_SEMAPHORE_DESTROYED',
183*8d741a5dSApple OSS Distributions    43: 'KERN_RPC_SERVER_TERMINATED',
184*8d741a5dSApple OSS Distributions    44: 'KERN_RPC_TERMINATE_ORPHAN',
185*8d741a5dSApple OSS Distributions    45: 'KERN_RPC_CONTINUE_ORPHAN',
186*8d741a5dSApple OSS Distributions    46: 'KERN_NOT_SUPPORTED',
187*8d741a5dSApple OSS Distributions    47: 'KERN_NODE_DOWN',
188*8d741a5dSApple OSS Distributions    48: 'KERN_NOT_WAITING',
189*8d741a5dSApple OSS Distributions    49: 'KERN_OPERATION_TIMED_OUT',
190*8d741a5dSApple OSS Distributions    50: 'KERN_CODESIGN_ERROR',
191*8d741a5dSApple OSS Distributions    51: 'KERN_POLICY_STATIC',
192*8d741a5dSApple OSS Distributions    52: 'KERN_INSUFFICIENT_BUFFER_SIZE',
193*8d741a5dSApple OSS Distributions    53: 'KERN_DENIED',
194*8d741a5dSApple OSS Distributions    54: 'KERN_MISSING_KC',
195*8d741a5dSApple OSS Distributions    55: 'KERN_INVALID_KC',
196*8d741a5dSApple OSS Distributions    56: 'KERN_NOT_FOUND',
197*8d741a5dSApple OSS Distributions    100: 'KERN_RETURN_MAX',
198*8d741a5dSApple OSS Distributions    -304: 'MIG_BAD_ARGUMENTS (server type check failure)',
199*8d741a5dSApple OSS Distributions    0x1000000c : 'MACH_SEND_INVALID_MEMORY',
200*8d741a5dSApple OSS Distributions}
201*8d741a5dSApple OSS Distributions
202*8d741a5dSApple OSS Distributions# display names for the special return values used by the test machinery
203*8d741a5dSApple OSS Distributionsother_return_values = {
204*8d741a5dSApple OSS Distributions    RESULT_BUSTED: format_result[RESULT_BUSTED].lstrip() + ': trial broken, not performed',
205*8d741a5dSApple OSS Distributions    RESULT_IGNORED: '<empty> trial ignored, not performed',
206*8d741a5dSApple OSS Distributions    RESULT_ZEROSIZE: format_result[RESULT_ZEROSIZE].lstrip() + ': size == 0',
207*8d741a5dSApple OSS Distributions    RESULT_PANIC: format_result[RESULT_PANIC].lstrip() + ': trial is believed to panic, not performed',
208*8d741a5dSApple OSS Distributions    RESULT_GUARD: format_result[RESULT_GUARD].lstrip() + ': trial is believed to throw EXC_GUARD, not performed',
209*8d741a5dSApple OSS Distributions    RESULT_OUT_PARAM_BAD: format_result[RESULT_OUT_PARAM_BAD].lstrip() + ': trial set incorrect values to out parameters',
210*8d741a5dSApple OSS Distributions}
211*8d741a5dSApple OSS Distributions
212*8d741a5dSApple OSS Distributions# inside line, replace 'return 123' with 'return ERR_CODE_NAME'
213*8d741a5dSApple OSS Distributionsdef replace_error_code_return(test, line):
214*8d741a5dSApple OSS Distributions    known_return_values = error_code_values_for_test(test)
215*8d741a5dSApple OSS Distributions    for code, name in known_return_values.items():
216*8d741a5dSApple OSS Distributions        line = line.replace('return ' + str(code) + ';', 'return ' + name + ';')
217*8d741a5dSApple OSS Distributions    return line
218*8d741a5dSApple OSS Distributions
219*8d741a5dSApple OSS Distributionsdef dimensions(results):
220*8d741a5dSApple OSS Distributions    if len(results) == 0:
221*8d741a5dSApple OSS Distributions        return 0
222*8d741a5dSApple OSS Distributions    return len(results[0].parameters)
223*8d741a5dSApple OSS Distributions
224*8d741a5dSApple OSS Distributions# given one k-dimensional results
225*8d741a5dSApple OSS Distributions# return a list of k counts that is the size of each dimension
226*8d741a5dSApple OSS Distributionsdef count_each_dimension(results):
227*8d741a5dSApple OSS Distributions    if len(results) == 0:
228*8d741a5dSApple OSS Distributions        return []
229*8d741a5dSApple OSS Distributions    first = results[0].parameters
230*8d741a5dSApple OSS Distributions    k = dimensions(results)
231*8d741a5dSApple OSS Distributions    counts = []
232*8d741a5dSApple OSS Distributions    step = 1
233*8d741a5dSApple OSS Distributions    for dim in range(k-1, -1, -1):
234*8d741a5dSApple OSS Distributions        count = round(len(results) / step)
235*8d741a5dSApple OSS Distributions        for i in range(0, len(results), step):
236*8d741a5dSApple OSS Distributions            cur = results[i].parameters
237*8d741a5dSApple OSS Distributions            if i != 0 and cur[dim] == first[dim]:
238*8d741a5dSApple OSS Distributions                count = round(i / step)
239*8d741a5dSApple OSS Distributions                break;
240*8d741a5dSApple OSS Distributions        step *= count
241*8d741a5dSApple OSS Distributions        counts.append(count)
242*8d741a5dSApple OSS Distributions
243*8d741a5dSApple OSS Distributions    counts.reverse()
244*8d741a5dSApple OSS Distributions    return counts;
245*8d741a5dSApple OSS Distributions
246*8d741a5dSApple OSS Distributions# Reduce one k-dimensional results to many (k-1) dimensional results
247*8d741a5dSApple OSS Distributions# Yields a sequence of [results, name] pairs
248*8d741a5dSApple OSS Distributions# where results has k-1 dimensions
249*8d741a5dSApple OSS Distributions# and name is the parameter name from the removed dimension
250*8d741a5dSApple OSS Distributionsdef iterate_dimension(results, dim = 0):
251*8d741a5dSApple OSS Distributions    if len(results) == 0:
252*8d741a5dSApple OSS Distributions        return
253*8d741a5dSApple OSS Distributions
254*8d741a5dSApple OSS Distributions    k = dimensions(results)
255*8d741a5dSApple OSS Distributions    dim_counts = count_each_dimension(results)
256*8d741a5dSApple OSS Distributions
257*8d741a5dSApple OSS Distributions    inner_count = 1
258*8d741a5dSApple OSS Distributions    for d in range(dim+1, k):
259*8d741a5dSApple OSS Distributions        inner_count *= dim_counts[d]
260*8d741a5dSApple OSS Distributions
261*8d741a5dSApple OSS Distributions    outer_step = len(results)
262*8d741a5dSApple OSS Distributions    for d in range(0, dim):
263*8d741a5dSApple OSS Distributions        outer_step = int(outer_step / dim_counts[d])
264*8d741a5dSApple OSS Distributions
265*8d741a5dSApple OSS Distributions    for r in range(dim_counts[dim]):
266*8d741a5dSApple OSS Distributions        start = r * inner_count
267*8d741a5dSApple OSS Distributions        name = results[start].parameters[dim]
268*8d741a5dSApple OSS Distributions        new_results = []
269*8d741a5dSApple OSS Distributions        for i in range(start, len(results), outer_step):
270*8d741a5dSApple OSS Distributions            for j in range(inner_count):
271*8d741a5dSApple OSS Distributions                new_result = copy.deepcopy(results[i+j])
272*8d741a5dSApple OSS Distributions                del new_result.parameters[dim]
273*8d741a5dSApple OSS Distributions                new_results.append(new_result)
274*8d741a5dSApple OSS Distributions        yield [new_results, name]
275*8d741a5dSApple OSS Distributions
276*8d741a5dSApple OSS Distributions# Print the results of a test that has two parameters (for example a test of start/size)
277*8d741a5dSApple OSS Distributions# If overrides!=None, use any non-SUCCESS return values from override in place of the other results.
278*8d741a5dSApple OSS Distributionsdef print_results_2D(results, overrides=None):
279*8d741a5dSApple OSS Distributions    # complain if results and override have different dimensions
280*8d741a5dSApple OSS Distributions    if overrides:
281*8d741a5dSApple OSS Distributions        if len(overrides) != len(results):
282*8d741a5dSApple OSS Distributions            print("WARNING: override results have a different height; overrides ignored")
283*8d741a5dSApple OSS Distributions        for i, result in enumerate(results):
284*8d741a5dSApple OSS Distributions            if len(overrides[i].parameters) != len(result.parameters):
285*8d741a5dSApple OSS Distributions                print("WARNING: override results have a different width; overrides ignored")
286*8d741a5dSApple OSS Distributions
287*8d741a5dSApple OSS Distributions    columns = []
288*8d741a5dSApple OSS Distributions    prev_row_label = ''
289*8d741a5dSApple OSS Distributions    first_row_label = ''
290*8d741a5dSApple OSS Distributions    for i, result in enumerate(results):
291*8d741a5dSApple OSS Distributions        if overrides: override = overrides[i].ret
292*8d741a5dSApple OSS Distributions
293*8d741a5dSApple OSS Distributions        if first_row_label == '':
294*8d741a5dSApple OSS Distributions            # record first row's name so we can use it to find columns
295*8d741a5dSApple OSS Distributions            # (assumes every row has the same column labels)
296*8d741a5dSApple OSS Distributions            first_row_label = result.parameters[0]
297*8d741a5dSApple OSS Distributions
298*8d741a5dSApple OSS Distributions        if result.parameters[0] == first_row_label:
299*8d741a5dSApple OSS Distributions            # record column names in the first row
300*8d741a5dSApple OSS Distributions            columns.append(result.parameters[1])
301*8d741a5dSApple OSS Distributions
302*8d741a5dSApple OSS Distributions        if result.parameters[0] != prev_row_label:
303*8d741a5dSApple OSS Distributions            # new row
304*8d741a5dSApple OSS Distributions            if prev_row_label != '': print(format_indent + prev_row_label)
305*8d741a5dSApple OSS Distributions            print(format_indent, end='')
306*8d741a5dSApple OSS Distributions            prev_row_label = result.parameters[0]
307*8d741a5dSApple OSS Distributions
308*8d741a5dSApple OSS Distributions        if overrides and override != RESULT_SUCCESS:
309*8d741a5dSApple OSS Distributions            print_one_result(override)
310*8d741a5dSApple OSS Distributions        else:
311*8d741a5dSApple OSS Distributions            print_one_result(result.ret)
312*8d741a5dSApple OSS Distributions
313*8d741a5dSApple OSS Distributions    if prev_row_label: print(format_indent + prev_row_label)
314*8d741a5dSApple OSS Distributions    print_column_labels(columns, format_indent_width + format_col_width - 1, format_col_width)
315*8d741a5dSApple OSS Distributions
316*8d741a5dSApple OSS Distributionsdef print_results_2D_try_condensed(results):
317*8d741a5dSApple OSS Distributions    if 0 == len(results):
318*8d741a5dSApple OSS Distributions        return
319*8d741a5dSApple OSS Distributions    singleton = results[0].ret
320*8d741a5dSApple OSS Distributions    if any([result.ret != singleton for result in results]):
321*8d741a5dSApple OSS Distributions        print_results_2D(results)
322*8d741a5dSApple OSS Distributions        return
323*8d741a5dSApple OSS Distributions    # will print as condensed
324*8d741a5dSApple OSS Distributions    rows = set()
325*8d741a5dSApple OSS Distributions    cols = set()
326*8d741a5dSApple OSS Distributions    for result in results:
327*8d741a5dSApple OSS Distributions        rows.add(result.parameters[0].split()[1])
328*8d741a5dSApple OSS Distributions        cols.add(result.parameters[1].split()[1])
329*8d741a5dSApple OSS Distributions    print_one_result(result.ret)
330*8d741a5dSApple OSS Distributions    print(" for all pairs")
331*8d741a5dSApple OSS Distributions
332*8d741a5dSApple OSS Distributionsdef print_results_3D(results, testname):
333*8d741a5dSApple OSS Distributions    # foreach parameter[1], print 2D table of parameter[0] and parameter[2]
334*8d741a5dSApple OSS Distributions    for results2D, name in iterate_dimension(results, 1):
335*8d741a5dSApple OSS Distributions        print(testname + ': ' + name)
336*8d741a5dSApple OSS Distributions        print_results_2D(results2D)
337*8d741a5dSApple OSS Distributions
338*8d741a5dSApple OSS Distributions    # foreach parameter[0], print 2D table of parameter[1] and parameter[2]
339*8d741a5dSApple OSS Distributions    # This is redundant but can be useful for human readers.
340*8d741a5dSApple OSS Distributions    for results2D, name in iterate_dimension(results, 0):
341*8d741a5dSApple OSS Distributions        print(testname + ': ' + name)
342*8d741a5dSApple OSS Distributions        print_results_2D(results2D)
343*8d741a5dSApple OSS Distributions
344*8d741a5dSApple OSS Distributionsdef print_results_4D(results):
345*8d741a5dSApple OSS Distributions    x, y, z = '', '', ''
346*8d741a5dSApple OSS Distributions    # Make a map[{3rd_param, 4th_param, ...}] = {all options}
347*8d741a5dSApple OSS Distributions    # For now, we print 2d tables of 1st, 2nd param for each possible combination of remaining values
348*8d741a5dSApple OSS Distributions
349*8d741a5dSApple OSS Distributions    map_of_results = {}
350*8d741a5dSApple OSS Distributions    for _, result in enumerate(results):
351*8d741a5dSApple OSS Distributions        k = tuple(result.parameters[2:])
352*8d741a5dSApple OSS Distributions
353*8d741a5dSApple OSS Distributions        if k not in map_of_results:
354*8d741a5dSApple OSS Distributions            map_of_results[k] = [result]
355*8d741a5dSApple OSS Distributions        else:
356*8d741a5dSApple OSS Distributions            map_of_results[k].append(result)
357*8d741a5dSApple OSS Distributions
358*8d741a5dSApple OSS Distributions    # prepare to iterate
359*8d741a5dSApple OSS Distributions    prev_matrix = []
360*8d741a5dSApple OSS Distributions    iterable = []
361*8d741a5dSApple OSS Distributions    for k, result_list in map_of_results.items():
362*8d741a5dSApple OSS Distributions        one_2d_result = []
363*8d741a5dSApple OSS Distributions        matrix = []
364*8d741a5dSApple OSS Distributions        for result in result_list:
365*8d741a5dSApple OSS Distributions            x = result.parameters[0]
366*8d741a5dSApple OSS Distributions            y = result.parameters[1]
367*8d741a5dSApple OSS Distributions            repl_result = Result(result.ret, (x, y))
368*8d741a5dSApple OSS Distributions            one_2d_result.append(repl_result)
369*8d741a5dSApple OSS Distributions            matrix.append(result.ret)
370*8d741a5dSApple OSS Distributions        if matrix == prev_matrix:
371*8d741a5dSApple OSS Distributions            # if the return codes are the same everywhere, we will print successive tables only once
372*8d741a5dSApple OSS Distributions            # note that this assumes that the sets of 2D labels are the same everywhere, and doesn't check that assumption
373*8d741a5dSApple OSS Distributions            iterable[-1][0].append(k)
374*8d741a5dSApple OSS Distributions        else:
375*8d741a5dSApple OSS Distributions            iterable.append(([k], one_2d_result))
376*8d741a5dSApple OSS Distributions        prev_matrix = matrix
377*8d741a5dSApple OSS Distributions
378*8d741a5dSApple OSS Distributions    # print
379*8d741a5dSApple OSS Distributions    for iter in iterable:
380*8d741a5dSApple OSS Distributions        print(iter[0])
381*8d741a5dSApple OSS Distributions        print_results_2D_try_condensed(iter[1])
382*8d741a5dSApple OSS Distributions
383*8d741a5dSApple OSS Distributions
384*8d741a5dSApple OSS Distributions# Print the results of a test that has two parameters
385*8d741a5dSApple OSS Distributions# (for example a test of addr only, or size only)
386*8d741a5dSApple OSS Distributions# If overrides!=None, use any non-SUCCESS return values from override in place of the other results.
387*8d741a5dSApple OSS Distributionsdef print_results_1D(results, overrides=None):
388*8d741a5dSApple OSS Distributions    # complain if results and overrides have different dimensions
389*8d741a5dSApple OSS Distributions    if overrides:
390*8d741a5dSApple OSS Distributions        if len(overrides) != len(results):
391*8d741a5dSApple OSS Distributions            print("WARNING: override results have a different height; overrides ignored")
392*8d741a5dSApple OSS Distributions        for i, result in enumerate(results):
393*8d741a5dSApple OSS Distributions            if len(overrides[i].parameters) != len(result.parameters):
394*8d741a5dSApple OSS Distributions                print("WARNING: override results have a different width; overrides ignored")
395*8d741a5dSApple OSS Distributions
396*8d741a5dSApple OSS Distributions    for i, result in enumerate(results):
397*8d741a5dSApple OSS Distributions        if overrides: override = overrides[i].ret
398*8d741a5dSApple OSS Distributions
399*8d741a5dSApple OSS Distributions        # indent, value, indent, label
400*8d741a5dSApple OSS Distributions        print(format_indent, end='')
401*8d741a5dSApple OSS Distributions        if overrides and override != RESULT_SUCCESS:
402*8d741a5dSApple OSS Distributions            print_one_result(override)
403*8d741a5dSApple OSS Distributions        else:
404*8d741a5dSApple OSS Distributions            print_one_result(result.ret)
405*8d741a5dSApple OSS Distributions        print(format_indent + result.parameters[0])
406*8d741a5dSApple OSS Distributions
407*8d741a5dSApple OSS Distributionsdef print_results_nD(results, testname, overrides=None):
408*8d741a5dSApple OSS Distributions    if (dimensions(results) == 1):
409*8d741a5dSApple OSS Distributions        print_results_1D(results, overrides)
410*8d741a5dSApple OSS Distributions    elif (dimensions(results) == 2):
411*8d741a5dSApple OSS Distributions        print_results_2D(results, overrides)
412*8d741a5dSApple OSS Distributions    elif dimensions(results) == 3:
413*8d741a5dSApple OSS Distributions        print_results_3D(results, testname)
414*8d741a5dSApple OSS Distributions    elif dimensions(results) == 4:
415*8d741a5dSApple OSS Distributions        print_results_4D(results)
416*8d741a5dSApple OSS Distributions    else:
417*8d741a5dSApple OSS Distributions        print(format_indent + 'too many dimensions')
418*8d741a5dSApple OSS Distributions
419*8d741a5dSApple OSS Distributions
420*8d741a5dSApple OSS Distributionsdef main():
421*8d741a5dSApple OSS Distributions    data = sys.stdin.readlines()
422*8d741a5dSApple OSS Distributions
423*8d741a5dSApple OSS Distributions
424*8d741a5dSApple OSS Distributions    # remove any lines that don't start with "TESTNAME" or "TESTCONFIG" or "RESULT"
425*8d741a5dSApple OSS Distributions    # (including darwintest output like "PASS" or "FAIL")
426*8d741a5dSApple OSS Distributions    # and print them now
427*8d741a5dSApple OSS Distributions    # Also verify that the counts of "TEST BEGIN" == "TEST END"
428*8d741a5dSApple OSS Distributions    # (they will mismatch if a test suite crashed)
429*8d741a5dSApple OSS Distributions    testbegincount = 0
430*8d741a5dSApple OSS Distributions    testendcount = 0
431*8d741a5dSApple OSS Distributions    testlines = []
432*8d741a5dSApple OSS Distributions    for line in data:
433*8d741a5dSApple OSS Distributions        unmodified_line = line
434*8d741a5dSApple OSS Distributions        # count TEST BEGIN and TEST END
435*8d741a5dSApple OSS Distributions        if ('TEST BEGIN' in line):
436*8d741a5dSApple OSS Distributions            testbegincount += 1
437*8d741a5dSApple OSS Distributions        if ('TEST END' in line):
438*8d741a5dSApple OSS Distributions            testendcount += 1
439*8d741a5dSApple OSS Distributions        # remove any T_LOG() timestamp prefixes and KTEST prefixes
440*8d741a5dSApple OSS Distributions        line = re.sub('^\s*\d+:\d+:\d+ ', '', line)
441*8d741a5dSApple OSS Distributions        line = re.sub('^\[KTEST\]\s+[A-Z]+\s+\d+\s+(\d+\s+)?\S+\s+\d+\s+', '', line)
442*8d741a5dSApple OSS Distributions        line = line.lstrip()
443*8d741a5dSApple OSS Distributions
444*8d741a5dSApple OSS Distributions        if (line.startswith('TESTNAME') or line.startswith('RESULT')
445*8d741a5dSApple OSS Distributions            or line.startswith('TESTCONFIG') or line.startswith('TESTCOMPAT')):
446*8d741a5dSApple OSS Distributions            testlines.append(line)  # line is test output
447*8d741a5dSApple OSS Distributions        elif line == '':
448*8d741a5dSApple OSS Distributions            pass # ignore empty lines
449*8d741a5dSApple OSS Distributions        else:
450*8d741a5dSApple OSS Distributions            print(unmodified_line, end='')  # line is other output
451*8d741a5dSApple OSS Distributions
452*8d741a5dSApple OSS Distributions    # parse test output into Test and Result objects
453*8d741a5dSApple OSS Distributions
454*8d741a5dSApple OSS Distributions    testnum = 0
455*8d741a5dSApple OSS Distributions    def group_by_test(line):
456*8d741a5dSApple OSS Distributions        nonlocal testnum
457*8d741a5dSApple OSS Distributions        if line.startswith('TESTNAME '):
458*8d741a5dSApple OSS Distributions            testnum = testnum+1
459*8d741a5dSApple OSS Distributions        return testnum
460*8d741a5dSApple OSS Distributions
461*8d741a5dSApple OSS Distributions    tests = []
462*8d741a5dSApple OSS Distributions    for _, group in itertools.groupby(testlines, group_by_test):
463*8d741a5dSApple OSS Distributions        lines = list(group)
464*8d741a5dSApple OSS Distributions
465*8d741a5dSApple OSS Distributions        name = lines.pop(0).removeprefix('TESTNAME ').rstrip()
466*8d741a5dSApple OSS Distributions        config = lines.pop(0).removeprefix('TESTCONFIG ').rstrip()
467*8d741a5dSApple OSS Distributions        compat = []
468*8d741a5dSApple OSS Distributions        results = []
469*8d741a5dSApple OSS Distributions        for line in lines:
470*8d741a5dSApple OSS Distributions            if line.startswith('RESULT'):
471*8d741a5dSApple OSS Distributions                components = line.removeprefix('RESULT ').rstrip().split(', ')
472*8d741a5dSApple OSS Distributions                ret = int(components.pop(0))
473*8d741a5dSApple OSS Distributions                results.append(Result(ret, components))
474*8d741a5dSApple OSS Distributions
475*8d741a5dSApple OSS Distributions        tests.append(Test(name, config, compat, results))
476*8d741a5dSApple OSS Distributions
477*8d741a5dSApple OSS Distributions    print('found %d tests' % (len(tests)))
478*8d741a5dSApple OSS Distributions
479*8d741a5dSApple OSS Distributions    # stats to print at the end
480*8d741a5dSApple OSS Distributions    test_count = len(tests)
481*8d741a5dSApple OSS Distributions    all_configurations = set()
482*8d741a5dSApple OSS Distributions
483*8d741a5dSApple OSS Distributions    # print test output
484*8d741a5dSApple OSS Distributions    for test in tests:
485*8d741a5dSApple OSS Distributions        # print test name and test config on separate lines
486*8d741a5dSApple OSS Distributions        # `diff` handles this better than putting both on the same line
487*8d741a5dSApple OSS Distributions        print('test ' + test.testname)
488*8d741a5dSApple OSS Distributions
489*8d741a5dSApple OSS Distributions        print(format_indent + 'config ' + test.config)
490*8d741a5dSApple OSS Distributions        all_configurations.add(test.config)
491*8d741a5dSApple OSS Distributions
492*8d741a5dSApple OSS Distributions        if len(test.results) == 0:
493*8d741a5dSApple OSS Distributions            print(format_indent + 'no results')
494*8d741a5dSApple OSS Distributions        else:
495*8d741a5dSApple OSS Distributions            print_legend(test)
496*8d741a5dSApple OSS Distributions            print_results_nD(test.results, test.testname)
497*8d741a5dSApple OSS Distributions
498*8d741a5dSApple OSS Distributions
499*8d741a5dSApple OSS Distributions        print('end  ' + test.testname)
500*8d741a5dSApple OSS Distributions
501*8d741a5dSApple OSS Distributions    print()
502*8d741a5dSApple OSS Distributions    print(str(test_count) + ' test(s) performed')
503*8d741a5dSApple OSS Distributions
504*8d741a5dSApple OSS Distributions    if (testbegincount != testendcount):
505*8d741a5dSApple OSS Distributions        print('### error: %d TEST BEGINs, %d TEST ENDs - some tests may have crashed'
506*8d741a5dSApple OSS Distributions              % (testbegincount, testendcount))
507*8d741a5dSApple OSS Distributions
508*8d741a5dSApple OSS Distributions    print(str(len(all_configurations)) + ' configuration(s) tested:')
509*8d741a5dSApple OSS Distributions    for config in sorted(all_configurations):
510*8d741a5dSApple OSS Distributions        print(format_indent + '[' + config + ']')
511*8d741a5dSApple OSS Distributions
512*8d741a5dSApple OSS Distributions
513*8d741a5dSApple OSS Distributionsmain()
514