blob: f803ea38005ec4bd7258e7c3350b48a1aa88e000 [file] [log] [blame]
Andreas Gampedbfe2542014-11-25 22:21:42 -08001#!/usr/bin/env python
2#
3# Copyright (C) 2014 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Analyzes the dump of initialization failures and creates a Graphviz dot file
18 representing dependencies."""
19
20import codecs
21import os
22import re
23import string
24import sys
25
26
27_CLASS_RE = re.compile(r'^L(.*);$')
28_ERROR_LINE_RE = re.compile(r'^java.lang.InternalError: (.*)')
29_STACK_LINE_RE = re.compile(r'^\s*at\s[^\s]*\s([^\s]*)')
30
31def Confused(filename, line_number, line):
32 sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line))
33 raise Exception("giving up!")
34 sys.exit(1)
35
36
37def ProcessFile(filename):
38 lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
39 it = iter(lines)
40
41 class_fail_class = {}
42 class_fail_method = {}
43 root_failures = set()
44 root_errors = {}
45
46 while True:
47 try:
48 # We start with a class descriptor.
49 raw_line = it.next()
50 m = _CLASS_RE.search(raw_line)
51 # print(raw_line)
52 if m is None:
53 continue
54 # Found a class.
55 failed_clazz = m.group(1).replace('/','.')
56 # print('Is a class %s' % failed_clazz)
57 # The error line should be next.
58 raw_line = it.next()
59 m = _ERROR_LINE_RE.search(raw_line)
60 # print(raw_line)
61 if m is None:
62 Confused(filename, -1, raw_line)
63 continue
64 # Found an error line.
65 error = m.group(1)
66 # print('Is an error %s' % error)
67 # Get the top of the stack
68 raw_line = it.next()
69 m = _STACK_LINE_RE.search(raw_line)
70 if m is None:
71 continue
72 # Found a stack line. Get the method.
73 method = m.group(1)
74 # print('Is a stack element %s' % method)
75 (left_of_paren,paren,right_of_paren) = method.partition('(')
76 (root_err_class,dot,root_method_name) = left_of_paren.rpartition('.')
77 # print('Error class %s' % err_class)
78 # print('Error method %s' % method_name)
79 # Record the root error.
80 root_failures.add(root_err_class)
81 # Parse all the trace elements to find the "immediate" cause.
82 immediate_class = root_err_class
83 immediate_method = root_method_name
84 root_errors[root_err_class] = error
85 # Now go "up" the stack.
86 while True:
87 raw_line = it.next()
88 m = _STACK_LINE_RE.search(raw_line)
89 if m is None:
90 break # Nothing more to see here.
91 method = m.group(1)
92 (left_of_paren,paren,right_of_paren) = method.partition('(')
93 (err_class,dot,err_method_name) = left_of_paren.rpartition('.')
94 if err_method_name == "<clinit>":
95 # A class initializer is on the stack...
96 class_fail_class[err_class] = immediate_class
97 class_fail_method[err_class] = immediate_method
98 immediate_class = err_class
99 immediate_method = err_method_name
100 except StopIteration:
101 # print('Done')
102 break # Done
103
104 # Assign IDs.
105 fail_sources = set(class_fail_class.values());
106 all_classes = fail_sources | set(class_fail_class.keys())
107 i = 0
108 class_index = {}
109 for clazz in all_classes:
110 class_index[clazz] = i
111 i = i + 1
112
113 # Now create the nodes.
114 for (r_class, r_id) in class_index.items():
115 error_string = ''
116 if r_class in root_failures:
117 error_string = ',color=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"'
118 print(' n%d [shape=box,label="%s"%s];' % (r_id, r_class, error_string))
119
120 # Some space.
121 print('')
122
123 # Connections.
124 for (failed_class,error_class) in class_fail_class.items():
125 print(' n%d -> n%d;' % (class_index[failed_class], class_index[error_class]))
126
127
128def main():
129 print('digraph {')
130 print(' overlap=false;')
131 print(' splines=true;')
132 ProcessFile(sys.argv[1])
133 print('}')
134 sys.exit(0)
135
136
137if __name__ == '__main__':
138 main()