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

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 import tempfile
64 import logging
65
66 class PathContext(object):
67 """A PathContext is used to carry the information used to construct URLs and
68 paths when dealing with the storage server and archives."""
69 def __init__(self, original_gs_url, repackage_gs_url,
70 builder_name=BUILDER_NAME, file_prefix=ARCHIVE_PREFIX,
71 revision_file=REVISION_MAP_FILE):
72 super(PathContext, self).__init__()
73 self.original_gs_url = original_gs_url
74 self.repackage_gs_url = repackage_gs_url
75 self.builder_name = builder_name
76 self.file_prefix = file_prefix
77 self.revision_file = revision_file
78
79
80 def get_cp_from_hash(git_hash):
81 """Converts a GitHash to Commit position number"""
82 json_url = CHROMIUM_GITHASH_TO_SVN_URL % git_hash
83 response = urllib.urlopen(json_url)
84 if response.getcode() == 200:
85 try:
86 data = json.loads(response.read())
87 except ValueError:
88 logging.warning('ValueError for JSON URL: %s' % json_url)
89 raise ValueError('Failed to extract commit position')
90 else:
91 logging.warning('ValueError for JSON URL: %s' % json_url)
92 raise ValueError('Failed to extract commit position')
93 if 'number' in data:
94 return data['number']
95 logging.warning('Failed to get svn revision number for %s' % git_hash)
96 raise ValueError('Failed to extract commit position')
97
98 def create_cp_from_hash_map(hash_list):
99 """Creates a dictionary that maps from Commit positio number
100 to corresponding GitHash"""
101 hash_map = {}
102 count = 0
103 for git_hash in hash_list:
104 try:
105 cp_num = get_cp_from_hash(git_hash)
106 hash_map[cp_num] = git_hash
107 print "Downloaded %s / %s git hash" %(count, len(hash_list))
108 count += 1
109 except ValueError:
110 pass
111 return hash_map
112
113 def get_list_of_suffix(bucket_address, prefix, filter_function):
114 """Gets the list of suffixes in files in a google storage bucket.
115 For example, a google storage bucket containing one file
116 'full-build-linux_20983' will return ['20983'] if prefix is
117 provided as 'full-build-linux'. Google Storage bucket
118 containing multiple files will return multiple suffixes. """
119 file_list = bisect_repackage_utils.GSutilList(bucket_address)
120 suffix_list = []
121 extract_suffix = '.*?%s_(.*?)\.zip' %(prefix)
122 for file in file_list:
123 match = re.match(extract_suffix, file)
124 if match and filter_function(match.groups()[0]):
125 suffix_list.append(match.groups()[0])
126 return suffix_list
127
128 def download_build(cp_num, revision_map, zip_file_name, context):
129 """ Download a single build corresponding to the cp_num and context."""
130 file_url = '%s/%s/%s_%s.zip' %(context.original_gs_url, context.builder_name,
131 context.file_prefix, revision_map[cp_num])
132 bisect_repackage_utils.GSUtilDownloadFile(file_url, zip_file_name)
133
134 def upload_build(zip_file, context):
135 """Uploads a single build in zip_file to the repackage_gs_url in context."""
136 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
137 upload_url = gs_base_url + '/'
138 bisect_repackage_utils.GSUtilCopy(zip_file, upload_url)
139
140 def download_revision_map(context):
141 """Downloads the revision map in original_gs_url in context."""
142 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
143 download_url = gs_base_url + '/' + context.revision_file
144 try:
145 bisect_repackage_utils.GSUtilDownloadFile(download_url,
146 context.revision_file)
147 except Exception, e:
148 raise ValueError('Revision map does not exist. Re run the program with'
149 '-c option to upload a revision map to google storage')
150
151 def get_revision_map(context):
152 """Downloads and returns the revision map in repackage_gs_url in context."""
153 bisect_repackage_utils.RemoveFile(context.revision_file)
154 download_revision_map(context)
155 with open(context.revision_file, 'r') as f:
156 revision_map = json.load(f)
157 bisect_repackage_utils.RemoveFile(context.revision_file)
158 return revision_map
159
160 def upload_revision_map(revision_map, context):
161 """Upload the given revision_map to the repackage_gs_url in context."""
162 with open(context.revision_file, 'w') as fp:
163 json.dump(revision_map, fp)
164 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
165 upload_url = gs_base_url + '/'
166 bisect_repackage_utils.GSUtilCopy(context.revision_file, upload_url)
167 bisect_repackage_utils.RemoveFile(context.revision_file)
168
169 def create_upload_revision_map(context):
170 """ Creates and uploads a dictionary that maps from GitHash to CP number."""
171 gs_base_url = '%s/%s' %(context.original_gs_url, context.builder_name)
172 hash_list = get_list_of_suffix(gs_base_url, context.file_prefix,
173 bisect_repackage_utils.isGitCommitHash)
174 cp_num_to_hash_map = create_cp_from_hash_map(hash_list)
175 upload_revision_map(cp_num_to_hash_map, context)
176
177 def update_upload_revision_map(context):
178 """ Updates and uploads a dictionary that maps from GitHash to CP number."""
179 gs_base_url = '%s/%s' %(context.original_gs_url, context.builder_name)
180 revision_map = get_revision_map(context)
181 hash_list = get_list_of_suffix(gs_base_url, context.file_prefix,
182 bisect_repackage_utils.isGitCommitHash)
183 hash_list = list(set(hash_list)-set(revision_map.values()))
184 cp_num_to_hash_map = create_cp_from_hash_map(hash_list)
185 merged_dict = dict(cp.num_to_hash_map.items() + revision_map.items())
186 upload_revision_map(cp_num_to_hash_map, context)
187
188 def make_lightweight_archive(file_archive, archive_name):
189 """Repackages and strips the archive according to FILES_TO_ARCHIVE and
190 STRIP_LIST defined on the top."""
191 return bisect_repackage_utils.MakeZip('.',
192 archive_name,
193 FILES_TO_ARCHIVE,
194 file_archive,
195 raise_error=False,
196 strip_files=STRIP_LIST)
197
198 def remove_created_files_and_path(files, paths):
199 """ Removes all the files and paths passed in."""
200 for file in files:
201 bisect_repackage_utils.RemoveFile(file)
202 for path in paths:
203 bisect_repackage_utils.RemovePath(path)
204
205 def verify_chrome_run(zip_dir):
206 """This function executes chrome executable in zip_dir. Raises error if the
207 execution fails for any reason."""
208 try:
209 command = [os.path.join(zip_dir, 'chrome')]
210 code = bisect_repackage_utils.RunCommand(command)
211 if code != 0:
212 raise ValueError("Executing Chrome Failed: Need to check ")
213 except Exception, e:
214 raise ValueError("Executing Chrome Failed: Need to check ")
215
216 def repackage_single_revision(revision_map, isForwardArchive, verify_run,
217 staging_dir, context, cp_num):
218 """Repackages a single Chrome build for manual bisect."""
219 archive_name = '%s_%s' %(context.file_prefix, cp_num)
220 file_archive = os.path.join(staging_dir, archive_name)
221 zip_file_name = '%s.zip' %file_archive
222
223 bisect_repackage_utils.RemoveFile(zip_file_name)
224 download_build(cp_num, revision_map, zip_file_name, context)
225 extract_dir = os.path.join(staging_dir, archive_name)
226 bisect_repackage_utils.ExtractZip(zip_file_name, extract_dir)
227 extracted_folder = os.path.join(extract_dir, context.file_prefix)
228
229 (zip_dir, zip_file) = make_lightweight_archive(extracted_folder, archive_name)
230
231 if verify_run:
232 verify_chrome_run(zip_dir)
233
234 upload_build(zip_file, context)
235 # Removed temporary files created during repackaging process.
236 remove_created_files_and_path([zip_file, zip_file_name],
237 [zip_dir, extract_dir])
238
239 def repackage_revisions(revisions, revision_map, isForwardArchive, verify_run,
240 staging_dir, context, quit_event=None,
241 progress_event=None):
242 """ Repackages all Chrome builds listed in revisions. This function calls
243 'repackage_single_revision' with multithreading pool.'"""
244 p = Pool(3)
245 func = partial(repackage_single_revision, revision_map, isForwardArchive,
246 verify_run, staging_dir, context)
247 p.imap(func, revisions)
248 p.close()
249 p.join()
250
251 def get_uploaded_builds(context):
252 """Returns already uploaded commit positions numbers in
253 context.repackage_gs_url"""
254 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
255 return get_list_of_suffix(gs_base_url, context.file_prefix,
256 bisect_repackage_utils.isCommitPosition)
257
258 def get_revisions_to_package(revision_map, context):
259 """ Returns revisions that need to be repackaged. The first half of
260 the revisions will be sorted in ascending order and the second half of
261 the revisions will be sorted in desending order.
262
263 The first half will be used for repackaging revisions in forward direction,
264 and the second half will be used for repackaging revisions in backward
265 direction."""
266 already_packaged = get_uploaded_builds(context)
267 not_already_packaged = list(set(revision_map.keys())-set(already_packaged))
268 revisions_to_package = sorted(not_already_packaged)
269 revisions_to_package = filter(lambda a: a != 'null', revisions_to_package)
270
271 forward_rev = revisions_to_package[:len(revisions_to_package)/2]
272 backward_rev = sorted(revisions_to_package[len(revisions_to_package)/2:],
273 reverse=True)
274 return (forward_rev, backward_rev)
275
276 class RepackageJob(object):
277 def __init__(self, name, revisions_to_package, revision_map, isForwardArchive,
278 verify_run, staging_dir, context):
279 super(RepackageJob, self).__init__()
280 self.name = name
281 self.revisions_to_package = revisions_to_package
282 self.revision_map = revision_map
283 self.isForwardArchive = isForwardArchive
284 self.verify_run = verify_run
285 self.staging_dir = staging_dir
286 self.context = context
287 self.quit_event = threading.Event()
288 self.progress_event = threading.Event()
289 self.thread = None
290
291 def Start(self):
292 """Starts the download."""
293 fetchargs = (self.revisions_to_package,
294 self.revision_map,
295 self.isForwardArchive,
296 self.verify_run,
297 self.staging_dir,
298 self.context,
299 self.quit_event,
300 self.progress_event)
301 self.thread = threading.Thread(target=repackage_revisions,
302 name=self.name,
303 args=fetchargs)
304 self.thread.start()
305
306 def Stop(self):
307 """Stops the download which must have been started previously."""
308 assert self.thread, 'DownloadJob must be started before Stop is called.'
309 self.quit_event.set()
310 self.thread.join()
311
312 def WaitFor(self):
313 """Prints a message and waits for the download to complete. The download
314 must have been started previously."""
315 assert self.thread, 'DownloadJob must be started before WaitFor is called.'
316 self.progress_event.set() # Display progress of download. def Stop(self):
317 assert self.thread, 'DownloadJob must be started before Stop is called.'
318 self.quit_event.set()
319 self.thread.join()
320
321 def main(argv):
322 option_parser = optparse.OptionParser()
323
324 # Verifies that the chrome executable runs
325 option_parser.add_option('-v', '--verify',
326 action='store_true',
327 help='Verifies that the Chrome executes normally'
328 'without errors')
329
330 # This option will update the revision map.
331 option_parser.add_option('-u', '--update',
332 action='store_true',
333 help='Updates the list of revisions to repackage')
334
335 # This option will creates the revision map.
336 option_parser.add_option('-c', '--create',
337 action='store_true',
338 help='Creates the list of revisions to repackage')
339
340 # Original bucket that contains perf builds
341 option_parser.add_option('-o', '--original',
342 type='str',
343 help='Google storage url containing original Chrome builds')
344
345 # Bucket that should archive lightweight perf builds
346 option_parser.add_option('-r', '--repackage',
347 type='str',
348 help='Google storage url to re-archive Chrome builds')
349
350 verify_run = False
351 (opts, args) = option_parser.parse_args()
352 if not opts.original or not opts.repackage:
353 raise ValueError('Need to specify original gs bucket url and'
354 'repackage gs bucket url')
355 context = PathContext(opts.original, opts.repackage)
356
357 if opts.create:
358 create_upload_revision_map(context)
359
360 if opts.update:
361 update_upload_revision_map(context)
362
363 if opts.verify:
364 verify_run = True
365
366 revision_map = get_revision_map(context)
367 (forward_rev, backward_rev) = get_revisions_to_package(revision_map, context)
368 bisect_repackage_utils.MaybeMakeDirectory(STAGING_DIR)
369 bisect_repackage_utils.MaybeMakeDirectory(STAGING_DIR_BACKWARD)
370 staging_dir = tempfile.mkdtemp(prefix='staging_fwd')
371 staging_dir_bwd = tempfile.mkdtemp(prefix='staging_bwd')
372
373 # Spwan two threads: One repackaging from oldest builds. The other repackaging
374 # from the newest builds.
375 forward_fetch = RepackageJob('forward_fetch', forward_rev, revision_map, True,
376 verify_run, staging_dir, context)
377 backward_fetch = RepackageJob('backward_fetch', backward_rev, revision_map,
378 False, verify_run, staging_dir_bwd,
379 context)
380 forward_fetch.Start()
381 backward_fetch.Start()
382 forward_fetch.WaitFor()
383 backward_fetch.WaitFor()
384
385 if '__main__' == __name__:
386 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