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