blob: 008b458f5f2d84a0727ed78f8fe65e43acb7205e [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 """
479 for p in paths:
Don Garrett25f309a2014-03-19 14:02:12 -0700480 # Pylint is confused about members of ParseResult.
Don Garrettf8bf7842014-03-20 17:03:42 -0700481
Mike Frysingerd41938e2014-02-10 06:37:55 -0500482 o = urlparse.urlparse(p)
Don Garrettf8bf7842014-03-20 17:03:42 -0700483 if o.scheme: # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500484 # Support globs of filenames.
485 ctx = gs.GSContext()
486 for p in ctx.LS(p):
487 cros_build_lib.Info('processing files inside %s', p)
488 o = urlparse.urlparse(p)
489 cache_dir = commandline.GetCacheDir()
490 common_path = os.path.join(cache_dir, constants.COMMON_CACHE)
491 tar_cache = cache.TarballCache(common_path)
Don Garrettf8bf7842014-03-20 17:03:42 -0700492 key = ('%s%s' % (o.netloc, o.path)).split('/') # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500493 # The common cache will not be LRU, removing the need to hold a read
494 # lock on the cached gsutil.
495 ref = tar_cache.Lookup(key)
496 try:
497 ref.SetDefault(p)
498 except cros_build_lib.RunCommandError as e:
499 cros_build_lib.Warning('ignoring %s\n%s', p, e)
500 continue
501 for p in SymbolFinder(tempdir, [ref.path]):
502 yield p
503
504 elif os.path.isdir(p):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500505 for root, _, files in os.walk(p):
506 for f in files:
507 if f.endswith('.sym'):
508 yield os.path.join(root, f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500509
510 elif IsTarball(p):
511 cros_build_lib.Info('processing files inside %s', p)
512 tardir = tempfile.mkdtemp(dir=tempdir)
513 cache.Untar(os.path.realpath(p), tardir)
514 for p in SymbolFinder(tardir, [tardir]):
515 yield p
516
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500517 else:
518 yield p
519
520
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500521def WriteQueueToFile(listing, queue, relpath=None):
522 """Write all the items in |queue| to the |listing|.
523
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500524 Note: The queue must have a sentinel None appended to the end.
525
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500526 Args:
527 listing: Where to write out the list of files.
528 queue: The queue of paths to drain.
529 relpath: If set, write out paths relative to this one.
530 """
531 if not listing:
Mike Frysingera0ddac62014-03-14 10:30:25 -0400532 # Still drain the queue so we make sure the producer has finished
533 # before we return. Otherwise, the queue might get destroyed too
534 # quickly which will trigger a traceback in the producer.
535 while queue.get() is not None:
536 continue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500537 return
538
539 with cros_build_lib.Open(listing, 'wb+') as f:
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500540 while True:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500541 path = queue.get()
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500542 if path is None:
543 return
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500544 if relpath:
545 path = os.path.relpath(path, relpath)
546 f.write('%s\n' % path)
547
548
Mike Frysinger38647542014-09-12 18:15:39 -0700549def UploadSymbols(board=None, official=False, server=None, breakpad_dir=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400550 file_limit=DEFAULT_FILE_LIMIT, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500551 upload_limit=None, sym_paths=None, failed_list=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500552 root=None, retry=True, dedupe_namespace=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400553 """Upload all the generated symbols for |board| to the crash server
554
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400555 You can use in a few ways:
556 * pass |board| to locate all of its symbols
557 * pass |breakpad_dir| to upload all the symbols in there
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500558 * pass |sym_paths| to upload specific symbols (or dirs of symbols)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400559
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400560 Args:
561 board: The board whose symbols we wish to upload
562 official: Use the official symbol server rather than the staging one
Mike Frysinger38647542014-09-12 18:15:39 -0700563 server: Explicit server to post symbols to
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400564 breakpad_dir: The full path to the breakpad directory where symbols live
565 file_limit: The max file size of a symbol file before we try to strip it
566 sleep: How long to sleep in between uploads
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500567 upload_limit: If set, only upload this many symbols (meant for testing)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500568 sym_paths: Specific symbol files (or dirs of sym files) to upload,
569 otherwise search |breakpad_dir|
Mike Frysinger7f9be142014-01-15 02:16:42 -0500570 failed_list: Write the names of all sym files we did not upload; can be a
571 filename or file-like object.
Mike Frysinger118d2502013-08-19 03:36:56 -0400572 root: The tree to prefix to |breakpad_dir| (if |breakpad_dir| is not set)
Mike Frysinger02e92402013-11-22 16:22:02 -0500573 retry: Whether we should retry failures.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500574 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500575
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400576 Returns:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400577 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400578 """
Mike Frysinger38647542014-09-12 18:15:39 -0700579 if server is None:
580 if official:
581 upload_url = OFFICIAL_UPLOAD_URL
582 else:
583 cros_build_lib.Warning('unofficial builds upload to the staging server')
584 upload_url = STAGING_UPLOAD_URL
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400585 else:
Mike Frysinger38647542014-09-12 18:15:39 -0700586 upload_url = server
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400587
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500588 if sym_paths:
589 cros_build_lib.Info('uploading specified symbols to %s', upload_url)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400590 else:
591 if breakpad_dir is None:
Mike Frysingerc5597f22014-11-27 15:39:15 -0500592 if root is None:
593 raise ValueError('breakpad_dir requires root to be set')
Mike Frysinger118d2502013-08-19 03:36:56 -0400594 breakpad_dir = os.path.join(
595 root,
596 cros_generate_breakpad_symbols.FindBreakpadDir(board).lstrip('/'))
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400597 cros_build_lib.Info('uploading all symbols to %s from %s', upload_url,
598 breakpad_dir)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500599 sym_paths = [breakpad_dir]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400600
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500601 # We use storage_query to ask the server about existing symbols. The
602 # storage_notify_proc process is used to post updates to the server. We
603 # cannot safely share the storage object between threads/processes, but
604 # we also want to minimize creating new ones as each object has to init
605 # new state (like server connections).
Mike Frysinger650e6722014-04-28 18:29:15 -0400606 storage_query = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500607 if dedupe_namespace:
608 dedupe_limit = DEDUPE_LIMIT
609 dedupe_queue = multiprocessing.Queue()
Mike Frysinger650e6722014-04-28 18:29:15 -0400610 try:
611 with timeout_util.Timeout(DEDUPE_TIMEOUT):
612 storage_query = isolateserver.get_storage_api(constants.ISOLATESERVER,
613 dedupe_namespace)
614 except Exception:
615 cros_build_lib.Warning('initializing dedupe server connection failed',
616 exc_info=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500617 else:
618 dedupe_limit = 1
Mike Frysinger650e6722014-04-28 18:29:15 -0400619 dedupe_queue = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500620 # Can't use parallel.BackgroundTaskRunner because that'll create multiple
621 # processes and we want only one the whole time (see comment above).
622 storage_notify_proc = multiprocessing.Process(
623 target=SymbolDeduplicatorNotify, args=(dedupe_namespace, dedupe_queue))
624
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400625 bg_errors = multiprocessing.Value('i')
Mike Frysingereb753bf2013-11-22 16:05:35 -0500626 watermark_errors = multiprocessing.Value('f')
Mike Frysinger02e92402013-11-22 16:22:02 -0500627 failed_queue = multiprocessing.Queue()
628 uploader = functools.partial(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500629 UploadSymbol, upload_url, file_limit=file_limit, sleep=sleep,
630 num_errors=bg_errors, watermark_errors=watermark_errors,
631 failed_queue=failed_queue, passed_queue=dedupe_queue)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400632
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500633 start_time = datetime.datetime.now()
634 Counters = cros_build_lib.Collection(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500635 'Counters', upload_limit=upload_limit, uploaded_count=0, deduped_count=0)
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500636 counters = Counters()
637
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500638 def _Upload(queue, counters, files):
639 if not files:
640 return
641
642 missing_count = 0
643 for item in SymbolDeduplicator(storage_query, files):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500644 missing_count += 1
Mike Frysingerd42e5f02014-03-14 11:19:37 -0400645
646 if counters.upload_limit == 0:
647 continue
648
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500649 queue.put((item,))
650 counters.uploaded_count += 1
651 if counters.upload_limit is not None:
652 counters.upload_limit -= 1
653
654 counters.deduped_count += (len(files) - missing_count)
655
Mike Frysinger13870082014-03-14 10:41:20 -0400656 try:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500657 storage_notify_proc.start()
Mike Frysinger02e92402013-11-22 16:22:02 -0500658
Mike Frysinger13870082014-03-14 10:41:20 -0400659 with osutils.TempDir(prefix='upload_symbols.') as tempdir:
660 # For the first run, we collect the symbols that failed. If the
661 # overall failure rate was low, we'll retry them on the second run.
662 for retry in (retry, False):
663 # We need to limit ourselves to one upload at a time to avoid the server
664 # kicking in DoS protection. See these bugs for more details:
665 # http://crbug.com/209442
666 # http://crbug.com/212496
667 with parallel.BackgroundTaskRunner(uploader, processes=1) as queue:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500668 dedupe_list = []
Mike Frysinger13870082014-03-14 10:41:20 -0400669 for sym_file in SymbolFinder(tempdir, sym_paths):
670 dedupe_list.append(sym_file)
671 dedupe_len = len(dedupe_list)
672 if dedupe_len < dedupe_limit:
673 if (counters.upload_limit is None or
674 dedupe_len < counters.upload_limit):
675 continue
Mike Frysinger02e92402013-11-22 16:22:02 -0500676
Mike Frysinger1010a892014-03-14 11:24:17 -0400677 # We check the counter before _Upload so that we don't keep talking
678 # to the dedupe server. Otherwise, we end up sending one symbol at
679 # a time to it and that slows things down a lot.
680 if counters.upload_limit == 0:
681 break
682
Mike Frysinger13870082014-03-14 10:41:20 -0400683 _Upload(queue, counters, dedupe_list)
684 dedupe_list = []
685 _Upload(queue, counters, dedupe_list)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500686
Mike Frysinger13870082014-03-14 10:41:20 -0400687 # See if we need to retry, and if we haven't failed too many times yet.
688 if not retry or ErrorLimitHit(bg_errors, watermark_errors):
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500689 break
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500690
Mike Frysinger13870082014-03-14 10:41:20 -0400691 sym_paths = []
692 failed_queue.put(None)
693 while True:
694 sym_path = failed_queue.get()
695 if sym_path is None:
696 break
697 sym_paths.append(sym_path)
Mike Frysinger02e92402013-11-22 16:22:02 -0500698
Mike Frysinger13870082014-03-14 10:41:20 -0400699 if sym_paths:
700 cros_build_lib.Warning('retrying %i symbols', len(sym_paths))
701 if counters.upload_limit is not None:
702 counters.upload_limit += len(sym_paths)
703 # Decrement the error count in case we recover in the second pass.
704 assert bg_errors.value >= len(sym_paths), \
705 'more failed files than errors?'
706 bg_errors.value -= len(sym_paths)
707 else:
708 # No failed symbols, so just return now.
709 break
Mike Frysinger7f9be142014-01-15 02:16:42 -0500710
Mike Frysinger13870082014-03-14 10:41:20 -0400711 # If the user has requested it, save all the symbol files that we failed to
712 # upload to a listing file. This should help with recovery efforts later.
713 failed_queue.put(None)
714 WriteQueueToFile(failed_list, failed_queue, breakpad_dir)
715
716 finally:
Mike Frysingerae298452014-03-24 22:45:23 -0400717 cros_build_lib.Info('finished uploading; joining background process')
Mike Frysinger13870082014-03-14 10:41:20 -0400718 if dedupe_queue:
719 dedupe_queue.put(None)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400720
721 # The notification might be slow going, so give it some time to finish.
722 # We have to poll here as the process monitor is watching for output and
723 # will kill us if we go silent for too long.
724 wait_minutes = DEDUPE_NOTIFY_TIMEOUT
725 while storage_notify_proc.is_alive() and wait_minutes > 0:
Aviv Keshetd1f04632014-05-09 11:33:46 -0700726 if dedupe_queue:
727 qsize = str(dedupe_queue.qsize())
728 else:
729 qsize = '[None]'
730 cros_build_lib.Info('waiting up to %i minutes for ~%s notifications',
731 wait_minutes, qsize)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400732 storage_notify_proc.join(60)
733 wait_minutes -= 1
734
735 # The process is taking too long, so kill it and complain.
736 if storage_notify_proc.is_alive():
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400737 cros_build_lib.Warning('notification process took too long')
738 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500739
Mike Frysinger66e51e92014-05-03 16:52:00 -0400740 # Kill it gracefully first (traceback) before tacking it down harder.
741 pid = storage_notify_proc.pid
742 for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGKILL):
743 cros_build_lib.Warning('sending %s to %i', signals.StrSignal(sig), pid)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400744 # The process might have exited between the last check and the
745 # actual kill below, so ignore ESRCH errors.
746 try:
747 os.kill(pid, sig)
748 except OSError as e:
749 if e.errno == errno.ESRCH:
750 break
751 else:
752 raise
Mike Frysinger66e51e92014-05-03 16:52:00 -0400753 time.sleep(5)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400754 if not storage_notify_proc.is_alive():
Mike Frysinger66e51e92014-05-03 16:52:00 -0400755 break
756
757 # Drain the queue so we don't hang when we finish.
758 try:
Mike Frysinger4f5ea832014-05-12 00:54:28 -0400759 cros_build_lib.Warning('draining the notify queue manually')
760 with timeout_util.Timeout(60):
761 try:
762 while dedupe_queue.get_nowait():
763 pass
764 except Queue.Empty:
765 pass
766 except timeout_util.TimeoutError:
767 cros_build_lib.Warning('draining the notify queue failed; trashing it')
768 dedupe_queue.cancel_join_thread()
Mike Frysinger66e51e92014-05-03 16:52:00 -0400769
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500770 cros_build_lib.Info('uploaded %i symbols (%i were deduped) which took: %s',
771 counters.uploaded_count, counters.deduped_count,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500772 datetime.datetime.now() - start_time)
773
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500774 return bg_errors.value
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400775
776
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400777def main(argv):
778 parser = commandline.ArgumentParser(description=__doc__)
779
Mike Frysingerd41938e2014-02-10 06:37:55 -0500780 parser.add_argument('sym_paths', type='path_or_uri', nargs='*', default=None,
781 help='symbol file or directory or URL or tarball')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400782 parser.add_argument('--board', default=None,
783 help='board to build packages for')
784 parser.add_argument('--breakpad_root', type='path', default=None,
Mike Frysingerc5597f22014-11-27 15:39:15 -0500785 help='full path to the breakpad symbol directory')
786 parser.add_argument('--root', type='path', default=None,
787 help='full path to the chroot dir')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400788 parser.add_argument('--official_build', action='store_true', default=False,
789 help='point to official symbol server')
Mike Frysinger38647542014-09-12 18:15:39 -0700790 parser.add_argument('--server', type=str, default=None,
791 help='URI for custom symbol server')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400792 parser.add_argument('--regenerate', action='store_true', default=False,
793 help='regenerate all symbols')
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500794 parser.add_argument('--upload-limit', type=int, default=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400795 help='only upload # number of symbols')
796 parser.add_argument('--strip_cfi', type=int,
797 default=CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024),
798 help='strip CFI data for files above this size')
Mike Frysinger7f9be142014-01-15 02:16:42 -0500799 parser.add_argument('--failed-list', type='path',
800 help='where to save a list of failed symbols')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500801 parser.add_argument('--dedupe', action='store_true', default=False,
802 help='use the swarming service to avoid re-uploading')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400803 parser.add_argument('--testing', action='store_true', default=False,
804 help='run in testing mode')
805 parser.add_argument('--yes', action='store_true', default=False,
806 help='answer yes to all prompts')
807
808 opts = parser.parse_args(argv)
Mike Frysinger90e49ca2014-01-14 14:42:07 -0500809 opts.Freeze()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400810
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500811 if opts.sym_paths:
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400812 if opts.regenerate:
813 cros_build_lib.Die('--regenerate may not be used with specific files')
814 else:
815 if opts.board is None:
816 cros_build_lib.Die('--board is required')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400817
818 if opts.breakpad_root and opts.regenerate:
819 cros_build_lib.Die('--regenerate may not be used with --breakpad_root')
820
821 if opts.testing:
822 # TODO(build): Kill off --testing mode once unittests are up-to-snuff.
823 cros_build_lib.Info('running in testing mode')
824 # pylint: disable=W0601,W0603
825 global INITIAL_RETRY_DELAY, SymUpload, DEFAULT_SLEEP_DELAY
826 INITIAL_RETRY_DELAY = DEFAULT_SLEEP_DELAY = 0
827 SymUpload = TestingSymUpload
828
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500829 dedupe_namespace = None
830 if opts.dedupe:
831 if opts.official_build and not opts.testing:
832 dedupe_namespace = OFFICIAL_DEDUPE_NAMESPACE
833 else:
834 dedupe_namespace = STAGING_DEDUPE_NAMESPACE
835
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400836 if not opts.yes:
Mike Frysingerc5de9602014-02-09 02:42:36 -0500837 prolog = '\n'.join(textwrap.wrap(textwrap.dedent("""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400838 Uploading symbols for an entire Chromium OS build is really only
839 necessary for release builds and in a few cases for developers
840 to debug problems. It will take considerable time to run. For
841 developer debugging purposes, consider instead passing specific
842 files to upload.
Mike Frysingerc5de9602014-02-09 02:42:36 -0500843 """), 80)).strip()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400844 if not cros_build_lib.BooleanPrompt(
845 prompt='Are you sure you want to upload all build symbols',
Mike Frysingerc5de9602014-02-09 02:42:36 -0500846 default=False, prolog=prolog):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400847 cros_build_lib.Die('better safe than sorry')
848
849 ret = 0
850 if opts.regenerate:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400851 ret += cros_generate_breakpad_symbols.GenerateBreakpadSymbols(
852 opts.board, breakpad_dir=opts.breakpad_root)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400853
854 ret += UploadSymbols(opts.board, official=opts.official_build,
Mike Frysinger38647542014-09-12 18:15:39 -0700855 server=opts.server, breakpad_dir=opts.breakpad_root,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400856 file_limit=opts.strip_cfi, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500857 upload_limit=opts.upload_limit, sym_paths=opts.sym_paths,
Mike Frysingerc5597f22014-11-27 15:39:15 -0500858 failed_list=opts.failed_list, root=opts.root,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500859 dedupe_namespace=dedupe_namespace)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400860 if ret:
861 cros_build_lib.Error('encountered %i problem(s)', ret)
862 # Since exit(status) gets masked, clamp it to 1 so we don't inadvertently
863 # return 0 in case we are a multiple of the mask.
864 ret = 1
865
866 return ret
Mike Frysinger094a2172013-08-14 12:54:35 -0400867
868
869# We need this to run once per process. Do it at module import time as that
870# will let us avoid doing it inline at function call time (see SymUpload) as
871# that func might be called by the multiprocessing module which means we'll
872# do the opener logic multiple times overall. Plus, if you're importing this
873# module, it's a pretty good chance that you're going to need this.
874poster.streaminghttp.register_openers()