OLD | NEW |
| (Empty) |
1 #!/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 """ | |
7 Script to check for new clusterfuzz issues since the last rolled v8 revision. | |
8 | |
9 Returns a json list with test case IDs if any. | |
10 | |
11 Security considerations: The security key and request data must never be | |
12 written to public logs. Public automated callers of this script should | |
13 suppress stdout and stderr and only process contents of the results_file. | |
14 """ | |
15 | |
16 | |
17 import argparse | |
18 import httplib | |
19 import json | |
20 import os | |
21 import re | |
22 import sys | |
23 import urllib | |
24 import urllib2 | |
25 | |
26 | |
27 # Constants to git repos. | |
28 BASE_URL = "https://chromium.googlesource.com" | |
29 DEPS_LOG = BASE_URL + "/chromium/src/+log/master/DEPS?format=JSON" | |
30 | |
31 # Constants for retrieving v8 rolls. | |
32 CRREV = "https://cr-rev.appspot.com/_ah/api/crrev/v1/commit/%s" | |
33 V8_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. | |
37 HOSTNAME = "backend-dot-cluster-fuzz.appspot.com" | |
38 | |
39 # Crash patterns. | |
40 V8_INTERNAL_RE = re.compile(r"^v8::internal.*") | |
41 ANY_RE = re.compile(r".*") | |
42 | |
43 # List of all api requests. | |
44 BUG_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 | |
66 def 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 | |
74 def 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 | |
105 def 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 | |
135 def 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 | |
173 if __name__ == "__main__": | |
174 sys.exit(Main()) | |
OLD | NEW |