blob: fc826c1df3cdcbd590ede6e9021f0194f8767cc5 [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": {
74 "job_type": "linux_asan_d8_v8_arm_dbg",
75 "reproducible": "True",
76 "open": "True",
77 "bug_information": "",
78 },
79 "crash_state": ANY_RE,
80 },
81 {
82 "args": {
83 "job_type": "linux_asan_d8_v8_arm64_dbg",
84 "reproducible": "True",
85 "open": "True",
86 "bug_information": "",
87 },
88 "crash_state": ANY_RE,
89 },
90 {
91 "args": {
92 "job_type": "linux_asan_d8_v8_mipsel_dbg",
93 "reproducible": "True",
94 "open": "True",
95 "bug_information": "",
96 },
97 "crash_state": ANY_RE,
98 },
99]
100
101
102def GetRequest(url):
103 url_fh = urllib2.urlopen(url, None, 60)
104 try:
105 return url_fh.read()
106 finally:
107 url_fh.close()
108
109
110def GetLatestV8InChromium():
111 """Returns the commit position number of the latest v8 roll in chromium."""
112
113 # Check currently rolled v8 revision.
114 result = GetRequest(DEPS_LOG)
115 if not result:
116 return None
117
118 # Strip security header and load json.
119 commits = json.loads(result[5:])
120
121 git_revision = None
122 for commit in commits["log"]:
123 # Get latest commit that matches the v8 roll pattern. Ignore cherry-picks.
124 match = re.match(V8_COMMIT_RE, commit["message"])
125 if match:
126 git_revision = match.group(1)
127 break
128 else:
129 return None
130
131 # Get commit position number for v8 revision.
132 result = GetRequest(CRREV % git_revision)
133 if not result:
134 return None
135
136 commit = json.loads(result)
137 assert commit["repo"] == "v8/v8"
138 return commit["number"]
139
140
141def APIRequest(key, **params):
142 """Send a request to the clusterfuzz api.
143
144 Returns a json dict of the response.
145 """
146
147 params["api_key"] = key
148 params = urllib.urlencode(params)
149
150 headers = {"Content-type": "application/x-www-form-urlencoded"}
151
152 try:
153 conn = httplib.HTTPSConnection(HOSTNAME)
154 conn.request("POST", "/_api/", params, headers)
155
156 response = conn.getresponse()
157
158 # Never leak "data" into public logs.
159 data = response.read()
160 except:
161 raise Exception("ERROR: Connection problem.")
162
163 try:
164 return json.loads(data)
165 except:
166 raise Exception("ERROR: Could not read response. Is your key valid?")
167
168 return None
169
170
171def Main():
172 parser = argparse.ArgumentParser()
173 parser.add_argument("-k", "--key-file", required=True,
174 help="A file with the clusterfuzz api key.")
175 parser.add_argument("-r", "--results-file",
176 help="A file to write the results to.")
177 options = parser.parse_args()
178
179 # Get api key. The key's content must never be logged.
180 assert options.key_file
181 with open(options.key_file) as f:
182 key = f.read().strip()
183 assert key
184
185 revision_number = GetLatestV8InChromium()
186
187 results = []
188 for spec in BUG_SPECS:
189 args = dict(spec["args"])
190 # Use incremented revision as we're interested in all revision greater than
191 # what's currently rolled into chromium.
192 if revision_number:
193 args["revision_greater_or_equal"] = str(int(revision_number) + 1)
194
195 # Never print issue details in public logs.
196 issues = APIRequest(key, **args)
197 assert issues is not None
198 for issue in issues:
199 if re.match(spec["crash_state"], issue["crash_state"]):
200 results.append(issue["id"])
201
202 if options.results_file:
203 with open(options.results_file, "w") as f:
204 f.write(json.dumps(results))
205 else:
206 print results
207
208
209if __name__ == "__main__":
210 sys.exit(Main())