[Session restore] Rename group name Enabled to Restore.
[chromium-blink-merge.git] / tools / findit / findit_for_clusterfuzz.py
blobc101fbcae3610a42ab7cb3a4fe3a38c5f8d8bc9a
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
3 # found in the LICENSE file.
5 import chromium_deps
6 from common import utils
7 import crash_utils
8 import findit_for_crash as findit
9 import stacktrace
12 def SplitStacktrace(stacktrace_string):
13 """Preprocesses stacktrace string into two parts, release and debug.
15 Args:
16 stacktrace_string: A string representation of stacktrace,
17 in clusterfuzz format.
19 Returns:
20 A tuple of list of strings, release build stacktrace and
21 debug build stacktrace.
22 """
23 # Make sure we only parse release/debug build stacktrace, and ignore
24 # unsymbolised stacktrace.
25 in_release_or_debug_stacktrace = False
26 release_build_stacktrace_lines = None
27 debug_build_stacktrace_lines = None
28 current_stacktrace_lines = []
30 # Iterate through all lines in stacktrace.
31 for line in stacktrace_string.splitlines():
32 line = line.strip()
34 # If the line starts with +, it signifies the start of new stacktrace.
35 if line.startswith('+-') and line.endswith('-+'):
36 if 'Release Build Stacktrace' in line:
37 in_release_or_debug_stacktrace = True
38 current_stacktrace_lines = []
39 release_build_stacktrace_lines = current_stacktrace_lines
41 elif 'Debug Build Stacktrace' in line:
42 in_release_or_debug_stacktrace = True
43 current_stacktrace_lines = []
44 debug_build_stacktrace_lines = current_stacktrace_lines
46 # If the stacktrace is neither release/debug build stacktrace, ignore
47 # all lines after it until we encounter release/debug build stacktrace.
48 else:
49 in_release_or_debug_stacktrace = False
51 # This case, it must be that the line is an actual stack frame, so add to
52 # the current stacktrace.
53 elif in_release_or_debug_stacktrace:
54 current_stacktrace_lines.append(line)
56 return (release_build_stacktrace_lines, debug_build_stacktrace_lines)
59 def FindCulpritCLs(stacktrace_string,
60 build_type,
61 chrome_regression=None,
62 component_regression=None,
63 chrome_crash_revision=None,
64 component_crash_revision=None,
65 crashing_component_path=None,
66 crashing_component_name=None,
67 crashing_component_repo_url=None):
68 """Returns the result, a list of result.Result objects and message.
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.
74 Args:
75 stacktrace_string: A string representing stacktrace.
76 build_type: The type of the job.
77 chrome_regression: A string, chrome regression from clusterfuzz, in format
78 '123456:123457'
79 component_regression: A string, component regression in the same format.
80 chrome_crash_revision: A crash revision of chrome, in string.
81 component_crash_revision: A crash revision of the component,
82 if component build.
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.
92 Returns:
93 A list of result objects, along with the short description on where the
94 result is from.
95 """
96 build_type = build_type.lower()
97 component_to_crash_revision_dict = {}
98 component_to_regression_dict = {}
100 # If chrome regression is available, parse DEPS file.
101 chrome_regression = crash_utils.SplitRange(chrome_regression)
102 if chrome_regression:
103 chrome_regression_start = chrome_regression[0]
104 chrome_regression_end = chrome_regression[1]
106 # Do not parse regression information for crashes introduced before the
107 # first archived build.
108 if chrome_regression_start != '0':
109 component_to_regression_dict = chromium_deps.GetChromiumComponentRange(
110 chrome_regression_start, chrome_regression_end)
111 if not component_to_regression_dict:
112 return (('Failed to get component regression ranges for chromium '
113 'regression range %s:%s'
114 % (chrome_regression_start, chrome_regression_end)), [])
116 # Parse crash revision.
117 if chrome_crash_revision:
118 component_to_crash_revision_dict = chromium_deps.GetChromiumComponents(
119 chrome_crash_revision)
120 if not component_to_crash_revision_dict:
121 return (('Failed to get component dependencies for chromium revision "%s"'
122 % chrome_crash_revision), [])
124 # Check if component regression information is available.
125 component_regression = crash_utils.SplitRange(component_regression)
126 if component_regression:
127 component_regression_start = component_regression[0]
128 component_regression_end = component_regression[1]
130 # If this component already has an entry in parsed DEPS file, overwrite
131 # regression range and url.
132 if crashing_component_path in component_to_regression_dict:
133 component_regression_info = \
134 component_to_regression_dict[crashing_component_path]
135 component_regression_info['old_revision'] = component_regression_start
136 component_regression_info['new_revision'] = component_regression_end
137 component_regression_info['repository'] = crashing_component_repo_url
139 # if this component does not have an entry, add the entry to the parsed
140 # DEPS file.
141 else:
142 repository_type = crash_utils.GetRepositoryType(
143 component_regression_start)
144 component_regression_info = {
145 'path': crashing_component_path,
146 'rolled': True,
147 'name': crashing_component_name,
148 'old_revision': component_regression_start,
149 'new_revision': component_regression_end,
150 'repository': crashing_component_repo_url,
151 'repository_type': repository_type
153 component_to_regression_dict[crashing_component_path] = \
154 component_regression_info
156 # If component crash revision is available, add it to the parsed crash
157 # revisions.
158 if component_crash_revision:
160 # If this component has already a crash revision info, overwrite it.
161 if crashing_component_path in component_to_crash_revision_dict:
162 component_crash_revision_info = \
163 component_to_crash_revision_dict[crashing_component_path]
164 component_crash_revision_info['revision'] = component_crash_revision
165 component_crash_revision_info['repository'] = crashing_component_repo_url
167 # If not, add it to the parsed DEPS.
168 else:
169 if utils.IsGitHash(component_crash_revision):
170 repository_type = 'git'
171 else:
172 repository_type = 'svn'
173 component_crash_revision_info = {
174 'path': crashing_component_path,
175 'name': crashing_component_name,
176 'repository': crashing_component_repo_url,
177 'repository_type': repository_type,
178 'revision': component_crash_revision
180 component_to_crash_revision_dict[crashing_component_path] = \
181 component_crash_revision_info
183 # Parsed DEPS is used to normalize the stacktrace. Since parsed regression
184 # and parsed crash state essentially contain same information, use either.
185 if component_to_regression_dict:
186 parsed_deps = component_to_regression_dict
187 elif component_to_crash_revision_dict:
188 parsed_deps = component_to_crash_revision_dict
189 else:
190 return (('Identifying culprit CL requires at lease one of regression '
191 'information or crash revision'), [])
193 # Split stacktrace into release build/debug build and parse them.
194 (release_build_stacktrace, debug_build_stacktrace) = SplitStacktrace(
195 stacktrace_string)
196 if not (release_build_stacktrace or debug_build_stacktrace):
197 parsed_release_build_stacktrace = stacktrace.Stacktrace(
198 stacktrace_string.splitlines(), build_type, parsed_deps)
199 else:
200 parsed_release_build_stacktrace = stacktrace.Stacktrace(
201 release_build_stacktrace, build_type, parsed_deps)
203 parsed_debug_build_stacktrace = stacktrace.Stacktrace(
204 debug_build_stacktrace, build_type, parsed_deps)
206 # Get a highest priority callstack (main_stack) from stacktrace, with release
207 # build stacktrace in higher priority than debug build stacktace. This stack
208 # is the callstack to find blame information for.
209 if parsed_release_build_stacktrace.stack_list:
210 main_stack = parsed_release_build_stacktrace.GetCrashStack()
211 elif parsed_debug_build_stacktrace.stack_list:
212 main_stack = parsed_debug_build_stacktrace.GetCrashStack()
213 else:
214 if 'mac_' in build_type:
215 return ('No line information available in stacktrace.', [])
217 return ('Findit failed to find any stack trace. Is it in a new format?', [])
219 # Run the algorithm on the parsed stacktrace, and return the result.
220 stacktrace_list = [parsed_release_build_stacktrace,
221 parsed_debug_build_stacktrace]
222 return findit.FindItForCrash(
223 stacktrace_list, main_stack, component_to_regression_dict,
224 component_to_crash_revision_dict)