|
| 1 | +''' |
| 2 | +Process logs to compute pointwise success rate metrics. |
| 3 | +
|
| 4 | +These metrics are measured as follows. For each function, we run the static |
| 5 | +analysis and rewrite that function in isolation, producing a new `.rs` file |
| 6 | +where that function has been rewritten but all other code remains the same. |
| 7 | +Then we remove the `unsafe` qualifier from the target function and try to |
| 8 | +compile the code. The "pointwise success rate" is the number of functions on |
| 9 | +which this procedure succeeds. |
| 10 | +
|
| 11 | +As a performance optimization, instead of running analysis separately for each |
| 12 | +function, we run `c2rust-analyze` with `--rewrite-mode pointwise`, which runs |
| 13 | +the analysis part once and then rewrites each function in isolation using the |
| 14 | +same analysis results. This provides a significant speedup for large codebases |
| 15 | +where the static analysis portion is very slow. |
| 16 | +
|
| 17 | +To provide a basis for comparison, in addition to attempting to compile all |
| 18 | +pointwise rewrites, we also try removing `unsafe` and compiling each function |
| 19 | +in the original, unmodified code. This provides a baseline for how many |
| 20 | +functions are "trivially safe" without rewriting. |
| 21 | +''' |
| 22 | + |
| 23 | +from pprint import pprint |
| 24 | +import re |
| 25 | +import sys |
| 26 | + |
| 27 | +# `pointwise_log_path` should be a log generated by running |
| 28 | +# `pointwise_try_build.sh` on each output file of a pointwise rewrite |
| 29 | +# (`foo.*.rs`, one per function). The outputs for all files should be |
| 30 | +# concatenated in a single log. This gives the results of pointwise rewriting |
| 31 | +# and compiling each function. |
| 32 | +# |
| 33 | +# `unmodified_log_path` should come from `pointwise_try_build_unmodified.sh` |
| 34 | +# instead. This gives results of pointwise compiling each function without |
| 35 | +# rewriting. |
| 36 | +pointwise_log_path, unmodified_log_path = sys.argv[1:] |
| 37 | + |
| 38 | + |
| 39 | +FUNC_ERRORS_RE = re.compile(r'^got ([0-9]+) errors for ([^ \n]+)$') |
| 40 | + |
| 41 | +def read_func_errors(f): |
| 42 | + func_errors = {} |
| 43 | + for line in f: |
| 44 | + m = FUNC_ERRORS_RE.match(line) |
| 45 | + if m is None: |
| 46 | + continue |
| 47 | + func = m.group(2) |
| 48 | + errors = int(m.group(1)) |
| 49 | + assert func not in func_errors, 'duplicate entry for %r' % func |
| 50 | + func_errors[func] = errors |
| 51 | + return func_errors |
| 52 | + |
| 53 | +pointwise_func_errors = read_func_errors(open(pointwise_log_path)) |
| 54 | +pointwise_ok = set(func for func, errors in pointwise_func_errors.items() if errors == 0) |
| 55 | +print('pointwise: %5d/%d functions passed (%.1f%%)' % ( |
| 56 | + len(pointwise_ok), len(pointwise_func_errors), |
| 57 | + len(pointwise_ok) / len(pointwise_func_errors) * 100)) |
| 58 | + |
| 59 | +unmodified_func_errors = read_func_errors(open(unmodified_log_path)) |
| 60 | +unmodified_ok = set(func for func, errors in unmodified_func_errors.items() if errors == 0) |
| 61 | +print('unmodified: %5d/%d functions passed (%.1f%%)' % ( |
| 62 | + len(unmodified_ok), len(unmodified_func_errors), |
| 63 | + len(unmodified_ok) / len(unmodified_func_errors) * 100)) |
| 64 | + |
| 65 | +assert len(pointwise_func_errors) == len(unmodified_func_errors) |
| 66 | +num_total = len(pointwise_func_errors) |
| 67 | +num_unmodified_ok = len(unmodified_ok) |
| 68 | +num_unmodified_bad = num_total - num_unmodified_ok |
| 69 | + |
| 70 | +improved = pointwise_ok - unmodified_ok |
| 71 | +print('improved: %5d/%d functions (%.1f%%)' % ( |
| 72 | + len(improved), num_unmodified_bad, len(improved) / num_unmodified_bad * 100)) |
| 73 | +broke = unmodified_ok - pointwise_ok |
| 74 | +print('broke: %5d/%d functions (%.1f%%)' % ( |
| 75 | + len(broke), num_unmodified_ok, len(broke) / num_unmodified_ok * 100)) |
0 commit comments