Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # Copyright (C) 2017 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | """Cleans up overlapping portions of traces provided by logcat.""" |
| 18 | |
| 19 | from __future__ import absolute_import |
| 20 | from __future__ import division |
| 21 | from __future__ import print_function |
| 22 | |
| 23 | import argparse |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 24 | import re |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 25 | import os |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 26 | |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 27 | STACK_DIVIDER = 65 * "=" |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 28 | |
| 29 | |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 30 | def match_to_int(match): |
| 31 | """Returns trace line number matches as integers for sorting. |
| 32 | Maps other matches to negative integers. |
| 33 | """ |
| 34 | # Hard coded string are necessary since each trace must have the address |
| 35 | # accessed, which is printed before trace lines. |
Bharadwaj Kalandhabhatta | 188ac6c | 2017-07-17 15:15:41 -0700 | [diff] [blame] | 36 | if match == "use-after-poison" or match == "unknown-crash": |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 37 | return -2 |
| 38 | elif match == "READ": |
| 39 | return -1 |
| 40 | # Cutting off non-integer part of match |
| 41 | return int(match[1:-1]) |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 42 | |
| 43 | |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 44 | def clean_trace_if_valid(trace, stack_min_size, prune_exact): |
| 45 | """Cleans trace if it meets a certain standard. Returns None otherwise.""" |
Bharadwaj Kalandhabhatta | 188ac6c | 2017-07-17 15:15:41 -0700 | [diff] [blame] | 46 | # Note: Sample input may contain "unknown-crash" instead of |
| 47 | # "use-after-poison" |
| 48 | # |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 49 | # Sample input: |
| 50 | # trace: |
| 51 | # "...ERROR: AddressSanitizer: use-after-poison on address 0x0071126a870a... |
| 52 | # ...READ of size 2 at 0x0071126a870a thread T0 (droid.deskclock) |
| 53 | # ... #0 0x71281013b3 (/data/asan/system/lib64/libart.so+0x2263b3) |
| 54 | # ... #1 0x71280fe6b7 (/data/asan/system/lib64/libart.so+0x2236b7) |
| 55 | # ... #3 0x71280c22ef (/data/asan/system/lib64/libart.so+0x1e72ef) |
| 56 | # ... #2 0x712810a84f (/data/asan/system/lib64/libart.so+0x22f84f)" |
| 57 | # |
| 58 | # stack_min_size: 2 |
| 59 | # prune_exact: False |
| 60 | # |
| 61 | # Sample output: |
| 62 | # |
| 63 | # "...ERROR: AddressSanitizer: use-after-poison on address 0x0071126a870a... |
| 64 | # ...READ of size 2 at 0x0071126a870a thread T0 (droid.deskclock) |
| 65 | # ... #0 0x71281013b3 (/data/asan/system/lib64/libart.so+0x2263b3) |
| 66 | # ... #1 0x71280fe6b7 (/data/asan/system/lib64/libart.so+0x2236b7) |
| 67 | # " |
| 68 | |
| 69 | # Adds a newline character if not present at the end of trace |
| 70 | trace = trace if trace[-1] == "\n" else trace + "\n" |
| 71 | trace_line_matches = [(match_to_int(match.group()), match.start()) |
| 72 | for match in re.finditer("#[0-9]+ " |
| 73 | "|use-after-poison" |
Bharadwaj Kalandhabhatta | 188ac6c | 2017-07-17 15:15:41 -0700 | [diff] [blame] | 74 | "|unknown-crash" |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 75 | "|READ", trace) |
| 76 | ] |
| 77 | # Finds the first index where the line number ordering isn't in sequence or |
| 78 | # returns the number of matches if it everything is in order. |
| 79 | bad_line_no = next((i - 2 for i, match in enumerate(trace_line_matches) |
| 80 | if i - 2 != match[0]), len(trace_line_matches) - 2) |
| 81 | # If the number ordering breaks after minimum stack size, then the trace is |
| 82 | # still valid. |
| 83 | if bad_line_no >= stack_min_size: |
| 84 | # Added if the trace is already clean |
| 85 | trace_line_matches.append((trace_line_matches[-1][0] + 1, len(trace))) |
| 86 | bad_match = trace_line_matches[bad_line_no + 2] |
| 87 | if prune_exact: |
| 88 | bad_match = trace_line_matches[stack_min_size + 2] |
| 89 | # Up to new-line that comes before bad line number |
| 90 | return trace[:trace.rindex("\n", 0, bad_match[1]) + 1] |
| 91 | return None |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 92 | |
| 93 | |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 94 | def extant_directory(path_name): |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 95 | """Checks if a path is an actual directory.""" |
| 96 | if not os.path.isdir(path_name): |
| 97 | dir_error = "%s is not a directory" % (path_name) |
| 98 | raise argparse.ArgumentTypeError(dir_error) |
| 99 | return path_name |
| 100 | |
| 101 | |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 102 | def parse_args(): |
| 103 | """Parses arguments passed in.""" |
| 104 | parser = argparse.ArgumentParser() |
| 105 | parser.add_argument("-d", action="store", |
| 106 | default="", dest="out_dir_name", type=extant_directory, |
| 107 | help="Output Directory") |
| 108 | parser.add_argument("-e", action="store_true", |
| 109 | default=False, dest="check_exact", |
| 110 | help="Forces each trace to be cut to have " |
| 111 | "minimum number of lines") |
| 112 | parser.add_argument("-m", action="store", |
| 113 | default=4, dest="stack_min_size", type=int, |
| 114 | help="minimum number of lines a trace should have") |
| 115 | parser.add_argument("trace_file", action="store", |
| 116 | type=argparse.FileType("r"), |
| 117 | help="File only containing lines that are related to " |
| 118 | "Sanitizer traces") |
| 119 | return parser.parse_args() |
| 120 | |
| 121 | |
| 122 | def main(): |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 123 | """Parses arguments and cleans up traces using other functions.""" |
| 124 | stack_min_size = 4 |
| 125 | check_exact = False |
| 126 | |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 127 | parsed_argv = parse_args() |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 128 | stack_min_size = parsed_argv.stack_min_size |
| 129 | check_exact = parsed_argv.check_exact |
| 130 | out_dir_name = parsed_argv.out_dir_name |
| 131 | trace_file = parsed_argv.trace_file |
| 132 | |
| 133 | trace_split = trace_file.read().split(STACK_DIVIDER) |
| 134 | trace_file.close() |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 135 | trace_clean_split = [clean_trace_if_valid(trace, |
| 136 | stack_min_size, |
| 137 | check_exact) |
| 138 | for trace in trace_split |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 139 | ] |
Bharadwaj Kalandhabhatta | ad47e4d | 2017-06-19 16:40:30 -0700 | [diff] [blame] | 140 | trace_clean_split = [trace for trace in trace_clean_split |
| 141 | if trace is not None] |
Bharadwaj Kalandhabhatta | 188ac6c | 2017-07-17 15:15:41 -0700 | [diff] [blame] | 142 | filename = os.path.basename(trace_file.name + "_filtered") |
| 143 | outfile = os.path.join(out_dir_name, filename) |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 144 | with open(outfile, "w") as output_file: |
| 145 | output_file.write(STACK_DIVIDER.join(trace_clean_split)) |
| 146 | |
| 147 | filter_percent = 100.0 - (float(len(trace_clean_split)) / |
| 148 | len(trace_split) * 100) |
| 149 | filter_amount = len(trace_split) - len(trace_clean_split) |
Bharadwaj Kalandhabhatta | 188ac6c | 2017-07-17 15:15:41 -0700 | [diff] [blame] | 150 | print("Filtered out %d (%f%%) of %d. %d (%f%%) remain." |
| 151 | % (filter_amount, filter_percent, len(trace_split), |
| 152 | len(trace_split) - filter_amount, 1 - filter_percent)) |
Bharadwaj Kalandhabhatta | 9e1c45d | 2017-06-13 08:56:51 -0700 | [diff] [blame] | 153 | |
| 154 | |
| 155 | if __name__ == "__main__": |
| 156 | main() |