Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(468)

Side by Side Diff: tools/bisect_repackage/bisect_repackage_linux.py

Issue 2236703003: tool for repackaging chrome.perf builds for manual bisect (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: tool for repackaging chrome.perf builds for manual bisect Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """ Bisect repackage tool for Linux
6
7 This script repacakges chrome builds for manual bisect script.
8 """
9
10 #Declares required files to run manual bisect script on chrome Linux
11 #builds in perf. Binary files that should be stripped to reduce zip file
12 #size are declared. The file list was gotten from the local chrome
13 #executable path in Linux. (This can be retrieved by typing 'chrome://version'
14 #in chrome and following the executable path. The list needs to be updated if
15 #future chrome versions require additional files.
16 FILES_TO_ARCHIVE = [
17 'chrome',
18 'chrome_100_percent.pak',
19 'chrome_200_percent.pak',
20 'default_apps',
21 'icudtl.dat',
22 'libwidevinecdm.so',
23 'locales',
24 'nacl_helper',
25 'nacl_helper_bootstrap',
26 'nacl_irt_x86_64.nexe',
27 'natives_blob.bin',
28 'PepperFlash',
29 'product_logo_48.png',
30 'resources.pak',
31 'snapshot_blob.bin',
32 'xdg-mime',
33 'xdg-settings',
34 ]
35
36 # Declares what files should be stripped of symbols to reduce the archive size.
37 STRIP_LIST = [
38 'chrome',
39 'nacl_helper'
40 ]
41
42 # API to convert Githash to Commit position number.
43 CHROMIUM_GITHASH_TO_SVN_URL = (
44 'https://cr-rev.appspot.com/_ah/api/crrev/v1/commit/%s')
45
46 BUILDER_NAME = 'Linux Builder'
47 ARCHIVE_PREFIX = 'full-build-linux'
48 REVISION_MAP_FILE = 'revision_map.json'
49
50 ################################################################################
51 import os
52 import bisect_repackage_utils
53 import zipfile
54 import zlib
55 import json
56 import sys
57 import optparse
58 import re
59 import urllib
60 import threading
61 from multiprocessing import Pool
62 from functools import partial
63
64 # Declares where files should be stored temporarily for staging.
65 # TODO (miimnk): use tempfile.mkdtemp to make temporary folders
66 STAGING_DIR= os.path.join('.', 'a', 'tmp_forward')
dimu 2016/08/19 04:14:05 is there any cleanup for the staging dir?
miimnk 2016/08/19 20:29:43 Erased the two global variables and used 'tempfile
67 STAGING_DIR_BACKWARD = os.path.join('.', 'a', 'tmp_backward')
68
69
70 class PathContext(object):
71 """A PathContext is used to carry the information used to construct URLs and
72 paths when dealing with the storage server and archives."""
73 def __init__(self, original_gs_url, repackage_gs_url,
74 builder_name=BUILDER_NAME, file_prefix=ARCHIVE_PREFIX,
75 revision_map=REVISION_MAP_FILE):
76 super(PathContext, self).__init__()
77 self.original_gs_url = original_gs_url
78 self.repackage_gs_url = repackage_gs_url
79 self.builder_name = builder_name
80 self.file_prefix = file_prefix
81 self.revision_map = revision_map
82
83
84 def get_cp_from_hash(git_hash):
85 """Converts a GitHash to Commit position number"""
86 json_url = CHROMIUM_GITHASH_TO_SVN_URL % git_hash
87 response = urllib.urlopen(json_url)
88 if response.getcode() == 200:
89 try:
90 data = json.loads(response.read())
91 except ValueError:
92 print 'ValueError for JSON URL: %s' % json_url
dimu 2016/08/19 04:14:05 It would be better to use logging instead of print
miimnk 2016/08/19 20:29:43 Done.
93 return None
dimu 2016/08/19 04:14:05 throw exception for failure, and use try/catch in
miimnk 2016/08/19 20:29:43 Done.
94 else:
95 print 'ValueError for JSON URL: %s' % json_url
96 return None
97 if 'number' in data:
98 return data['number']
99 print 'Failed to get svn revision number for %s' % git_hash
100 return None
101
102 def create_cp_from_hash_map(hash_list):
103 """Creates a dictionary that maps from Commit positio number
104 to corresponding GitHash"""
105 hash_map = {}
106 count = 0
107 for git_hash in hash_list:
108 cp_num = get_cp_from_hash(git_hash)
109 hash_map[cp_num] = git_hash
110 print "Downloaded %s / %s git hash" %(count, len(hash_list))
111 count += 1
112 return hash_map
113
114 def isProperHash(regex_match):
dimu 2016/08/19 04:14:05 move it to utils
dimu 2016/08/19 04:14:05 isGitCommitHash
miimnk 2016/08/19 20:29:42 Done.
miimnk 2016/08/19 20:29:43 Done.
115 """Checks if match is correct SHA1 hash"""
116 if len(regex_match) == 40: return True
dimu 2016/08/19 04:14:05 use re.match(r"^[0-9,A-F]{40}$", regex_match.upper
miimnk 2016/08/19 20:29:43 Done.
117 return False
118
119 def isProperRevision(regex_match):
dimu 2016/08/19 04:14:05 move this to utils
miimnk 2016/08/19 20:29:43 Done.
120 """Checks if match is correct revision(Cp number) format"""
121 if len(regex_match) == 6: return True
dimu 2016/08/19 04:14:05 also use regex
miimnk 2016/08/19 20:29:43 Done.
122 return False
123
124 def get_list_of_suffix(bucket_address, prefix, filter_function):
125 """Gets the list of suffixes in files in a google storage bucket.
126 For example, a google storage bucket containing one file
127 'full-build-linux_20983' will return ['20983'] if prefix is
128 provided as 'full-build-linux'. Google Storage bucket
129 containing multiple files will return multiple suffixes. """
130 file_list = bisect_repackage_utils.GSutilList(bucket_address)
131 suffix_list = []
132 extract_suffix = '.*?%s_(.*?)\.zip' %(prefix)
133 for file in file_list:
134 match = re.match(extract_suffix, file)
135 if match and filter_function(match.groups()[0]):
136 suffix_list.append(match.groups()[0])
137 return suffix_list
138
139 def download_build(cp_num, revision_map, zip_file_name, context):
140 """ Download a single build corresponding to the cp_num and context."""
141 file_url = '%s/%s/%s_%s.zip' %(context.original_gs_url, context.builder_name,
142 context.file_prefix, revision_map[cp_num])
143 bisect_repackage_utils.GSUtilDownloadFile(file_url, zip_file_name)
144
145 def upload_build(zip_file, context):
146 """Uploads a single build in zip_file to the repackage_gs_url in context."""
147 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
148 upload_url = gs_base_url + '/'
149 bisect_repackage_utils.GSUtilCopy(zip_file, upload_url)
150
151 def download_revision_map(context):
152 """Downloads the revision map in original_gs_url in context."""
153 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
154 download_url = gs_base_url + '/' + context.revision_map
155 try:
156 bisect_repackage_utils.GSUtilDownloadFile(download_url,
157 context.revision_map)
158 except Exception, e:
159 raise ValueError('Revision map does not exist. Re run the program with'
160 '-c option to upload a revision map to google storage')
161
162 def get_revision_map(context):
163 """Downloads and returns the revision map in repackage_gs_url in context."""
164 bisect_repackage_utils.RemoveFile(context.revision_map)
165 download_revision_map(context)
166 with open(context.revision_map, 'r') as f:
dimu 2016/08/19 04:14:05 nit: better naming: revision_file
miimnk 2016/08/19 20:29:43 Done.
167 revision_map = json.load(f)
168 bisect_repackage_utils.RemoveFile(context.revision_map)
169 return revision_map
170
171 def upload_revision_map(revision_map, context):
172 """Upload the given revision_map to the repackage_gs_url in context."""
173 with open(context.revision_map, 'w') as fp:
174 json.dump(revision_map, fp)
175 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
176 upload_url = gs_base_url + '/'
177 bisect_repackage_utils.GSUtilCopy(context.revision_map, upload_url)
178 bisect_repackage_utils.RemoveFile(context.revision_map)
179
180 def create_upload_revision_map(context):
181 """ Creates and uploads a dictionary that maps from GitHash to CP number."""
182 gs_base_url = '%s/%s' %(context.original_gs_url, context.builder_name)
183 hash_list = get_list_of_suffix(gs_base_url, context.file_prefix, isProperHash)
184 cp_num_to_hash_map = create_cp_from_hash_map(hash_list)
185 upload_revision_map(cp_num_to_hash_map, context)
186
187 def update_upload_revision_map(context):
188 """ Updates and uploads a dictionary that maps from GitHash to CP number."""
189 gs_base_url = '%s/%s' %(context.original_gs_url, context.builder_name)
190 revision_map = get_revision_map(context)
191 hash_list = get_list_of_suffix(gs_base_url, context.file_prefix, isProperHash)
192 hash_list = list(set(hash_list)-set(revision_map.values()))
193 cp_num_to_hash_map = create_cp_from_hash_map(hash_list)
194 upload_revision_map(cp_num_to_hash_map, context)
195
196 def make_filtered_archive(file_archive, archive_name):
dimu 2016/08/19 04:14:05 better naming
miimnk 2016/08/19 20:29:43 Done. Changed to 'make_lightweight_archive'.
197 """Repackages and strips the archive according to FILES_TO_ARCHIVE and
198 STRIP_LIST defined on the top."""
199 return bisect_repackage_utils.MakeZip('.',
200 archive_name,
201 FILES_TO_ARCHIVE,
202 file_archive,
203 raise_error=False,
204 strip_files=STRIP_LIST)
205
206 def remove_created_files_and_path(files, paths):
207 """ Removes all the files and paths passed in."""
208 for file in files:
209 bisect_repackage_utils.RemoveFile(file)
210 for path in paths:
211 bisect_repackage_utils.RemovePath(path)
212
213 def verify_chrome_run(zip_dir):
214 """This function executes chrome executable in zip_dir. Raises error if the
215 execution fails for any reason."""
216 try:
217 command = [os.path.join(zip_dir, 'chrome')]
218 code = bisect_repackage_utils.RunCommand(command)
219 if code != 0:
220 raise ValueError("Executing Chrome Failed: Need to check ")
221 except Exception, e:
222 raise ValueError("Executing Chrome Failed: Need to check ")
223
224 def repackage_single_revision(revision_map, isForwardArchive, verify_run,
225 staging_dir, context, cp_num):
226 """Repackages a single Chrome build for manual bisect."""
227 archive_name = '%s_%s' %(context.file_prefix, cp_num)
228 file_archive = os.path.join(staging_dir, archive_name)
229 zip_file_name = '%s.zip' %file_archive
230
231 bisect_repackage_utils.RemoveFile(zip_file_name)
232 download_build(cp_num, revision_map, zip_file_name, context)
233 extract_dir = os.path.join(staging_dir, archive_name)
234 bisect_repackage_utils.ExtractZip(zip_file_name, extract_dir)
235 extracted_folder = os.path.join(extract_dir, context.file_prefix)
236
237 (zip_dir, zip_file) = make_filtered_archive(extracted_folder, archive_name)
238
239 if verify_run:
240 verify_chrome_run(zip_dir)
241
242 upload_build(zip_file, context)
243 # Removed temporary files created during repackaging process.
244 remove_created_files_and_path([zip_file, zip_file_name],
245 [zip_dir, extract_dir])
246
247 def repackage_revisions(revisions, revision_map, isForwardArchive, verify_run,
248 staging_dir, context, quit_event=None,
249 progress_event=None):
250 """ Repackages all Chrome builds listed in revisions. This function calls
251 'repackage_single_revision' with multithreading pool.'"""
252 p = Pool(3)
253 func = partial(repackage_single_revision, revision_map, isForwardArchive,
254 verify_run, staging_dir, context)
255 p.imap(func, revisions)
256 p.close()
257 p.join()
258
259 def get_uploaded_builds(context):
260 """Returns already uploaded commit positions numbers in
261 context.repackage_gs_url"""
262 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
263 return get_list_of_suffix(gs_base_url, context.file_prefix, isProperRevision)
264
265 def get_revisions_to_package(revision_map, context):
266 """ Returns revisions that need to be repackaged. The first half of
267 the revisions will be sorted in ascending order and the second half of
268 the revisions will be sorted in desending order.
269
270 The first half will be used for repackaging revisions in forward direction,
271 and the second half will be used for repackaging revisions in backward
272 direction."""
273 already_packaged = get_uploaded_builds(context)
274 not_already_packaged = list(set(revision_map.keys())-set(already_packaged))
275 revisions_to_package = sorted(not_already_packaged)
276 revisions_to_package = filter(lambda a: a != 'null', revisions_to_package)
277
278 forward_rev = revisions_to_package[:len(revisions_to_package)/2]
279 backward_rev = sorted(revisions_to_package[len(revisions_to_package)/2:],
280 reverse=True)
281 return (forward_rev, backward_rev)
282
283 class RepackageJob(object):
284 def __init__(self, name, revisions_to_package, revision_map, isForwardArchive,
285 verify_run, staging_dir, context):
286 super(RepackageJob, self).__init__()
287 self.name = name
288 self.revisions_to_package = revisions_to_package
289 self.revision_map = revision_map
290 self.isForwardArchive = isForwardArchive
291 self.verify_run = verify_run
292 self.staging_dir = staging_dir
293 self.context = context
294 self.quit_event = threading.Event()
295 self.progress_event = threading.Event()
296 self.thread = None
297
298 def Start(self):
299 """Starts the download."""
300 fetchargs = (self.revisions_to_package,
301 self.revision_map,
302 self.isForwardArchive,
303 self.verify_run,
304 self.staging_dir,
305 self.context,
306 self.quit_event,
307 self.progress_event)
308 self.thread = threading.Thread(target=repackage_revisions,
309 name=self.name,
310 args=fetchargs)
311 self.thread.start()
312
313 def Stop(self):
314 """Stops the download which must have been started previously."""
315 assert self.thread, 'DownloadJob must be started before Stop is called.'
316 self.quit_event.set()
317 self.thread.join()
318
319 def WaitFor(self):
320 """Prints a message and waits for the download to complete. The download
321 must have been started previously."""
322 assert self.thread, 'DownloadJob must be started before WaitFor is called.'
323 self.progress_event.set() # Display progress of download. def Stop(self):
324 assert self.thread, 'DownloadJob must be started before Stop is called.'
325 self.quit_event.set()
326 self.thread.join()
327
328 def main(argv):
329 option_parser = optparse.OptionParser()
330
331 # Verifies that the chrome executable runs
332 option_parser.add_option('-v', '--verify',
333 action='store_true',
334 help='Verifies that the Chrome executes normally'
335 'without errors')
336
337 # This option will update the revision map.
338 option_parser.add_option('-u', '--update',
339 action='store_true',
340 help='Updates the list of revisions to repackage')
341
342 # This option will creates the revision map.
343 option_parser.add_option('-c', '--create',
344 action='store_true',
345 help='Creates the list of revisions to repackage')
346
347 # Original bucket that contains perf builds
348 option_parser.add_option('-o', '--original',
349 type='str',
350 help='Google storage url containing original Chrome builds')
351
352 # Bucket that should archive lightweight perf builds
353 option_parser.add_option('-r', '--repackage',
354 type='str',
355 help='Google storage url to re-archive Chrome builds')
356
357 verify_run = False
358 (opts, args) = option_parser.parse_args()
359 if not opts.original or not opts.repackage:
360 raise ValueError('Need to specify original gs bucket url and'
361 'repackage gs bucket url')
362 context = PathContext(opts.original, opts.repackage)
363
364 if opts.create:
365 create_upload_revision_map(context)
366
367 if opts.update:
368 update_upload_revision_map(context)
369
370 if opts.verify:
371 verify_run = True
372
373 revision_map = get_revision_map(context)
374 (forward_rev, backward_rev) = get_revisions_to_package(revision_map, context)
375 bisect_repackage_utils.MaybeMakeDirectory(STAGING_DIR)
376 bisect_repackage_utils.MaybeMakeDirectory(STAGING_DIR_BACKWARD)
377
378 # Spwan two threads: One repackaging from oldest builds. The other repackaging
379 # from the newest builds.
380 forward_fetch = RepackageJob('forward_fetch', forward_rev, revision_map, True,
381 verify_run, os.path.abspath(STAGING_DIR),
382 context)
383 backward_fetch = RepackageJob('backward_fetch', backward_rev, revision_map,
384 False, verify_run,
385 os.path.abspath(STAGING_DIR_BACKWARD),
386 context)
387 forward_fetch.Start()
388 backward_fetch.Start()
389 forward_fetch.WaitFor()
390 backward_fetch.WaitFor()
391
392 if '__main__' == __name__:
393 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | tools/bisect_repackage/bisect_repackage_utils.py » ('j') | tools/bisect_repackage/bisect_repackage_utils.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698