blob: 0fdffd93ac2756cb5640dd6fe00304b422ab8dd9 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001#!/usr/bin/env python
2# Copyright 2014 the V8 project authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""
7Script to check for new clusterfuzz issues since the last rolled v8 revision.
8
9Returns a json list with test case IDs if any.
10
11Security considerations: The security key and request data must never be
12written to public logs. Public automated callers of this script should
13suppress stdout and stderr and only process contents of the results_file.
14"""
15
16
17import argparse
18import httplib
19import json
20import os
21import re
22import sys
23import urllib
24import urllib2
25
26
27# Constants to git repos.
28BASE_URL = "https://chromium.googlesource.com"
29DEPS_LOG = BASE_URL + "/chromium/src/+log/master/DEPS?format=JSON"
30
31# Constants for retrieving v8 rolls.
32CRREV = "https://cr-rev.appspot.com/_ah/api/crrev/v1/commit/%s"
33V8_COMMIT_RE = re.compile(
34 r"^Update V8 to version \d+\.\d+\.\d+ \(based on ([a-fA-F0-9]+)\)\..*")
35
36# Constants for the clusterfuzz backend.
37HOSTNAME = "backend-dot-cluster-fuzz.appspot.com"
38
39# Crash patterns.
40V8_INTERNAL_RE = re.compile(r"^v8::internal.*")
41ANY_RE = re.compile(r".*")
42
43# List of all api requests.
44BUG_SPECS = [
45 {
46 "args": {
47 "job_type": "linux_asan_chrome_v8",
48 "reproducible": "True",
49 "open": "True",
50 "bug_information": "",
51 },
52 "crash_state": V8_INTERNAL_RE,
53 },
54 {
55 "args": {
56 "job_type": "linux_asan_d8",
57 "reproducible": "True",
58 "open": "True",
59 "bug_information": "",
60 },
61 "crash_state": ANY_RE,
62 },
63 {
64 "args": {
65 "job_type": "linux_asan_d8_dbg",
66 "reproducible": "True",
67 "open": "True",
68 "bug_information": "",
69 },
70 "crash_state": ANY_RE,
71 },
72 {
73 "args": {
Ben Murdochda12d292016-06-02 14:46:10 +010074 "job_type": "linux_asan_d8_ignition_dbg",
75 "reproducible": "True",
76 "open": "True",
77 "bug_information": "",
78 },
79 "crash_state": ANY_RE,
80 },
81 {
82 "args": {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000083 "job_type": "linux_asan_d8_v8_arm_dbg",
84 "reproducible": "True",
85 "open": "True",
86 "bug_information": "",
87 },
88 "crash_state": ANY_RE,
89 },
90 {
91 "args": {
Ben Murdoch61f157c2016-09-16 13:49:30 +010092 "job_type": "linux_asan_d8_ignition_v8_arm_dbg",
93 "reproducible": "True",
94 "open": "True",
95 "bug_information": "",
96 },
97 "crash_state": ANY_RE,
98 },
99 {
100 "args": {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000101 "job_type": "linux_asan_d8_v8_arm64_dbg",
102 "reproducible": "True",
103 "open": "True",
104 "bug_information": "",
105 },
106 "crash_state": ANY_RE,
107 },
108 {
109 "args": {
110 "job_type": "linux_asan_d8_v8_mipsel_dbg",
111 "reproducible": "True",
112 "open": "True",
113 "bug_information": "",
114 },
115 "crash_state": ANY_RE,
116 },
117]
118
119
120def GetRequest(url):
121 url_fh = urllib2.urlopen(url, None, 60)
122 try:
123 return url_fh.read()
124 finally:
125 url_fh.close()
126
127
128def GetLatestV8InChromium():
129 """Returns the commit position number of the latest v8 roll in chromium."""
130
131 # Check currently rolled v8 revision.
132 result = GetRequest(DEPS_LOG)
133 if not result:
134 return None
135
136 # Strip security header and load json.
137 commits = json.loads(result[5:])
138
139 git_revision = None
140 for commit in commits["log"]:
141 # Get latest commit that matches the v8 roll pattern. Ignore cherry-picks.
142 match = re.match(V8_COMMIT_RE, commit["message"])
143 if match:
144 git_revision = match.group(1)
145 break
146 else:
147 return None
148
149 # Get commit position number for v8 revision.
150 result = GetRequest(CRREV % git_revision)
151 if not result:
152 return None
153
154 commit = json.loads(result)
155 assert commit["repo"] == "v8/v8"
156 return commit["number"]
157
158
159def APIRequest(key, **params):
160 """Send a request to the clusterfuzz api.
161
162 Returns a json dict of the response.
163 """
164
165 params["api_key"] = key
166 params = urllib.urlencode(params)
167
168 headers = {"Content-type": "application/x-www-form-urlencoded"}
169
170 try:
171 conn = httplib.HTTPSConnection(HOSTNAME)
172 conn.request("POST", "/_api/", params, headers)
173
174 response = conn.getresponse()
175
176 # Never leak "data" into public logs.
177 data = response.read()
178 except:
179 raise Exception("ERROR: Connection problem.")
180
181 try:
182 return json.loads(data)
183 except:
184 raise Exception("ERROR: Could not read response. Is your key valid?")
185
186 return None
187
188
189def Main():
190 parser = argparse.ArgumentParser()
191 parser.add_argument("-k", "--key-file", required=True,
192 help="A file with the clusterfuzz api key.")
193 parser.add_argument("-r", "--results-file",
194 help="A file to write the results to.")
195 options = parser.parse_args()
196
197 # Get api key. The key's content must never be logged.
198 assert options.key_file
199 with open(options.key_file) as f:
200 key = f.read().strip()
201 assert key
202
203 revision_number = GetLatestV8InChromium()
204
205 results = []
206 for spec in BUG_SPECS:
207 args = dict(spec["args"])
208 # Use incremented revision as we're interested in all revision greater than
209 # what's currently rolled into chromium.
210 if revision_number:
211 args["revision_greater_or_equal"] = str(int(revision_number) + 1)
212
213 # Never print issue details in public logs.
214 issues = APIRequest(key, **args)
215 assert issues is not None
216 for issue in issues:
217 if re.match(spec["crash_state"], issue["crash_state"]):
218 results.append(issue["id"])
219
220 if options.results_file:
221 with open(options.results_file, "w") as f:
222 f.write(json.dumps(results))
223 else:
224 print results
225
226
227if __name__ == "__main__":
228 sys.exit(Main())