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