blob: 2927283387381a18b493765e7d4a6a92ddde2bb3 [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 Frysinger1d4752b2014-11-08 04:00:18 -050012# pylint: disable=bad-continuation
13
Mike Frysingera4fa1e82014-01-15 01:45:56 -050014from __future__ import print_function
15
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040016import ctypes
Mike Frysinger8ec8c502014-02-10 00:19:13 -050017import datetime
Mike Frysingerdad2a032014-05-11 23:05:11 -040018import errno
Mike Frysinger02e92402013-11-22 16:22:02 -050019import functools
Mike Frysinger0c0efa22014-02-09 23:32:23 -050020import hashlib
Mike Frysingera4fa1e82014-01-15 01:45:56 -050021import httplib
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040022import multiprocessing
23import os
Mike Frysinger094a2172013-08-14 12:54:35 -040024import poster
Mike Frysinger66e51e92014-05-03 16:52:00 -040025try:
26 import Queue
27except ImportError:
28 # Python-3 renamed to "queue". We still use Queue to avoid collisions
29 # with naming variables as "queue". Maybe we'll transition at some point.
30 # pylint: disable=F0401
31 import queue as Queue
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040032import random
Mike Frysinger66e51e92014-05-03 16:52:00 -040033import signal
Mike Frysingerfd355652014-01-23 02:57:48 -050034import socket
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040035import textwrap
36import tempfile
37import time
Mike Frysinger094a2172013-08-14 12:54:35 -040038import urllib2
Mike Frysingerd41938e2014-02-10 06:37:55 -050039import urlparse
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040040
Don Garrett88b8d782014-05-13 17:30:55 -070041from chromite.cbuildbot import constants
Mike Frysingerd41938e2014-02-10 06:37:55 -050042from chromite.lib import cache
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040043from chromite.lib import commandline
44from chromite.lib import cros_build_lib
Mike Frysingerd41938e2014-02-10 06:37:55 -050045from chromite.lib import gs
46from chromite.lib import osutils
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040047from chromite.lib import parallel
David Jamesc93e6a4d2014-01-13 11:37:36 -080048from chromite.lib import retry_util
Mike Frysinger66e51e92014-05-03 16:52:00 -040049from chromite.lib import signals
Mike Frysinger0c0efa22014-02-09 23:32:23 -050050from chromite.lib import timeout_util
Mike Frysinger69cb41d2013-08-11 20:08:19 -040051from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040052
Mike Frysinger0c0efa22014-02-09 23:32:23 -050053# Needs to be after chromite imports.
54# TODO(build): When doing the initial buildbot bootstrap, we won't have any
55# other repos available. So ignore isolateserver imports. But buildbot will
56# re-exec itself once it has done a full repo sync and then the module will
57# be available -- it isn't needed that early. http://crbug.com/341152
58try:
59 import isolateserver
60except ImportError:
61 isolateserver = None
62
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040063
64# URLs used for uploading symbols.
65OFFICIAL_UPLOAD_URL = 'http://clients2.google.com/cr/symbol'
66STAGING_UPLOAD_URL = 'http://clients2.google.com/cr/staging_symbol'
67
68
69# The crash server rejects files that are this big.
70CRASH_SERVER_FILE_LIMIT = 350 * 1024 * 1024
71# Give ourselves a little breathing room from what the server expects.
72DEFAULT_FILE_LIMIT = CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024)
73
74
Mike Frysinger0c0efa22014-02-09 23:32:23 -050075# The batch limit when talking to the dedup server. We avoid sending one at a
76# time as the round trip overhead will dominate. Conversely, we avoid sending
77# all at once so we can start uploading symbols asap -- the symbol server is a
78# bit slow and will take longer than anything else.
79# TODO: A better algorithm would be adaptive. If we have more than one symbol
80# in the upload queue waiting, we could send more symbols to the dedupe server
81# at a time.
82DEDUPE_LIMIT = 100
83
84# How long to wait for the server to respond with the results. Note that the
85# larger the limit above, the larger this will need to be. So we give it ~1
86# second per item max.
87DEDUPE_TIMEOUT = DEDUPE_LIMIT
88
Mike Frysinger4dd462e2014-04-30 16:21:51 -040089# How long to wait for the notification to finish (in minutes). If it takes
90# longer than this, we'll stop notifiying, but that's not a big deal as we
91# will be able to recover in later runs.
92DEDUPE_NOTIFY_TIMEOUT = 20
93
Mike Frysinger0c0efa22014-02-09 23:32:23 -050094# The unique namespace in the dedupe server that only we use. Helps avoid
95# collisions with all the hashed values and unrelated content.
96OFFICIAL_DEDUPE_NAMESPACE = 'chromium-os-upload-symbols'
97STAGING_DEDUPE_NAMESPACE = '%s-staging' % OFFICIAL_DEDUPE_NAMESPACE
98
99
Mike Frysinger71046662014-09-12 18:15:15 -0700100# The minimum average rate (in bytes per second) that we expect to maintain
101# when uploading symbols. This has to allow for symbols that are up to
102# CRASH_SERVER_FILE_LIMIT in size.
103UPLOAD_MIN_RATE = CRASH_SERVER_FILE_LIMIT / (30 * 60)
104
105# The lowest timeout (in seconds) we'll allow. If the server is overloaded,
106# then there might be a delay in setting up the connection, not just with the
107# transfer. So even a small file might need a larger value.
108UPLOAD_MIN_TIMEOUT = 2 * 60
Mike Frysingercd78a082013-06-26 17:13:04 -0400109
110
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400111# Sleep for 200ms in between uploads to avoid DoS'ing symbol server.
112DEFAULT_SLEEP_DELAY = 0.2
113
114
115# Number of seconds to wait before retrying an upload. The delay will double
116# for each subsequent retry of the same symbol file.
117INITIAL_RETRY_DELAY = 1
118
119# Allow up to 7 attempts to upload a symbol file (total delay may be
120# 1+2+4+8+16+32=63 seconds).
121MAX_RETRIES = 6
122
Mike Frysingereb753bf2013-11-22 16:05:35 -0500123# Number of total errors, before uploads are no longer attempted.
124# This is used to avoid lots of errors causing unreasonable delays.
125# See the related, but independent, error values below.
126MAX_TOTAL_ERRORS_FOR_RETRY = 30
127
128# A watermark of transient errors which we allow recovery from. If we hit
129# errors infrequently, overall we're probably doing fine. For example, if
130# we have one failure every 100 passes, then we probably don't want to fail
131# right away. But if we hit a string of failures in a row, we want to abort.
132#
133# The watermark starts at 0 (and can never go below that). When this error
134# level is exceeded, we stop uploading. When a failure happens, we add the
135# fail adjustment, and when an upload succeeds, we add the pass adjustment.
136# We want to penalize failures more so that we ramp up when there is a string
137# of them, but then slowly back off as things start working.
138#
139# A quick example:
140# 0.0: Starting point.
141# 0.0: Upload works, so add -0.5, and then clamp to 0.
142# 1.0: Upload fails, so add 1.0.
143# 2.0: Upload fails, so add 1.0.
144# 1.5: Upload works, so add -0.5.
145# 1.0: Upload works, so add -0.5.
146ERROR_WATERMARK = 3.0
147ERROR_ADJUST_FAIL = 1.0
148ERROR_ADJUST_PASS = -0.5
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400149
150
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700151def GetUploadTimeout(path):
152 """How long to wait for a specific file to upload to the crash server.
153
154 This is a function largely to make unittesting easier.
155
156 Args:
157 path: The path to the file to calculate the timeout for
158
159 Returns:
160 Timeout length (in seconds)
161 """
162 # Scale the timeout based on the filesize.
163 return max(os.path.getsize(path) / UPLOAD_MIN_RATE, UPLOAD_MIN_TIMEOUT)
164
165
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500166def SymUpload(upload_url, sym_item):
Mike Frysinger094a2172013-08-14 12:54:35 -0400167 """Upload a symbol file to a HTTP server
168
169 The upload is a multipart/form-data POST with the following parameters:
170 code_file: the basename of the module, e.g. "app"
171 code_identifier: the module file's identifier
172 debug_file: the basename of the debugging file, e.g. "app"
173 debug_identifier: the debug file's identifier, usually consisting of
174 the guid and age embedded in the pdb, e.g.
175 "11111111BBBB3333DDDD555555555555F"
176 version: the file version of the module, e.g. "1.2.3.4"
177 product: HTTP-friendly product name
178 os: the operating system that the module was built for
179 cpu: the CPU that the module was built for
180 symbol_file: the contents of the breakpad-format symbol file
181
182 Args:
Mike Frysinger094a2172013-08-14 12:54:35 -0400183 upload_url: The crash URL to POST the |sym_file| to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500184 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysinger094a2172013-08-14 12:54:35 -0400185 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500186 sym_header = sym_item.sym_header
187 sym_file = sym_item.sym_file
Mike Frysinger094a2172013-08-14 12:54:35 -0400188
189 fields = (
190 ('code_file', sym_header.name),
191 ('debug_file', sym_header.name),
192 ('debug_identifier', sym_header.id.replace('-', '')),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400193 # The product/version fields are used by the server only for statistic
194 # purposes. They do not impact symbolization, so they're safe to set
195 # to any value all the time.
196 # In this case, we use it to help see the load our build system is
197 # placing on the server.
198 # Not sure what to set for the version. Maybe the git sha1 of this file.
199 # Note: the server restricts this to 30 chars.
Mike Frysinger094a2172013-08-14 12:54:35 -0400200 #('version', None),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400201 ('product', 'ChromeOS'),
Mike Frysinger094a2172013-08-14 12:54:35 -0400202 ('os', sym_header.os),
203 ('cpu', sym_header.cpu),
204 poster.encode.MultipartParam.from_file('symbol_file', sym_file),
205 )
206
207 data, headers = poster.encode.multipart_encode(fields)
208 request = urllib2.Request(upload_url, data, headers)
209 request.add_header('User-agent', 'chromite.upload_symbols')
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700210 urllib2.urlopen(request, timeout=GetUploadTimeout(sym_file))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400211
212
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500213def TestingSymUpload(upload_url, sym_item):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400214 """A stub version of SymUpload for --testing usage"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500215 cmd = ['sym_upload', sym_item.sym_file, upload_url]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400216 # Randomly fail 80% of the time (the retry logic makes this 80%/3 per file).
217 returncode = random.randint(1, 100) <= 80
218 cros_build_lib.Debug('would run (and return %i): %s', returncode,
Matt Tennant7feda352013-12-20 14:03:40 -0800219 cros_build_lib.CmdToStr(cmd))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400220 if returncode:
221 output = 'Failed to send the symbol file.'
222 else:
223 output = 'Successfully sent the symbol file.'
224 result = cros_build_lib.CommandResult(cmd=cmd, error=None, output=output,
225 returncode=returncode)
226 if returncode:
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500227 exceptions = (
Mike Frysingerfd355652014-01-23 02:57:48 -0500228 socket.error('[socket.error] forced test fail'),
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500229 httplib.BadStatusLine('[BadStatusLine] forced test fail'),
230 urllib2.HTTPError(upload_url, 400, '[HTTPError] forced test fail',
231 {}, None),
232 urllib2.URLError('[URLError] forced test fail'),
233 )
234 raise random.choice(exceptions)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400235 else:
236 return result
237
238
Mike Frysingereb753bf2013-11-22 16:05:35 -0500239def ErrorLimitHit(num_errors, watermark_errors):
240 """See if our error limit has been hit
241
242 Args:
243 num_errors: A multiprocessing.Value of the raw number of failures.
244 watermark_errors: A multiprocessing.Value of the current rate of failures.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500245
Mike Frysingereb753bf2013-11-22 16:05:35 -0500246 Returns:
247 True if our error limits have been exceeded.
248 """
249 return ((num_errors is not None and
250 num_errors.value > MAX_TOTAL_ERRORS_FOR_RETRY) or
251 (watermark_errors is not None and
252 watermark_errors.value > ERROR_WATERMARK))
253
254
255def _UpdateCounter(counter, adj):
256 """Update |counter| by |adj|
257
258 Handle atomic updates of |counter|. Also make sure it does not
259 fall below 0.
260
261 Args:
262 counter: A multiprocessing.Value to update
263 adj: The value to add to |counter|
264 """
265 def _Update():
266 clamp = 0 if type(adj) is int else 0.0
267 counter.value = max(clamp, counter.value + adj)
268
269 if hasattr(counter, 'get_lock'):
270 with counter.get_lock():
271 _Update()
272 elif counter is not None:
273 _Update()
274
275
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500276def UploadSymbol(upload_url, sym_item, file_limit=DEFAULT_FILE_LIMIT,
Mike Frysinger02e92402013-11-22 16:22:02 -0500277 sleep=0, num_errors=None, watermark_errors=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500278 failed_queue=None, passed_queue=None):
279 """Upload |sym_item| to |upload_url|
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400280
281 Args:
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400282 upload_url: The crash server to upload things to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500283 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400284 file_limit: The max file size of a symbol file before we try to strip it
285 sleep: Number of seconds to sleep before running
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400286 num_errors: An object to update with the error count (needs a .value member)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500287 watermark_errors: An object to track current error behavior (needs a .value)
Mike Frysinger02e92402013-11-22 16:22:02 -0500288 failed_queue: When a symbol fails, add it to this queue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500289 passed_queue: When a symbol passes, add it to this queue
Mike Frysinger1a736a82013-12-12 01:50:59 -0500290
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400291 Returns:
292 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400293 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500294 sym_file = sym_item.sym_file
295 upload_item = sym_item
296
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400297 if num_errors is None:
298 num_errors = ctypes.c_int()
Mike Frysingereb753bf2013-11-22 16:05:35 -0500299 if ErrorLimitHit(num_errors, watermark_errors):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400300 # Abandon ship! It's on fire! NOoooooooooooOOOoooooo.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500301 if failed_queue:
302 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400303 return 0
304
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400305 if sleep:
306 # Keeps us from DoS-ing the symbol server.
307 time.sleep(sleep)
308
309 cros_build_lib.Debug('uploading %s' % sym_file)
310
311 # Ideally there'd be a tempfile.SpooledNamedTemporaryFile that we could use.
312 with tempfile.NamedTemporaryFile(prefix='upload_symbols',
313 bufsize=0) as temp_sym_file:
314 if file_limit:
315 # If the symbols size is too big, strip out the call frame info. The CFI
316 # is unnecessary for 32bit x86 targets where the frame pointer is used (as
317 # all of ours have) and it accounts for over half the size of the symbols
318 # uploaded.
319 file_size = os.path.getsize(sym_file)
320 if file_size > file_limit:
321 cros_build_lib.Warning('stripping CFI from %s due to size %s > %s',
322 sym_file, file_size, file_limit)
323 temp_sym_file.writelines([x for x in open(sym_file, 'rb').readlines()
324 if not x.startswith('STACK CFI')])
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500325
326 upload_item = FakeItem(sym_file=temp_sym_file.name,
327 sym_header=sym_item.sym_header)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400328
329 # Hopefully the crash server will let it through. But it probably won't.
330 # Not sure what the best answer is in this case.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500331 file_size = os.path.getsize(upload_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400332 if file_size > CRASH_SERVER_FILE_LIMIT:
333 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger02e92402013-11-22 16:22:02 -0500334 cros_build_lib.Warning('upload file %s is awfully large, risking '
335 'rejection by the symbol server (%s > %s)',
336 sym_file, file_size, CRASH_SERVER_FILE_LIMIT)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400337
338 # Upload the symbol file.
Mike Frysingereb753bf2013-11-22 16:05:35 -0500339 success = False
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400340 try:
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400341 cros_build_lib.TimedCommand(
David Jamesc93e6a4d2014-01-13 11:37:36 -0800342 retry_util.RetryException,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400343 (urllib2.HTTPError, urllib2.URLError), MAX_RETRIES, SymUpload,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500344 upload_url, upload_item, sleep=INITIAL_RETRY_DELAY,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400345 timed_log_msg='upload of %10i bytes took %%s: %s' %
346 (file_size, os.path.basename(sym_file)))
Mike Frysingereb753bf2013-11-22 16:05:35 -0500347 success = True
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500348
349 if passed_queue:
350 passed_queue.put(sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400351 except urllib2.HTTPError as e:
352 cros_build_lib.Warning('could not upload: %s: HTTP %s: %s',
353 os.path.basename(sym_file), e.code, e.reason)
Mike Frysingerfd355652014-01-23 02:57:48 -0500354 except (urllib2.URLError, httplib.HTTPException, socket.error) as e:
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400355 cros_build_lib.Warning('could not upload: %s: %s',
356 os.path.basename(sym_file), e)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500357 finally:
358 if success:
359 _UpdateCounter(watermark_errors, ERROR_ADJUST_PASS)
360 else:
361 _UpdateCounter(num_errors, 1)
362 _UpdateCounter(watermark_errors, ERROR_ADJUST_FAIL)
Mike Frysinger02e92402013-11-22 16:22:02 -0500363 if failed_queue:
364 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400365
366 return num_errors.value
367
368
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500369# A dummy class that allows for stubbing in tests and SymUpload.
370FakeItem = cros_build_lib.Collection(
371 'FakeItem', sym_file=None, sym_header=None, content=lambda x: '')
372
373
374# TODO(build): Delete this if check. http://crbug.com/341152
375if isolateserver:
376 class SymbolItem(isolateserver.BufferItem):
377 """Turn a sym_file into an isolateserver.Item"""
378
379 ALGO = hashlib.sha1
380
381 def __init__(self, sym_file):
382 sym_header = cros_generate_breakpad_symbols.ReadSymsHeader(sym_file)
383 super(SymbolItem, self).__init__(str(sym_header), self.ALGO)
384 self.sym_header = sym_header
385 self.sym_file = sym_file
386
387
388def SymbolDeduplicatorNotify(dedupe_namespace, dedupe_queue):
389 """Send a symbol file to the swarming service
390
391 Notify the swarming service of a successful upload. If the notification fails
392 for any reason, we ignore it. We don't care as it just means we'll upload it
393 again later on, and the symbol server will handle that graciously.
394
395 This func runs in a different process from the main one, so we cannot share
396 the storage object. Instead, we create our own. This func stays alive for
397 the life of the process, so we only create one here overall.
398
399 Args:
400 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
401 dedupe_queue: The queue to read SymbolItems from
402 """
403 if dedupe_queue is None:
404 return
405
406 item = None
407 try:
Mike Frysinger650e6722014-04-28 18:29:15 -0400408 with timeout_util.Timeout(DEDUPE_TIMEOUT):
409 storage = isolateserver.get_storage_api(constants.ISOLATESERVER,
410 dedupe_namespace)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500411 for item in iter(dedupe_queue.get, None):
412 with timeout_util.Timeout(DEDUPE_TIMEOUT):
Mike Frysingerefef3672014-04-20 10:06:45 -0400413 cros_build_lib.Debug('sending %s to dedupe server', item.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500414 storage.push(item, item.content(0))
Mike Frysinger66e51e92014-05-03 16:52:00 -0400415 cros_build_lib.Debug('sent %s', item.sym_file)
Mike Frysingerae298452014-03-24 22:45:23 -0400416 cros_build_lib.Info('dedupe notification finished; exiting')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500417 except Exception:
418 sym_file = item.sym_file if (item and item.sym_file) else ''
419 cros_build_lib.Warning('posting %s to dedupe server failed',
420 os.path.basename(sym_file), exc_info=True)
421
Mike Frysinger58312e92014-03-18 04:18:36 -0400422 # Keep draining the queue though so it doesn't fill up.
423 while dedupe_queue.get() is not None:
424 continue
425
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500426
427def SymbolDeduplicator(storage, sym_paths):
428 """Filter out symbol files that we've already uploaded
429
430 Using the swarming service, ask it to tell us which symbol files we've already
431 uploaded in previous runs and/or by other bots. If the query fails for any
432 reason, we'll just upload all symbols. This is fine as the symbol server will
433 do the right thing and this phase is purely an optimization.
434
435 This code runs in the main thread which is why we can re-use the existing
436 storage object. Saves us from having to recreate one all the time.
437
438 Args:
439 storage: An isolateserver.StorageApi object
440 sym_paths: List of symbol files to check against the dedupe server
441
442 Returns:
443 List of symbol files that have not been uploaded before
444 """
445 if not sym_paths:
446 return sym_paths
447
448 items = [SymbolItem(x) for x in sym_paths]
449 if storage:
450 try:
451 with timeout_util.Timeout(DEDUPE_TIMEOUT):
452 items = storage.contains(items)
453 except Exception:
454 cros_build_lib.Warning('talking to dedupe server failed', exc_info=True)
455
456 return items
457
458
Mike Frysingerd41938e2014-02-10 06:37:55 -0500459def IsTarball(path):
460 """Guess if this is a tarball based on the filename."""
461 parts = path.split('.')
462 if len(parts) <= 1:
463 return False
464
465 if parts[-1] == 'tar':
466 return True
467
468 if parts[-2] == 'tar':
469 return parts[-1] in ('bz2', 'gz', 'xz')
470
471 return parts[-1] in ('tbz2', 'tbz', 'tgz', 'txz')
472
473
474def SymbolFinder(tempdir, paths):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500475 """Locate symbol files in |paths|
476
477 Args:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500478 tempdir: Path to use for temporary files (caller will clean up).
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500479 paths: A list of input paths to walk. Files are returned w/out any checks.
Mike Frysingerd41938e2014-02-10 06:37:55 -0500480 Dirs are searched for files that end in ".sym". Urls are fetched and then
481 processed. Tarballs are unpacked and walked.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500482
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500483 Returns:
484 Yield every viable sym file.
485 """
486 for p in paths:
Don Garrett25f309a2014-03-19 14:02:12 -0700487 # Pylint is confused about members of ParseResult.
Don Garrettf8bf7842014-03-20 17:03:42 -0700488
Mike Frysingerd41938e2014-02-10 06:37:55 -0500489 o = urlparse.urlparse(p)
Don Garrettf8bf7842014-03-20 17:03:42 -0700490 if o.scheme: # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500491 # Support globs of filenames.
492 ctx = gs.GSContext()
493 for p in ctx.LS(p):
494 cros_build_lib.Info('processing files inside %s', p)
495 o = urlparse.urlparse(p)
496 cache_dir = commandline.GetCacheDir()
497 common_path = os.path.join(cache_dir, constants.COMMON_CACHE)
498 tar_cache = cache.TarballCache(common_path)
Don Garrettf8bf7842014-03-20 17:03:42 -0700499 key = ('%s%s' % (o.netloc, o.path)).split('/') # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500500 # The common cache will not be LRU, removing the need to hold a read
501 # lock on the cached gsutil.
502 ref = tar_cache.Lookup(key)
503 try:
504 ref.SetDefault(p)
505 except cros_build_lib.RunCommandError as e:
506 cros_build_lib.Warning('ignoring %s\n%s', p, e)
507 continue
508 for p in SymbolFinder(tempdir, [ref.path]):
509 yield p
510
511 elif os.path.isdir(p):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500512 for root, _, files in os.walk(p):
513 for f in files:
514 if f.endswith('.sym'):
515 yield os.path.join(root, f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500516
517 elif IsTarball(p):
518 cros_build_lib.Info('processing files inside %s', p)
519 tardir = tempfile.mkdtemp(dir=tempdir)
520 cache.Untar(os.path.realpath(p), tardir)
521 for p in SymbolFinder(tardir, [tardir]):
522 yield p
523
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500524 else:
525 yield p
526
527
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500528def WriteQueueToFile(listing, queue, relpath=None):
529 """Write all the items in |queue| to the |listing|.
530
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500531 Note: The queue must have a sentinel None appended to the end.
532
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500533 Args:
534 listing: Where to write out the list of files.
535 queue: The queue of paths to drain.
536 relpath: If set, write out paths relative to this one.
537 """
538 if not listing:
Mike Frysingera0ddac62014-03-14 10:30:25 -0400539 # Still drain the queue so we make sure the producer has finished
540 # before we return. Otherwise, the queue might get destroyed too
541 # quickly which will trigger a traceback in the producer.
542 while queue.get() is not None:
543 continue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500544 return
545
546 with cros_build_lib.Open(listing, 'wb+') as f:
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500547 while True:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500548 path = queue.get()
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500549 if path is None:
550 return
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500551 if relpath:
552 path = os.path.relpath(path, relpath)
553 f.write('%s\n' % path)
554
555
Mike Frysinger38647542014-09-12 18:15:39 -0700556def UploadSymbols(board=None, official=False, server=None, breakpad_dir=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400557 file_limit=DEFAULT_FILE_LIMIT, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500558 upload_limit=None, sym_paths=None, failed_list=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500559 root=None, retry=True, dedupe_namespace=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400560 """Upload all the generated symbols for |board| to the crash server
561
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400562 You can use in a few ways:
563 * pass |board| to locate all of its symbols
564 * pass |breakpad_dir| to upload all the symbols in there
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500565 * pass |sym_paths| to upload specific symbols (or dirs of symbols)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400566
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400567 Args:
568 board: The board whose symbols we wish to upload
569 official: Use the official symbol server rather than the staging one
Mike Frysinger38647542014-09-12 18:15:39 -0700570 server: Explicit server to post symbols to
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400571 breakpad_dir: The full path to the breakpad directory where symbols live
572 file_limit: The max file size of a symbol file before we try to strip it
573 sleep: How long to sleep in between uploads
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500574 upload_limit: If set, only upload this many symbols (meant for testing)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500575 sym_paths: Specific symbol files (or dirs of sym files) to upload,
576 otherwise search |breakpad_dir|
Mike Frysinger7f9be142014-01-15 02:16:42 -0500577 failed_list: Write the names of all sym files we did not upload; can be a
578 filename or file-like object.
Mike Frysinger118d2502013-08-19 03:36:56 -0400579 root: The tree to prefix to |breakpad_dir| (if |breakpad_dir| is not set)
Mike Frysinger02e92402013-11-22 16:22:02 -0500580 retry: Whether we should retry failures.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500581 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500582
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400583 Returns:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400584 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400585 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500586 # TODO(build): Delete this assert.
587 assert isolateserver, 'Missing isolateserver import http://crbug.com/341152'
588
Mike Frysinger38647542014-09-12 18:15:39 -0700589 if server is None:
590 if official:
591 upload_url = OFFICIAL_UPLOAD_URL
592 else:
593 cros_build_lib.Warning('unofficial builds upload to the staging server')
594 upload_url = STAGING_UPLOAD_URL
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400595 else:
Mike Frysinger38647542014-09-12 18:15:39 -0700596 upload_url = server
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400597
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500598 if sym_paths:
599 cros_build_lib.Info('uploading specified symbols to %s', upload_url)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400600 else:
601 if breakpad_dir is None:
Mike Frysinger118d2502013-08-19 03:36:56 -0400602 breakpad_dir = os.path.join(
603 root,
604 cros_generate_breakpad_symbols.FindBreakpadDir(board).lstrip('/'))
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400605 cros_build_lib.Info('uploading all symbols to %s from %s', upload_url,
606 breakpad_dir)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500607 sym_paths = [breakpad_dir]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400608
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500609 # We use storage_query to ask the server about existing symbols. The
610 # storage_notify_proc process is used to post updates to the server. We
611 # cannot safely share the storage object between threads/processes, but
612 # we also want to minimize creating new ones as each object has to init
613 # new state (like server connections).
Mike Frysinger650e6722014-04-28 18:29:15 -0400614 storage_query = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500615 if dedupe_namespace:
616 dedupe_limit = DEDUPE_LIMIT
617 dedupe_queue = multiprocessing.Queue()
Mike Frysinger650e6722014-04-28 18:29:15 -0400618 try:
619 with timeout_util.Timeout(DEDUPE_TIMEOUT):
620 storage_query = isolateserver.get_storage_api(constants.ISOLATESERVER,
621 dedupe_namespace)
622 except Exception:
623 cros_build_lib.Warning('initializing dedupe server connection failed',
624 exc_info=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500625 else:
626 dedupe_limit = 1
Mike Frysinger650e6722014-04-28 18:29:15 -0400627 dedupe_queue = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500628 # Can't use parallel.BackgroundTaskRunner because that'll create multiple
629 # processes and we want only one the whole time (see comment above).
630 storage_notify_proc = multiprocessing.Process(
631 target=SymbolDeduplicatorNotify, args=(dedupe_namespace, dedupe_queue))
632
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400633 bg_errors = multiprocessing.Value('i')
Mike Frysingereb753bf2013-11-22 16:05:35 -0500634 watermark_errors = multiprocessing.Value('f')
Mike Frysinger02e92402013-11-22 16:22:02 -0500635 failed_queue = multiprocessing.Queue()
636 uploader = functools.partial(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500637 UploadSymbol, upload_url, file_limit=file_limit, sleep=sleep,
638 num_errors=bg_errors, watermark_errors=watermark_errors,
639 failed_queue=failed_queue, passed_queue=dedupe_queue)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400640
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500641 start_time = datetime.datetime.now()
642 Counters = cros_build_lib.Collection(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500643 'Counters', upload_limit=upload_limit, uploaded_count=0, deduped_count=0)
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500644 counters = Counters()
645
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500646 def _Upload(queue, counters, files):
647 if not files:
648 return
649
650 missing_count = 0
651 for item in SymbolDeduplicator(storage_query, files):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500652 missing_count += 1
Mike Frysingerd42e5f02014-03-14 11:19:37 -0400653
654 if counters.upload_limit == 0:
655 continue
656
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500657 queue.put((item,))
658 counters.uploaded_count += 1
659 if counters.upload_limit is not None:
660 counters.upload_limit -= 1
661
662 counters.deduped_count += (len(files) - missing_count)
663
Mike Frysinger13870082014-03-14 10:41:20 -0400664 try:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500665 storage_notify_proc.start()
Mike Frysinger02e92402013-11-22 16:22:02 -0500666
Mike Frysinger13870082014-03-14 10:41:20 -0400667 with osutils.TempDir(prefix='upload_symbols.') as tempdir:
668 # For the first run, we collect the symbols that failed. If the
669 # overall failure rate was low, we'll retry them on the second run.
670 for retry in (retry, False):
671 # We need to limit ourselves to one upload at a time to avoid the server
672 # kicking in DoS protection. See these bugs for more details:
673 # http://crbug.com/209442
674 # http://crbug.com/212496
675 with parallel.BackgroundTaskRunner(uploader, processes=1) as queue:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500676 dedupe_list = []
Mike Frysinger13870082014-03-14 10:41:20 -0400677 for sym_file in SymbolFinder(tempdir, sym_paths):
678 dedupe_list.append(sym_file)
679 dedupe_len = len(dedupe_list)
680 if dedupe_len < dedupe_limit:
681 if (counters.upload_limit is None or
682 dedupe_len < counters.upload_limit):
683 continue
Mike Frysinger02e92402013-11-22 16:22:02 -0500684
Mike Frysinger1010a892014-03-14 11:24:17 -0400685 # We check the counter before _Upload so that we don't keep talking
686 # to the dedupe server. Otherwise, we end up sending one symbol at
687 # a time to it and that slows things down a lot.
688 if counters.upload_limit == 0:
689 break
690
Mike Frysinger13870082014-03-14 10:41:20 -0400691 _Upload(queue, counters, dedupe_list)
692 dedupe_list = []
693 _Upload(queue, counters, dedupe_list)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500694
Mike Frysinger13870082014-03-14 10:41:20 -0400695 # See if we need to retry, and if we haven't failed too many times yet.
696 if not retry or ErrorLimitHit(bg_errors, watermark_errors):
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500697 break
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500698
Mike Frysinger13870082014-03-14 10:41:20 -0400699 sym_paths = []
700 failed_queue.put(None)
701 while True:
702 sym_path = failed_queue.get()
703 if sym_path is None:
704 break
705 sym_paths.append(sym_path)
Mike Frysinger02e92402013-11-22 16:22:02 -0500706
Mike Frysinger13870082014-03-14 10:41:20 -0400707 if sym_paths:
708 cros_build_lib.Warning('retrying %i symbols', len(sym_paths))
709 if counters.upload_limit is not None:
710 counters.upload_limit += len(sym_paths)
711 # Decrement the error count in case we recover in the second pass.
712 assert bg_errors.value >= len(sym_paths), \
713 'more failed files than errors?'
714 bg_errors.value -= len(sym_paths)
715 else:
716 # No failed symbols, so just return now.
717 break
Mike Frysinger7f9be142014-01-15 02:16:42 -0500718
Mike Frysinger13870082014-03-14 10:41:20 -0400719 # If the user has requested it, save all the symbol files that we failed to
720 # upload to a listing file. This should help with recovery efforts later.
721 failed_queue.put(None)
722 WriteQueueToFile(failed_list, failed_queue, breakpad_dir)
723
724 finally:
Mike Frysingerae298452014-03-24 22:45:23 -0400725 cros_build_lib.Info('finished uploading; joining background process')
Mike Frysinger13870082014-03-14 10:41:20 -0400726 if dedupe_queue:
727 dedupe_queue.put(None)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400728
729 # The notification might be slow going, so give it some time to finish.
730 # We have to poll here as the process monitor is watching for output and
731 # will kill us if we go silent for too long.
732 wait_minutes = DEDUPE_NOTIFY_TIMEOUT
733 while storage_notify_proc.is_alive() and wait_minutes > 0:
Aviv Keshetd1f04632014-05-09 11:33:46 -0700734 if dedupe_queue:
735 qsize = str(dedupe_queue.qsize())
736 else:
737 qsize = '[None]'
738 cros_build_lib.Info('waiting up to %i minutes for ~%s notifications',
739 wait_minutes, qsize)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400740 storage_notify_proc.join(60)
741 wait_minutes -= 1
742
743 # The process is taking too long, so kill it and complain.
744 if storage_notify_proc.is_alive():
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400745 cros_build_lib.Warning('notification process took too long')
746 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500747
Mike Frysinger66e51e92014-05-03 16:52:00 -0400748 # Kill it gracefully first (traceback) before tacking it down harder.
749 pid = storage_notify_proc.pid
750 for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGKILL):
751 cros_build_lib.Warning('sending %s to %i', signals.StrSignal(sig), pid)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400752 # The process might have exited between the last check and the
753 # actual kill below, so ignore ESRCH errors.
754 try:
755 os.kill(pid, sig)
756 except OSError as e:
757 if e.errno == errno.ESRCH:
758 break
759 else:
760 raise
Mike Frysinger66e51e92014-05-03 16:52:00 -0400761 time.sleep(5)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400762 if not storage_notify_proc.is_alive():
Mike Frysinger66e51e92014-05-03 16:52:00 -0400763 break
764
765 # Drain the queue so we don't hang when we finish.
766 try:
Mike Frysinger4f5ea832014-05-12 00:54:28 -0400767 cros_build_lib.Warning('draining the notify queue manually')
768 with timeout_util.Timeout(60):
769 try:
770 while dedupe_queue.get_nowait():
771 pass
772 except Queue.Empty:
773 pass
774 except timeout_util.TimeoutError:
775 cros_build_lib.Warning('draining the notify queue failed; trashing it')
776 dedupe_queue.cancel_join_thread()
Mike Frysinger66e51e92014-05-03 16:52:00 -0400777
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500778 cros_build_lib.Info('uploaded %i symbols (%i were deduped) which took: %s',
779 counters.uploaded_count, counters.deduped_count,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500780 datetime.datetime.now() - start_time)
781
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500782 return bg_errors.value
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400783
784
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400785def main(argv):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500786 # TODO(build): Delete this assert.
787 assert isolateserver, 'Missing isolateserver import http://crbug.com/341152'
788
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400789 parser = commandline.ArgumentParser(description=__doc__)
790
Mike Frysingerd41938e2014-02-10 06:37:55 -0500791 parser.add_argument('sym_paths', type='path_or_uri', nargs='*', default=None,
792 help='symbol file or directory or URL or tarball')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400793 parser.add_argument('--board', default=None,
794 help='board to build packages for')
795 parser.add_argument('--breakpad_root', type='path', default=None,
796 help='root directory for breakpad symbols')
797 parser.add_argument('--official_build', action='store_true', default=False,
798 help='point to official symbol server')
Mike Frysinger38647542014-09-12 18:15:39 -0700799 parser.add_argument('--server', type=str, default=None,
800 help='URI for custom symbol server')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400801 parser.add_argument('--regenerate', action='store_true', default=False,
802 help='regenerate all symbols')
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500803 parser.add_argument('--upload-limit', type=int, default=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400804 help='only upload # number of symbols')
805 parser.add_argument('--strip_cfi', type=int,
806 default=CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024),
807 help='strip CFI data for files above this size')
Mike Frysinger7f9be142014-01-15 02:16:42 -0500808 parser.add_argument('--failed-list', type='path',
809 help='where to save a list of failed symbols')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500810 parser.add_argument('--dedupe', action='store_true', default=False,
811 help='use the swarming service to avoid re-uploading')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400812 parser.add_argument('--testing', action='store_true', default=False,
813 help='run in testing mode')
814 parser.add_argument('--yes', action='store_true', default=False,
815 help='answer yes to all prompts')
816
817 opts = parser.parse_args(argv)
Mike Frysinger90e49ca2014-01-14 14:42:07 -0500818 opts.Freeze()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400819
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500820 if opts.sym_paths:
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400821 if opts.regenerate:
822 cros_build_lib.Die('--regenerate may not be used with specific files')
823 else:
824 if opts.board is None:
825 cros_build_lib.Die('--board is required')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400826
827 if opts.breakpad_root and opts.regenerate:
828 cros_build_lib.Die('--regenerate may not be used with --breakpad_root')
829
830 if opts.testing:
831 # TODO(build): Kill off --testing mode once unittests are up-to-snuff.
832 cros_build_lib.Info('running in testing mode')
833 # pylint: disable=W0601,W0603
834 global INITIAL_RETRY_DELAY, SymUpload, DEFAULT_SLEEP_DELAY
835 INITIAL_RETRY_DELAY = DEFAULT_SLEEP_DELAY = 0
836 SymUpload = TestingSymUpload
837
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500838 dedupe_namespace = None
839 if opts.dedupe:
840 if opts.official_build and not opts.testing:
841 dedupe_namespace = OFFICIAL_DEDUPE_NAMESPACE
842 else:
843 dedupe_namespace = STAGING_DEDUPE_NAMESPACE
844
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400845 if not opts.yes:
Mike Frysingerc5de9602014-02-09 02:42:36 -0500846 prolog = '\n'.join(textwrap.wrap(textwrap.dedent("""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400847 Uploading symbols for an entire Chromium OS build is really only
848 necessary for release builds and in a few cases for developers
849 to debug problems. It will take considerable time to run. For
850 developer debugging purposes, consider instead passing specific
851 files to upload.
Mike Frysingerc5de9602014-02-09 02:42:36 -0500852 """), 80)).strip()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400853 if not cros_build_lib.BooleanPrompt(
854 prompt='Are you sure you want to upload all build symbols',
Mike Frysingerc5de9602014-02-09 02:42:36 -0500855 default=False, prolog=prolog):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400856 cros_build_lib.Die('better safe than sorry')
857
858 ret = 0
859 if opts.regenerate:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400860 ret += cros_generate_breakpad_symbols.GenerateBreakpadSymbols(
861 opts.board, breakpad_dir=opts.breakpad_root)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400862
863 ret += UploadSymbols(opts.board, official=opts.official_build,
Mike Frysinger38647542014-09-12 18:15:39 -0700864 server=opts.server, breakpad_dir=opts.breakpad_root,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400865 file_limit=opts.strip_cfi, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500866 upload_limit=opts.upload_limit, sym_paths=opts.sym_paths,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500867 failed_list=opts.failed_list,
868 dedupe_namespace=dedupe_namespace)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400869 if ret:
870 cros_build_lib.Error('encountered %i problem(s)', ret)
871 # Since exit(status) gets masked, clamp it to 1 so we don't inadvertently
872 # return 0 in case we are a multiple of the mask.
873 ret = 1
874
875 return ret
Mike Frysinger094a2172013-08-14 12:54:35 -0400876
877
878# We need this to run once per process. Do it at module import time as that
879# will let us avoid doing it inline at function call time (see SymUpload) as
880# that func might be called by the multiprocessing module which means we'll
881# do the opener logic multiple times overall. Plus, if you're importing this
882# module, it's a pretty good chance that you're going to need this.
883poster.streaminghttp.register_openers()