OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import logging | |
6 | |
7 import chromium_deps | 5 import chromium_deps |
| 6 from common import utils |
8 import crash_utils | 7 import crash_utils |
9 import findit_for_crash as findit | 8 import findit_for_crash as findit |
10 import stacktrace | 9 import stacktrace |
11 | 10 |
12 | 11 |
13 def SplitStacktrace(stacktrace_string): | 12 def SplitStacktrace(stacktrace_string): |
14 """Preprocesses stacktrace string into two parts, release and debug. | 13 """Preprocesses stacktrace string into two parts, release and debug. |
15 | 14 |
16 Args: | 15 Args: |
17 stacktrace_string: A string representation of stacktrace, | 16 stacktrace_string: A string representation of stacktrace, |
18 in clusterfuzz format. | 17 in clusterfuzz format. |
19 | 18 |
20 Returns: | 19 Returns: |
21 A tuple of list of strings, release build stacktrace and | 20 A tuple of list of strings, release build stacktrace and |
22 debug build stacktrace. | 21 debug build stacktrace. |
23 """ | 22 """ |
24 # Make sure we only parse release/debug build stacktrace, and ignore | 23 # Make sure we only parse release/debug build stacktrace, and ignore |
25 # unsymbolised stacktrace. | 24 # unsymbolised stacktrace. |
26 in_release_or_debug_stacktrace = False | 25 in_release_or_debug_stacktrace = False |
27 release_build_stacktrace_lines = None | 26 release_build_stacktrace_lines = None |
28 debug_build_stacktrace_lines = None | 27 debug_build_stacktrace_lines = None |
29 current_stacktrace_lines = [] | 28 current_stacktrace_lines = [] |
30 | 29 |
31 # Iterate through all lines in stacktrace. | 30 # Iterate through all lines in stacktrace. |
32 for line in stacktrace_string.splitlines(): | 31 for line in stacktrace_string.splitlines(): |
33 line = line.strip() | 32 line = line.strip() |
34 | 33 |
35 # If the line starts with +, it signifies the start of new stacktrace. | 34 # If the line starts with +, it signifies the start of new stacktrace. |
36 if line.startswith('+'): | 35 if line.startswith('+-') and line.endswith('-+'): |
37 if 'Release Build Stacktrace' in line: | 36 if 'Release Build Stacktrace' in line: |
38 in_release_or_debug_stacktrace = True | 37 in_release_or_debug_stacktrace = True |
39 current_stacktrace_lines = [] | 38 current_stacktrace_lines = [] |
40 release_build_stacktrace_lines = current_stacktrace_lines | 39 release_build_stacktrace_lines = current_stacktrace_lines |
41 | 40 |
42 elif 'Debug Build Stacktrace' in line: | 41 elif 'Debug Build Stacktrace' in line: |
43 in_release_or_debug_stacktrace = True | 42 in_release_or_debug_stacktrace = True |
44 current_stacktrace_lines = [] | 43 current_stacktrace_lines = [] |
45 debug_build_stacktrace_lines = current_stacktrace_lines | 44 debug_build_stacktrace_lines = current_stacktrace_lines |
46 | 45 |
47 # If the stacktrace is neither release/debug build stacktrace, ignore | 46 # If the stacktrace is neither release/debug build stacktrace, ignore |
48 # all lines after it until we encounter release/debug build stacktrace. | 47 # all lines after it until we encounter release/debug build stacktrace. |
49 else: | 48 else: |
50 in_release_or_debug_stacktrace = False | 49 in_release_or_debug_stacktrace = False |
51 | 50 |
52 # This case, it must be that the line is an actual stack frame, so add to | 51 # This case, it must be that the line is an actual stack frame, so add to |
53 # the current stacktrace. | 52 # the current stacktrace. |
54 elif in_release_or_debug_stacktrace: | 53 elif in_release_or_debug_stacktrace: |
55 current_stacktrace_lines.append(line) | 54 current_stacktrace_lines.append(line) |
56 | 55 |
57 return (release_build_stacktrace_lines, debug_build_stacktrace_lines) | 56 return (release_build_stacktrace_lines, debug_build_stacktrace_lines) |
58 | 57 |
59 | 58 |
60 def FindCulpritCLs(stacktrace_string, | 59 def FindCulpritCLs(stacktrace_string, |
61 build_type, | 60 build_type, |
62 chrome_regression=None, | 61 chrome_regression=None, |
63 component_regression=None, | 62 component_regression=None, |
64 chrome_crash_revision=None, | 63 chrome_crash_revision=None, |
65 component_crash_revision=None, | 64 component_crash_revision=None, |
66 crashing_component=None): | 65 crashing_component_path=None, |
| 66 crashing_component_name=None, |
| 67 crashing_component_repo_url=None): |
67 """Returns the result, a list of result.Result objects and message. | 68 """Returns the result, a list of result.Result objects and message. |
68 | 69 |
| 70 If either or both of component_regression and component_crash_revision is not |
| 71 None, is is assumed that crashing_component_path and |
| 72 crashing_component_repo_url are not None. |
| 73 |
69 Args: | 74 Args: |
70 stacktrace_string: A string representing stacktrace. | 75 stacktrace_string: A string representing stacktrace. |
71 build_type: The type of the job. | 76 build_type: The type of the job. |
72 chrome_regression: A string, chrome regression from clusterfuzz, in format | 77 chrome_regression: A string, chrome regression from clusterfuzz, in format |
73 '123456:123457' | 78 '123456:123457' |
74 component_regression: A string, component regression in the same format. | 79 component_regression: A string, component regression in the same format. |
75 chrome_crash_revision: A crash revision of chrome, in string. | 80 chrome_crash_revision: A crash revision of chrome, in string. |
76 component_crash_revision: A crash revision of the component, | 81 component_crash_revision: A crash revision of the component, |
77 if component build. | 82 if component build. |
78 crashing_component: Yet to be decided. | 83 crashing_component_path: A relative path of the crashing component, as in |
| 84 DEPS file. For example, it would be 'src/v8' for |
| 85 v8 and 'src/third_party/WebKit' for blink. |
| 86 crashing_component_name: A name of the crashing component, such as v8. |
| 87 crashing_component_repo_url: The URL of the crashing component's repo, as |
| 88 shown in DEPS file. For example, |
| 89 'https://chromium.googlesource.com/skia.git' |
| 90 for skia. |
79 | 91 |
80 Returns: | 92 Returns: |
81 A list of result objects, along with the short description on where the | 93 A list of result objects, along with the short description on where the |
82 result is from. | 94 result is from. |
83 """ | 95 """ |
84 build_type = build_type.lower() | 96 build_type = build_type.lower() |
85 if 'syzyasan' in build_type: | |
86 return ('This build type is currently not supported.', []) | |
87 | |
88 logging.basicConfig(filename='errors.log', level=logging.WARNING, | |
89 filemode='w') | |
90 | |
91 component_to_crash_revision_dict = {} | 97 component_to_crash_revision_dict = {} |
92 component_to_regression_dict = {} | 98 component_to_regression_dict = {} |
93 | 99 |
94 # TODO(jeun): Come up with a good way to connect crashing component name to | |
95 # its path. | |
96 if component_regression or component_crash_revision: | |
97 return ('Component builds are not supported yet.', []) | |
98 | |
99 # If chrome regression is available, parse DEPS file. | 100 # If chrome regression is available, parse DEPS file. |
100 chrome_regression = crash_utils.SplitRange(chrome_regression) | 101 chrome_regression = crash_utils.SplitRange(chrome_regression) |
101 if chrome_regression: | 102 if chrome_regression: |
102 chrome_regression_start = chrome_regression[0] | 103 chrome_regression_start = chrome_regression[0] |
103 chrome_regression_end = chrome_regression[1] | 104 chrome_regression_end = chrome_regression[1] |
104 | 105 |
105 # Do not parse regression information for crashes introduced before the | 106 # Do not parse regression information for crashes introduced before the |
106 # first archived build. | 107 # first archived build. |
107 if chrome_regression_start != '0': | 108 if chrome_regression_start != '0': |
108 component_to_regression_dict = chromium_deps.GetChromiumComponentRange( | 109 component_to_regression_dict = chromium_deps.GetChromiumComponentRange( |
109 chrome_regression_start, chrome_regression_end) | 110 chrome_regression_start, chrome_regression_end) |
110 | 111 |
111 # Parse crash revision. | 112 # Parse crash revision. |
112 if chrome_crash_revision: | 113 if chrome_crash_revision: |
113 component_to_crash_revision_dict = chromium_deps.GetChromiumComponents( | 114 component_to_crash_revision_dict = chromium_deps.GetChromiumComponents( |
114 chrome_crash_revision) | 115 chrome_crash_revision) |
115 | 116 |
| 117 # Check if component regression information is available. |
| 118 component_regression = crash_utils.SplitRange(component_regression) |
| 119 if component_regression: |
| 120 component_regression_start = component_regression[0] |
| 121 component_regression_end = component_regression[1] |
| 122 |
| 123 # If this component already has an entry in parsed DEPS file, overwrite |
| 124 # regression range and url. |
| 125 if crashing_component_path in component_to_regression_dict: |
| 126 component_regression_info = \ |
| 127 component_to_regression_dict[crashing_component_path] |
| 128 component_regression_info['old_revision'] = component_regression_start |
| 129 component_regression_info['new_revision'] = component_regression_end |
| 130 component_regression_info['repository'] = crashing_component_repo_url |
| 131 |
| 132 # if this component does not have an entry, add the entry to the parsed |
| 133 # DEPS file. |
| 134 else: |
| 135 repository_type = crash_utils.GetRepositoryType( |
| 136 component_regression_start) |
| 137 component_regression_info = { |
| 138 'path': crashing_component_path, |
| 139 'rolled': True, |
| 140 'name': crashing_component_name, |
| 141 'old_revision': component_regression_start, |
| 142 'new_revision': component_regression_end, |
| 143 'repository': crashing_component_repo_url, |
| 144 'repository_type': repository_type |
| 145 } |
| 146 component_to_regression_dict[crashing_component_path] = \ |
| 147 component_regression_info |
| 148 |
| 149 # If component crash revision is available, add it to the parsed crash |
| 150 # revisions. |
| 151 if component_crash_revision: |
| 152 |
| 153 # If this component has already a crash revision info, overwrite it. |
| 154 if crashing_component_path in component_to_crash_revision_dict: |
| 155 component_crash_revision_info = \ |
| 156 component_to_crash_revision_dict[crashing_component_path] |
| 157 component_crash_revision_info['revision'] = component_crash_revision |
| 158 component_crash_revision_info['repository'] = crashing_component_repo_url |
| 159 |
| 160 # If not, add it to the parsed DEPS. |
| 161 else: |
| 162 if utils.IsGitHash(component_crash_revision): |
| 163 repository_type = 'git' |
| 164 else: |
| 165 repository_type = 'svn' |
| 166 component_crash_revision_info = { |
| 167 'path': crashing_component_path, |
| 168 'name': crashing_component_name, |
| 169 'repository': crashing_component_repo_url, |
| 170 'repository_type': repository_type, |
| 171 'revision': component_crash_revision |
| 172 } |
| 173 component_to_crash_revision_dict[crashing_component_path] = \ |
| 174 component_crash_revision_info |
| 175 |
116 # Parsed DEPS is used to normalize the stacktrace. Since parsed regression | 176 # Parsed DEPS is used to normalize the stacktrace. Since parsed regression |
117 # and parsed crash state essentially contain same information, use either. | 177 # and parsed crash state essentially contain same information, use either. |
118 if component_to_regression_dict: | 178 if component_to_regression_dict: |
119 parsed_deps = component_to_regression_dict | 179 parsed_deps = component_to_regression_dict |
120 elif component_to_crash_revision_dict: | 180 elif component_to_crash_revision_dict: |
121 parsed_deps = component_to_crash_revision_dict | 181 parsed_deps = component_to_crash_revision_dict |
122 else: | 182 else: |
123 return (('Identifying culprit CL requires at lease one of regression ' | 183 return (('Identifying culprit CL requires at lease one of regression ' |
124 'information or crash revision'), []) | 184 'information or crash revision'), []) |
125 | 185 |
126 # Split stacktrace into release build/debug build and parse them. | 186 # Split stacktrace into release build/debug build and parse them. |
127 (release_build_stacktrace, debug_build_stacktrace) = SplitStacktrace( | 187 (release_build_stacktrace, debug_build_stacktrace) = SplitStacktrace( |
128 stacktrace_string) | 188 stacktrace_string) |
129 parsed_release_build_stacktrace = stacktrace.Stacktrace( | 189 if not (release_build_stacktrace or debug_build_stacktrace): |
130 release_build_stacktrace, build_type, parsed_deps) | 190 parsed_release_build_stacktrace = stacktrace.Stacktrace( |
| 191 stacktrace_string.splitlines(), build_type, parsed_deps) |
| 192 else: |
| 193 parsed_release_build_stacktrace = stacktrace.Stacktrace( |
| 194 release_build_stacktrace, build_type, parsed_deps) |
| 195 |
131 parsed_debug_build_stacktrace = stacktrace.Stacktrace( | 196 parsed_debug_build_stacktrace = stacktrace.Stacktrace( |
132 debug_build_stacktrace, build_type, parsed_deps) | 197 debug_build_stacktrace, build_type, parsed_deps) |
133 | 198 |
134 # Get a highest priority callstack (main_stack) from stacktrace, with release | 199 # Get a highest priority callstack (main_stack) from stacktrace, with release |
135 # build stacktrace in higher priority than debug build stacktace. This stack | 200 # build stacktrace in higher priority than debug build stacktace. This stack |
136 # is the callstack to find blame information for. | 201 # is the callstack to find blame information for. |
137 if parsed_release_build_stacktrace.stack_list: | 202 if parsed_release_build_stacktrace.stack_list: |
138 main_stack = parsed_release_build_stacktrace.GetCrashStack() | 203 main_stack = parsed_release_build_stacktrace.GetCrashStack() |
139 elif parsed_debug_build_stacktrace.stack_list: | 204 elif parsed_debug_build_stacktrace.stack_list: |
140 main_stack = parsed_debug_build_stacktrace.GetCrashStack() | 205 main_stack = parsed_debug_build_stacktrace.GetCrashStack() |
141 else: | 206 else: |
| 207 if 'mac_' in build_type: |
| 208 return ('No line information available in stacktrace.', []) |
| 209 |
142 return ('Stacktrace is malformed.', []) | 210 return ('Stacktrace is malformed.', []) |
143 | 211 |
144 # Run the algorithm on the parsed stacktrace, and return the result. | 212 # Run the algorithm on the parsed stacktrace, and return the result. |
145 stacktrace_list = [parsed_release_build_stacktrace, | 213 stacktrace_list = [parsed_release_build_stacktrace, |
146 parsed_debug_build_stacktrace] | 214 parsed_debug_build_stacktrace] |
147 return findit.FindItForCrash( | 215 return findit.FindItForCrash( |
148 stacktrace_list, main_stack, component_to_regression_dict, | 216 stacktrace_list, main_stack, component_to_regression_dict, |
149 component_to_crash_revision_dict) | 217 component_to_crash_revision_dict) |
OLD | NEW |