Loading...
tools/format_vm_parameter_validation.py xnu-12377.101.15 /dev/null
--- xnu/xnu-12377.101.15/tools/format_vm_parameter_validation.py
+++ /dev/null
@@ -1,551 +0,0 @@
-#!/usr/bin/python3
-
-# format_vm_parameter_validation.py
-# Pretty-print the output of tests/vm/vm_parameter_validation.c
-#
-# usage:
-#     vm_parameter_validation | format_vm_parameter_validation.py
-
-import re
-import sys
-import copy
-import itertools
-
-# magic return values used for in-band signalling
-# fixme duplicated in vm_parameter_validation.c
-# fixme also duplicated in other_return_values below
-RESULT_SUCCESS  = 0
-RESULT_BUSTED   = -99
-RESULT_IGNORED  = -98
-RESULT_ZEROSIZE = -97
-RESULT_PANIC    = -96
-RESULT_GUARD    = -95
-RESULT_MISMATCH = -94
-RESULT_OUT_PARAM_BAD = -93
-# Some Mach errors use their normal integer values, 
-# but we handle them specially here because those
-# integers are too long to fit in the grid output.
-RESULT_MACH_SEND_INVALID_MEMORY = 0x1000000c
-RESULT_MACH_SEND_INVALID_DEST = 0x10000003
-
-# output formatting
-format_result = {
-    RESULT_SUCCESS       : '  .',
-    RESULT_BUSTED        : ' **',
-    RESULT_MISMATCH      : ' ##',
-    RESULT_IGNORED       : '   ',
-    RESULT_ZEROSIZE      : '  o',
-    RESULT_PANIC         : ' pp',
-    RESULT_GUARD         : ' gg',
-    RESULT_OUT_PARAM_BAD : ' ot',
-    RESULT_MACH_SEND_INVALID_MEMORY : ' mi',
-    RESULT_MACH_SEND_INVALID_DEST :   ' md',
-}
-
-# same as format_result, but for functions 
-# where 0=failure and 1=success
-format_bool_result = format_result.copy()
-format_bool_result.update({
-    0 : '  x',
-    1 : format_result[RESULT_SUCCESS],
-})
-
-def formatter_for_testname(testname):
-    if (error_code_values_for_testname(testname) == bool_return_values):
-        return format_bool_result
-    return format_result
-
-format_default = '%3d'
-format_col_width = 3
-format_empty_col = format_col_width * ' '
-format_indent_width = 4
-format_indent = format_indent_width * ' '
-
-
-# record the result of one trial:
-# ret: the return value from the tested function
-# parameters: array of the input parameter names for that trial
-#   (for example ["start PGSZ-2", "size -1"])
-class Result:
-    def __init__(self, new_ret, new_parameters):
-        self.ret = new_ret
-        self.parameters = new_parameters
-    def __repr__(self):
-        return str(self.ret) + " = " + str(self.parameters)
-
-# record the results of all trials in one test
-# testname: the name of the test (including the function being tested)
-# config: a string describing OS, CPU, etc
-# compat: code for error compatibility
-# results: an array of Result, one per trial
-class Test:
-    def __init__(self, new_name, new_config, new_compat, new_results = []):
-        self.testname = new_name
-        self.config = new_config
-        self.compat = new_compat
-        self.results = new_results
-
-# print column labels under some output
-# example output given indent=2 col_width=4 labels=[foo,bar,baz,qux]:
-#  |   |   |   |
-#  |   |   |   qux
-#  |   |   baz
-#  |   bar
-#  foo
-def print_column_labels(labels, indent_width, col_width):
-    indent = indent_width * ' '
-    empty_column = '|' + (col_width-1) * ' '
-
-    unprinted = len(labels)
-    print(indent + unprinted*empty_column)
-
-    for label in reversed(labels):
-        unprinted -= 1
-        print(indent + unprinted*empty_column + label)
-
-# pretty-print one function return code
-def print_one_result(ret, formatter):
-    if ret in formatter:
-        print(formatter[ret], end='')
-    else:
-        print(format_default % (ret), end='')
-
-# choose the appropriate error code table for a test
-# (either errno_return_values, bool_return_values, or kern_return_values)
-def error_code_values_for_testname(testname):
-    errno_fns = ['mprotect', 'msync', 'minherit', 'mincore', 'mlock', 'munlock',
-                 'mmap', 'munmap', 'mremap_encrypted', 'vslock', 'vsunlock',
-                 'madvise']
-    bool_fns = ['useracc', 'task_find_region_details']
-    for fn in errno_fns:
-        if testname.startswith(fn):
-            return errno_return_values
-    for fn in bool_fns:
-        if testname.startswith(fn):
-            return bool_return_values
-    return kern_return_values
-
-# print a helpful description of the return values seen in results
-# fixme these won't include RESULT_MISMATCH
-def print_legend(test):
-    # find all error codes represented in the results
-    codes = {}
-    for result in test.results:
-        codes[result.ret] = True
-
-    known_return_values = error_code_values_for_testname(test.testname)
-
-    # print the names of the detected error codes
-    output = []
-    for code in sorted(codes.keys()):
-        if code in known_return_values:
-            output.append(known_return_values[code])
-        elif code in other_return_values:
-            output.append(other_return_values[code])
-        elif code != 0:
-            output.append(str(code) + ': ????')
-
-    print(format_indent + '(' + ', '.join(output) + ')')
-
-# display names for error codes returned in errno
-errno_return_values = {
-    1: 'EPERM',
-    9: 'EBADF',
-    12: 'ENOMEM',
-    13: 'EACCES',
-    14: 'EFAULT',
-    22: 'EINVAL',
-    45: 'ENOTSUP',
-}
-for k, v in errno_return_values.items():
-    errno_return_values[k] = str(k) + ': ' + v
-
-# display names for error codes returned in kern_return_t
-kern_return_values = {
-    1: 'KERN_INVALID_ADDRESS',
-    2: 'KERN_PROTECTION_FAILURE',
-    3: 'KERN_NO_SPACE',
-    4: 'KERN_INVALID_ARGUMENT',
-    5: 'KERN_FAILURE',
-    6: 'KERN_RESOURCE_SHORTAGE',
-    7: 'KERN_NOT_RECEIVER',
-    8: 'KERN_NO_ACCESS',
-    9: 'KERN_MEMORY_FAILURE',
-    10: 'KERN_MEMORY_ERROR',
-    11: 'KERN_ALREADY_IN_SET',
-    12: 'KERN_NOT_IN_SET',
-    13: 'KERN_NAME_EXISTS',
-    14: 'KERN_ABORTED',
-    15: 'KERN_INVALID_NAME',
-    16: 'KERN_INVALID_TASK',
-    17: 'KERN_INVALID_RIGHT',
-    18: 'KERN_INVALID_VALUE',
-    19: 'KERN_UREFS_OVERFLOW',
-    20: 'KERN_INVALID_CAPABILITY',
-    21: 'KERN_RIGHT_EXISTS',
-    22: 'KERN_INVALID_HOST',
-    23: 'KERN_MEMORY_PRESENT',
-    24: 'KERN_MEMORY_DATA_MOVED',
-    25: 'KERN_MEMORY_RESTART_COPY',
-    26: 'KERN_INVALID_PROCESSOR_SET',
-    27: 'KERN_POLICY_LIMIT',
-    28: 'KERN_INVALID_POLICY',
-    29: 'KERN_INVALID_OBJECT',
-    30: 'KERN_ALREADY_WAITING',
-    31: 'KERN_DEFAULT_SET',
-    32: 'KERN_EXCEPTION_PROTECTED',
-    33: 'KERN_INVALID_LEDGER',
-    34: 'KERN_INVALID_MEMORY_CONTROL',
-    35: 'KERN_INVALID_SECURITY',
-    36: 'KERN_NOT_DEPRESSED',
-    37: 'KERN_TERMINATED',
-    38: 'KERN_LOCK_SET_DESTROYED',
-    39: 'KERN_LOCK_UNSTABLE',
-    40: 'KERN_LOCK_OWNED',
-    41: 'KERN_LOCK_OWNED_SELF',
-    42: 'KERN_SEMAPHORE_DESTROYED',
-    43: 'KERN_RPC_SERVER_TERMINATED',
-    44: 'KERN_RPC_TERMINATE_ORPHAN',
-    45: 'KERN_RPC_CONTINUE_ORPHAN',
-    46: 'KERN_NOT_SUPPORTED',
-    47: 'KERN_NODE_DOWN',
-    48: 'KERN_NOT_WAITING',
-    49: 'KERN_OPERATION_TIMED_OUT',
-    50: 'KERN_CODESIGN_ERROR',
-    51: 'KERN_POLICY_STATIC',
-    52: 'KERN_INSUFFICIENT_BUFFER_SIZE',
-    53: 'KERN_DENIED',
-    54: 'KERN_MISSING_KC',
-    55: 'KERN_INVALID_KC',
-    56: 'KERN_NOT_FOUND',
-    100: 'KERN_RETURN_MAX',
-    -304: 'MIG_BAD_ARGUMENTS (server type check failure)',
-    # MACH_SEND_INVALID_MEMORY and other Mach errors with large integer values
-    # are not handled here. They use format_result and other_return_values instead.
-}
-for k, v in kern_return_values.items():
-    kern_return_values[k] = str(k) + ': ' + v
-
-# display names for error codes return by a boolean function
-# where 0=failure and 1=success
-bool_return_values = {
-    0: format_bool_result[0].lstrip() + ': false/failure',
-    1: format_bool_result[1].lstrip() + ': true/success',
-}
-
-# display names for the special return values used by the test machinery
-other_return_values = {
-    RESULT_BUSTED:   format_result[RESULT_BUSTED].lstrip() + ': trial broken, not performed',
-    RESULT_IGNORED:  '<empty> trial ignored, not performed',
-    RESULT_ZEROSIZE: format_result[RESULT_ZEROSIZE].lstrip() + ': size == 0',
-    RESULT_PANIC:    format_result[RESULT_PANIC].lstrip() + ': trial is believed to panic, not performed',
-    RESULT_GUARD:    format_result[RESULT_GUARD].lstrip() + ': trial is believed to throw EXC_GUARD, not performed',
-    RESULT_OUT_PARAM_BAD: format_result[RESULT_OUT_PARAM_BAD].lstrip() + ': trial set incorrect values to out parameters',
-    RESULT_MACH_SEND_INVALID_MEMORY: format_result[RESULT_MACH_SEND_INVALID_MEMORY].lstrip() + ': MACH_SEND_INVALID_MEMORY',
-    RESULT_MACH_SEND_INVALID_DEST:   format_result[RESULT_MACH_SEND_INVALID_DEST].lstrip() + ': MACH_SEND_INVALID_DEST',
-}
-
-# inside line, replace 'return 123' with 'return ERR_CODE_NAME'
-def replace_error_code_return(test, line):
-    known_return_values = error_code_values_for_testname(test.testname)
-    for code, name in known_return_values.items():
-        line = line.replace('return ' + str(code) + ';', 'return ' + name + ';')
-    return line
-
-def dimensions(results):
-    if len(results) == 0:
-        return 0
-    return len(results[0].parameters)
-
-# given one k-dimensional results
-# return a list of k counts that is the size of each dimension
-def count_each_dimension(results):
-    if len(results) == 0:
-        return []
-    first = results[0].parameters
-    k = dimensions(results)
-    counts = []
-    step = 1
-    for dim in range(k-1, -1, -1):
-        count = round(len(results) / step)
-        for i in range(0, len(results), step):
-            cur = results[i].parameters
-            if i != 0 and cur[dim] == first[dim]:
-                count = round(i / step)
-                break;
-        step *= count
-        counts.append(count)
-
-    counts.reverse()
-    return counts;
-
-# Reduce one k-dimensional results to many (k-1) dimensional results
-# Yields a sequence of [results, name] pairs
-# where results has k-1 dimensions
-# and name is the parameter name from the removed dimension
-def iterate_dimension(results, dim = 0):
-    if len(results) == 0:
-        return
-
-    k = dimensions(results)
-    dim_counts = count_each_dimension(results)
-
-    inner_count = 1
-    for d in range(dim+1, k):
-        inner_count *= dim_counts[d]
-
-    outer_step = len(results)
-    for d in range(0, dim):
-        outer_step = int(outer_step / dim_counts[d])
-
-    for r in range(dim_counts[dim]):
-        start = r * inner_count
-        name = results[start].parameters[dim]
-        new_results = []
-        for i in range(start, len(results), outer_step):
-            for j in range(inner_count):
-                new_result = copy.deepcopy(results[i+j])
-                del new_result.parameters[dim]
-                new_results.append(new_result)
-        yield [new_results, name]
-
-# Print the results of a test that has two parameters (for example a test of start/size)
-# When overrides!=None, use any non-SUCCESS return values from override in place of the other results.
-def print_results_2D(results, formatter, overrides=None):
-    # complain if results and override have different dimensions
-    if overrides:
-        if len(overrides) != len(results):
-            print("WARNING: override results have a different height; overrides ignored")
-        for i, result in enumerate(results):
-            if len(overrides[i].parameters) != len(result.parameters):
-                print("WARNING: override results have a different width; overrides ignored")
-
-    columns = []
-    prev_row_label = ''
-    first_row_label = ''
-    for i, result in enumerate(results):
-        if overrides: override = overrides[i].ret
-
-        if first_row_label == '':
-            # record first row's name so we can use it to find columns
-            # (assumes every row has the same column labels)
-            first_row_label = result.parameters[0]
-
-        if result.parameters[0] == first_row_label:
-            # record column names in the first row
-            columns.append(result.parameters[1])
-
-        if result.parameters[0] != prev_row_label:
-            # new row
-            if prev_row_label != '': print(format_indent + prev_row_label)
-            print(format_indent, end='')
-            prev_row_label = result.parameters[0]
-
-        if overrides and override != RESULT_SUCCESS:
-            print_one_result(override, formatter)
-        else:
-            print_one_result(result.ret, formatter)
-
-    if prev_row_label: print(format_indent + prev_row_label)
-    print_column_labels(columns, format_indent_width + format_col_width - 1, format_col_width)
-
-def print_results_2D_try_condensed(results, formatter):
-    if 0 == len(results):
-        return
-    singleton = results[0].ret
-    if any([result.ret != singleton for result in results]):
-        print_results_2D(results, formatter)
-        return
-    # will print as condensed
-    rows = set()
-    cols = set()
-    for result in results:
-        rows.add(result.parameters[0].split()[1])
-        cols.add(result.parameters[1].split()[1])
-    print_one_result(result.ret, formatter)
-    print(" for all pairs")
-
-def print_results_3D(results, formatter, testname):
-    # foreach parameter[1], print 2D table of parameter[0] and parameter[2]
-    for results2D, name in iterate_dimension(results, 1):
-        print(testname + ': ' + name)
-        print_results_2D(results2D, formatter)
-
-    # foreach parameter[0], print 2D table of parameter[1] and parameter[2]
-    # This is redundant but can be useful for human readers.
-    for results2D, name in iterate_dimension(results, 0):
-        print(testname + ': ' + name)
-        print_results_2D(results2D, formatter)
-
-def print_results_4D(results, formatter):
-    x, y, z = '', '', ''
-    # Make a map[{3rd_param, 4th_param, ...}] = {all options}
-    # For now, we print 2d tables of 1st, 2nd param for each possible combination of remaining values
-
-    map_of_results = {}
-    for _, result in enumerate(results):
-        k = tuple(result.parameters[2:])
-
-        if k not in map_of_results:
-            map_of_results[k] = [result]
-        else:
-            map_of_results[k].append(result)
-
-    # prepare to iterate
-    prev_matrix = []
-    iterable = []
-    for k, result_list in map_of_results.items():
-        one_2d_result = []
-        matrix = []
-        for result in result_list:
-            x = result.parameters[0]
-            y = result.parameters[1]
-            repl_result = Result(result.ret, (x, y))
-            one_2d_result.append(repl_result)
-            matrix.append(result.ret)
-        if matrix == prev_matrix:
-            # In the case the return codes are the same everywhere, we will print successive tables only once
-            # note that this assumes that the sets of 2D labels are the same everywhere, and doesn't check that assumption
-            iterable[-1][0].append(k)
-        else:
-            iterable.append(([k], one_2d_result))
-        prev_matrix = matrix
-
-    # print
-    for iter in iterable:
-        print(iter[0])
-        print_results_2D_try_condensed(iter[1], formatter)
-
-
-# Print the results of a test that has two parameters
-# (for example a test of addr only, or size only)
-# When overrides!=None, use any non-SUCCESS return values from override in place of the other results.
-def print_results_1D(results, formatter, overrides=None):
-    # complain if results and overrides have different dimensions
-    if overrides:
-        if len(overrides) != len(results):
-            print("WARNING: override results have a different height; overrides ignored")
-        for i, result in enumerate(results):
-            if len(overrides[i].parameters) != len(result.parameters):
-                print("WARNING: override results have a different width; overrides ignored")
-
-    for i, result in enumerate(results):
-        if overrides: override = overrides[i].ret
-
-        # indent, value, indent, label
-        print(format_indent, end='')
-        if overrides and override != RESULT_SUCCESS:
-            print_one_result(override, formatter)
-        else:
-            print_one_result(result.ret, formatter)
-        print(format_indent + result.parameters[0])
-
-def print_results_nD(results, testname, overrides=None):
-    formatter = formatter_for_testname(testname)
-    
-    if (dimensions(results) == 1):
-        print_results_1D(results, formatter, overrides)
-    elif (dimensions(results) == 2):
-        print_results_2D(results, formatter, overrides)
-    elif dimensions(results) == 3:
-        print_results_3D(results, formatter, testname)
-    elif dimensions(results) == 4:
-        print_results_4D(results, formatter)
-    else:
-        print(format_indent + 'too many dimensions')
-
-
-def main():
-    data = sys.stdin.readlines()
-
-
-    # remove any lines that don't start with "TESTNAME" or "TESTCONFIG" or "RESULT"
-    # (including darwintest output like "PASS" or "FAIL")
-    # and print them now
-    # Also verify that the counts of "TEST BEGIN" == "TEST END"
-    # (they will mismatch if a test suite crashed)
-    testbegincount = 0
-    testendcount = 0
-    testlines = []
-    for line in data:
-        unmodified_line = line
-        # count TEST BEGIN and TEST END
-        if ('TEST BEGIN' in line):
-            testbegincount += 1
-        if ('TEST END' in line):
-            testendcount += 1
-        # remove any T_LOG() timestamp prefixes and KTEST prefixes
-        line = re.sub('^\s*\d+:\d+:\d+ ', '', line)
-        line = re.sub('^\[KTEST\]\s+[A-Z]+\s+\d+\s+(\d+\s+)?\S+\s+\d+\s+', '', line)
-        line = line.lstrip()
-
-        if (line.startswith('TESTNAME') or line.startswith('RESULT')
-            or line.startswith('TESTCONFIG') or line.startswith('TESTCOMPAT')):
-            testlines.append(line)  # line is test output
-        elif line == '':
-            pass # ignore empty lines
-        else:
-            print(unmodified_line, end='')  # line is other output
-
-    # parse test output into Test and Result objects
-
-    testnum = 0
-    def group_by_test(line):
-        nonlocal testnum
-        if line.startswith('TESTNAME '):
-            testnum = testnum+1
-        return testnum
-
-    tests = []
-    for _, group in itertools.groupby(testlines, group_by_test):
-        lines = list(group)
-
-        name = lines.pop(0).removeprefix('TESTNAME ').rstrip()
-        config = lines.pop(0).removeprefix('TESTCONFIG ').rstrip()
-        compat = []
-        results = []
-        for line in lines:
-            if line.startswith('RESULT'):
-                components = line.removeprefix('RESULT ').rstrip().split(', ')
-                ret = int(components.pop(0))
-                results.append(Result(ret, components))
-
-        tests.append(Test(name, config, compat, results))
-
-    print('found %d tests' % (len(tests)))
-
-    # stats to print at the end
-    test_count = len(tests)
-    all_configurations = set()
-
-    # print test output
-    for test in tests:
-        # print test name and test config on separate lines
-        # `diff` handles this better than putting both on the same line
-        print('test ' + test.testname)
-
-        print(format_indent + 'config ' + test.config)
-        all_configurations.add(test.config)
-
-        if len(test.results) == 0:
-            print(format_indent + 'no results')
-        else:
-            print_legend(test)
-            print_results_nD(test.results, test.testname)
-
-
-        print('end  ' + test.testname)
-
-    print()
-    print(str(test_count) + ' test(s) performed')
-
-    if (testbegincount != testendcount):
-        print('### error: %d TEST BEGINs, %d TEST ENDs - some tests may have crashed'
-              % (testbegincount, testendcount))
-
-    print(str(len(all_configurations)) + ' configuration(s) tested:')
-    for config in sorted(all_configurations):
-        print(format_indent + '[' + config + ']')
-
-
-main()