|
2 | 2 | import pwd |
3 | 3 | import re |
4 | 4 | import sys |
| 5 | +from collections import OrderedDict |
| 6 | +import json |
5 | 7 |
|
6 | | -HOOKS = [ |
7 | | - 'strip_paths', |
8 | | - 'anonymize', |
9 | | - 'make_expected_failures_pass', |
10 | | - 'patch_logs' |
| 8 | +PRE_HOOKS = [ |
| 9 | + # nothing |
11 | 10 | ] |
12 | 11 |
|
13 | | -def strip_paths(line): |
| 12 | +POST_HOOKS = [ |
| 13 | + 'drop_entries_on_failure', |
| 14 | + 'patch_logs', |
| 15 | + 'inverse_result_from_expectation' |
| 16 | + ] |
| 17 | + |
| 18 | +def do_something_on_the_raw_log(line: str): |
14 | 19 | """ |
15 | | - strip all path from known file extensions |
16 | | - this can prevent broken test do to mismatch in path |
| 20 | + this is an example preprocessing hook. |
17 | 21 | """ |
18 | | - # its a bit limiting, maybe a better regex would do |
19 | | - # but it seems to work fine for now |
20 | | - path_regex = r"[\/]?[a-zA-Z_.\-0-9]*\/" |
21 | | - file_regex = r"[^\/\s]*(_input|_output|\.xml|\.v|\.vh|\.blif|\.log|\.do|\.dot|_vectors|_activity)" |
22 | | - return re.sub(r"(" + path_regex + r")+(?=" + file_regex + r"[\s\n]+)", "", line) |
| 22 | + return line |
23 | 23 |
|
24 | | -def anonymize(line): |
| 24 | +def do_something_on_the_parsed_log(values: OrderedDict): |
25 | 25 | """ |
26 | | - strip all occurance of this user in the log file |
| 26 | + this is an example post-processing hook. |
27 | 27 | """ |
28 | | - user_name = pwd.getpwuid(os.getuid()).pw_name |
29 | | - return re.sub(r"(" + user_name + r")", "",line) |
30 | | - |
| 28 | + # if 'exit' in values: |
| 29 | + # # do |
31 | 30 |
|
32 | | -def patch_logs(line): |
| 31 | + return values |
| 32 | + |
| 33 | + |
| 34 | +def drop_entries_on_failure(values): |
33 | 35 | """ |
34 | | - Some librairies and tools Odin uses can have different display messages |
35 | | - we do replacements for possible mismatch to stop false positives |
36 | | - we don't use regex |
| 36 | + if we failed we drop the folowing sections |
| 37 | + Failure may happen late in the log, so we may still end up parsing them |
37 | 38 | """ |
38 | | - for old_str, new_string in { |
| 39 | + if 'exit' in values: |
| 40 | + if values['exit'] != 0: |
| 41 | + for sections in [ |
| 42 | + # all the statistical values are not relevant if we failed |
| 43 | + 'max_rss(MiB)', |
| 44 | + 'exec_time(ms)', |
| 45 | + 'synthesis_time(ms)', |
| 46 | + 'simulation_time(ms)', |
| 47 | + 'Latch Drivers', |
| 48 | + 'Pi', |
| 49 | + 'Po', |
| 50 | + 'logic element', |
| 51 | + 'latch', |
| 52 | + 'Adder', |
| 53 | + 'Multiplier', |
| 54 | + 'Memory', |
| 55 | + 'Hard Ip', |
| 56 | + 'generic logic size', |
| 57 | + 'Longest Path', |
| 58 | + 'Average Path', |
| 59 | + 'Estimated LUTs', |
| 60 | + 'Total Node', |
| 61 | + ]: |
| 62 | + if sections in values: |
| 63 | + del values[sections] |
| 64 | + return values |
| 65 | + |
| 66 | +def patch_logs(values): |
| 67 | + """ |
| 68 | + patch the string logs |
| 69 | + """ |
| 70 | + sub_re = { |
| 71 | + # strip username from the logs |
| 72 | + r"(" + pwd.getpwuid(os.getuid()).pw_name + r")": "", |
| 73 | + # strip path from known file extensions |
| 74 | + r"([\/]?[a-zA-Z_.\-0-9]*\/)(?=[^\/\s]*(_input|_output|\.xml|\.v|\.vh|\.blif|\.log|\.do|\.dot|_vectors|_activity)[\s\n]+)": "", |
39 | 75 | # bison used to call EOF $end, but switched to end of file since |
40 | | - r"syntax error, unexpected \$end": r"syntax error, unexpected end of file", |
41 | | - }.items(): |
42 | | - line = re.sub(old_str, new_string,line) |
| 76 | + r"syntax error, unexpected \$end": r"syntax error, unexpected end of file" |
| 77 | + } |
43 | 78 |
|
44 | | - return line |
| 79 | + if isinstance(values, str): |
| 80 | + for old_str, new_string in sub_re.items(): |
| 81 | + values = re.sub(old_str, new_string, values) |
| 82 | + elif isinstance(values, list): |
| 83 | + values = [ patch_logs(log_entry) for log_entry in values ] |
| 84 | + elif isinstance(values, ( OrderedDict, dict )): |
| 85 | + for section in values: |
| 86 | + values[section] = patch_logs(values[section]) |
45 | 87 |
|
| 88 | + return values |
46 | 89 |
|
47 | | -IS_EXPECTED_TO_FAIL = False |
48 | | -def make_expected_failures_pass(line): |
49 | | - # figure out if we expected this to fail |
50 | | - global IS_EXPECTED_TO_FAIL |
51 | | - if re.search(r"^EXPECT:: failure$",line) is not None: |
52 | | - IS_EXPECTED_TO_FAIL = True |
| 90 | +def inverse_result_from_expectation(values): |
53 | 91 |
|
54 | | - # if this was expected to fail |
55 | | - if IS_EXPECTED_TO_FAIL: |
56 | | - # make it pass |
57 | | - line = re.sub(r"(?!Odin exited with code: )(\d+)", "0",line) |
58 | | - |
59 | | - return line |
| 92 | + should_fail = False |
| 93 | + if 'expectation' in values: |
| 94 | + for log in values['expectation']: |
| 95 | + if log == "failure": |
| 96 | + should_fail = True |
| 97 | + break |
| 98 | + |
| 99 | + if 'exit' in values: |
| 100 | + if values['exit'] == 0 and should_fail: |
| 101 | + values['exit'] = 51 |
| 102 | + elif values['exit'] != 0 and should_fail: |
| 103 | + values['exit'] = 0 |
| 104 | + values['expectation'].append("Failure caught and flipped to success by the post processor") |
| 105 | + |
| 106 | + return values |
0 commit comments