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 Prints the number of issues found or a list of the issue IDs if specified. | |
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 not use the --print-ids option. | |
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.*") | |
Michael Achenbach
2014/12/17 13:25:36
+ jkummerow for the v8 crash patterns.
| |
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 conn = httplib.HTTPSConnection(HOSTNAME) | |
117 conn.request("POST", "/_api/", params, headers) | |
118 | |
119 response = conn.getresponse() | |
120 | |
121 # Never leak "data" into public logs. | |
122 data = response.read() | |
123 try: | |
124 return json.loads(data) | |
125 except: | |
126 raise Exception("ERROR: Could not read response. Is your key valid?") | |
127 | |
128 return None | |
129 | |
130 | |
131 def Main(): | |
132 parser = argparse.ArgumentParser() | |
133 parser.add_argument("-k", "--key-file", required=True, | |
134 help="A file with the clusterfuzz api key.") | |
135 parser.add_argument("-p", "--print-ids", action="store_true", default=False, | |
136 help="Print json with the test case IDs. Don't use this " | |
137 "from public callers. Otherwise, the script prints " | |
138 "just the number of issues found.") | |
139 options = parser.parse_args() | |
140 | |
141 # Get api key. The key's content must never be logged. | |
142 assert options.key_file | |
143 with open(options.key_file) as f: | |
144 key = f.read().strip() | |
145 assert key | |
146 | |
147 revision_number = GetLatestV8InChromium() | |
148 | |
149 results = [] | |
150 for spec in BUG_SPECS: | |
151 args = dict(spec["args"]) | |
152 # Use incremented revision as we're interested in all revision greater than | |
153 # what's currently rolled into chromium. | |
154 if revision_number: | |
155 args["revision_greater_or_equal"] = str(int(revision_number) + 1) | |
156 | |
157 # Never print issue details in public logs. | |
158 issues = APIRequest(key, **args) | |
159 assert issues is not None | |
160 for issue in issues: | |
161 if re.match(spec["crash_state"], issue["crash_state"]): | |
162 results.append(issue["id"]) | |
163 | |
164 if options.print_ids: | |
165 print json.dumps(results, indent=2) | |
166 else: | |
167 print len(results) | |
168 | |
169 | |
170 if __name__ == "__main__": | |
171 sys.exit(Main()) | |
OLD | NEW |