blob: 8dc51b17fddfdc59d9c705bc87d58fba28be261f [file] [log] [blame]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -04001# Copyright (c) 2013 The Chromium OS 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"""Upload all debug symbols required for crash reporting purposes.
6
7This script need only be used to upload release builds symbols or to debug
8crashes on non-release builds (in which case try to only upload the symbols
Mike Frysinger02e1e072013-11-10 22:11:34 -05009for those executables involved).
10"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040011
Mike Frysingera4fa1e82014-01-15 01:45:56 -050012from __future__ import print_function
13
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040014import ctypes
Mike Frysinger8ec8c502014-02-10 00:19:13 -050015import datetime
Mike Frysinger02e92402013-11-22 16:22:02 -050016import functools
Mike Frysingera4fa1e82014-01-15 01:45:56 -050017import httplib
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040018import multiprocessing
19import os
Mike Frysinger094a2172013-08-14 12:54:35 -040020import poster
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040021import random
Mike Frysingerfd355652014-01-23 02:57:48 -050022import socket
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040023import textwrap
24import tempfile
25import time
Mike Frysinger094a2172013-08-14 12:54:35 -040026import urllib2
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040027
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040028from chromite.lib import commandline
29from chromite.lib import cros_build_lib
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040030from chromite.lib import parallel
David Jamesc93e6a4d2014-01-13 11:37:36 -080031from chromite.lib import retry_util
Mike Frysinger69cb41d2013-08-11 20:08:19 -040032from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040033
34
35# URLs used for uploading symbols.
36OFFICIAL_UPLOAD_URL = 'http://clients2.google.com/cr/symbol'
37STAGING_UPLOAD_URL = 'http://clients2.google.com/cr/staging_symbol'
38
39
40# The crash server rejects files that are this big.
41CRASH_SERVER_FILE_LIMIT = 350 * 1024 * 1024
42# Give ourselves a little breathing room from what the server expects.
43DEFAULT_FILE_LIMIT = CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024)
44
45
Mike Frysingercd78a082013-06-26 17:13:04 -040046# How long to wait (in seconds) for a single upload to complete. This has
47# to allow for symbols that are up to CRASH_SERVER_FILE_LIMIT in size.
48UPLOAD_TIMEOUT = 30 * 60
49
50
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040051# Sleep for 200ms in between uploads to avoid DoS'ing symbol server.
52DEFAULT_SLEEP_DELAY = 0.2
53
54
55# Number of seconds to wait before retrying an upload. The delay will double
56# for each subsequent retry of the same symbol file.
57INITIAL_RETRY_DELAY = 1
58
59# Allow up to 7 attempts to upload a symbol file (total delay may be
60# 1+2+4+8+16+32=63 seconds).
61MAX_RETRIES = 6
62
Mike Frysingereb753bf2013-11-22 16:05:35 -050063# Number of total errors, before uploads are no longer attempted.
64# This is used to avoid lots of errors causing unreasonable delays.
65# See the related, but independent, error values below.
66MAX_TOTAL_ERRORS_FOR_RETRY = 30
67
68# A watermark of transient errors which we allow recovery from. If we hit
69# errors infrequently, overall we're probably doing fine. For example, if
70# we have one failure every 100 passes, then we probably don't want to fail
71# right away. But if we hit a string of failures in a row, we want to abort.
72#
73# The watermark starts at 0 (and can never go below that). When this error
74# level is exceeded, we stop uploading. When a failure happens, we add the
75# fail adjustment, and when an upload succeeds, we add the pass adjustment.
76# We want to penalize failures more so that we ramp up when there is a string
77# of them, but then slowly back off as things start working.
78#
79# A quick example:
80# 0.0: Starting point.
81# 0.0: Upload works, so add -0.5, and then clamp to 0.
82# 1.0: Upload fails, so add 1.0.
83# 2.0: Upload fails, so add 1.0.
84# 1.5: Upload works, so add -0.5.
85# 1.0: Upload works, so add -0.5.
86ERROR_WATERMARK = 3.0
87ERROR_ADJUST_FAIL = 1.0
88ERROR_ADJUST_PASS = -0.5
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040089
90
91def SymUpload(sym_file, upload_url):
Mike Frysinger094a2172013-08-14 12:54:35 -040092 """Upload a symbol file to a HTTP server
93
94 The upload is a multipart/form-data POST with the following parameters:
95 code_file: the basename of the module, e.g. "app"
96 code_identifier: the module file's identifier
97 debug_file: the basename of the debugging file, e.g. "app"
98 debug_identifier: the debug file's identifier, usually consisting of
99 the guid and age embedded in the pdb, e.g.
100 "11111111BBBB3333DDDD555555555555F"
101 version: the file version of the module, e.g. "1.2.3.4"
102 product: HTTP-friendly product name
103 os: the operating system that the module was built for
104 cpu: the CPU that the module was built for
105 symbol_file: the contents of the breakpad-format symbol file
106
107 Args:
108 sym_file: The symbol file to upload
109 upload_url: The crash URL to POST the |sym_file| to
110 """
111 sym_header = cros_generate_breakpad_symbols.ReadSymsHeader(sym_file)
112
113 fields = (
114 ('code_file', sym_header.name),
115 ('debug_file', sym_header.name),
116 ('debug_identifier', sym_header.id.replace('-', '')),
117 # Should we set these fields? They aren't critical, but it might be nice?
118 # We'd have to figure out what file this symbol is coming from and what
119 # package provides it ...
120 #('version', None),
121 #('product', 'ChromeOS'),
122 ('os', sym_header.os),
123 ('cpu', sym_header.cpu),
124 poster.encode.MultipartParam.from_file('symbol_file', sym_file),
125 )
126
127 data, headers = poster.encode.multipart_encode(fields)
128 request = urllib2.Request(upload_url, data, headers)
129 request.add_header('User-agent', 'chromite.upload_symbols')
130 urllib2.urlopen(request, timeout=UPLOAD_TIMEOUT)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400131
132
133def TestingSymUpload(sym_file, upload_url):
134 """A stub version of SymUpload for --testing usage"""
135 cmd = ['sym_upload', sym_file, upload_url]
136 # Randomly fail 80% of the time (the retry logic makes this 80%/3 per file).
137 returncode = random.randint(1, 100) <= 80
138 cros_build_lib.Debug('would run (and return %i): %s', returncode,
Matt Tennant7feda352013-12-20 14:03:40 -0800139 cros_build_lib.CmdToStr(cmd))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400140 if returncode:
141 output = 'Failed to send the symbol file.'
142 else:
143 output = 'Successfully sent the symbol file.'
144 result = cros_build_lib.CommandResult(cmd=cmd, error=None, output=output,
145 returncode=returncode)
146 if returncode:
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500147 exceptions = (
Mike Frysingerfd355652014-01-23 02:57:48 -0500148 socket.error('[socket.error] forced test fail'),
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500149 httplib.BadStatusLine('[BadStatusLine] forced test fail'),
150 urllib2.HTTPError(upload_url, 400, '[HTTPError] forced test fail',
151 {}, None),
152 urllib2.URLError('[URLError] forced test fail'),
153 )
154 raise random.choice(exceptions)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400155 else:
156 return result
157
158
Mike Frysingereb753bf2013-11-22 16:05:35 -0500159def ErrorLimitHit(num_errors, watermark_errors):
160 """See if our error limit has been hit
161
162 Args:
163 num_errors: A multiprocessing.Value of the raw number of failures.
164 watermark_errors: A multiprocessing.Value of the current rate of failures.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500165
Mike Frysingereb753bf2013-11-22 16:05:35 -0500166 Returns:
167 True if our error limits have been exceeded.
168 """
169 return ((num_errors is not None and
170 num_errors.value > MAX_TOTAL_ERRORS_FOR_RETRY) or
171 (watermark_errors is not None and
172 watermark_errors.value > ERROR_WATERMARK))
173
174
175def _UpdateCounter(counter, adj):
176 """Update |counter| by |adj|
177
178 Handle atomic updates of |counter|. Also make sure it does not
179 fall below 0.
180
181 Args:
182 counter: A multiprocessing.Value to update
183 adj: The value to add to |counter|
184 """
185 def _Update():
186 clamp = 0 if type(adj) is int else 0.0
187 counter.value = max(clamp, counter.value + adj)
188
189 if hasattr(counter, 'get_lock'):
190 with counter.get_lock():
191 _Update()
192 elif counter is not None:
193 _Update()
194
195
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400196def UploadSymbol(sym_file, upload_url, file_limit=DEFAULT_FILE_LIMIT,
Mike Frysinger02e92402013-11-22 16:22:02 -0500197 sleep=0, num_errors=None, watermark_errors=None,
198 failed_queue=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400199 """Upload |sym_file| to |upload_url|
200
201 Args:
202 sym_file: The full path to the breakpad symbol to upload
203 upload_url: The crash server to upload things to
204 file_limit: The max file size of a symbol file before we try to strip it
205 sleep: Number of seconds to sleep before running
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400206 num_errors: An object to update with the error count (needs a .value member)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500207 watermark_errors: An object to track current error behavior (needs a .value)
Mike Frysinger02e92402013-11-22 16:22:02 -0500208 failed_queue: When a symbol fails, add it to this queue
Mike Frysinger1a736a82013-12-12 01:50:59 -0500209
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400210 Returns:
211 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400212 """
213 if num_errors is None:
214 num_errors = ctypes.c_int()
Mike Frysingereb753bf2013-11-22 16:05:35 -0500215 if ErrorLimitHit(num_errors, watermark_errors):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400216 # Abandon ship! It's on fire! NOoooooooooooOOOoooooo.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500217 if failed_queue:
218 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400219 return 0
220
221 upload_file = sym_file
222
223 if sleep:
224 # Keeps us from DoS-ing the symbol server.
225 time.sleep(sleep)
226
227 cros_build_lib.Debug('uploading %s' % sym_file)
228
229 # Ideally there'd be a tempfile.SpooledNamedTemporaryFile that we could use.
230 with tempfile.NamedTemporaryFile(prefix='upload_symbols',
231 bufsize=0) as temp_sym_file:
232 if file_limit:
233 # If the symbols size is too big, strip out the call frame info. The CFI
234 # is unnecessary for 32bit x86 targets where the frame pointer is used (as
235 # all of ours have) and it accounts for over half the size of the symbols
236 # uploaded.
237 file_size = os.path.getsize(sym_file)
238 if file_size > file_limit:
239 cros_build_lib.Warning('stripping CFI from %s due to size %s > %s',
240 sym_file, file_size, file_limit)
241 temp_sym_file.writelines([x for x in open(sym_file, 'rb').readlines()
242 if not x.startswith('STACK CFI')])
243 upload_file = temp_sym_file.name
244
245 # Hopefully the crash server will let it through. But it probably won't.
246 # Not sure what the best answer is in this case.
247 file_size = os.path.getsize(upload_file)
248 if file_size > CRASH_SERVER_FILE_LIMIT:
249 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger02e92402013-11-22 16:22:02 -0500250 cros_build_lib.Warning('upload file %s is awfully large, risking '
251 'rejection by the symbol server (%s > %s)',
252 sym_file, file_size, CRASH_SERVER_FILE_LIMIT)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400253
254 # Upload the symbol file.
Mike Frysingereb753bf2013-11-22 16:05:35 -0500255 success = False
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400256 try:
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400257 cros_build_lib.TimedCommand(
David Jamesc93e6a4d2014-01-13 11:37:36 -0800258 retry_util.RetryException,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400259 (urllib2.HTTPError, urllib2.URLError), MAX_RETRIES, SymUpload,
260 upload_file, upload_url, sleep=INITIAL_RETRY_DELAY,
261 timed_log_msg='upload of %10i bytes took %%s: %s' %
262 (file_size, os.path.basename(sym_file)))
Mike Frysingereb753bf2013-11-22 16:05:35 -0500263 success = True
Mike Frysinger094a2172013-08-14 12:54:35 -0400264 except urllib2.HTTPError as e:
265 cros_build_lib.Warning('could not upload: %s: HTTP %s: %s',
266 os.path.basename(sym_file), e.code, e.reason)
Mike Frysingerfd355652014-01-23 02:57:48 -0500267 except (urllib2.URLError, httplib.HTTPException, socket.error) as e:
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400268 cros_build_lib.Warning('could not upload: %s: %s',
269 os.path.basename(sym_file), e)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500270 finally:
271 if success:
272 _UpdateCounter(watermark_errors, ERROR_ADJUST_PASS)
273 else:
274 _UpdateCounter(num_errors, 1)
275 _UpdateCounter(watermark_errors, ERROR_ADJUST_FAIL)
Mike Frysinger02e92402013-11-22 16:22:02 -0500276 if failed_queue:
277 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400278
279 return num_errors.value
280
281
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500282def SymbolFinder(paths):
283 """Locate symbol files in |paths|
284
285 Args:
286 paths: A list of input paths to walk. Files are returned w/out any checks.
287 Dirs are searched for files that end in ".sym".
Mike Frysinger1a736a82013-12-12 01:50:59 -0500288
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500289 Returns:
290 Yield every viable sym file.
291 """
292 for p in paths:
293 if os.path.isdir(p):
294 for root, _, files in os.walk(p):
295 for f in files:
296 if f.endswith('.sym'):
297 yield os.path.join(root, f)
298 else:
299 yield p
300
301
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400302def UploadSymbols(board=None, official=False, breakpad_dir=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400303 file_limit=DEFAULT_FILE_LIMIT, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500304 upload_limit=None, sym_paths=None, failed_list=None,
Mike Frysinger7f9be142014-01-15 02:16:42 -0500305 root=None, retry=True):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400306 """Upload all the generated symbols for |board| to the crash server
307
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400308 You can use in a few ways:
309 * pass |board| to locate all of its symbols
310 * pass |breakpad_dir| to upload all the symbols in there
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500311 * pass |sym_paths| to upload specific symbols (or dirs of symbols)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400312
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400313 Args:
314 board: The board whose symbols we wish to upload
315 official: Use the official symbol server rather than the staging one
316 breakpad_dir: The full path to the breakpad directory where symbols live
317 file_limit: The max file size of a symbol file before we try to strip it
318 sleep: How long to sleep in between uploads
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500319 upload_limit: If set, only upload this many symbols (meant for testing)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500320 sym_paths: Specific symbol files (or dirs of sym files) to upload,
321 otherwise search |breakpad_dir|
Mike Frysinger7f9be142014-01-15 02:16:42 -0500322 failed_list: Write the names of all sym files we did not upload; can be a
323 filename or file-like object.
Mike Frysinger118d2502013-08-19 03:36:56 -0400324 root: The tree to prefix to |breakpad_dir| (if |breakpad_dir| is not set)
Mike Frysinger02e92402013-11-22 16:22:02 -0500325 retry: Whether we should retry failures.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500326
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400327 Returns:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400328 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400329 """
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400330 if official:
331 upload_url = OFFICIAL_UPLOAD_URL
332 else:
333 cros_build_lib.Warning('unofficial builds upload to the staging server')
334 upload_url = STAGING_UPLOAD_URL
335
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500336 if sym_paths:
337 cros_build_lib.Info('uploading specified symbols to %s', upload_url)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400338 else:
339 if breakpad_dir is None:
Mike Frysinger118d2502013-08-19 03:36:56 -0400340 breakpad_dir = os.path.join(
341 root,
342 cros_generate_breakpad_symbols.FindBreakpadDir(board).lstrip('/'))
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400343 cros_build_lib.Info('uploading all symbols to %s from %s', upload_url,
344 breakpad_dir)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500345 sym_paths = [breakpad_dir]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400346
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400347 bg_errors = multiprocessing.Value('i')
Mike Frysingereb753bf2013-11-22 16:05:35 -0500348 watermark_errors = multiprocessing.Value('f')
Mike Frysinger02e92402013-11-22 16:22:02 -0500349 failed_queue = multiprocessing.Queue()
350 uploader = functools.partial(
351 UploadSymbol, file_limit=file_limit, sleep=sleep, num_errors=bg_errors,
352 watermark_errors=watermark_errors, failed_queue=failed_queue)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400353
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500354 start_time = datetime.datetime.now()
355 Counters = cros_build_lib.Collection(
356 'Counters', upload_limit=upload_limit, uploaded_count=0)
357 counters = Counters()
358
Mike Frysinger02e92402013-11-22 16:22:02 -0500359 # For the first run, we collect the symbols that failed. If the
360 # overall failure rate was low, we'll retry them on the second run.
361 for retry in (retry, False):
362 # We need to limit ourselves to one upload at a time to avoid the server
363 # kicking in DoS protection. See these bugs for more details:
364 # http://crbug.com/209442
365 # http://crbug.com/212496
366 with parallel.BackgroundTaskRunner(uploader, processes=1) as queue:
367 for sym_file in SymbolFinder(sym_paths):
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500368 if counters.upload_limit == 0:
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500369 break
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400370
Mike Frysinger02e92402013-11-22 16:22:02 -0500371 queue.put([sym_file, upload_url])
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500372 counters.uploaded_count += 1
Mike Frysinger02e92402013-11-22 16:22:02 -0500373
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500374 if counters.upload_limit is not None:
375 counters.upload_limit -= 1
Mike Frysinger02e92402013-11-22 16:22:02 -0500376
377 # See if we need to retry, and if we haven't failed too many times already.
378 if not retry or ErrorLimitHit(bg_errors, watermark_errors):
379 break
380
381 sym_paths = []
382 while not failed_queue.empty():
383 sym_paths.append(failed_queue.get())
384 if sym_paths:
385 cros_build_lib.Warning('retrying %i symbols', len(sym_paths))
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500386 if counters.upload_limit is not None:
387 counters.upload_limit += len(sym_paths)
Mike Frysinger02e92402013-11-22 16:22:02 -0500388 # Decrement the error count in case we recover in the second pass.
389 assert bg_errors.value >= len(sym_paths), 'more failed files than errors?'
390 bg_errors.value -= len(sym_paths)
391 else:
392 # No failed symbols, so just return now.
393 break
394
Mike Frysinger7f9be142014-01-15 02:16:42 -0500395 # If the user has requested it, save all the symbol files that we failed to
396 # upload to a listing file. This should help with recovery efforts later on.
397 if failed_list:
398 with cros_build_lib.Open(failed_list, 'wb+') as f:
399 while not failed_queue.empty():
400 path = failed_queue.get()
401 if breakpad_dir:
402 path = os.path.relpath(path, breakpad_dir)
403 f.write('%s\n' % path)
404
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500405 cros_build_lib.Info('uploaded %i symbols which took: %s',
406 counters.uploaded_count,
407 datetime.datetime.now() - start_time)
408
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500409 return bg_errors.value
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400410
411
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400412def main(argv):
413 parser = commandline.ArgumentParser(description=__doc__)
414
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500415 parser.add_argument('sym_paths', type='path', nargs='*', default=None)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400416 parser.add_argument('--board', default=None,
417 help='board to build packages for')
418 parser.add_argument('--breakpad_root', type='path', default=None,
419 help='root directory for breakpad symbols')
420 parser.add_argument('--official_build', action='store_true', default=False,
421 help='point to official symbol server')
422 parser.add_argument('--regenerate', action='store_true', default=False,
423 help='regenerate all symbols')
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500424 parser.add_argument('--upload-limit', type=int, default=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400425 help='only upload # number of symbols')
426 parser.add_argument('--strip_cfi', type=int,
427 default=CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024),
428 help='strip CFI data for files above this size')
Mike Frysinger7f9be142014-01-15 02:16:42 -0500429 parser.add_argument('--failed-list', type='path',
430 help='where to save a list of failed symbols')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400431 parser.add_argument('--testing', action='store_true', default=False,
432 help='run in testing mode')
433 parser.add_argument('--yes', action='store_true', default=False,
434 help='answer yes to all prompts')
435
436 opts = parser.parse_args(argv)
Mike Frysinger90e49ca2014-01-14 14:42:07 -0500437 opts.Freeze()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400438
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500439 if opts.sym_paths:
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400440 if opts.regenerate:
441 cros_build_lib.Die('--regenerate may not be used with specific files')
442 else:
443 if opts.board is None:
444 cros_build_lib.Die('--board is required')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400445
446 if opts.breakpad_root and opts.regenerate:
447 cros_build_lib.Die('--regenerate may not be used with --breakpad_root')
448
449 if opts.testing:
450 # TODO(build): Kill off --testing mode once unittests are up-to-snuff.
451 cros_build_lib.Info('running in testing mode')
452 # pylint: disable=W0601,W0603
453 global INITIAL_RETRY_DELAY, SymUpload, DEFAULT_SLEEP_DELAY
454 INITIAL_RETRY_DELAY = DEFAULT_SLEEP_DELAY = 0
455 SymUpload = TestingSymUpload
456
457 if not opts.yes:
Mike Frysingerc5de9602014-02-09 02:42:36 -0500458 prolog = '\n'.join(textwrap.wrap(textwrap.dedent("""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400459 Uploading symbols for an entire Chromium OS build is really only
460 necessary for release builds and in a few cases for developers
461 to debug problems. It will take considerable time to run. For
462 developer debugging purposes, consider instead passing specific
463 files to upload.
Mike Frysingerc5de9602014-02-09 02:42:36 -0500464 """), 80)).strip()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400465 if not cros_build_lib.BooleanPrompt(
466 prompt='Are you sure you want to upload all build symbols',
Mike Frysingerc5de9602014-02-09 02:42:36 -0500467 default=False, prolog=prolog):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400468 cros_build_lib.Die('better safe than sorry')
469
470 ret = 0
471 if opts.regenerate:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400472 ret += cros_generate_breakpad_symbols.GenerateBreakpadSymbols(
473 opts.board, breakpad_dir=opts.breakpad_root)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400474
475 ret += UploadSymbols(opts.board, official=opts.official_build,
476 breakpad_dir=opts.breakpad_root,
477 file_limit=opts.strip_cfi, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500478 upload_limit=opts.upload_limit, sym_paths=opts.sym_paths,
Mike Frysinger7f9be142014-01-15 02:16:42 -0500479 failed_list=opts.failed_list)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400480 if ret:
481 cros_build_lib.Error('encountered %i problem(s)', ret)
482 # Since exit(status) gets masked, clamp it to 1 so we don't inadvertently
483 # return 0 in case we are a multiple of the mask.
484 ret = 1
485
486 return ret
Mike Frysinger094a2172013-08-14 12:54:35 -0400487
488
489# We need this to run once per process. Do it at module import time as that
490# will let us avoid doing it inline at function call time (see SymUpload) as
491# that func might be called by the multiprocessing module which means we'll
492# do the opener logic multiple times overall. Plus, if you're importing this
493# module, it's a pretty good chance that you're going to need this.
494poster.streaminghttp.register_openers()