xref: /aosp_15_r20/art/tools/runtime_memusage/prune_sanitizer_output.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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