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