analyze-init-failures.py revision dbfe254f5ca96f6c5b2284478597d6140c01a394
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(.*);$')
28dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe_ERROR_LINE_RE = re.compile(r'^java.lang.InternalError: (.*)')
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 = {}
43dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  root_failures = set()
44dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  root_errors = {}
45dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
46dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  while True:
47dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    try:
48dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # We start with a class descriptor.
49dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      raw_line = it.next()
50dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      m = _CLASS_RE.search(raw_line)
51dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print(raw_line)
52dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      if m is None:
53dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        continue
54dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # Found a class.
55dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      failed_clazz = m.group(1).replace('/','.')
56dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print('Is a class %s' % failed_clazz)
57dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # The error line should be next.
58dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      raw_line = it.next()
59dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      m = _ERROR_LINE_RE.search(raw_line)
60dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print(raw_line)
61dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      if m is None:
62dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        Confused(filename, -1, raw_line)
63dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        continue
64dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # Found an error line.
65dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      error = m.group(1)
66dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print('Is an error %s' % error)
67dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # Get the top of the stack
68dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      raw_line = it.next()
69dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      m = _STACK_LINE_RE.search(raw_line)
70dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      if m is None:
71dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        continue
72dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # Found a stack line. Get the method.
73dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      method = m.group(1)
74dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print('Is a stack element %s' % method)
75dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      (left_of_paren,paren,right_of_paren) = method.partition('(')
76dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      (root_err_class,dot,root_method_name) = left_of_paren.rpartition('.')
77dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print('Error class %s' % err_class)
78dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print('Error method %s' % method_name)
79dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # Record the root error.
80dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      root_failures.add(root_err_class)
81dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # Parse all the trace elements to find the "immediate" cause.
82dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      immediate_class = root_err_class
83dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      immediate_method = root_method_name
84dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      root_errors[root_err_class] = error
85dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # Now go "up" the stack.
86dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      while True:
87dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        raw_line = it.next()
88dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        m = _STACK_LINE_RE.search(raw_line)
89dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        if m is None:
90dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe          break  # Nothing more to see here.
91dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        method = m.group(1)
92dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        (left_of_paren,paren,right_of_paren) = method.partition('(')
93dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        (err_class,dot,err_method_name) = left_of_paren.rpartition('.')
94dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe        if err_method_name == "<clinit>":
95dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe          # A class initializer is on the stack...
96dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe          class_fail_class[err_class] = immediate_class
97dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe          class_fail_method[err_class] = immediate_method
98dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe          immediate_class = err_class
99dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe          immediate_method = err_method_name
100dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    except StopIteration:
101dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      # print('Done')
102dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      break  # Done
103dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
104dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  # Assign IDs.
105dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  fail_sources = set(class_fail_class.values());
106dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  all_classes = fail_sources | set(class_fail_class.keys())
107dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  i = 0
108dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  class_index = {}
109dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  for clazz in all_classes:
110dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    class_index[clazz] = i
111dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    i = i + 1
112dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
113dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  # Now create the nodes.
114dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  for (r_class, r_id) in class_index.items():
115dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    error_string = ''
116dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    if r_class in root_failures:
117dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe      error_string = ',color=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"'
118dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    print('  n%d [shape=box,label="%s"%s];' % (r_id, r_class, error_string))
119dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
120dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  # Some space.
121dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  print('')
122dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
123dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  # Connections.
124dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  for (failed_class,error_class) in class_fail_class.items():
125dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe    print('  n%d -> n%d;' % (class_index[failed_class], class_index[error_class]))
126dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
127dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
128dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampedef main():
129dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  print('digraph {')
130dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  print('  overlap=false;')
131dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  print('  splines=true;')
132dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  ProcessFile(sys.argv[1])
133dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  print('}')
134dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  sys.exit(0)
135dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
136dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe
137dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampeif __name__ == '__main__':
138dbfe254f5ca96f6c5b2284478597d6140c01a394Andreas Gampe  main()
139