1dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe#!/usr/bin/env python 2dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# 3dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# Copyright (C) 2014 The Android Open Source Project 4dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# 5dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# Licensed under the Apache License, Version 2.0 (the "License"); 6dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# you may not use this file except in compliance with the License. 7dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# You may obtain a copy of the License at 8dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# 9dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# http://www.apache.org/licenses/LICENSE-2.0 10dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# 11dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# Unless required by applicable law or agreed to in writing, software 12dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# distributed under the License is distributed on an "AS IS" BASIS, 13dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# See the License for the specific language governing permissions and 15dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe# limitations under the License. 16dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 17dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe"""Analyzes the dump of initialization failures and creates a Graphviz dot file 18dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe representing dependencies.""" 19dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 20dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampeimport codecs 21dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampeimport os 22dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampeimport re 23dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampeimport string 24dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampeimport sys 25dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 26dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 27dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe_CLASS_RE = re.compile(r'^L(.*);$') 28e9b160e60da52264c496d6028bbadf48f02c15dbAndreas Gampe_ERROR_LINE_RE = re.compile(r'^dalvik.system.TransactionAbortError: (.*)') 29dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe_STACK_LINE_RE = re.compile(r'^\s*at\s[^\s]*\s([^\s]*)') 30dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 31dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampedef Confused(filename, line_number, line): 32dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line)) 33dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe raise Exception("giving up!") 34dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe sys.exit(1) 35dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 36dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 37dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampedef ProcessFile(filename): 38dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') 39dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe it = iter(lines) 40dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 41dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe class_fail_class = {} 42dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe class_fail_method = {} 43869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe class_fail_load_library = {} 44869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe class_fail_get_property = {} 45dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe root_failures = set() 46dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe root_errors = {} 47dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 48dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe while True: 49dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe try: 50dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # We start with a class descriptor. 51dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe raw_line = it.next() 52dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe m = _CLASS_RE.search(raw_line) 53dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print(raw_line) 54dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe if m is None: 55dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe continue 56dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Found a class. 57dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe failed_clazz = m.group(1).replace('/','.') 58dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print('Is a class %s' % failed_clazz) 59dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # The error line should be next. 60dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe raw_line = it.next() 61dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe m = _ERROR_LINE_RE.search(raw_line) 62dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print(raw_line) 63dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe if m is None: 64dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe Confused(filename, -1, raw_line) 65dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe continue 66dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Found an error line. 67dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe error = m.group(1) 68dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print('Is an error %s' % error) 69dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Get the top of the stack 70dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe raw_line = it.next() 71dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe m = _STACK_LINE_RE.search(raw_line) 72dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe if m is None: 73dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe continue 74dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Found a stack line. Get the method. 75dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe method = m.group(1) 76dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print('Is a stack element %s' % method) 77dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe (left_of_paren,paren,right_of_paren) = method.partition('(') 78dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe (root_err_class,dot,root_method_name) = left_of_paren.rpartition('.') 79dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print('Error class %s' % err_class) 80dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print('Error method %s' % method_name) 81dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Record the root error. 82dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe root_failures.add(root_err_class) 83dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Parse all the trace elements to find the "immediate" cause. 84dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe immediate_class = root_err_class 85dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe immediate_method = root_method_name 86dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe root_errors[root_err_class] = error 87869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe was_load_library = False 88869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe was_get_property = False 89dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Now go "up" the stack. 90dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe while True: 91dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe raw_line = it.next() 92dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe m = _STACK_LINE_RE.search(raw_line) 93dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe if m is None: 94dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe break # Nothing more to see here. 95dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe method = m.group(1) 96dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe (left_of_paren,paren,right_of_paren) = method.partition('(') 97dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe (err_class,dot,err_method_name) = left_of_paren.rpartition('.') 98dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe if err_method_name == "<clinit>": 99dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # A class initializer is on the stack... 100dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe class_fail_class[err_class] = immediate_class 101dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe class_fail_method[err_class] = immediate_method 102869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe class_fail_load_library[err_class] = was_load_library 103dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe immediate_class = err_class 104dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe immediate_method = err_method_name 105869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe class_fail_get_property[err_class] = was_get_property 106869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe was_get_property = False 107869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe was_load_library = err_method_name == "loadLibrary" 108869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe was_get_property = was_get_property or err_method_name == "getProperty" 109869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe failed_clazz_norm = re.sub(r"^L", "", failed_clazz) 110869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe failed_clazz_norm = re.sub(r";$", "", failed_clazz_norm) 111869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe failed_clazz_norm = re.sub(r"/", "", failed_clazz_norm) 112869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe if immediate_class != failed_clazz_norm: 113869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe class_fail_class[failed_clazz_norm] = immediate_class 114869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe class_fail_method[failed_clazz_norm] = immediate_method 115dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe except StopIteration: 116dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # print('Done') 117dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe break # Done 118dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 119dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Assign IDs. 120dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe fail_sources = set(class_fail_class.values()); 121dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe all_classes = fail_sources | set(class_fail_class.keys()) 122dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe i = 0 123dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe class_index = {} 124dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe for clazz in all_classes: 125dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe class_index[clazz] = i 126dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe i = i + 1 127dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 128dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Now create the nodes. 129dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe for (r_class, r_id) in class_index.items(): 130dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe error_string = '' 131dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe if r_class in root_failures: 132869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe error_string = ',style=filled,fillcolor=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"' 133869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe elif r_class in class_fail_load_library and class_fail_load_library[r_class] == True: 134869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe error_string = error_string + ',style=filled,fillcolor=Bisque' 135869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe elif r_class in class_fail_get_property and class_fail_get_property[r_class] == True: 136869c2dfad606b19cd4089f14c4310e66c079dcbcAndreas Gampe error_string = error_string + ',style=filled,fillcolor=Darkseagreen' 137dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe print(' n%d [shape=box,label="%s"%s];' % (r_id, r_class, error_string)) 138dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 139dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Some space. 140dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe print('') 141dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 142dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe # Connections. 143dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe for (failed_class,error_class) in class_fail_class.items(): 144dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe print(' n%d -> n%d;' % (class_index[failed_class], class_index[error_class])) 145dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 146dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 147dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampedef main(): 148dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe print('digraph {') 149dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe print(' overlap=false;') 150dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe print(' splines=true;') 151dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe ProcessFile(sys.argv[1]) 152dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe print('}') 153dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe sys.exit(0) 154dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 155dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe 156dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampeif __name__ == '__main__': 157dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe main() 158