blob: eb1251c48f456543f276f0fd9276623783bb12db [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 Frysingerdad2a032014-05-11 23:05:11 -040016import errno
Mike Frysinger02e92402013-11-22 16:22:02 -050017import functools
Mike Frysinger0c0efa22014-02-09 23:32:23 -050018import hashlib
Mike Frysingera4fa1e82014-01-15 01:45:56 -050019import httplib
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040020import multiprocessing
21import os
Mike Frysinger094a2172013-08-14 12:54:35 -040022import poster
Mike Frysinger66e51e92014-05-03 16:52:00 -040023try:
24 import Queue
25except ImportError:
26 # Python-3 renamed to "queue". We still use Queue to avoid collisions
27 # with naming variables as "queue". Maybe we'll transition at some point.
28 # pylint: disable=F0401
29 import queue as Queue
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040030import random
Mike Frysinger66e51e92014-05-03 16:52:00 -040031import signal
Mike Frysingerfd355652014-01-23 02:57:48 -050032import socket
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040033import textwrap
34import tempfile
35import time
Mike Frysinger094a2172013-08-14 12:54:35 -040036import urllib2
Mike Frysingerd41938e2014-02-10 06:37:55 -050037import urlparse
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040038
Don Garrett88b8d782014-05-13 17:30:55 -070039from chromite.cbuildbot import constants
Mike Frysingerd41938e2014-02-10 06:37:55 -050040from chromite.lib import cache
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040041from chromite.lib import commandline
42from chromite.lib import cros_build_lib
Mike Frysingerd41938e2014-02-10 06:37:55 -050043from chromite.lib import gs
44from chromite.lib import osutils
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040045from chromite.lib import parallel
David Jamesc93e6a4d2014-01-13 11:37:36 -080046from chromite.lib import retry_util
Mike Frysinger66e51e92014-05-03 16:52:00 -040047from chromite.lib import signals
Mike Frysinger0c0efa22014-02-09 23:32:23 -050048from chromite.lib import timeout_util
Mike Frysinger69cb41d2013-08-11 20:08:19 -040049from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040050
Mike Frysinger0c0efa22014-02-09 23:32:23 -050051# Needs to be after chromite imports.
52# TODO(build): When doing the initial buildbot bootstrap, we won't have any
53# other repos available. So ignore isolateserver imports. But buildbot will
54# re-exec itself once it has done a full repo sync and then the module will
55# be available -- it isn't needed that early. http://crbug.com/341152
56try:
57 import isolateserver
58except ImportError:
59 isolateserver = None
60
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040061
62# URLs used for uploading symbols.
63OFFICIAL_UPLOAD_URL = 'http://clients2.google.com/cr/symbol'
64STAGING_UPLOAD_URL = 'http://clients2.google.com/cr/staging_symbol'
65
66
67# The crash server rejects files that are this big.
68CRASH_SERVER_FILE_LIMIT = 350 * 1024 * 1024
69# Give ourselves a little breathing room from what the server expects.
70DEFAULT_FILE_LIMIT = CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024)
71
72
Mike Frysinger0c0efa22014-02-09 23:32:23 -050073# The batch limit when talking to the dedup server. We avoid sending one at a
74# time as the round trip overhead will dominate. Conversely, we avoid sending
75# all at once so we can start uploading symbols asap -- the symbol server is a
76# bit slow and will take longer than anything else.
77# TODO: A better algorithm would be adaptive. If we have more than one symbol
78# in the upload queue waiting, we could send more symbols to the dedupe server
79# at a time.
80DEDUPE_LIMIT = 100
81
82# How long to wait for the server to respond with the results. Note that the
83# larger the limit above, the larger this will need to be. So we give it ~1
84# second per item max.
85DEDUPE_TIMEOUT = DEDUPE_LIMIT
86
Mike Frysinger4dd462e2014-04-30 16:21:51 -040087# How long to wait for the notification to finish (in minutes). If it takes
88# longer than this, we'll stop notifiying, but that's not a big deal as we
89# will be able to recover in later runs.
90DEDUPE_NOTIFY_TIMEOUT = 20
91
Mike Frysinger0c0efa22014-02-09 23:32:23 -050092# The unique namespace in the dedupe server that only we use. Helps avoid
93# collisions with all the hashed values and unrelated content.
94OFFICIAL_DEDUPE_NAMESPACE = 'chromium-os-upload-symbols'
95STAGING_DEDUPE_NAMESPACE = '%s-staging' % OFFICIAL_DEDUPE_NAMESPACE
96
97
Mike Frysinger71046662014-09-12 18:15:15 -070098# The minimum average rate (in bytes per second) that we expect to maintain
99# when uploading symbols. This has to allow for symbols that are up to
100# CRASH_SERVER_FILE_LIMIT in size.
101UPLOAD_MIN_RATE = CRASH_SERVER_FILE_LIMIT / (30 * 60)
102
103# The lowest timeout (in seconds) we'll allow. If the server is overloaded,
104# then there might be a delay in setting up the connection, not just with the
105# transfer. So even a small file might need a larger value.
106UPLOAD_MIN_TIMEOUT = 2 * 60
Mike Frysingercd78a082013-06-26 17:13:04 -0400107
108
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400109# Sleep for 200ms in between uploads to avoid DoS'ing symbol server.
110DEFAULT_SLEEP_DELAY = 0.2
111
112
113# Number of seconds to wait before retrying an upload. The delay will double
114# for each subsequent retry of the same symbol file.
115INITIAL_RETRY_DELAY = 1
116
117# Allow up to 7 attempts to upload a symbol file (total delay may be
118# 1+2+4+8+16+32=63 seconds).
119MAX_RETRIES = 6
120
Mike Frysingereb753bf2013-11-22 16:05:35 -0500121# Number of total errors, before uploads are no longer attempted.
122# This is used to avoid lots of errors causing unreasonable delays.
123# See the related, but independent, error values below.
124MAX_TOTAL_ERRORS_FOR_RETRY = 30
125
126# A watermark of transient errors which we allow recovery from. If we hit
127# errors infrequently, overall we're probably doing fine. For example, if
128# we have one failure every 100 passes, then we probably don't want to fail
129# right away. But if we hit a string of failures in a row, we want to abort.
130#
131# The watermark starts at 0 (and can never go below that). When this error
132# level is exceeded, we stop uploading. When a failure happens, we add the
133# fail adjustment, and when an upload succeeds, we add the pass adjustment.
134# We want to penalize failures more so that we ramp up when there is a string
135# of them, but then slowly back off as things start working.
136#
137# A quick example:
138# 0.0: Starting point.
139# 0.0: Upload works, so add -0.5, and then clamp to 0.
140# 1.0: Upload fails, so add 1.0.
141# 2.0: Upload fails, so add 1.0.
142# 1.5: Upload works, so add -0.5.
143# 1.0: Upload works, so add -0.5.
144ERROR_WATERMARK = 3.0
145ERROR_ADJUST_FAIL = 1.0
146ERROR_ADJUST_PASS = -0.5
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400147
148
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500149def SymUpload(upload_url, sym_item):
Mike Frysinger094a2172013-08-14 12:54:35 -0400150 """Upload a symbol file to a HTTP server
151
152 The upload is a multipart/form-data POST with the following parameters:
153 code_file: the basename of the module, e.g. "app"
154 code_identifier: the module file's identifier
155 debug_file: the basename of the debugging file, e.g. "app"
156 debug_identifier: the debug file's identifier, usually consisting of
157 the guid and age embedded in the pdb, e.g.
158 "11111111BBBB3333DDDD555555555555F"
159 version: the file version of the module, e.g. "1.2.3.4"
160 product: HTTP-friendly product name
161 os: the operating system that the module was built for
162 cpu: the CPU that the module was built for
163 symbol_file: the contents of the breakpad-format symbol file
164
165 Args:
Mike Frysinger094a2172013-08-14 12:54:35 -0400166 upload_url: The crash URL to POST the |sym_file| to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500167 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysinger094a2172013-08-14 12:54:35 -0400168 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500169 sym_header = sym_item.sym_header
170 sym_file = sym_item.sym_file
Mike Frysinger094a2172013-08-14 12:54:35 -0400171
172 fields = (
173 ('code_file', sym_header.name),
174 ('debug_file', sym_header.name),
175 ('debug_identifier', sym_header.id.replace('-', '')),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400176 # The product/version fields are used by the server only for statistic
177 # purposes. They do not impact symbolization, so they're safe to set
178 # to any value all the time.
179 # In this case, we use it to help see the load our build system is
180 # placing on the server.
181 # Not sure what to set for the version. Maybe the git sha1 of this file.
182 # Note: the server restricts this to 30 chars.
Mike Frysinger094a2172013-08-14 12:54:35 -0400183 #('version', None),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400184 ('product', 'ChromeOS'),
Mike Frysinger094a2172013-08-14 12:54:35 -0400185 ('os', sym_header.os),
186 ('cpu', sym_header.cpu),
187 poster.encode.MultipartParam.from_file('symbol_file', sym_file),
188 )
189
Mike Frysinger71046662014-09-12 18:15:15 -0700190 # Scale the timeout based on the filesize.
191 timeout = max(os.path.getsize(sym_file) / UPLOAD_MIN_RATE, UPLOAD_MIN_TIMEOUT)
192
Mike Frysinger094a2172013-08-14 12:54:35 -0400193 data, headers = poster.encode.multipart_encode(fields)
194 request = urllib2.Request(upload_url, data, headers)
195 request.add_header('User-agent', 'chromite.upload_symbols')
Mike Frysinger71046662014-09-12 18:15:15 -0700196 urllib2.urlopen(request, timeout=timeout)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400197
198
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500199def TestingSymUpload(upload_url, sym_item):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400200 """A stub version of SymUpload for --testing usage"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500201 cmd = ['sym_upload', sym_item.sym_file, upload_url]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400202 # Randomly fail 80% of the time (the retry logic makes this 80%/3 per file).
203 returncode = random.randint(1, 100) <= 80
204 cros_build_lib.Debug('would run (and return %i): %s', returncode,
Matt Tennant7feda352013-12-20 14:03:40 -0800205 cros_build_lib.CmdToStr(cmd))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400206 if returncode:
207 output = 'Failed to send the symbol file.'
208 else:
209 output = 'Successfully sent the symbol file.'
210 result = cros_build_lib.CommandResult(cmd=cmd, error=None, output=output,
211 returncode=returncode)
212 if returncode:
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500213 exceptions = (
Mike Frysingerfd355652014-01-23 02:57:48 -0500214 socket.error('[socket.error] forced test fail'),
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500215 httplib.BadStatusLine('[BadStatusLine] forced test fail'),
216 urllib2.HTTPError(upload_url, 400, '[HTTPError] forced test fail',
217 {}, None),
218 urllib2.URLError('[URLError] forced test fail'),
219 )
220 raise random.choice(exceptions)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400221 else:
222 return result
223
224
Mike Frysingereb753bf2013-11-22 16:05:35 -0500225def ErrorLimitHit(num_errors, watermark_errors):
226 """See if our error limit has been hit
227
228 Args:
229 num_errors: A multiprocessing.Value of the raw number of failures.
230 watermark_errors: A multiprocessing.Value of the current rate of failures.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500231
Mike Frysingereb753bf2013-11-22 16:05:35 -0500232 Returns:
233 True if our error limits have been exceeded.
234 """
235 return ((num_errors is not None and
236 num_errors.value > MAX_TOTAL_ERRORS_FOR_RETRY) or
237 (watermark_errors is not None and
238 watermark_errors.value > ERROR_WATERMARK))
239
240
241def _UpdateCounter(counter, adj):
242 """Update |counter| by |adj|
243
244 Handle atomic updates of |counter|. Also make sure it does not
245 fall below 0.
246
247 Args:
248 counter: A multiprocessing.Value to update
249 adj: The value to add to |counter|
250 """
251 def _Update():
252 clamp = 0 if type(adj) is int else 0.0
253 counter.value = max(clamp, counter.value + adj)
254
255 if hasattr(counter, 'get_lock'):
256 with counter.get_lock():
257 _Update()
258 elif counter is not None:
259 _Update()
260
261
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500262def UploadSymbol(upload_url, sym_item, file_limit=DEFAULT_FILE_LIMIT,
Mike Frysinger02e92402013-11-22 16:22:02 -0500263 sleep=0, num_errors=None, watermark_errors=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500264 failed_queue=None, passed_queue=None):
265 """Upload |sym_item| to |upload_url|
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400266
267 Args:
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400268 upload_url: The crash server to upload things to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500269 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400270 file_limit: The max file size of a symbol file before we try to strip it
271 sleep: Number of seconds to sleep before running
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400272 num_errors: An object to update with the error count (needs a .value member)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500273 watermark_errors: An object to track current error behavior (needs a .value)
Mike Frysinger02e92402013-11-22 16:22:02 -0500274 failed_queue: When a symbol fails, add it to this queue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500275 passed_queue: When a symbol passes, add it to this queue
Mike Frysinger1a736a82013-12-12 01:50:59 -0500276
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400277 Returns:
278 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400279 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500280 sym_file = sym_item.sym_file
281 upload_item = sym_item
282
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400283 if num_errors is None:
284 num_errors = ctypes.c_int()
Mike Frysingereb753bf2013-11-22 16:05:35 -0500285 if ErrorLimitHit(num_errors, watermark_errors):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400286 # Abandon ship! It's on fire! NOoooooooooooOOOoooooo.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500287 if failed_queue:
288 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400289 return 0
290
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400291 if sleep:
292 # Keeps us from DoS-ing the symbol server.
293 time.sleep(sleep)
294
295 cros_build_lib.Debug('uploading %s' % sym_file)
296
297 # Ideally there'd be a tempfile.SpooledNamedTemporaryFile that we could use.
298 with tempfile.NamedTemporaryFile(prefix='upload_symbols',
299 bufsize=0) as temp_sym_file:
300 if file_limit:
301 # If the symbols size is too big, strip out the call frame info. The CFI
302 # is unnecessary for 32bit x86 targets where the frame pointer is used (as
303 # all of ours have) and it accounts for over half the size of the symbols
304 # uploaded.
305 file_size = os.path.getsize(sym_file)
306 if file_size > file_limit:
307 cros_build_lib.Warning('stripping CFI from %s due to size %s > %s',
308 sym_file, file_size, file_limit)
309 temp_sym_file.writelines([x for x in open(sym_file, 'rb').readlines()
310 if not x.startswith('STACK CFI')])
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500311
312 upload_item = FakeItem(sym_file=temp_sym_file.name,
313 sym_header=sym_item.sym_header)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400314
315 # Hopefully the crash server will let it through. But it probably won't.
316 # Not sure what the best answer is in this case.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500317 file_size = os.path.getsize(upload_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400318 if file_size > CRASH_SERVER_FILE_LIMIT:
319 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger02e92402013-11-22 16:22:02 -0500320 cros_build_lib.Warning('upload file %s is awfully large, risking '
321 'rejection by the symbol server (%s > %s)',
322 sym_file, file_size, CRASH_SERVER_FILE_LIMIT)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400323
324 # Upload the symbol file.
Mike Frysingereb753bf2013-11-22 16:05:35 -0500325 success = False
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400326 try:
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400327 cros_build_lib.TimedCommand(
David Jamesc93e6a4d2014-01-13 11:37:36 -0800328 retry_util.RetryException,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400329 (urllib2.HTTPError, urllib2.URLError), MAX_RETRIES, SymUpload,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500330 upload_url, upload_item, sleep=INITIAL_RETRY_DELAY,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400331 timed_log_msg='upload of %10i bytes took %%s: %s' %
332 (file_size, os.path.basename(sym_file)))
Mike Frysingereb753bf2013-11-22 16:05:35 -0500333 success = True
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500334
335 if passed_queue:
336 passed_queue.put(sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400337 except urllib2.HTTPError as e:
338 cros_build_lib.Warning('could not upload: %s: HTTP %s: %s',
339 os.path.basename(sym_file), e.code, e.reason)
Mike Frysingerfd355652014-01-23 02:57:48 -0500340 except (urllib2.URLError, httplib.HTTPException, socket.error) as e:
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400341 cros_build_lib.Warning('could not upload: %s: %s',
342 os.path.basename(sym_file), e)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500343 finally:
344 if success:
345 _UpdateCounter(watermark_errors, ERROR_ADJUST_PASS)
346 else:
347 _UpdateCounter(num_errors, 1)
348 _UpdateCounter(watermark_errors, ERROR_ADJUST_FAIL)
Mike Frysinger02e92402013-11-22 16:22:02 -0500349 if failed_queue:
350 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400351
352 return num_errors.value
353
354
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500355# A dummy class that allows for stubbing in tests and SymUpload.
356FakeItem = cros_build_lib.Collection(
357 'FakeItem', sym_file=None, sym_header=None, content=lambda x: '')
358
359
360# TODO(build): Delete this if check. http://crbug.com/341152
361if isolateserver:
362 class SymbolItem(isolateserver.BufferItem):
363 """Turn a sym_file into an isolateserver.Item"""
364
365 ALGO = hashlib.sha1
366
367 def __init__(self, sym_file):
368 sym_header = cros_generate_breakpad_symbols.ReadSymsHeader(sym_file)
369 super(SymbolItem, self).__init__(str(sym_header), self.ALGO)
370 self.sym_header = sym_header
371 self.sym_file = sym_file
372
373
374def SymbolDeduplicatorNotify(dedupe_namespace, dedupe_queue):
375 """Send a symbol file to the swarming service
376
377 Notify the swarming service of a successful upload. If the notification fails
378 for any reason, we ignore it. We don't care as it just means we'll upload it
379 again later on, and the symbol server will handle that graciously.
380
381 This func runs in a different process from the main one, so we cannot share
382 the storage object. Instead, we create our own. This func stays alive for
383 the life of the process, so we only create one here overall.
384
385 Args:
386 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
387 dedupe_queue: The queue to read SymbolItems from
388 """
389 if dedupe_queue is None:
390 return
391
392 item = None
393 try:
Mike Frysinger650e6722014-04-28 18:29:15 -0400394 with timeout_util.Timeout(DEDUPE_TIMEOUT):
395 storage = isolateserver.get_storage_api(constants.ISOLATESERVER,
396 dedupe_namespace)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500397 for item in iter(dedupe_queue.get, None):
398 with timeout_util.Timeout(DEDUPE_TIMEOUT):
Mike Frysingerefef3672014-04-20 10:06:45 -0400399 cros_build_lib.Debug('sending %s to dedupe server', item.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500400 storage.push(item, item.content(0))
Mike Frysinger66e51e92014-05-03 16:52:00 -0400401 cros_build_lib.Debug('sent %s', item.sym_file)
Mike Frysingerae298452014-03-24 22:45:23 -0400402 cros_build_lib.Info('dedupe notification finished; exiting')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500403 except Exception:
404 sym_file = item.sym_file if (item and item.sym_file) else ''
405 cros_build_lib.Warning('posting %s to dedupe server failed',
406 os.path.basename(sym_file), exc_info=True)
407
Mike Frysinger58312e92014-03-18 04:18:36 -0400408 # Keep draining the queue though so it doesn't fill up.
409 while dedupe_queue.get() is not None:
410 continue
411
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500412
413def SymbolDeduplicator(storage, sym_paths):
414 """Filter out symbol files that we've already uploaded
415
416 Using the swarming service, ask it to tell us which symbol files we've already
417 uploaded in previous runs and/or by other bots. If the query fails for any
418 reason, we'll just upload all symbols. This is fine as the symbol server will
419 do the right thing and this phase is purely an optimization.
420
421 This code runs in the main thread which is why we can re-use the existing
422 storage object. Saves us from having to recreate one all the time.
423
424 Args:
425 storage: An isolateserver.StorageApi object
426 sym_paths: List of symbol files to check against the dedupe server
427
428 Returns:
429 List of symbol files that have not been uploaded before
430 """
431 if not sym_paths:
432 return sym_paths
433
434 items = [SymbolItem(x) for x in sym_paths]
435 if storage:
436 try:
437 with timeout_util.Timeout(DEDUPE_TIMEOUT):
438 items = storage.contains(items)
439 except Exception:
440 cros_build_lib.Warning('talking to dedupe server failed', exc_info=True)
441
442 return items
443
444
Mike Frysingerd41938e2014-02-10 06:37:55 -0500445def IsTarball(path):
446 """Guess if this is a tarball based on the filename."""
447 parts = path.split('.')
448 if len(parts) <= 1:
449 return False
450
451 if parts[-1] == 'tar':
452 return True
453
454 if parts[-2] == 'tar':
455 return parts[-1] in ('bz2', 'gz', 'xz')
456
457 return parts[-1] in ('tbz2', 'tbz', 'tgz', 'txz')
458
459
460def SymbolFinder(tempdir, paths):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500461 """Locate symbol files in |paths|
462
463 Args:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500464 tempdir: Path to use for temporary files (caller will clean up).
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500465 paths: A list of input paths to walk. Files are returned w/out any checks.
Mike Frysingerd41938e2014-02-10 06:37:55 -0500466 Dirs are searched for files that end in ".sym". Urls are fetched and then
467 processed. Tarballs are unpacked and walked.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500468
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500469 Returns:
470 Yield every viable sym file.
471 """
472 for p in paths:
Don Garrett25f309a2014-03-19 14:02:12 -0700473 # Pylint is confused about members of ParseResult.
Don Garrettf8bf7842014-03-20 17:03:42 -0700474
Mike Frysingerd41938e2014-02-10 06:37:55 -0500475 o = urlparse.urlparse(p)
Don Garrettf8bf7842014-03-20 17:03:42 -0700476 if o.scheme: # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500477 # Support globs of filenames.
478 ctx = gs.GSContext()
479 for p in ctx.LS(p):
480 cros_build_lib.Info('processing files inside %s', p)
481 o = urlparse.urlparse(p)
482 cache_dir = commandline.GetCacheDir()
483 common_path = os.path.join(cache_dir, constants.COMMON_CACHE)
484 tar_cache = cache.TarballCache(common_path)
Don Garrettf8bf7842014-03-20 17:03:42 -0700485 key = ('%s%s' % (o.netloc, o.path)).split('/') # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500486 # The common cache will not be LRU, removing the need to hold a read
487 # lock on the cached gsutil.
488 ref = tar_cache.Lookup(key)
489 try:
490 ref.SetDefault(p)
491 except cros_build_lib.RunCommandError as e:
492 cros_build_lib.Warning('ignoring %s\n%s', p, e)
493 continue
494 for p in SymbolFinder(tempdir, [ref.path]):
495 yield p
496
497 elif os.path.isdir(p):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500498 for root, _, files in os.walk(p):
499 for f in files:
500 if f.endswith('.sym'):
501 yield os.path.join(root, f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500502
503 elif IsTarball(p):
504 cros_build_lib.Info('processing files inside %s', p)
505 tardir = tempfile.mkdtemp(dir=tempdir)
506 cache.Untar(os.path.realpath(p), tardir)
507 for p in SymbolFinder(tardir, [tardir]):
508 yield p
509
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500510 else:
511 yield p
512
513
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500514def WriteQueueToFile(listing, queue, relpath=None):
515 """Write all the items in |queue| to the |listing|.
516
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500517 Note: The queue must have a sentinel None appended to the end.
518
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500519 Args:
520 listing: Where to write out the list of files.
521 queue: The queue of paths to drain.
522 relpath: If set, write out paths relative to this one.
523 """
524 if not listing:
Mike Frysingera0ddac62014-03-14 10:30:25 -0400525 # Still drain the queue so we make sure the producer has finished
526 # before we return. Otherwise, the queue might get destroyed too
527 # quickly which will trigger a traceback in the producer.
528 while queue.get() is not None:
529 continue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500530 return
531
532 with cros_build_lib.Open(listing, 'wb+') as f:
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500533 while True:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500534 path = queue.get()
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500535 if path is None:
536 return
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500537 if relpath:
538 path = os.path.relpath(path, relpath)
539 f.write('%s\n' % path)
540
541
Mike Frysinger38647542014-09-12 18:15:39 -0700542def UploadSymbols(board=None, official=False, server=None, breakpad_dir=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400543 file_limit=DEFAULT_FILE_LIMIT, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500544 upload_limit=None, sym_paths=None, failed_list=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500545 root=None, retry=True, dedupe_namespace=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400546 """Upload all the generated symbols for |board| to the crash server
547
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400548 You can use in a few ways:
549 * pass |board| to locate all of its symbols
550 * pass |breakpad_dir| to upload all the symbols in there
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500551 * pass |sym_paths| to upload specific symbols (or dirs of symbols)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400552
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400553 Args:
554 board: The board whose symbols we wish to upload
555 official: Use the official symbol server rather than the staging one
Mike Frysinger38647542014-09-12 18:15:39 -0700556 server: Explicit server to post symbols to
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400557 breakpad_dir: The full path to the breakpad directory where symbols live
558 file_limit: The max file size of a symbol file before we try to strip it
559 sleep: How long to sleep in between uploads
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500560 upload_limit: If set, only upload this many symbols (meant for testing)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500561 sym_paths: Specific symbol files (or dirs of sym files) to upload,
562 otherwise search |breakpad_dir|
Mike Frysinger7f9be142014-01-15 02:16:42 -0500563 failed_list: Write the names of all sym files we did not upload; can be a
564 filename or file-like object.
Mike Frysinger118d2502013-08-19 03:36:56 -0400565 root: The tree to prefix to |breakpad_dir| (if |breakpad_dir| is not set)
Mike Frysinger02e92402013-11-22 16:22:02 -0500566 retry: Whether we should retry failures.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500567 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500568
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400569 Returns:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400570 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400571 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500572 # TODO(build): Delete this assert.
573 assert isolateserver, 'Missing isolateserver import http://crbug.com/341152'
574
Mike Frysinger38647542014-09-12 18:15:39 -0700575 if server is None:
576 if official:
577 upload_url = OFFICIAL_UPLOAD_URL
578 else:
579 cros_build_lib.Warning('unofficial builds upload to the staging server')
580 upload_url = STAGING_UPLOAD_URL
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400581 else:
Mike Frysinger38647542014-09-12 18:15:39 -0700582 upload_url = server
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400583
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500584 if sym_paths:
585 cros_build_lib.Info('uploading specified symbols to %s', upload_url)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400586 else:
587 if breakpad_dir is None:
Mike Frysinger118d2502013-08-19 03:36:56 -0400588 breakpad_dir = os.path.join(
589 root,
590 cros_generate_breakpad_symbols.FindBreakpadDir(board).lstrip('/'))
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400591 cros_build_lib.Info('uploading all symbols to %s from %s', upload_url,
592 breakpad_dir)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500593 sym_paths = [breakpad_dir]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400594
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500595 # We use storage_query to ask the server about existing symbols. The
596 # storage_notify_proc process is used to post updates to the server. We
597 # cannot safely share the storage object between threads/processes, but
598 # we also want to minimize creating new ones as each object has to init
599 # new state (like server connections).
Mike Frysinger650e6722014-04-28 18:29:15 -0400600 storage_query = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500601 if dedupe_namespace:
602 dedupe_limit = DEDUPE_LIMIT
603 dedupe_queue = multiprocessing.Queue()
Mike Frysinger650e6722014-04-28 18:29:15 -0400604 try:
605 with timeout_util.Timeout(DEDUPE_TIMEOUT):
606 storage_query = isolateserver.get_storage_api(constants.ISOLATESERVER,
607 dedupe_namespace)
608 except Exception:
609 cros_build_lib.Warning('initializing dedupe server connection failed',
610 exc_info=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500611 else:
612 dedupe_limit = 1
Mike Frysinger650e6722014-04-28 18:29:15 -0400613 dedupe_queue = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500614 # Can't use parallel.BackgroundTaskRunner because that'll create multiple
615 # processes and we want only one the whole time (see comment above).
616 storage_notify_proc = multiprocessing.Process(
617 target=SymbolDeduplicatorNotify, args=(dedupe_namespace, dedupe_queue))
618
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400619 bg_errors = multiprocessing.Value('i')
Mike Frysingereb753bf2013-11-22 16:05:35 -0500620 watermark_errors = multiprocessing.Value('f')
Mike Frysinger02e92402013-11-22 16:22:02 -0500621 failed_queue = multiprocessing.Queue()
622 uploader = functools.partial(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500623 UploadSymbol, upload_url, file_limit=file_limit, sleep=sleep,
624 num_errors=bg_errors, watermark_errors=watermark_errors,
625 failed_queue=failed_queue, passed_queue=dedupe_queue)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400626
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500627 start_time = datetime.datetime.now()
628 Counters = cros_build_lib.Collection(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500629 'Counters', upload_limit=upload_limit, uploaded_count=0, deduped_count=0)
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500630 counters = Counters()
631
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500632 def _Upload(queue, counters, files):
633 if not files:
634 return
635
636 missing_count = 0
637 for item in SymbolDeduplicator(storage_query, files):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500638 missing_count += 1
Mike Frysingerd42e5f02014-03-14 11:19:37 -0400639
640 if counters.upload_limit == 0:
641 continue
642
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500643 queue.put((item,))
644 counters.uploaded_count += 1
645 if counters.upload_limit is not None:
646 counters.upload_limit -= 1
647
648 counters.deduped_count += (len(files) - missing_count)
649
Mike Frysinger13870082014-03-14 10:41:20 -0400650 try:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500651 storage_notify_proc.start()
Mike Frysinger02e92402013-11-22 16:22:02 -0500652
Mike Frysinger13870082014-03-14 10:41:20 -0400653 with osutils.TempDir(prefix='upload_symbols.') as tempdir:
654 # For the first run, we collect the symbols that failed. If the
655 # overall failure rate was low, we'll retry them on the second run.
656 for retry in (retry, False):
657 # We need to limit ourselves to one upload at a time to avoid the server
658 # kicking in DoS protection. See these bugs for more details:
659 # http://crbug.com/209442
660 # http://crbug.com/212496
661 with parallel.BackgroundTaskRunner(uploader, processes=1) as queue:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500662 dedupe_list = []
Mike Frysinger13870082014-03-14 10:41:20 -0400663 for sym_file in SymbolFinder(tempdir, sym_paths):
664 dedupe_list.append(sym_file)
665 dedupe_len = len(dedupe_list)
666 if dedupe_len < dedupe_limit:
667 if (counters.upload_limit is None or
668 dedupe_len < counters.upload_limit):
669 continue
Mike Frysinger02e92402013-11-22 16:22:02 -0500670
Mike Frysinger1010a892014-03-14 11:24:17 -0400671 # We check the counter before _Upload so that we don't keep talking
672 # to the dedupe server. Otherwise, we end up sending one symbol at
673 # a time to it and that slows things down a lot.
674 if counters.upload_limit == 0:
675 break
676
Mike Frysinger13870082014-03-14 10:41:20 -0400677 _Upload(queue, counters, dedupe_list)
678 dedupe_list = []
679 _Upload(queue, counters, dedupe_list)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500680
Mike Frysinger13870082014-03-14 10:41:20 -0400681 # See if we need to retry, and if we haven't failed too many times yet.
682 if not retry or ErrorLimitHit(bg_errors, watermark_errors):
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500683 break
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500684
Mike Frysinger13870082014-03-14 10:41:20 -0400685 sym_paths = []
686 failed_queue.put(None)
687 while True:
688 sym_path = failed_queue.get()
689 if sym_path is None:
690 break
691 sym_paths.append(sym_path)
Mike Frysinger02e92402013-11-22 16:22:02 -0500692
Mike Frysinger13870082014-03-14 10:41:20 -0400693 if sym_paths:
694 cros_build_lib.Warning('retrying %i symbols', len(sym_paths))
695 if counters.upload_limit is not None:
696 counters.upload_limit += len(sym_paths)
697 # Decrement the error count in case we recover in the second pass.
698 assert bg_errors.value >= len(sym_paths), \
699 'more failed files than errors?'
700 bg_errors.value -= len(sym_paths)
701 else:
702 # No failed symbols, so just return now.
703 break
Mike Frysinger7f9be142014-01-15 02:16:42 -0500704
Mike Frysinger13870082014-03-14 10:41:20 -0400705 # If the user has requested it, save all the symbol files that we failed to
706 # upload to a listing file. This should help with recovery efforts later.
707 failed_queue.put(None)
708 WriteQueueToFile(failed_list, failed_queue, breakpad_dir)
709
710 finally:
Mike Frysingerae298452014-03-24 22:45:23 -0400711 cros_build_lib.Info('finished uploading; joining background process')
Mike Frysinger13870082014-03-14 10:41:20 -0400712 if dedupe_queue:
713 dedupe_queue.put(None)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400714
715 # The notification might be slow going, so give it some time to finish.
716 # We have to poll here as the process monitor is watching for output and
717 # will kill us if we go silent for too long.
718 wait_minutes = DEDUPE_NOTIFY_TIMEOUT
719 while storage_notify_proc.is_alive() and wait_minutes > 0:
Aviv Keshetd1f04632014-05-09 11:33:46 -0700720 if dedupe_queue:
721 qsize = str(dedupe_queue.qsize())
722 else:
723 qsize = '[None]'
724 cros_build_lib.Info('waiting up to %i minutes for ~%s notifications',
725 wait_minutes, qsize)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400726 storage_notify_proc.join(60)
727 wait_minutes -= 1
728
729 # The process is taking too long, so kill it and complain.
730 if storage_notify_proc.is_alive():
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400731 cros_build_lib.Warning('notification process took too long')
732 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500733
Mike Frysinger66e51e92014-05-03 16:52:00 -0400734 # Kill it gracefully first (traceback) before tacking it down harder.
735 pid = storage_notify_proc.pid
736 for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGKILL):
737 cros_build_lib.Warning('sending %s to %i', signals.StrSignal(sig), pid)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400738 # The process might have exited between the last check and the
739 # actual kill below, so ignore ESRCH errors.
740 try:
741 os.kill(pid, sig)
742 except OSError as e:
743 if e.errno == errno.ESRCH:
744 break
745 else:
746 raise
Mike Frysinger66e51e92014-05-03 16:52:00 -0400747 time.sleep(5)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400748 if not storage_notify_proc.is_alive():
Mike Frysinger66e51e92014-05-03 16:52:00 -0400749 break
750
751 # Drain the queue so we don't hang when we finish.
752 try:
Mike Frysinger4f5ea832014-05-12 00:54:28 -0400753 cros_build_lib.Warning('draining the notify queue manually')
754 with timeout_util.Timeout(60):
755 try:
756 while dedupe_queue.get_nowait():
757 pass
758 except Queue.Empty:
759 pass
760 except timeout_util.TimeoutError:
761 cros_build_lib.Warning('draining the notify queue failed; trashing it')
762 dedupe_queue.cancel_join_thread()
Mike Frysinger66e51e92014-05-03 16:52:00 -0400763
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500764 cros_build_lib.Info('uploaded %i symbols (%i were deduped) which took: %s',
765 counters.uploaded_count, counters.deduped_count,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500766 datetime.datetime.now() - start_time)
767
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500768 return bg_errors.value
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400769
770
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400771def main(argv):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500772 # TODO(build): Delete this assert.
773 assert isolateserver, 'Missing isolateserver import http://crbug.com/341152'
774
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400775 parser = commandline.ArgumentParser(description=__doc__)
776
Mike Frysingerd41938e2014-02-10 06:37:55 -0500777 parser.add_argument('sym_paths', type='path_or_uri', nargs='*', default=None,
778 help='symbol file or directory or URL or tarball')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400779 parser.add_argument('--board', default=None,
780 help='board to build packages for')
781 parser.add_argument('--breakpad_root', type='path', default=None,
782 help='root directory for breakpad symbols')
783 parser.add_argument('--official_build', action='store_true', default=False,
784 help='point to official symbol server')
Mike Frysinger38647542014-09-12 18:15:39 -0700785 parser.add_argument('--server', type=str, default=None,
786 help='URI for custom symbol server')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400787 parser.add_argument('--regenerate', action='store_true', default=False,
788 help='regenerate all symbols')
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500789 parser.add_argument('--upload-limit', type=int, default=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400790 help='only upload # number of symbols')
791 parser.add_argument('--strip_cfi', type=int,
792 default=CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024),
793 help='strip CFI data for files above this size')
Mike Frysinger7f9be142014-01-15 02:16:42 -0500794 parser.add_argument('--failed-list', type='path',
795 help='where to save a list of failed symbols')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500796 parser.add_argument('--dedupe', action='store_true', default=False,
797 help='use the swarming service to avoid re-uploading')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400798 parser.add_argument('--testing', action='store_true', default=False,
799 help='run in testing mode')
800 parser.add_argument('--yes', action='store_true', default=False,
801 help='answer yes to all prompts')
802
803 opts = parser.parse_args(argv)
Mike Frysinger90e49ca2014-01-14 14:42:07 -0500804 opts.Freeze()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400805
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500806 if opts.sym_paths:
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400807 if opts.regenerate:
808 cros_build_lib.Die('--regenerate may not be used with specific files')
809 else:
810 if opts.board is None:
811 cros_build_lib.Die('--board is required')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400812
813 if opts.breakpad_root and opts.regenerate:
814 cros_build_lib.Die('--regenerate may not be used with --breakpad_root')
815
816 if opts.testing:
817 # TODO(build): Kill off --testing mode once unittests are up-to-snuff.
818 cros_build_lib.Info('running in testing mode')
819 # pylint: disable=W0601,W0603
820 global INITIAL_RETRY_DELAY, SymUpload, DEFAULT_SLEEP_DELAY
821 INITIAL_RETRY_DELAY = DEFAULT_SLEEP_DELAY = 0
822 SymUpload = TestingSymUpload
823
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500824 dedupe_namespace = None
825 if opts.dedupe:
826 if opts.official_build and not opts.testing:
827 dedupe_namespace = OFFICIAL_DEDUPE_NAMESPACE
828 else:
829 dedupe_namespace = STAGING_DEDUPE_NAMESPACE
830
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400831 if not opts.yes:
Mike Frysingerc5de9602014-02-09 02:42:36 -0500832 prolog = '\n'.join(textwrap.wrap(textwrap.dedent("""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400833 Uploading symbols for an entire Chromium OS build is really only
834 necessary for release builds and in a few cases for developers
835 to debug problems. It will take considerable time to run. For
836 developer debugging purposes, consider instead passing specific
837 files to upload.
Mike Frysingerc5de9602014-02-09 02:42:36 -0500838 """), 80)).strip()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400839 if not cros_build_lib.BooleanPrompt(
840 prompt='Are you sure you want to upload all build symbols',
Mike Frysingerc5de9602014-02-09 02:42:36 -0500841 default=False, prolog=prolog):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400842 cros_build_lib.Die('better safe than sorry')
843
844 ret = 0
845 if opts.regenerate:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400846 ret += cros_generate_breakpad_symbols.GenerateBreakpadSymbols(
847 opts.board, breakpad_dir=opts.breakpad_root)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400848
849 ret += UploadSymbols(opts.board, official=opts.official_build,
Mike Frysinger38647542014-09-12 18:15:39 -0700850 server=opts.server, breakpad_dir=opts.breakpad_root,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400851 file_limit=opts.strip_cfi, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500852 upload_limit=opts.upload_limit, sym_paths=opts.sym_paths,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500853 failed_list=opts.failed_list,
854 dedupe_namespace=dedupe_namespace)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400855 if ret:
856 cros_build_lib.Error('encountered %i problem(s)', ret)
857 # Since exit(status) gets masked, clamp it to 1 so we don't inadvertently
858 # return 0 in case we are a multiple of the mask.
859 ret = 1
860
861 return ret
Mike Frysinger094a2172013-08-14 12:54:35 -0400862
863
864# We need this to run once per process. Do it at module import time as that
865# will let us avoid doing it inline at function call time (see SymUpload) as
866# that func might be called by the multiprocessing module which means we'll
867# do the opener logic multiple times overall. Plus, if you're importing this
868# module, it's a pretty good chance that you're going to need this.
869poster.streaminghttp.register_openers()