blob: ccb5cd10f390cdc67854b3df59bd34d785a279e3 [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 Frysingerc5597f22014-11-27 15:39:15 -050033import sys
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040034import textwrap
35import tempfile
36import time
Mike Frysinger094a2172013-08-14 12:54:35 -040037import urllib2
Mike Frysingerd41938e2014-02-10 06:37:55 -050038import urlparse
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040039
Don Garrett88b8d782014-05-13 17:30:55 -070040from chromite.cbuildbot import constants
Mike Frysingerd41938e2014-02-10 06:37:55 -050041from chromite.lib import cache
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040042from chromite.lib import commandline
43from chromite.lib import cros_build_lib
Ralph Nathan5a582ff2015-03-20 18:18:30 -070044from chromite.lib import cros_logging as logging
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.
Mike Frysingerc5597f22014-11-27 15:39:15 -050054# We don't want to import the general keyring module as that will implicitly
55# try to import & connect to a dbus server. That's a waste of time.
56sys.modules['keyring'] = None
57import isolateserver
Mike Frysinger0c0efa22014-02-09 23:32:23 -050058
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040059
60# URLs used for uploading symbols.
61OFFICIAL_UPLOAD_URL = 'http://clients2.google.com/cr/symbol'
62STAGING_UPLOAD_URL = 'http://clients2.google.com/cr/staging_symbol'
63
64
65# The crash server rejects files that are this big.
66CRASH_SERVER_FILE_LIMIT = 350 * 1024 * 1024
67# Give ourselves a little breathing room from what the server expects.
68DEFAULT_FILE_LIMIT = CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024)
69
70
Mike Frysinger0c0efa22014-02-09 23:32:23 -050071# The batch limit when talking to the dedup server. We avoid sending one at a
72# time as the round trip overhead will dominate. Conversely, we avoid sending
73# all at once so we can start uploading symbols asap -- the symbol server is a
74# bit slow and will take longer than anything else.
75# TODO: A better algorithm would be adaptive. If we have more than one symbol
76# in the upload queue waiting, we could send more symbols to the dedupe server
77# at a time.
78DEDUPE_LIMIT = 100
79
80# How long to wait for the server to respond with the results. Note that the
81# larger the limit above, the larger this will need to be. So we give it ~1
82# second per item max.
83DEDUPE_TIMEOUT = DEDUPE_LIMIT
84
Mike Frysinger4dd462e2014-04-30 16:21:51 -040085# How long to wait for the notification to finish (in minutes). If it takes
86# longer than this, we'll stop notifiying, but that's not a big deal as we
87# will be able to recover in later runs.
88DEDUPE_NOTIFY_TIMEOUT = 20
89
Mike Frysinger0c0efa22014-02-09 23:32:23 -050090# The unique namespace in the dedupe server that only we use. Helps avoid
91# collisions with all the hashed values and unrelated content.
92OFFICIAL_DEDUPE_NAMESPACE = 'chromium-os-upload-symbols'
93STAGING_DEDUPE_NAMESPACE = '%s-staging' % OFFICIAL_DEDUPE_NAMESPACE
94
95
Mike Frysinger71046662014-09-12 18:15:15 -070096# The minimum average rate (in bytes per second) that we expect to maintain
97# when uploading symbols. This has to allow for symbols that are up to
98# CRASH_SERVER_FILE_LIMIT in size.
99UPLOAD_MIN_RATE = CRASH_SERVER_FILE_LIMIT / (30 * 60)
100
101# The lowest timeout (in seconds) we'll allow. If the server is overloaded,
102# then there might be a delay in setting up the connection, not just with the
103# transfer. So even a small file might need a larger value.
104UPLOAD_MIN_TIMEOUT = 2 * 60
Mike Frysingercd78a082013-06-26 17:13:04 -0400105
106
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400107# Sleep for 200ms in between uploads to avoid DoS'ing symbol server.
108DEFAULT_SLEEP_DELAY = 0.2
109
110
111# Number of seconds to wait before retrying an upload. The delay will double
112# for each subsequent retry of the same symbol file.
113INITIAL_RETRY_DELAY = 1
114
115# Allow up to 7 attempts to upload a symbol file (total delay may be
116# 1+2+4+8+16+32=63 seconds).
117MAX_RETRIES = 6
118
Mike Frysingereb753bf2013-11-22 16:05:35 -0500119# Number of total errors, before uploads are no longer attempted.
120# This is used to avoid lots of errors causing unreasonable delays.
121# See the related, but independent, error values below.
122MAX_TOTAL_ERRORS_FOR_RETRY = 30
123
124# A watermark of transient errors which we allow recovery from. If we hit
125# errors infrequently, overall we're probably doing fine. For example, if
126# we have one failure every 100 passes, then we probably don't want to fail
127# right away. But if we hit a string of failures in a row, we want to abort.
128#
129# The watermark starts at 0 (and can never go below that). When this error
130# level is exceeded, we stop uploading. When a failure happens, we add the
131# fail adjustment, and when an upload succeeds, we add the pass adjustment.
132# We want to penalize failures more so that we ramp up when there is a string
133# of them, but then slowly back off as things start working.
134#
135# A quick example:
136# 0.0: Starting point.
137# 0.0: Upload works, so add -0.5, and then clamp to 0.
138# 1.0: Upload fails, so add 1.0.
139# 2.0: Upload fails, so add 1.0.
140# 1.5: Upload works, so add -0.5.
141# 1.0: Upload works, so add -0.5.
142ERROR_WATERMARK = 3.0
143ERROR_ADJUST_FAIL = 1.0
144ERROR_ADJUST_PASS = -0.5
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400145
146
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700147def GetUploadTimeout(path):
148 """How long to wait for a specific file to upload to the crash server.
149
150 This is a function largely to make unittesting easier.
151
152 Args:
153 path: The path to the file to calculate the timeout for
154
155 Returns:
156 Timeout length (in seconds)
157 """
158 # Scale the timeout based on the filesize.
159 return max(os.path.getsize(path) / UPLOAD_MIN_RATE, UPLOAD_MIN_TIMEOUT)
160
161
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500162def SymUpload(upload_url, sym_item):
Mike Frysinger094a2172013-08-14 12:54:35 -0400163 """Upload a symbol file to a HTTP server
164
165 The upload is a multipart/form-data POST with the following parameters:
166 code_file: the basename of the module, e.g. "app"
167 code_identifier: the module file's identifier
168 debug_file: the basename of the debugging file, e.g. "app"
169 debug_identifier: the debug file's identifier, usually consisting of
170 the guid and age embedded in the pdb, e.g.
171 "11111111BBBB3333DDDD555555555555F"
172 version: the file version of the module, e.g. "1.2.3.4"
173 product: HTTP-friendly product name
174 os: the operating system that the module was built for
175 cpu: the CPU that the module was built for
176 symbol_file: the contents of the breakpad-format symbol file
177
178 Args:
Mike Frysinger094a2172013-08-14 12:54:35 -0400179 upload_url: The crash URL to POST the |sym_file| to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500180 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysinger094a2172013-08-14 12:54:35 -0400181 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500182 sym_header = sym_item.sym_header
183 sym_file = sym_item.sym_file
Mike Frysinger094a2172013-08-14 12:54:35 -0400184
185 fields = (
186 ('code_file', sym_header.name),
187 ('debug_file', sym_header.name),
188 ('debug_identifier', sym_header.id.replace('-', '')),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400189 # The product/version fields are used by the server only for statistic
190 # purposes. They do not impact symbolization, so they're safe to set
191 # to any value all the time.
192 # In this case, we use it to help see the load our build system is
193 # placing on the server.
194 # Not sure what to set for the version. Maybe the git sha1 of this file.
195 # Note: the server restricts this to 30 chars.
Mike Frysinger094a2172013-08-14 12:54:35 -0400196 #('version', None),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400197 ('product', 'ChromeOS'),
Mike Frysinger094a2172013-08-14 12:54:35 -0400198 ('os', sym_header.os),
199 ('cpu', sym_header.cpu),
200 poster.encode.MultipartParam.from_file('symbol_file', sym_file),
201 )
202
203 data, headers = poster.encode.multipart_encode(fields)
204 request = urllib2.Request(upload_url, data, headers)
205 request.add_header('User-agent', 'chromite.upload_symbols')
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700206 urllib2.urlopen(request, timeout=GetUploadTimeout(sym_file))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400207
208
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500209def TestingSymUpload(upload_url, sym_item):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400210 """A stub version of SymUpload for --testing usage"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500211 cmd = ['sym_upload', sym_item.sym_file, upload_url]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400212 # Randomly fail 80% of the time (the retry logic makes this 80%/3 per file).
213 returncode = random.randint(1, 100) <= 80
Ralph Nathan5a582ff2015-03-20 18:18:30 -0700214 logging.debug('would run (and return %i): %s', returncode,
215 cros_build_lib.CmdToStr(cmd))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400216 if returncode:
217 output = 'Failed to send the symbol file.'
218 else:
219 output = 'Successfully sent the symbol file.'
220 result = cros_build_lib.CommandResult(cmd=cmd, error=None, output=output,
221 returncode=returncode)
222 if returncode:
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500223 exceptions = (
Mike Frysingerfd355652014-01-23 02:57:48 -0500224 socket.error('[socket.error] forced test fail'),
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500225 httplib.BadStatusLine('[BadStatusLine] forced test fail'),
226 urllib2.HTTPError(upload_url, 400, '[HTTPError] forced test fail',
227 {}, None),
228 urllib2.URLError('[URLError] forced test fail'),
229 )
230 raise random.choice(exceptions)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400231 else:
232 return result
233
234
Mike Frysingereb753bf2013-11-22 16:05:35 -0500235def ErrorLimitHit(num_errors, watermark_errors):
236 """See if our error limit has been hit
237
238 Args:
239 num_errors: A multiprocessing.Value of the raw number of failures.
240 watermark_errors: A multiprocessing.Value of the current rate of failures.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500241
Mike Frysingereb753bf2013-11-22 16:05:35 -0500242 Returns:
243 True if our error limits have been exceeded.
244 """
245 return ((num_errors is not None and
246 num_errors.value > MAX_TOTAL_ERRORS_FOR_RETRY) or
247 (watermark_errors is not None and
248 watermark_errors.value > ERROR_WATERMARK))
249
250
251def _UpdateCounter(counter, adj):
252 """Update |counter| by |adj|
253
254 Handle atomic updates of |counter|. Also make sure it does not
255 fall below 0.
256
257 Args:
258 counter: A multiprocessing.Value to update
259 adj: The value to add to |counter|
260 """
261 def _Update():
262 clamp = 0 if type(adj) is int else 0.0
263 counter.value = max(clamp, counter.value + adj)
264
265 if hasattr(counter, 'get_lock'):
266 with counter.get_lock():
267 _Update()
268 elif counter is not None:
269 _Update()
270
271
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500272def UploadSymbol(upload_url, sym_item, file_limit=DEFAULT_FILE_LIMIT,
Mike Frysinger02e92402013-11-22 16:22:02 -0500273 sleep=0, num_errors=None, watermark_errors=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500274 failed_queue=None, passed_queue=None):
275 """Upload |sym_item| to |upload_url|
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400276
277 Args:
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400278 upload_url: The crash server to upload things to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500279 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400280 file_limit: The max file size of a symbol file before we try to strip it
281 sleep: Number of seconds to sleep before running
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400282 num_errors: An object to update with the error count (needs a .value member)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500283 watermark_errors: An object to track current error behavior (needs a .value)
Mike Frysinger02e92402013-11-22 16:22:02 -0500284 failed_queue: When a symbol fails, add it to this queue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500285 passed_queue: When a symbol passes, add it to this queue
Mike Frysinger1a736a82013-12-12 01:50:59 -0500286
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400287 Returns:
288 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400289 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500290 sym_file = sym_item.sym_file
291 upload_item = sym_item
292
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400293 if num_errors is None:
294 num_errors = ctypes.c_int()
Mike Frysingereb753bf2013-11-22 16:05:35 -0500295 if ErrorLimitHit(num_errors, watermark_errors):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400296 # Abandon ship! It's on fire! NOoooooooooooOOOoooooo.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500297 if failed_queue:
298 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400299 return 0
300
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400301 if sleep:
302 # Keeps us from DoS-ing the symbol server.
303 time.sleep(sleep)
304
Ralph Nathan5a582ff2015-03-20 18:18:30 -0700305 logging.debug('uploading %s' % sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400306
307 # Ideally there'd be a tempfile.SpooledNamedTemporaryFile that we could use.
308 with tempfile.NamedTemporaryFile(prefix='upload_symbols',
309 bufsize=0) as temp_sym_file:
310 if file_limit:
311 # If the symbols size is too big, strip out the call frame info. The CFI
312 # is unnecessary for 32bit x86 targets where the frame pointer is used (as
313 # all of ours have) and it accounts for over half the size of the symbols
314 # uploaded.
315 file_size = os.path.getsize(sym_file)
316 if file_size > file_limit:
317 cros_build_lib.Warning('stripping CFI from %s due to size %s > %s',
318 sym_file, file_size, file_limit)
319 temp_sym_file.writelines([x for x in open(sym_file, 'rb').readlines()
320 if not x.startswith('STACK CFI')])
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500321
322 upload_item = FakeItem(sym_file=temp_sym_file.name,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500323 sym_header=sym_item.sym_header)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400324
325 # Hopefully the crash server will let it through. But it probably won't.
326 # Not sure what the best answer is in this case.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500327 file_size = os.path.getsize(upload_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400328 if file_size > CRASH_SERVER_FILE_LIMIT:
329 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger02e92402013-11-22 16:22:02 -0500330 cros_build_lib.Warning('upload file %s is awfully large, risking '
331 'rejection by the symbol server (%s > %s)',
332 sym_file, file_size, CRASH_SERVER_FILE_LIMIT)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400333
334 # Upload the symbol file.
Mike Frysingereb753bf2013-11-22 16:05:35 -0500335 success = False
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400336 try:
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400337 cros_build_lib.TimedCommand(
David Jamesc93e6a4d2014-01-13 11:37:36 -0800338 retry_util.RetryException,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400339 (urllib2.HTTPError, urllib2.URLError), MAX_RETRIES, SymUpload,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500340 upload_url, upload_item, sleep=INITIAL_RETRY_DELAY,
Mike Frysingerf50187c2014-11-27 22:01:29 -0500341 timed_log_msg=('upload of %10i bytes took %%(delta)s: %s' %
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500342 (file_size, os.path.basename(sym_file))))
Mike Frysingereb753bf2013-11-22 16:05:35 -0500343 success = True
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500344
345 if passed_queue:
346 passed_queue.put(sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400347 except urllib2.HTTPError as e:
348 cros_build_lib.Warning('could not upload: %s: HTTP %s: %s',
349 os.path.basename(sym_file), e.code, e.reason)
Mike Frysingerfd355652014-01-23 02:57:48 -0500350 except (urllib2.URLError, httplib.HTTPException, socket.error) as e:
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400351 cros_build_lib.Warning('could not upload: %s: %s',
352 os.path.basename(sym_file), e)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500353 finally:
354 if success:
355 _UpdateCounter(watermark_errors, ERROR_ADJUST_PASS)
356 else:
357 _UpdateCounter(num_errors, 1)
358 _UpdateCounter(watermark_errors, ERROR_ADJUST_FAIL)
Mike Frysinger02e92402013-11-22 16:22:02 -0500359 if failed_queue:
360 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400361
362 return num_errors.value
363
364
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500365# A dummy class that allows for stubbing in tests and SymUpload.
366FakeItem = cros_build_lib.Collection(
367 'FakeItem', sym_file=None, sym_header=None, content=lambda x: '')
368
369
Mike Frysingerc5597f22014-11-27 15:39:15 -0500370class SymbolItem(isolateserver.BufferItem):
371 """Turn a sym_file into an isolateserver.Item"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500372
Mike Frysingerc5597f22014-11-27 15:39:15 -0500373 ALGO = hashlib.sha1
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500374
Mike Frysingerc5597f22014-11-27 15:39:15 -0500375 def __init__(self, sym_file):
376 sym_header = cros_generate_breakpad_symbols.ReadSymsHeader(sym_file)
377 super(SymbolItem, self).__init__(str(sym_header), self.ALGO)
378 self.sym_header = sym_header
379 self.sym_file = sym_file
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500380
381
382def SymbolDeduplicatorNotify(dedupe_namespace, dedupe_queue):
383 """Send a symbol file to the swarming service
384
385 Notify the swarming service of a successful upload. If the notification fails
386 for any reason, we ignore it. We don't care as it just means we'll upload it
387 again later on, and the symbol server will handle that graciously.
388
389 This func runs in a different process from the main one, so we cannot share
390 the storage object. Instead, we create our own. This func stays alive for
391 the life of the process, so we only create one here overall.
392
393 Args:
394 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
395 dedupe_queue: The queue to read SymbolItems from
396 """
397 if dedupe_queue is None:
398 return
399
400 item = None
401 try:
Mike Frysinger650e6722014-04-28 18:29:15 -0400402 with timeout_util.Timeout(DEDUPE_TIMEOUT):
403 storage = isolateserver.get_storage_api(constants.ISOLATESERVER,
404 dedupe_namespace)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500405 for item in iter(dedupe_queue.get, None):
406 with timeout_util.Timeout(DEDUPE_TIMEOUT):
Ralph Nathan5a582ff2015-03-20 18:18:30 -0700407 logging.debug('sending %s to dedupe server', item.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500408 storage.push(item, item.content(0))
Ralph Nathan5a582ff2015-03-20 18:18:30 -0700409 logging.debug('sent %s', item.sym_file)
Mike Frysingerae298452014-03-24 22:45:23 -0400410 cros_build_lib.Info('dedupe notification finished; exiting')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500411 except Exception:
412 sym_file = item.sym_file if (item and item.sym_file) else ''
413 cros_build_lib.Warning('posting %s to dedupe server failed',
414 os.path.basename(sym_file), exc_info=True)
415
Mike Frysinger58312e92014-03-18 04:18:36 -0400416 # Keep draining the queue though so it doesn't fill up.
417 while dedupe_queue.get() is not None:
418 continue
419
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500420
421def SymbolDeduplicator(storage, sym_paths):
422 """Filter out symbol files that we've already uploaded
423
424 Using the swarming service, ask it to tell us which symbol files we've already
425 uploaded in previous runs and/or by other bots. If the query fails for any
426 reason, we'll just upload all symbols. This is fine as the symbol server will
427 do the right thing and this phase is purely an optimization.
428
429 This code runs in the main thread which is why we can re-use the existing
430 storage object. Saves us from having to recreate one all the time.
431
432 Args:
433 storage: An isolateserver.StorageApi object
434 sym_paths: List of symbol files to check against the dedupe server
435
436 Returns:
437 List of symbol files that have not been uploaded before
438 """
439 if not sym_paths:
440 return sym_paths
441
442 items = [SymbolItem(x) for x in sym_paths]
443 if storage:
444 try:
445 with timeout_util.Timeout(DEDUPE_TIMEOUT):
446 items = storage.contains(items)
447 except Exception:
448 cros_build_lib.Warning('talking to dedupe server failed', exc_info=True)
449
450 return items
451
452
Mike Frysingerd41938e2014-02-10 06:37:55 -0500453def IsTarball(path):
454 """Guess if this is a tarball based on the filename."""
455 parts = path.split('.')
456 if len(parts) <= 1:
457 return False
458
459 if parts[-1] == 'tar':
460 return True
461
462 if parts[-2] == 'tar':
463 return parts[-1] in ('bz2', 'gz', 'xz')
464
465 return parts[-1] in ('tbz2', 'tbz', 'tgz', 'txz')
466
467
468def SymbolFinder(tempdir, paths):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500469 """Locate symbol files in |paths|
470
471 Args:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500472 tempdir: Path to use for temporary files (caller will clean up).
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500473 paths: A list of input paths to walk. Files are returned w/out any checks.
Mike Frysingerd41938e2014-02-10 06:37:55 -0500474 Dirs are searched for files that end in ".sym". Urls are fetched and then
475 processed. Tarballs are unpacked and walked.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500476
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500477 Returns:
478 Yield every viable sym file.
479 """
Mike Frysingere847efd2015-01-08 03:57:24 -0500480 cache_dir = commandline.GetCacheDir()
481 common_path = os.path.join(cache_dir, constants.COMMON_CACHE)
482 tar_cache = cache.TarballCache(common_path)
483
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500484 for p in paths:
Don Garrett25f309a2014-03-19 14:02:12 -0700485 # Pylint is confused about members of ParseResult.
Don Garrettf8bf7842014-03-20 17:03:42 -0700486
Mike Frysingerd41938e2014-02-10 06:37:55 -0500487 o = urlparse.urlparse(p)
Don Garrettf8bf7842014-03-20 17:03:42 -0700488 if o.scheme: # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500489 # Support globs of filenames.
490 ctx = gs.GSContext()
491 for p in ctx.LS(p):
492 cros_build_lib.Info('processing files inside %s', p)
493 o = urlparse.urlparse(p)
Don Garrettf8bf7842014-03-20 17:03:42 -0700494 key = ('%s%s' % (o.netloc, o.path)).split('/') # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500495 # The common cache will not be LRU, removing the need to hold a read
496 # lock on the cached gsutil.
497 ref = tar_cache.Lookup(key)
498 try:
499 ref.SetDefault(p)
500 except cros_build_lib.RunCommandError as e:
501 cros_build_lib.Warning('ignoring %s\n%s', p, e)
502 continue
503 for p in SymbolFinder(tempdir, [ref.path]):
504 yield p
505
506 elif os.path.isdir(p):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500507 for root, _, files in os.walk(p):
508 for f in files:
509 if f.endswith('.sym'):
510 yield os.path.join(root, f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500511
512 elif IsTarball(p):
513 cros_build_lib.Info('processing files inside %s', p)
514 tardir = tempfile.mkdtemp(dir=tempdir)
515 cache.Untar(os.path.realpath(p), tardir)
516 for p in SymbolFinder(tardir, [tardir]):
517 yield p
518
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500519 else:
520 yield p
521
522
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500523def WriteQueueToFile(listing, queue, relpath=None):
524 """Write all the items in |queue| to the |listing|.
525
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500526 Note: The queue must have a sentinel None appended to the end.
527
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500528 Args:
529 listing: Where to write out the list of files.
530 queue: The queue of paths to drain.
531 relpath: If set, write out paths relative to this one.
532 """
533 if not listing:
Mike Frysingera0ddac62014-03-14 10:30:25 -0400534 # Still drain the queue so we make sure the producer has finished
535 # before we return. Otherwise, the queue might get destroyed too
536 # quickly which will trigger a traceback in the producer.
537 while queue.get() is not None:
538 continue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500539 return
540
541 with cros_build_lib.Open(listing, 'wb+') as f:
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500542 while True:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500543 path = queue.get()
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500544 if path is None:
545 return
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500546 if relpath:
547 path = os.path.relpath(path, relpath)
548 f.write('%s\n' % path)
549
550
Mike Frysinger38647542014-09-12 18:15:39 -0700551def UploadSymbols(board=None, official=False, server=None, breakpad_dir=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400552 file_limit=DEFAULT_FILE_LIMIT, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500553 upload_limit=None, sym_paths=None, failed_list=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500554 root=None, retry=True, dedupe_namespace=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400555 """Upload all the generated symbols for |board| to the crash server
556
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400557 You can use in a few ways:
558 * pass |board| to locate all of its symbols
559 * pass |breakpad_dir| to upload all the symbols in there
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500560 * pass |sym_paths| to upload specific symbols (or dirs of symbols)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400561
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400562 Args:
563 board: The board whose symbols we wish to upload
564 official: Use the official symbol server rather than the staging one
Mike Frysinger38647542014-09-12 18:15:39 -0700565 server: Explicit server to post symbols to
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400566 breakpad_dir: The full path to the breakpad directory where symbols live
567 file_limit: The max file size of a symbol file before we try to strip it
568 sleep: How long to sleep in between uploads
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500569 upload_limit: If set, only upload this many symbols (meant for testing)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500570 sym_paths: Specific symbol files (or dirs of sym files) to upload,
571 otherwise search |breakpad_dir|
Mike Frysinger7f9be142014-01-15 02:16:42 -0500572 failed_list: Write the names of all sym files we did not upload; can be a
573 filename or file-like object.
Mike Frysinger118d2502013-08-19 03:36:56 -0400574 root: The tree to prefix to |breakpad_dir| (if |breakpad_dir| is not set)
Mike Frysinger02e92402013-11-22 16:22:02 -0500575 retry: Whether we should retry failures.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500576 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500577
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400578 Returns:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400579 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400580 """
Mike Frysinger38647542014-09-12 18:15:39 -0700581 if server is None:
582 if official:
583 upload_url = OFFICIAL_UPLOAD_URL
584 else:
585 cros_build_lib.Warning('unofficial builds upload to the staging server')
586 upload_url = STAGING_UPLOAD_URL
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400587 else:
Mike Frysinger38647542014-09-12 18:15:39 -0700588 upload_url = server
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400589
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500590 if sym_paths:
591 cros_build_lib.Info('uploading specified symbols to %s', upload_url)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400592 else:
593 if breakpad_dir is None:
Mike Frysingerc5597f22014-11-27 15:39:15 -0500594 if root is None:
595 raise ValueError('breakpad_dir requires root to be set')
Mike Frysinger118d2502013-08-19 03:36:56 -0400596 breakpad_dir = os.path.join(
597 root,
598 cros_generate_breakpad_symbols.FindBreakpadDir(board).lstrip('/'))
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400599 cros_build_lib.Info('uploading all symbols to %s from %s', upload_url,
600 breakpad_dir)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500601 sym_paths = [breakpad_dir]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400602
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500603 # We use storage_query to ask the server about existing symbols. The
604 # storage_notify_proc process is used to post updates to the server. We
605 # cannot safely share the storage object between threads/processes, but
606 # we also want to minimize creating new ones as each object has to init
607 # new state (like server connections).
Mike Frysinger650e6722014-04-28 18:29:15 -0400608 storage_query = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500609 if dedupe_namespace:
610 dedupe_limit = DEDUPE_LIMIT
611 dedupe_queue = multiprocessing.Queue()
Mike Frysinger650e6722014-04-28 18:29:15 -0400612 try:
613 with timeout_util.Timeout(DEDUPE_TIMEOUT):
614 storage_query = isolateserver.get_storage_api(constants.ISOLATESERVER,
615 dedupe_namespace)
616 except Exception:
617 cros_build_lib.Warning('initializing dedupe server connection failed',
618 exc_info=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500619 else:
620 dedupe_limit = 1
Mike Frysinger650e6722014-04-28 18:29:15 -0400621 dedupe_queue = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500622 # Can't use parallel.BackgroundTaskRunner because that'll create multiple
623 # processes and we want only one the whole time (see comment above).
624 storage_notify_proc = multiprocessing.Process(
625 target=SymbolDeduplicatorNotify, args=(dedupe_namespace, dedupe_queue))
626
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400627 bg_errors = multiprocessing.Value('i')
Mike Frysingereb753bf2013-11-22 16:05:35 -0500628 watermark_errors = multiprocessing.Value('f')
Mike Frysinger02e92402013-11-22 16:22:02 -0500629 failed_queue = multiprocessing.Queue()
630 uploader = functools.partial(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500631 UploadSymbol, upload_url, file_limit=file_limit, sleep=sleep,
632 num_errors=bg_errors, watermark_errors=watermark_errors,
633 failed_queue=failed_queue, passed_queue=dedupe_queue)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400634
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500635 start_time = datetime.datetime.now()
636 Counters = cros_build_lib.Collection(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500637 'Counters', upload_limit=upload_limit, uploaded_count=0, deduped_count=0)
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500638 counters = Counters()
639
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500640 def _Upload(queue, counters, files):
641 if not files:
642 return
643
644 missing_count = 0
645 for item in SymbolDeduplicator(storage_query, files):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500646 missing_count += 1
Mike Frysingerd42e5f02014-03-14 11:19:37 -0400647
648 if counters.upload_limit == 0:
649 continue
650
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500651 queue.put((item,))
652 counters.uploaded_count += 1
653 if counters.upload_limit is not None:
654 counters.upload_limit -= 1
655
656 counters.deduped_count += (len(files) - missing_count)
657
Mike Frysinger13870082014-03-14 10:41:20 -0400658 try:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500659 storage_notify_proc.start()
Mike Frysinger02e92402013-11-22 16:22:02 -0500660
Mike Frysinger13870082014-03-14 10:41:20 -0400661 with osutils.TempDir(prefix='upload_symbols.') as tempdir:
662 # For the first run, we collect the symbols that failed. If the
663 # overall failure rate was low, we'll retry them on the second run.
664 for retry in (retry, False):
665 # We need to limit ourselves to one upload at a time to avoid the server
666 # kicking in DoS protection. See these bugs for more details:
667 # http://crbug.com/209442
668 # http://crbug.com/212496
669 with parallel.BackgroundTaskRunner(uploader, processes=1) as queue:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500670 dedupe_list = []
Mike Frysinger13870082014-03-14 10:41:20 -0400671 for sym_file in SymbolFinder(tempdir, sym_paths):
672 dedupe_list.append(sym_file)
673 dedupe_len = len(dedupe_list)
674 if dedupe_len < dedupe_limit:
675 if (counters.upload_limit is None or
676 dedupe_len < counters.upload_limit):
677 continue
Mike Frysinger02e92402013-11-22 16:22:02 -0500678
Mike Frysinger1010a892014-03-14 11:24:17 -0400679 # We check the counter before _Upload so that we don't keep talking
680 # to the dedupe server. Otherwise, we end up sending one symbol at
681 # a time to it and that slows things down a lot.
682 if counters.upload_limit == 0:
683 break
684
Mike Frysinger13870082014-03-14 10:41:20 -0400685 _Upload(queue, counters, dedupe_list)
686 dedupe_list = []
687 _Upload(queue, counters, dedupe_list)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500688
Mike Frysinger13870082014-03-14 10:41:20 -0400689 # See if we need to retry, and if we haven't failed too many times yet.
690 if not retry or ErrorLimitHit(bg_errors, watermark_errors):
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500691 break
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500692
Mike Frysinger13870082014-03-14 10:41:20 -0400693 sym_paths = []
694 failed_queue.put(None)
695 while True:
696 sym_path = failed_queue.get()
697 if sym_path is None:
698 break
699 sym_paths.append(sym_path)
Mike Frysinger02e92402013-11-22 16:22:02 -0500700
Mike Frysinger13870082014-03-14 10:41:20 -0400701 if sym_paths:
702 cros_build_lib.Warning('retrying %i symbols', len(sym_paths))
703 if counters.upload_limit is not None:
704 counters.upload_limit += len(sym_paths)
705 # Decrement the error count in case we recover in the second pass.
706 assert bg_errors.value >= len(sym_paths), \
707 'more failed files than errors?'
708 bg_errors.value -= len(sym_paths)
709 else:
710 # No failed symbols, so just return now.
711 break
Mike Frysinger7f9be142014-01-15 02:16:42 -0500712
Mike Frysinger13870082014-03-14 10:41:20 -0400713 # If the user has requested it, save all the symbol files that we failed to
714 # upload to a listing file. This should help with recovery efforts later.
715 failed_queue.put(None)
716 WriteQueueToFile(failed_list, failed_queue, breakpad_dir)
717
718 finally:
Mike Frysingerae298452014-03-24 22:45:23 -0400719 cros_build_lib.Info('finished uploading; joining background process')
Mike Frysinger13870082014-03-14 10:41:20 -0400720 if dedupe_queue:
721 dedupe_queue.put(None)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400722
723 # The notification might be slow going, so give it some time to finish.
724 # We have to poll here as the process monitor is watching for output and
725 # will kill us if we go silent for too long.
726 wait_minutes = DEDUPE_NOTIFY_TIMEOUT
727 while storage_notify_proc.is_alive() and wait_minutes > 0:
Aviv Keshetd1f04632014-05-09 11:33:46 -0700728 if dedupe_queue:
729 qsize = str(dedupe_queue.qsize())
730 else:
731 qsize = '[None]'
732 cros_build_lib.Info('waiting up to %i minutes for ~%s notifications',
733 wait_minutes, qsize)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400734 storage_notify_proc.join(60)
735 wait_minutes -= 1
736
737 # The process is taking too long, so kill it and complain.
738 if storage_notify_proc.is_alive():
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400739 cros_build_lib.Warning('notification process took too long')
740 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500741
Mike Frysinger66e51e92014-05-03 16:52:00 -0400742 # Kill it gracefully first (traceback) before tacking it down harder.
743 pid = storage_notify_proc.pid
744 for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGKILL):
745 cros_build_lib.Warning('sending %s to %i', signals.StrSignal(sig), pid)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400746 # The process might have exited between the last check and the
747 # actual kill below, so ignore ESRCH errors.
748 try:
749 os.kill(pid, sig)
750 except OSError as e:
751 if e.errno == errno.ESRCH:
752 break
753 else:
754 raise
Mike Frysinger66e51e92014-05-03 16:52:00 -0400755 time.sleep(5)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400756 if not storage_notify_proc.is_alive():
Mike Frysinger66e51e92014-05-03 16:52:00 -0400757 break
758
759 # Drain the queue so we don't hang when we finish.
760 try:
Mike Frysinger4f5ea832014-05-12 00:54:28 -0400761 cros_build_lib.Warning('draining the notify queue manually')
762 with timeout_util.Timeout(60):
763 try:
764 while dedupe_queue.get_nowait():
765 pass
766 except Queue.Empty:
767 pass
768 except timeout_util.TimeoutError:
769 cros_build_lib.Warning('draining the notify queue failed; trashing it')
770 dedupe_queue.cancel_join_thread()
Mike Frysinger66e51e92014-05-03 16:52:00 -0400771
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500772 cros_build_lib.Info('uploaded %i symbols (%i were deduped) which took: %s',
773 counters.uploaded_count, counters.deduped_count,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500774 datetime.datetime.now() - start_time)
775
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500776 return bg_errors.value
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400777
778
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400779def main(argv):
780 parser = commandline.ArgumentParser(description=__doc__)
781
Mike Frysingerd41938e2014-02-10 06:37:55 -0500782 parser.add_argument('sym_paths', type='path_or_uri', nargs='*', default=None,
783 help='symbol file or directory or URL or tarball')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400784 parser.add_argument('--board', default=None,
785 help='board to build packages for')
786 parser.add_argument('--breakpad_root', type='path', default=None,
Mike Frysingerc5597f22014-11-27 15:39:15 -0500787 help='full path to the breakpad symbol directory')
788 parser.add_argument('--root', type='path', default=None,
789 help='full path to the chroot dir')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400790 parser.add_argument('--official_build', action='store_true', default=False,
791 help='point to official symbol server')
Mike Frysinger38647542014-09-12 18:15:39 -0700792 parser.add_argument('--server', type=str, default=None,
793 help='URI for custom symbol server')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400794 parser.add_argument('--regenerate', action='store_true', default=False,
795 help='regenerate all symbols')
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500796 parser.add_argument('--upload-limit', type=int, default=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400797 help='only upload # number of symbols')
798 parser.add_argument('--strip_cfi', type=int,
799 default=CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024),
800 help='strip CFI data for files above this size')
Mike Frysinger7f9be142014-01-15 02:16:42 -0500801 parser.add_argument('--failed-list', type='path',
802 help='where to save a list of failed symbols')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500803 parser.add_argument('--dedupe', action='store_true', default=False,
804 help='use the swarming service to avoid re-uploading')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400805 parser.add_argument('--testing', action='store_true', default=False,
806 help='run in testing mode')
807 parser.add_argument('--yes', action='store_true', default=False,
808 help='answer yes to all prompts')
809
810 opts = parser.parse_args(argv)
Mike Frysinger90e49ca2014-01-14 14:42:07 -0500811 opts.Freeze()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400812
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500813 if opts.sym_paths:
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400814 if opts.regenerate:
815 cros_build_lib.Die('--regenerate may not be used with specific files')
816 else:
817 if opts.board is None:
818 cros_build_lib.Die('--board is required')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400819
820 if opts.breakpad_root and opts.regenerate:
821 cros_build_lib.Die('--regenerate may not be used with --breakpad_root')
822
823 if opts.testing:
824 # TODO(build): Kill off --testing mode once unittests are up-to-snuff.
825 cros_build_lib.Info('running in testing mode')
826 # pylint: disable=W0601,W0603
827 global INITIAL_RETRY_DELAY, SymUpload, DEFAULT_SLEEP_DELAY
828 INITIAL_RETRY_DELAY = DEFAULT_SLEEP_DELAY = 0
829 SymUpload = TestingSymUpload
830
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500831 dedupe_namespace = None
832 if opts.dedupe:
833 if opts.official_build and not opts.testing:
834 dedupe_namespace = OFFICIAL_DEDUPE_NAMESPACE
835 else:
836 dedupe_namespace = STAGING_DEDUPE_NAMESPACE
837
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400838 if not opts.yes:
Mike Frysingerc5de9602014-02-09 02:42:36 -0500839 prolog = '\n'.join(textwrap.wrap(textwrap.dedent("""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400840 Uploading symbols for an entire Chromium OS build is really only
841 necessary for release builds and in a few cases for developers
842 to debug problems. It will take considerable time to run. For
843 developer debugging purposes, consider instead passing specific
844 files to upload.
Mike Frysingerc5de9602014-02-09 02:42:36 -0500845 """), 80)).strip()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400846 if not cros_build_lib.BooleanPrompt(
847 prompt='Are you sure you want to upload all build symbols',
Mike Frysingerc5de9602014-02-09 02:42:36 -0500848 default=False, prolog=prolog):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400849 cros_build_lib.Die('better safe than sorry')
850
851 ret = 0
852 if opts.regenerate:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400853 ret += cros_generate_breakpad_symbols.GenerateBreakpadSymbols(
854 opts.board, breakpad_dir=opts.breakpad_root)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400855
856 ret += UploadSymbols(opts.board, official=opts.official_build,
Mike Frysinger38647542014-09-12 18:15:39 -0700857 server=opts.server, breakpad_dir=opts.breakpad_root,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400858 file_limit=opts.strip_cfi, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500859 upload_limit=opts.upload_limit, sym_paths=opts.sym_paths,
Mike Frysingerc5597f22014-11-27 15:39:15 -0500860 failed_list=opts.failed_list, root=opts.root,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500861 dedupe_namespace=dedupe_namespace)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400862 if ret:
863 cros_build_lib.Error('encountered %i problem(s)', ret)
864 # Since exit(status) gets masked, clamp it to 1 so we don't inadvertently
865 # return 0 in case we are a multiple of the mask.
866 ret = 1
867
868 return ret
Mike Frysinger094a2172013-08-14 12:54:35 -0400869
870
871# We need this to run once per process. Do it at module import time as that
872# will let us avoid doing it inline at function call time (see SymUpload) as
873# that func might be called by the multiprocessing module which means we'll
874# do the opener logic multiple times overall. Plus, if you're importing this
875# module, it's a pretty good chance that you're going to need this.
876poster.streaminghttp.register_openers()