diff options
author | Andreas Gampe <agampe@google.com> | 2014-11-25 22:21:42 -0800 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2014-11-26 15:07:36 -0800 |
commit | dbfe254f5ca96f6c5b2284478597d6140c01a394 (patch) | |
tree | deb37dd72a728df8cda0e3879f89f13e6e461a2a /tools/analyze-init-failures.py | |
parent | 220526b05d4365a1820a694c98527eda2d3dc980 (diff) | |
download | art-dbfe254f5ca96f6c5b2284478597d6140c01a394.zip art-dbfe254f5ca96f6c5b2284478597d6140c01a394.tar.gz art-dbfe254f5ca96f6c5b2284478597d6140c01a394.tar.bz2 |
ART: Print initialization failures to file
Add the ability to print boot image initialization failures to a
file.
Add a tool to convert said file into a Graphviz file.
Change-Id: Iedcc337bdf05654c154aa553236f20bdd15572ee
Diffstat (limited to 'tools/analyze-init-failures.py')
-rwxr-xr-x | tools/analyze-init-failures.py | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/tools/analyze-init-failures.py b/tools/analyze-init-failures.py new file mode 100755 index 0000000..f803ea3 --- /dev/null +++ b/tools/analyze-init-failures.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Analyzes the dump of initialization failures and creates a Graphviz dot file + representing dependencies.""" + +import codecs +import os +import re +import string +import sys + + +_CLASS_RE = re.compile(r'^L(.*);$') +_ERROR_LINE_RE = re.compile(r'^java.lang.InternalError: (.*)') +_STACK_LINE_RE = re.compile(r'^\s*at\s[^\s]*\s([^\s]*)') + +def Confused(filename, line_number, line): + sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line)) + raise Exception("giving up!") + sys.exit(1) + + +def ProcessFile(filename): + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + it = iter(lines) + + class_fail_class = {} + class_fail_method = {} + root_failures = set() + root_errors = {} + + while True: + try: + # We start with a class descriptor. + raw_line = it.next() + m = _CLASS_RE.search(raw_line) + # print(raw_line) + if m is None: + continue + # Found a class. + failed_clazz = m.group(1).replace('/','.') + # print('Is a class %s' % failed_clazz) + # The error line should be next. + raw_line = it.next() + m = _ERROR_LINE_RE.search(raw_line) + # print(raw_line) + if m is None: + Confused(filename, -1, raw_line) + continue + # Found an error line. + error = m.group(1) + # print('Is an error %s' % error) + # Get the top of the stack + raw_line = it.next() + m = _STACK_LINE_RE.search(raw_line) + if m is None: + continue + # Found a stack line. Get the method. + method = m.group(1) + # print('Is a stack element %s' % method) + (left_of_paren,paren,right_of_paren) = method.partition('(') + (root_err_class,dot,root_method_name) = left_of_paren.rpartition('.') + # print('Error class %s' % err_class) + # print('Error method %s' % method_name) + # Record the root error. + root_failures.add(root_err_class) + # Parse all the trace elements to find the "immediate" cause. + immediate_class = root_err_class + immediate_method = root_method_name + root_errors[root_err_class] = error + # Now go "up" the stack. + while True: + raw_line = it.next() + m = _STACK_LINE_RE.search(raw_line) + if m is None: + break # Nothing more to see here. + method = m.group(1) + (left_of_paren,paren,right_of_paren) = method.partition('(') + (err_class,dot,err_method_name) = left_of_paren.rpartition('.') + if err_method_name == "<clinit>": + # A class initializer is on the stack... + class_fail_class[err_class] = immediate_class + class_fail_method[err_class] = immediate_method + immediate_class = err_class + immediate_method = err_method_name + except StopIteration: + # print('Done') + break # Done + + # Assign IDs. + fail_sources = set(class_fail_class.values()); + all_classes = fail_sources | set(class_fail_class.keys()) + i = 0 + class_index = {} + for clazz in all_classes: + class_index[clazz] = i + i = i + 1 + + # Now create the nodes. + for (r_class, r_id) in class_index.items(): + error_string = '' + if r_class in root_failures: + error_string = ',color=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"' + print(' n%d [shape=box,label="%s"%s];' % (r_id, r_class, error_string)) + + # Some space. + print('') + + # Connections. + for (failed_class,error_class) in class_fail_class.items(): + print(' n%d -> n%d;' % (class_index[failed_class], class_index[error_class])) + + +def main(): + print('digraph {') + print(' overlap=false;') + print(' splines=true;') + ProcessFile(sys.argv[1]) + print('}') + sys.exit(0) + + +if __name__ == '__main__': + main() |