blob: c8caf5468be33acbfdc92bd312209bd91611e0d5 [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
Mike Frysingerd41938e2014-02-10 06:37:55 -050044from chromite.lib import gs
45from chromite.lib import osutils
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040046from chromite.lib import parallel
David Jamesc93e6a4d2014-01-13 11:37:36 -080047from chromite.lib import retry_util
Mike Frysinger66e51e92014-05-03 16:52:00 -040048from chromite.lib import signals
Mike Frysinger0c0efa22014-02-09 23:32:23 -050049from chromite.lib import timeout_util
Mike Frysinger69cb41d2013-08-11 20:08:19 -040050from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040051
Mike Frysinger0c0efa22014-02-09 23:32:23 -050052# Needs to be after chromite imports.
Mike Frysingerc5597f22014-11-27 15:39:15 -050053# We don't want to import the general keyring module as that will implicitly
54# try to import & connect to a dbus server. That's a waste of time.
55sys.modules['keyring'] = None
56import isolateserver
Mike Frysinger0c0efa22014-02-09 23:32:23 -050057
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040058
59# URLs used for uploading symbols.
60OFFICIAL_UPLOAD_URL = 'http://clients2.google.com/cr/symbol'
61STAGING_UPLOAD_URL = 'http://clients2.google.com/cr/staging_symbol'
62
63
64# The crash server rejects files that are this big.
65CRASH_SERVER_FILE_LIMIT = 350 * 1024 * 1024
66# Give ourselves a little breathing room from what the server expects.
67DEFAULT_FILE_LIMIT = CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024)
68
69
Mike Frysinger0c0efa22014-02-09 23:32:23 -050070# The batch limit when talking to the dedup server. We avoid sending one at a
71# time as the round trip overhead will dominate. Conversely, we avoid sending
72# all at once so we can start uploading symbols asap -- the symbol server is a
73# bit slow and will take longer than anything else.
74# TODO: A better algorithm would be adaptive. If we have more than one symbol
75# in the upload queue waiting, we could send more symbols to the dedupe server
76# at a time.
77DEDUPE_LIMIT = 100
78
79# How long to wait for the server to respond with the results. Note that the
80# larger the limit above, the larger this will need to be. So we give it ~1
81# second per item max.
82DEDUPE_TIMEOUT = DEDUPE_LIMIT
83
Mike Frysinger4dd462e2014-04-30 16:21:51 -040084# How long to wait for the notification to finish (in minutes). If it takes
85# longer than this, we'll stop notifiying, but that's not a big deal as we
86# will be able to recover in later runs.
87DEDUPE_NOTIFY_TIMEOUT = 20
88
Mike Frysinger0c0efa22014-02-09 23:32:23 -050089# The unique namespace in the dedupe server that only we use. Helps avoid
90# collisions with all the hashed values and unrelated content.
91OFFICIAL_DEDUPE_NAMESPACE = 'chromium-os-upload-symbols'
92STAGING_DEDUPE_NAMESPACE = '%s-staging' % OFFICIAL_DEDUPE_NAMESPACE
93
94
Mike Frysinger71046662014-09-12 18:15:15 -070095# The minimum average rate (in bytes per second) that we expect to maintain
96# when uploading symbols. This has to allow for symbols that are up to
97# CRASH_SERVER_FILE_LIMIT in size.
98UPLOAD_MIN_RATE = CRASH_SERVER_FILE_LIMIT / (30 * 60)
99
100# The lowest timeout (in seconds) we'll allow. If the server is overloaded,
101# then there might be a delay in setting up the connection, not just with the
102# transfer. So even a small file might need a larger value.
103UPLOAD_MIN_TIMEOUT = 2 * 60
Mike Frysingercd78a082013-06-26 17:13:04 -0400104
105
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400106# Sleep for 200ms in between uploads to avoid DoS'ing symbol server.
107DEFAULT_SLEEP_DELAY = 0.2
108
109
110# Number of seconds to wait before retrying an upload. The delay will double
111# for each subsequent retry of the same symbol file.
112INITIAL_RETRY_DELAY = 1
113
114# Allow up to 7 attempts to upload a symbol file (total delay may be
115# 1+2+4+8+16+32=63 seconds).
116MAX_RETRIES = 6
117
Mike Frysingereb753bf2013-11-22 16:05:35 -0500118# Number of total errors, before uploads are no longer attempted.
119# This is used to avoid lots of errors causing unreasonable delays.
120# See the related, but independent, error values below.
121MAX_TOTAL_ERRORS_FOR_RETRY = 30
122
123# A watermark of transient errors which we allow recovery from. If we hit
124# errors infrequently, overall we're probably doing fine. For example, if
125# we have one failure every 100 passes, then we probably don't want to fail
126# right away. But if we hit a string of failures in a row, we want to abort.
127#
128# The watermark starts at 0 (and can never go below that). When this error
129# level is exceeded, we stop uploading. When a failure happens, we add the
130# fail adjustment, and when an upload succeeds, we add the pass adjustment.
131# We want to penalize failures more so that we ramp up when there is a string
132# of them, but then slowly back off as things start working.
133#
134# A quick example:
135# 0.0: Starting point.
136# 0.0: Upload works, so add -0.5, and then clamp to 0.
137# 1.0: Upload fails, so add 1.0.
138# 2.0: Upload fails, so add 1.0.
139# 1.5: Upload works, so add -0.5.
140# 1.0: Upload works, so add -0.5.
141ERROR_WATERMARK = 3.0
142ERROR_ADJUST_FAIL = 1.0
143ERROR_ADJUST_PASS = -0.5
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400144
145
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700146def GetUploadTimeout(path):
147 """How long to wait for a specific file to upload to the crash server.
148
149 This is a function largely to make unittesting easier.
150
151 Args:
152 path: The path to the file to calculate the timeout for
153
154 Returns:
155 Timeout length (in seconds)
156 """
157 # Scale the timeout based on the filesize.
158 return max(os.path.getsize(path) / UPLOAD_MIN_RATE, UPLOAD_MIN_TIMEOUT)
159
160
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500161def SymUpload(upload_url, sym_item):
Mike Frysinger094a2172013-08-14 12:54:35 -0400162 """Upload a symbol file to a HTTP server
163
164 The upload is a multipart/form-data POST with the following parameters:
165 code_file: the basename of the module, e.g. "app"
166 code_identifier: the module file's identifier
167 debug_file: the basename of the debugging file, e.g. "app"
168 debug_identifier: the debug file's identifier, usually consisting of
169 the guid and age embedded in the pdb, e.g.
170 "11111111BBBB3333DDDD555555555555F"
171 version: the file version of the module, e.g. "1.2.3.4"
172 product: HTTP-friendly product name
173 os: the operating system that the module was built for
174 cpu: the CPU that the module was built for
175 symbol_file: the contents of the breakpad-format symbol file
176
177 Args:
Mike Frysinger094a2172013-08-14 12:54:35 -0400178 upload_url: The crash URL to POST the |sym_file| to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500179 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysinger094a2172013-08-14 12:54:35 -0400180 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500181 sym_header = sym_item.sym_header
182 sym_file = sym_item.sym_file
Mike Frysinger094a2172013-08-14 12:54:35 -0400183
184 fields = (
185 ('code_file', sym_header.name),
186 ('debug_file', sym_header.name),
187 ('debug_identifier', sym_header.id.replace('-', '')),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400188 # The product/version fields are used by the server only for statistic
189 # purposes. They do not impact symbolization, so they're safe to set
190 # to any value all the time.
191 # In this case, we use it to help see the load our build system is
192 # placing on the server.
193 # Not sure what to set for the version. Maybe the git sha1 of this file.
194 # Note: the server restricts this to 30 chars.
Mike Frysinger094a2172013-08-14 12:54:35 -0400195 #('version', None),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400196 ('product', 'ChromeOS'),
Mike Frysinger094a2172013-08-14 12:54:35 -0400197 ('os', sym_header.os),
198 ('cpu', sym_header.cpu),
199 poster.encode.MultipartParam.from_file('symbol_file', sym_file),
200 )
201
202 data, headers = poster.encode.multipart_encode(fields)
203 request = urllib2.Request(upload_url, data, headers)
204 request.add_header('User-agent', 'chromite.upload_symbols')
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700205 urllib2.urlopen(request, timeout=GetUploadTimeout(sym_file))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400206
207
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500208def TestingSymUpload(upload_url, sym_item):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400209 """A stub version of SymUpload for --testing usage"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500210 cmd = ['sym_upload', sym_item.sym_file, upload_url]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400211 # Randomly fail 80% of the time (the retry logic makes this 80%/3 per file).
212 returncode = random.randint(1, 100) <= 80
213 cros_build_lib.Debug('would run (and return %i): %s', returncode,
Matt Tennant7feda352013-12-20 14:03:40 -0800214 cros_build_lib.CmdToStr(cmd))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400215 if returncode:
216 output = 'Failed to send the symbol file.'
217 else:
218 output = 'Successfully sent the symbol file.'
219 result = cros_build_lib.CommandResult(cmd=cmd, error=None, output=output,
220 returncode=returncode)
221 if returncode:
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500222 exceptions = (
Mike Frysingerfd355652014-01-23 02:57:48 -0500223 socket.error('[socket.error] forced test fail'),
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500224 httplib.BadStatusLine('[BadStatusLine] forced test fail'),
225 urllib2.HTTPError(upload_url, 400, '[HTTPError] forced test fail',
226 {}, None),
227 urllib2.URLError('[URLError] forced test fail'),
228 )
229 raise random.choice(exceptions)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400230 else:
231 return result
232
233
Mike Frysingereb753bf2013-11-22 16:05:35 -0500234def ErrorLimitHit(num_errors, watermark_errors):
235 """See if our error limit has been hit
236
237 Args:
238 num_errors: A multiprocessing.Value of the raw number of failures.
239 watermark_errors: A multiprocessing.Value of the current rate of failures.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500240
Mike Frysingereb753bf2013-11-22 16:05:35 -0500241 Returns:
242 True if our error limits have been exceeded.
243 """
244 return ((num_errors is not None and
245 num_errors.value > MAX_TOTAL_ERRORS_FOR_RETRY) or
246 (watermark_errors is not None and
247 watermark_errors.value > ERROR_WATERMARK))
248
249
250def _UpdateCounter(counter, adj):
251 """Update |counter| by |adj|
252
253 Handle atomic updates of |counter|. Also make sure it does not
254 fall below 0.
255
256 Args:
257 counter: A multiprocessing.Value to update
258 adj: The value to add to |counter|
259 """
260 def _Update():
261 clamp = 0 if type(adj) is int else 0.0
262 counter.value = max(clamp, counter.value + adj)
263
264 if hasattr(counter, 'get_lock'):
265 with counter.get_lock():
266 _Update()
267 elif counter is not None:
268 _Update()
269
270
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500271def UploadSymbol(upload_url, sym_item, file_limit=DEFAULT_FILE_LIMIT,
Mike Frysinger02e92402013-11-22 16:22:02 -0500272 sleep=0, num_errors=None, watermark_errors=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500273 failed_queue=None, passed_queue=None):
274 """Upload |sym_item| to |upload_url|
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400275
276 Args:
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400277 upload_url: The crash server to upload things to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500278 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400279 file_limit: The max file size of a symbol file before we try to strip it
280 sleep: Number of seconds to sleep before running
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400281 num_errors: An object to update with the error count (needs a .value member)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500282 watermark_errors: An object to track current error behavior (needs a .value)
Mike Frysinger02e92402013-11-22 16:22:02 -0500283 failed_queue: When a symbol fails, add it to this queue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500284 passed_queue: When a symbol passes, add it to this queue
Mike Frysinger1a736a82013-12-12 01:50:59 -0500285
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400286 Returns:
287 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400288 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500289 sym_file = sym_item.sym_file
290 upload_item = sym_item
291
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400292 if num_errors is None:
293 num_errors = ctypes.c_int()
Mike Frysingereb753bf2013-11-22 16:05:35 -0500294 if ErrorLimitHit(num_errors, watermark_errors):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400295 # Abandon ship! It's on fire! NOoooooooooooOOOoooooo.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500296 if failed_queue:
297 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400298 return 0
299
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400300 if sleep:
301 # Keeps us from DoS-ing the symbol server.
302 time.sleep(sleep)
303
304 cros_build_lib.Debug('uploading %s' % sym_file)
305
306 # Ideally there'd be a tempfile.SpooledNamedTemporaryFile that we could use.
307 with tempfile.NamedTemporaryFile(prefix='upload_symbols',
308 bufsize=0) as temp_sym_file:
309 if file_limit:
310 # If the symbols size is too big, strip out the call frame info. The CFI
311 # is unnecessary for 32bit x86 targets where the frame pointer is used (as
312 # all of ours have) and it accounts for over half the size of the symbols
313 # uploaded.
314 file_size = os.path.getsize(sym_file)
315 if file_size > file_limit:
316 cros_build_lib.Warning('stripping CFI from %s due to size %s > %s',
317 sym_file, file_size, file_limit)
318 temp_sym_file.writelines([x for x in open(sym_file, 'rb').readlines()
319 if not x.startswith('STACK CFI')])
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500320
321 upload_item = FakeItem(sym_file=temp_sym_file.name,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500322 sym_header=sym_item.sym_header)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400323
324 # Hopefully the crash server will let it through. But it probably won't.
325 # Not sure what the best answer is in this case.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500326 file_size = os.path.getsize(upload_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400327 if file_size > CRASH_SERVER_FILE_LIMIT:
328 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger02e92402013-11-22 16:22:02 -0500329 cros_build_lib.Warning('upload file %s is awfully large, risking '
330 'rejection by the symbol server (%s > %s)',
331 sym_file, file_size, CRASH_SERVER_FILE_LIMIT)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400332
333 # Upload the symbol file.
Mike Frysingereb753bf2013-11-22 16:05:35 -0500334 success = False
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400335 try:
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400336 cros_build_lib.TimedCommand(
David Jamesc93e6a4d2014-01-13 11:37:36 -0800337 retry_util.RetryException,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400338 (urllib2.HTTPError, urllib2.URLError), MAX_RETRIES, SymUpload,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500339 upload_url, upload_item, sleep=INITIAL_RETRY_DELAY,
Mike Frysingerf50187c2014-11-27 22:01:29 -0500340 timed_log_msg=('upload of %10i bytes took %%(delta)s: %s' %
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500341 (file_size, os.path.basename(sym_file))))
Mike Frysingereb753bf2013-11-22 16:05:35 -0500342 success = True
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500343
344 if passed_queue:
345 passed_queue.put(sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400346 except urllib2.HTTPError as e:
347 cros_build_lib.Warning('could not upload: %s: HTTP %s: %s',
348 os.path.basename(sym_file), e.code, e.reason)
Mike Frysingerfd355652014-01-23 02:57:48 -0500349 except (urllib2.URLError, httplib.HTTPException, socket.error) as e:
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400350 cros_build_lib.Warning('could not upload: %s: %s',
351 os.path.basename(sym_file), e)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500352 finally:
353 if success:
354 _UpdateCounter(watermark_errors, ERROR_ADJUST_PASS)
355 else:
356 _UpdateCounter(num_errors, 1)
357 _UpdateCounter(watermark_errors, ERROR_ADJUST_FAIL)
Mike Frysinger02e92402013-11-22 16:22:02 -0500358 if failed_queue:
359 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400360
361 return num_errors.value
362
363
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500364# A dummy class that allows for stubbing in tests and SymUpload.
365FakeItem = cros_build_lib.Collection(
366 'FakeItem', sym_file=None, sym_header=None, content=lambda x: '')
367
368
Mike Frysingerc5597f22014-11-27 15:39:15 -0500369class SymbolItem(isolateserver.BufferItem):
370 """Turn a sym_file into an isolateserver.Item"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500371
Mike Frysingerc5597f22014-11-27 15:39:15 -0500372 ALGO = hashlib.sha1
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500373
Mike Frysingerc5597f22014-11-27 15:39:15 -0500374 def __init__(self, sym_file):
375 sym_header = cros_generate_breakpad_symbols.ReadSymsHeader(sym_file)
376 super(SymbolItem, self).__init__(str(sym_header), self.ALGO)
377 self.sym_header = sym_header
378 self.sym_file = sym_file
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500379
380
381def SymbolDeduplicatorNotify(dedupe_namespace, dedupe_queue):
382 """Send a symbol file to the swarming service
383
384 Notify the swarming service of a successful upload. If the notification fails
385 for any reason, we ignore it. We don't care as it just means we'll upload it
386 again later on, and the symbol server will handle that graciously.
387
388 This func runs in a different process from the main one, so we cannot share
389 the storage object. Instead, we create our own. This func stays alive for
390 the life of the process, so we only create one here overall.
391
392 Args:
393 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
394 dedupe_queue: The queue to read SymbolItems from
395 """
396 if dedupe_queue is None:
397 return
398
399 item = None
400 try:
Mike Frysinger650e6722014-04-28 18:29:15 -0400401 with timeout_util.Timeout(DEDUPE_TIMEOUT):
402 storage = isolateserver.get_storage_api(constants.ISOLATESERVER,
403 dedupe_namespace)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500404 for item in iter(dedupe_queue.get, None):
405 with timeout_util.Timeout(DEDUPE_TIMEOUT):
Mike Frysingerefef3672014-04-20 10:06:45 -0400406 cros_build_lib.Debug('sending %s to dedupe server', item.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500407 storage.push(item, item.content(0))
Mike Frysinger66e51e92014-05-03 16:52:00 -0400408 cros_build_lib.Debug('sent %s', item.sym_file)
Mike Frysingerae298452014-03-24 22:45:23 -0400409 cros_build_lib.Info('dedupe notification finished; exiting')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500410 except Exception:
411 sym_file = item.sym_file if (item and item.sym_file) else ''
412 cros_build_lib.Warning('posting %s to dedupe server failed',
413 os.path.basename(sym_file), exc_info=True)
414
Mike Frysinger58312e92014-03-18 04:18:36 -0400415 # Keep draining the queue though so it doesn't fill up.
416 while dedupe_queue.get() is not None:
417 continue
418
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500419
420def SymbolDeduplicator(storage, sym_paths):
421 """Filter out symbol files that we've already uploaded
422
423 Using the swarming service, ask it to tell us which symbol files we've already
424 uploaded in previous runs and/or by other bots. If the query fails for any
425 reason, we'll just upload all symbols. This is fine as the symbol server will
426 do the right thing and this phase is purely an optimization.
427
428 This code runs in the main thread which is why we can re-use the existing
429 storage object. Saves us from having to recreate one all the time.
430
431 Args:
432 storage: An isolateserver.StorageApi object
433 sym_paths: List of symbol files to check against the dedupe server
434
435 Returns:
436 List of symbol files that have not been uploaded before
437 """
438 if not sym_paths:
439 return sym_paths
440
441 items = [SymbolItem(x) for x in sym_paths]
442 if storage:
443 try:
444 with timeout_util.Timeout(DEDUPE_TIMEOUT):
445 items = storage.contains(items)
446 except Exception:
447 cros_build_lib.Warning('talking to dedupe server failed', exc_info=True)
448
449 return items
450
451
Mike Frysingerd41938e2014-02-10 06:37:55 -0500452def IsTarball(path):
453 """Guess if this is a tarball based on the filename."""
454 parts = path.split('.')
455 if len(parts) <= 1:
456 return False
457
458 if parts[-1] == 'tar':
459 return True
460
461 if parts[-2] == 'tar':
462 return parts[-1] in ('bz2', 'gz', 'xz')
463
464 return parts[-1] in ('tbz2', 'tbz', 'tgz', 'txz')
465
466
467def SymbolFinder(tempdir, paths):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500468 """Locate symbol files in |paths|
469
470 Args:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500471 tempdir: Path to use for temporary files (caller will clean up).
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500472 paths: A list of input paths to walk. Files are returned w/out any checks.
Mike Frysingerd41938e2014-02-10 06:37:55 -0500473 Dirs are searched for files that end in ".sym". Urls are fetched and then
474 processed. Tarballs are unpacked and walked.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500475
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500476 Returns:
477 Yield every viable sym file.
478 """
Mike Frysingere847efd2015-01-08 03:57:24 -0500479 cache_dir = commandline.GetCacheDir()
480 common_path = os.path.join(cache_dir, constants.COMMON_CACHE)
481 tar_cache = cache.TarballCache(common_path)
482
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500483 for p in paths:
Don Garrett25f309a2014-03-19 14:02:12 -0700484 # Pylint is confused about members of ParseResult.
Don Garrettf8bf7842014-03-20 17:03:42 -0700485
Mike Frysingerd41938e2014-02-10 06:37:55 -0500486 o = urlparse.urlparse(p)
Don Garrettf8bf7842014-03-20 17:03:42 -0700487 if o.scheme: # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500488 # Support globs of filenames.
489 ctx = gs.GSContext()
490 for p in ctx.LS(p):
491 cros_build_lib.Info('processing files inside %s', p)
492 o = urlparse.urlparse(p)
Don Garrettf8bf7842014-03-20 17:03:42 -0700493 key = ('%s%s' % (o.netloc, o.path)).split('/') # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500494 # The common cache will not be LRU, removing the need to hold a read
495 # lock on the cached gsutil.
496 ref = tar_cache.Lookup(key)
497 try:
498 ref.SetDefault(p)
499 except cros_build_lib.RunCommandError as e:
500 cros_build_lib.Warning('ignoring %s\n%s', p, e)
501 continue
502 for p in SymbolFinder(tempdir, [ref.path]):
503 yield p
504
505 elif os.path.isdir(p):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500506 for root, _, files in os.walk(p):
507 for f in files:
508 if f.endswith('.sym'):
509 yield os.path.join(root, f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500510
511 elif IsTarball(p):
512 cros_build_lib.Info('processing files inside %s', p)
513 tardir = tempfile.mkdtemp(dir=tempdir)
514 cache.Untar(os.path.realpath(p), tardir)
515 for p in SymbolFinder(tardir, [tardir]):
516 yield p
517
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500518 else:
519 yield p
520
521
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500522def WriteQueueToFile(listing, queue, relpath=None):
523 """Write all the items in |queue| to the |listing|.
524
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500525 Note: The queue must have a sentinel None appended to the end.
526
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500527 Args:
528 listing: Where to write out the list of files.
529 queue: The queue of paths to drain.
530 relpath: If set, write out paths relative to this one.
531 """
532 if not listing:
Mike Frysingera0ddac62014-03-14 10:30:25 -0400533 # Still drain the queue so we make sure the producer has finished
534 # before we return. Otherwise, the queue might get destroyed too
535 # quickly which will trigger a traceback in the producer.
536 while queue.get() is not None:
537 continue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500538 return
539
540 with cros_build_lib.Open(listing, 'wb+') as f:
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500541 while True:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500542 path = queue.get()
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500543 if path is None:
544 return
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500545 if relpath:
546 path = os.path.relpath(path, relpath)
547 f.write('%s\n' % path)
548
549
Mike Frysinger38647542014-09-12 18:15:39 -0700550def UploadSymbols(board=None, official=False, server=None, breakpad_dir=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400551 file_limit=DEFAULT_FILE_LIMIT, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500552 upload_limit=None, sym_paths=None, failed_list=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500553 root=None, retry=True, dedupe_namespace=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400554 """Upload all the generated symbols for |board| to the crash server
555
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400556 You can use in a few ways:
557 * pass |board| to locate all of its symbols
558 * pass |breakpad_dir| to upload all the symbols in there
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500559 * pass |sym_paths| to upload specific symbols (or dirs of symbols)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400560
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400561 Args:
562 board: The board whose symbols we wish to upload
563 official: Use the official symbol server rather than the staging one
Mike Frysinger38647542014-09-12 18:15:39 -0700564 server: Explicit server to post symbols to
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400565 breakpad_dir: The full path to the breakpad directory where symbols live
566 file_limit: The max file size of a symbol file before we try to strip it
567 sleep: How long to sleep in between uploads
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500568 upload_limit: If set, only upload this many symbols (meant for testing)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500569 sym_paths: Specific symbol files (or dirs of sym files) to upload,
570 otherwise search |breakpad_dir|
Mike Frysinger7f9be142014-01-15 02:16:42 -0500571 failed_list: Write the names of all sym files we did not upload; can be a
572 filename or file-like object.
Mike Frysinger118d2502013-08-19 03:36:56 -0400573 root: The tree to prefix to |breakpad_dir| (if |breakpad_dir| is not set)
Mike Frysinger02e92402013-11-22 16:22:02 -0500574 retry: Whether we should retry failures.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500575 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500576
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400577 Returns:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400578 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400579 """
Mike Frysinger38647542014-09-12 18:15:39 -0700580 if server is None:
581 if official:
582 upload_url = OFFICIAL_UPLOAD_URL
583 else:
584 cros_build_lib.Warning('unofficial builds upload to the staging server')
585 upload_url = STAGING_UPLOAD_URL
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400586 else:
Mike Frysinger38647542014-09-12 18:15:39 -0700587 upload_url = server
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400588
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500589 if sym_paths:
590 cros_build_lib.Info('uploading specified symbols to %s', upload_url)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400591 else:
592 if breakpad_dir is None:
Mike Frysingerc5597f22014-11-27 15:39:15 -0500593 if root is None:
594 raise ValueError('breakpad_dir requires root to be set')
Mike Frysinger118d2502013-08-19 03:36:56 -0400595 breakpad_dir = os.path.join(
596 root,
597 cros_generate_breakpad_symbols.FindBreakpadDir(board).lstrip('/'))
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400598 cros_build_lib.Info('uploading all symbols to %s from %s', upload_url,
599 breakpad_dir)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500600 sym_paths = [breakpad_dir]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400601
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500602 # We use storage_query to ask the server about existing symbols. The
603 # storage_notify_proc process is used to post updates to the server. We
604 # cannot safely share the storage object between threads/processes, but
605 # we also want to minimize creating new ones as each object has to init
606 # new state (like server connections).
Mike Frysinger650e6722014-04-28 18:29:15 -0400607 storage_query = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500608 if dedupe_namespace:
609 dedupe_limit = DEDUPE_LIMIT
610 dedupe_queue = multiprocessing.Queue()
Mike Frysinger650e6722014-04-28 18:29:15 -0400611 try:
612 with timeout_util.Timeout(DEDUPE_TIMEOUT):
613 storage_query = isolateserver.get_storage_api(constants.ISOLATESERVER,
614 dedupe_namespace)
615 except Exception:
616 cros_build_lib.Warning('initializing dedupe server connection failed',
617 exc_info=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500618 else:
619 dedupe_limit = 1
Mike Frysinger650e6722014-04-28 18:29:15 -0400620 dedupe_queue = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500621 # Can't use parallel.BackgroundTaskRunner because that'll create multiple
622 # processes and we want only one the whole time (see comment above).
623 storage_notify_proc = multiprocessing.Process(
624 target=SymbolDeduplicatorNotify, args=(dedupe_namespace, dedupe_queue))
625
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400626 bg_errors = multiprocessing.Value('i')
Mike Frysingereb753bf2013-11-22 16:05:35 -0500627 watermark_errors = multiprocessing.Value('f')
Mike Frysinger02e92402013-11-22 16:22:02 -0500628 failed_queue = multiprocessing.Queue()
629 uploader = functools.partial(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500630 UploadSymbol, upload_url, file_limit=file_limit, sleep=sleep,
631 num_errors=bg_errors, watermark_errors=watermark_errors,
632 failed_queue=failed_queue, passed_queue=dedupe_queue)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400633
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500634 start_time = datetime.datetime.now()
635 Counters = cros_build_lib.Collection(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500636 'Counters', upload_limit=upload_limit, uploaded_count=0, deduped_count=0)
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500637 counters = Counters()
638
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500639 def _Upload(queue, counters, files):
640 if not files:
641 return
642
643 missing_count = 0
644 for item in SymbolDeduplicator(storage_query, files):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500645 missing_count += 1
Mike Frysingerd42e5f02014-03-14 11:19:37 -0400646
647 if counters.upload_limit == 0:
648 continue
649
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500650 queue.put((item,))
651 counters.uploaded_count += 1
652 if counters.upload_limit is not None:
653 counters.upload_limit -= 1
654
655 counters.deduped_count += (len(files) - missing_count)
656
Mike Frysinger13870082014-03-14 10:41:20 -0400657 try:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500658 storage_notify_proc.start()
Mike Frysinger02e92402013-11-22 16:22:02 -0500659
Mike Frysinger13870082014-03-14 10:41:20 -0400660 with osutils.TempDir(prefix='upload_symbols.') as tempdir:
661 # For the first run, we collect the symbols that failed. If the
662 # overall failure rate was low, we'll retry them on the second run.
663 for retry in (retry, False):
664 # We need to limit ourselves to one upload at a time to avoid the server
665 # kicking in DoS protection. See these bugs for more details:
666 # http://crbug.com/209442
667 # http://crbug.com/212496
668 with parallel.BackgroundTaskRunner(uploader, processes=1) as queue:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500669 dedupe_list = []
Mike Frysinger13870082014-03-14 10:41:20 -0400670 for sym_file in SymbolFinder(tempdir, sym_paths):
671 dedupe_list.append(sym_file)
672 dedupe_len = len(dedupe_list)
673 if dedupe_len < dedupe_limit:
674 if (counters.upload_limit is None or
675 dedupe_len < counters.upload_limit):
676 continue
Mike Frysinger02e92402013-11-22 16:22:02 -0500677
Mike Frysinger1010a892014-03-14 11:24:17 -0400678 # We check the counter before _Upload so that we don't keep talking
679 # to the dedupe server. Otherwise, we end up sending one symbol at
680 # a time to it and that slows things down a lot.
681 if counters.upload_limit == 0:
682 break
683
Mike Frysinger13870082014-03-14 10:41:20 -0400684 _Upload(queue, counters, dedupe_list)
685 dedupe_list = []
686 _Upload(queue, counters, dedupe_list)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500687
Mike Frysinger13870082014-03-14 10:41:20 -0400688 # See if we need to retry, and if we haven't failed too many times yet.
689 if not retry or ErrorLimitHit(bg_errors, watermark_errors):
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500690 break
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500691
Mike Frysinger13870082014-03-14 10:41:20 -0400692 sym_paths = []
693 failed_queue.put(None)
694 while True:
695 sym_path = failed_queue.get()
696 if sym_path is None:
697 break
698 sym_paths.append(sym_path)
Mike Frysinger02e92402013-11-22 16:22:02 -0500699
Mike Frysinger13870082014-03-14 10:41:20 -0400700 if sym_paths:
701 cros_build_lib.Warning('retrying %i symbols', len(sym_paths))
702 if counters.upload_limit is not None:
703 counters.upload_limit += len(sym_paths)
704 # Decrement the error count in case we recover in the second pass.
705 assert bg_errors.value >= len(sym_paths), \
706 'more failed files than errors?'
707 bg_errors.value -= len(sym_paths)
708 else:
709 # No failed symbols, so just return now.
710 break
Mike Frysinger7f9be142014-01-15 02:16:42 -0500711
Mike Frysinger13870082014-03-14 10:41:20 -0400712 # If the user has requested it, save all the symbol files that we failed to
713 # upload to a listing file. This should help with recovery efforts later.
714 failed_queue.put(None)
715 WriteQueueToFile(failed_list, failed_queue, breakpad_dir)
716
717 finally:
Mike Frysingerae298452014-03-24 22:45:23 -0400718 cros_build_lib.Info('finished uploading; joining background process')
Mike Frysinger13870082014-03-14 10:41:20 -0400719 if dedupe_queue:
720 dedupe_queue.put(None)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400721
722 # The notification might be slow going, so give it some time to finish.
723 # We have to poll here as the process monitor is watching for output and
724 # will kill us if we go silent for too long.
725 wait_minutes = DEDUPE_NOTIFY_TIMEOUT
726 while storage_notify_proc.is_alive() and wait_minutes > 0:
Aviv Keshetd1f04632014-05-09 11:33:46 -0700727 if dedupe_queue:
728 qsize = str(dedupe_queue.qsize())
729 else:
730 qsize = '[None]'
731 cros_build_lib.Info('waiting up to %i minutes for ~%s notifications',
732 wait_minutes, qsize)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400733 storage_notify_proc.join(60)
734 wait_minutes -= 1
735
736 # The process is taking too long, so kill it and complain.
737 if storage_notify_proc.is_alive():
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400738 cros_build_lib.Warning('notification process took too long')
739 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500740
Mike Frysinger66e51e92014-05-03 16:52:00 -0400741 # Kill it gracefully first (traceback) before tacking it down harder.
742 pid = storage_notify_proc.pid
743 for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGKILL):
744 cros_build_lib.Warning('sending %s to %i', signals.StrSignal(sig), pid)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400745 # The process might have exited between the last check and the
746 # actual kill below, so ignore ESRCH errors.
747 try:
748 os.kill(pid, sig)
749 except OSError as e:
750 if e.errno == errno.ESRCH:
751 break
752 else:
753 raise
Mike Frysinger66e51e92014-05-03 16:52:00 -0400754 time.sleep(5)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400755 if not storage_notify_proc.is_alive():
Mike Frysinger66e51e92014-05-03 16:52:00 -0400756 break
757
758 # Drain the queue so we don't hang when we finish.
759 try:
Mike Frysinger4f5ea832014-05-12 00:54:28 -0400760 cros_build_lib.Warning('draining the notify queue manually')
761 with timeout_util.Timeout(60):
762 try:
763 while dedupe_queue.get_nowait():
764 pass
765 except Queue.Empty:
766 pass
767 except timeout_util.TimeoutError:
768 cros_build_lib.Warning('draining the notify queue failed; trashing it')
769 dedupe_queue.cancel_join_thread()
Mike Frysinger66e51e92014-05-03 16:52:00 -0400770
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500771 cros_build_lib.Info('uploaded %i symbols (%i were deduped) which took: %s',
772 counters.uploaded_count, counters.deduped_count,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500773 datetime.datetime.now() - start_time)
774
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500775 return bg_errors.value
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400776
777
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400778def main(argv):
779 parser = commandline.ArgumentParser(description=__doc__)
780
Mike Frysingerd41938e2014-02-10 06:37:55 -0500781 parser.add_argument('sym_paths', type='path_or_uri', nargs='*', default=None,
782 help='symbol file or directory or URL or tarball')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400783 parser.add_argument('--board', default=None,
784 help='board to build packages for')
785 parser.add_argument('--breakpad_root', type='path', default=None,
Mike Frysingerc5597f22014-11-27 15:39:15 -0500786 help='full path to the breakpad symbol directory')
787 parser.add_argument('--root', type='path', default=None,
788 help='full path to the chroot dir')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400789 parser.add_argument('--official_build', action='store_true', default=False,
790 help='point to official symbol server')
Mike Frysinger38647542014-09-12 18:15:39 -0700791 parser.add_argument('--server', type=str, default=None,
792 help='URI for custom symbol server')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400793 parser.add_argument('--regenerate', action='store_true', default=False,
794 help='regenerate all symbols')
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500795 parser.add_argument('--upload-limit', type=int, default=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400796 help='only upload # number of symbols')
797 parser.add_argument('--strip_cfi', type=int,
798 default=CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024),
799 help='strip CFI data for files above this size')
Mike Frysinger7f9be142014-01-15 02:16:42 -0500800 parser.add_argument('--failed-list', type='path',
801 help='where to save a list of failed symbols')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500802 parser.add_argument('--dedupe', action='store_true', default=False,
803 help='use the swarming service to avoid re-uploading')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400804 parser.add_argument('--testing', action='store_true', default=False,
805 help='run in testing mode')
806 parser.add_argument('--yes', action='store_true', default=False,
807 help='answer yes to all prompts')
808
809 opts = parser.parse_args(argv)
Mike Frysinger90e49ca2014-01-14 14:42:07 -0500810 opts.Freeze()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400811
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500812 if opts.sym_paths:
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400813 if opts.regenerate:
814 cros_build_lib.Die('--regenerate may not be used with specific files')
815 else:
816 if opts.board is None:
817 cros_build_lib.Die('--board is required')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400818
819 if opts.breakpad_root and opts.regenerate:
820 cros_build_lib.Die('--regenerate may not be used with --breakpad_root')
821
822 if opts.testing:
823 # TODO(build): Kill off --testing mode once unittests are up-to-snuff.
824 cros_build_lib.Info('running in testing mode')
825 # pylint: disable=W0601,W0603
826 global INITIAL_RETRY_DELAY, SymUpload, DEFAULT_SLEEP_DELAY
827 INITIAL_RETRY_DELAY = DEFAULT_SLEEP_DELAY = 0
828 SymUpload = TestingSymUpload
829
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500830 dedupe_namespace = None
831 if opts.dedupe:
832 if opts.official_build and not opts.testing:
833 dedupe_namespace = OFFICIAL_DEDUPE_NAMESPACE
834 else:
835 dedupe_namespace = STAGING_DEDUPE_NAMESPACE
836
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400837 if not opts.yes:
Mike Frysingerc5de9602014-02-09 02:42:36 -0500838 prolog = '\n'.join(textwrap.wrap(textwrap.dedent("""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400839 Uploading symbols for an entire Chromium OS build is really only
840 necessary for release builds and in a few cases for developers
841 to debug problems. It will take considerable time to run. For
842 developer debugging purposes, consider instead passing specific
843 files to upload.
Mike Frysingerc5de9602014-02-09 02:42:36 -0500844 """), 80)).strip()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400845 if not cros_build_lib.BooleanPrompt(
846 prompt='Are you sure you want to upload all build symbols',
Mike Frysingerc5de9602014-02-09 02:42:36 -0500847 default=False, prolog=prolog):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400848 cros_build_lib.Die('better safe than sorry')
849
850 ret = 0
851 if opts.regenerate:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400852 ret += cros_generate_breakpad_symbols.GenerateBreakpadSymbols(
853 opts.board, breakpad_dir=opts.breakpad_root)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400854
855 ret += UploadSymbols(opts.board, official=opts.official_build,
Mike Frysinger38647542014-09-12 18:15:39 -0700856 server=opts.server, breakpad_dir=opts.breakpad_root,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400857 file_limit=opts.strip_cfi, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500858 upload_limit=opts.upload_limit, sym_paths=opts.sym_paths,
Mike Frysingerc5597f22014-11-27 15:39:15 -0500859 failed_list=opts.failed_list, root=opts.root,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500860 dedupe_namespace=dedupe_namespace)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400861 if ret:
862 cros_build_lib.Error('encountered %i problem(s)', ret)
863 # Since exit(status) gets masked, clamp it to 1 so we don't inadvertently
864 # return 0 in case we are a multiple of the mask.
865 ret = 1
866
867 return ret
Mike Frysinger094a2172013-08-14 12:54:35 -0400868
869
870# We need this to run once per process. Do it at module import time as that
871# will let us avoid doing it inline at function call time (see SymUpload) as
872# that func might be called by the multiprocessing module which means we'll
873# do the opener logic multiple times overall. Plus, if you're importing this
874# module, it's a pretty good chance that you're going to need this.
875poster.streaminghttp.register_openers()