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