diff options
| author | 2017-06-19 16:40:30 -0700 | |
|---|---|---|
| committer | 2017-06-20 16:32:06 -0700 | |
| commit | ad47e4d1b2a967233bebce984865b4f11a037df5 (patch) | |
| tree | 66708cb1b1b7b1fe257cee7cd5778367a70c72c5 | |
| parent | 9e1c45d0bfc2f75df24697d537b03dc1b3519e3f (diff) | |
Fixing syntax and behavior from previous commit
Referring to minor mistakes pointed out in
Commit: 9e1c45d0bfc2f75df24697d537b03dc1b3519e3f
There were also some bad traces that could have potentially been missed
in previous commit.
Bug: 37754950
Test: art/tools/runtime_memusage/sanitizer_logcat_analysis.sh
[LOGCAT_FILE]
Change-Id: I6f403ebc949ef9b924a07f68043cae188c3936b1
| -rwxr-xr-x | tools/runtime_memusage/prune_sanitizer_output.py | 199 | ||||
| -rwxr-xr-x | tools/runtime_memusage/symbol_trace_info.py | 38 |
2 files changed, 116 insertions, 121 deletions
diff --git a/tools/runtime_memusage/prune_sanitizer_output.py b/tools/runtime_memusage/prune_sanitizer_output.py index 222c3c77b9..d95b2ced1c 100755 --- a/tools/runtime_memusage/prune_sanitizer_output.py +++ b/tools/runtime_memusage/prune_sanitizer_output.py @@ -21,86 +21,73 @@ from __future__ import division from __future__ import print_function import argparse +import re import os -import sys - -STACK_DIVIDER = 65 * '=' - - -def has_min_lines(trace, stack_min_size): - """Checks if the trace has a minimum amount of levels in trace.""" - # Line containing 'use-after-poison' contains address accessed, which is - # useful for extracting Dex File offsets - string_checks = ['use-after-poison', 'READ'] - required_checks = string_checks + ['#%d ' % line_ctr - for line_ctr in - range(stack_min_size) - ] - try: - trace_indices = [trace.index(check) for check in required_checks] - return all(trace_indices[trace_ind] < trace_indices[trace_ind + 1] - for trace_ind in range(len(trace_indices) - 1)) - except ValueError: - return False - return True - - -def prune_exact(trace, stack_min_size): - """Removes all of trace that comes after the (stack_min_size)th trace.""" - string_checks = ['use-after-poison', 'READ'] - required_checks = string_checks + ['#%d ' % line_ctr - for line_ctr in - range(stack_min_size) - ] - trace_indices = [trace.index(check) for check in required_checks] - new_line_index = trace.index("\n", trace_indices[-1]) - return trace[:new_line_index + 1] - - -def make_unique(trace): - """Removes overlapping line numbers and lines out of order.""" - string_checks = ['use-after-poison', 'READ'] - hard_checks = string_checks + ['#%d ' % line_ctr - for line_ctr in range(100) - ] - last_ind = -1 - for str_check in hard_checks: - try: - location_ind = trace.index(str_check) - if last_ind > location_ind: - trace = trace[:trace[:location_ind].find("\n") + 1] - last_ind = location_ind - try: - next_location_ind = trace.index(str_check, location_ind + 1) - trace = trace[:next_location_ind] - except ValueError: - pass - except ValueError: - pass - return trace - - -def parse_args(argv): - """Parses arguments passed in.""" - parser = argparse.ArgumentParser() - parser.add_argument('-d', action='store', - default="", dest="out_dir_name", type=is_directory, - help='Output Directory') - parser.add_argument('-e', action='store_true', - default=False, dest='check_exact', - help='Forces each trace to be cut to have ' - 'minimum number of lines') - parser.add_argument('-m', action='store', - default=4, dest='stack_min_size', type=int, - help='minimum number of lines a trace should have') - parser.add_argument('trace_file', action='store', - type=argparse.FileType('r'), - help='File only containing lines that are related to ' - 'Sanitizer traces') - return parser.parse_args(argv) - - -def is_directory(path_name): + +STACK_DIVIDER = 65 * "=" + + +def match_to_int(match): + """Returns trace line number matches as integers for sorting. + Maps other matches to negative integers. + """ + # Hard coded string are necessary since each trace must have the address + # accessed, which is printed before trace lines. + if match == "use-after-poison": + return -2 + elif match == "READ": + return -1 + # Cutting off non-integer part of match + return int(match[1:-1]) + + +def clean_trace_if_valid(trace, stack_min_size, prune_exact): + """Cleans trace if it meets a certain standard. Returns None otherwise.""" + # Sample input: + # trace: + # "...ERROR: AddressSanitizer: use-after-poison on address 0x0071126a870a... + # ...READ of size 2 at 0x0071126a870a thread T0 (droid.deskclock) + # ... #0 0x71281013b3 (/data/asan/system/lib64/libart.so+0x2263b3) + # ... #1 0x71280fe6b7 (/data/asan/system/lib64/libart.so+0x2236b7) + # ... #3 0x71280c22ef (/data/asan/system/lib64/libart.so+0x1e72ef) + # ... #2 0x712810a84f (/data/asan/system/lib64/libart.so+0x22f84f)" + # + # stack_min_size: 2 + # prune_exact: False + # + # Sample output: + # + # "...ERROR: AddressSanitizer: use-after-poison on address 0x0071126a870a... + # ...READ of size 2 at 0x0071126a870a thread T0 (droid.deskclock) + # ... #0 0x71281013b3 (/data/asan/system/lib64/libart.so+0x2263b3) + # ... #1 0x71280fe6b7 (/data/asan/system/lib64/libart.so+0x2236b7) + # " + + # Adds a newline character if not present at the end of trace + trace = trace if trace[-1] == "\n" else trace + "\n" + trace_line_matches = [(match_to_int(match.group()), match.start()) + for match in re.finditer("#[0-9]+ " + "|use-after-poison" + "|READ", trace) + ] + # Finds the first index where the line number ordering isn't in sequence or + # returns the number of matches if it everything is in order. + bad_line_no = next((i - 2 for i, match in enumerate(trace_line_matches) + if i - 2 != match[0]), len(trace_line_matches) - 2) + # If the number ordering breaks after minimum stack size, then the trace is + # still valid. + if bad_line_no >= stack_min_size: + # Added if the trace is already clean + trace_line_matches.append((trace_line_matches[-1][0] + 1, len(trace))) + bad_match = trace_line_matches[bad_line_no + 2] + if prune_exact: + bad_match = trace_line_matches[stack_min_size + 2] + # Up to new-line that comes before bad line number + return trace[:trace.rindex("\n", 0, bad_match[1]) + 1] + return None + + +def extant_directory(path_name): """Checks if a path is an actual directory.""" if not os.path.isdir(path_name): dir_error = "%s is not a directory" % (path_name) @@ -108,15 +95,32 @@ def is_directory(path_name): return path_name -def main(argv=None): +def parse_args(): + """Parses arguments passed in.""" + parser = argparse.ArgumentParser() + parser.add_argument("-d", action="store", + default="", dest="out_dir_name", type=extant_directory, + help="Output Directory") + parser.add_argument("-e", action="store_true", + default=False, dest="check_exact", + help="Forces each trace to be cut to have " + "minimum number of lines") + parser.add_argument("-m", action="store", + default=4, dest="stack_min_size", type=int, + help="minimum number of lines a trace should have") + parser.add_argument("trace_file", action="store", + type=argparse.FileType("r"), + help="File only containing lines that are related to " + "Sanitizer traces") + return parser.parse_args() + + +def main(): """Parses arguments and cleans up traces using other functions.""" stack_min_size = 4 check_exact = False - if argv is None: - argv = sys.argv - - parsed_argv = parse_args(argv[1:]) + parsed_argv = parse_args() stack_min_size = parsed_argv.stack_min_size check_exact = parsed_argv.check_exact out_dir_name = parsed_argv.out_dir_name @@ -124,28 +128,15 @@ def main(argv=None): trace_split = trace_file.read().split(STACK_DIVIDER) trace_file.close() - # if flag -e is enabled - if check_exact: - trace_prune_split = [prune_exact(trace, stack_min_size) - for trace in trace_split if - has_min_lines(trace, stack_min_size) - ] - trace_unique_split = [make_unique(trace) - for trace in trace_prune_split - ] - else: - trace_unique_split = [make_unique(trace) - for trace in trace_split if - has_min_lines(trace, stack_min_size) - ] - # has_min_lines is called again because removing lines can prune too much - trace_clean_split = [trace for trace - in trace_unique_split if - has_min_lines(trace, - stack_min_size) + trace_clean_split = [clean_trace_if_valid(trace, + stack_min_size, + check_exact) + for trace in trace_split ] + trace_clean_split = [trace for trace in trace_clean_split + if trace is not None] - outfile = os.path.join(out_dir_name, trace_file.name + '_filtered') + outfile = os.path.join(out_dir_name, trace_file.name + "_filtered") with open(outfile, "w") as output_file: output_file.write(STACK_DIVIDER.join(trace_clean_split)) diff --git a/tools/runtime_memusage/symbol_trace_info.py b/tools/runtime_memusage/symbol_trace_info.py index 57ed6ce954..e539be2217 100755 --- a/tools/runtime_memusage/symbol_trace_info.py +++ b/tools/runtime_memusage/symbol_trace_info.py @@ -120,23 +120,23 @@ def is_directory(path_name): def parse_args(argv): """Parses arguments passed in.""" parser = argparse.ArgumentParser() - parser.add_argument('-d', action='store', + parser.add_argument("-d", action="store", default="", dest="out_dir_name", type=is_directory, - help='Output Directory') - parser.add_argument('sanitizer_trace', action='store', - type=argparse.FileType('r'), - help='File containing sanitizer traces filtered by ' - 'prune_sanitizer_output.py') - parser.add_argument('symbol_trace', action='store', - type=argparse.FileType('r'), - help='File containing symbolized traces that match ' - 'sanitizer_trace') - parser.add_argument('dex_starts', action='store', - type=argparse.FileType('r'), - help='File containing starting addresses of Dex Files') - parser.add_argument('categories', action='store', nargs='*', - help='Keywords expected to show in large amounts of' - ' symbolized traces') + help="Output Directory") + parser.add_argument("sanitizer_trace", action="store", + type=argparse.FileType("r"), + help="File containing sanitizer traces filtered by " + "prune_sanitizer_output.py") + parser.add_argument("symbol_trace", action="store", + type=argparse.FileType("r"), + help="File containing symbolized traces that match " + "sanitizer_trace") + parser.add_argument("dex_starts", action="store", + type=argparse.FileType("r"), + help="File containing starting addresses of Dex Files") + parser.add_argument("categories", action="store", nargs="*", + help="Keywords expected to show in large amounts of" + " symbolized traces") return parser.parse_args(argv) @@ -177,6 +177,10 @@ def read_data(parsed_argv): for line in dex_start_file_data if "RegisterDexFile" in line ] + # Dex File Starting addresses must be sorted because bisect requires sorted + # lists. + data_lists["dex_start_list"].sort() + return data_lists, categories, symbol_file_split @@ -210,5 +214,5 @@ def main(argv=None): print_categories(categories, symbol_file_split, parsed_argv.out_dir_name) -if __name__ == '__main__': +if __name__ == "__main__": main() |