blob: 4aabf2b5f4082a0e197177bf332978353acfb558 [file] [log] [blame]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -04001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Upload all debug symbols required for crash reporting purposes.
6
7This script need only be used to upload release builds symbols or to debug
8crashes on non-release builds (in which case try to only upload the symbols
Mike Frysinger02e1e072013-11-10 22:11:34 -05009for those executables involved).
10"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040011
Mike Frysingera4fa1e82014-01-15 01:45:56 -050012from __future__ import print_function
13
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040014import ctypes
Mike Frysinger8ec8c502014-02-10 00:19:13 -050015import datetime
Mike Frysingerdad2a032014-05-11 23:05:11 -040016import errno
Mike Frysinger02e92402013-11-22 16:22:02 -050017import functools
Mike Frysinger0c0efa22014-02-09 23:32:23 -050018import hashlib
Mike Frysingera4fa1e82014-01-15 01:45:56 -050019import httplib
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040020import multiprocessing
21import os
Mike Frysinger094a2172013-08-14 12:54:35 -040022import poster
Mike Frysinger66e51e92014-05-03 16:52:00 -040023try:
24 import Queue
25except ImportError:
26 # Python-3 renamed to "queue". We still use Queue to avoid collisions
27 # with naming variables as "queue". Maybe we'll transition at some point.
28 # pylint: disable=F0401
29 import queue as Queue
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040030import random
Mike Frysinger66e51e92014-05-03 16:52:00 -040031import signal
Mike Frysingerfd355652014-01-23 02:57:48 -050032import socket
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040033import textwrap
34import tempfile
35import time
Mike Frysinger094a2172013-08-14 12:54:35 -040036import urllib2
Mike Frysingerd41938e2014-02-10 06:37:55 -050037import urlparse
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040038
Don Garrett88b8d782014-05-13 17:30:55 -070039from chromite.cbuildbot import constants
Mike Frysingerd41938e2014-02-10 06:37:55 -050040from chromite.lib import cache
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040041from chromite.lib import commandline
42from chromite.lib import cros_build_lib
Mike Frysingerd41938e2014-02-10 06:37:55 -050043from chromite.lib import gs
44from chromite.lib import osutils
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040045from chromite.lib import parallel
David Jamesc93e6a4d2014-01-13 11:37:36 -080046from chromite.lib import retry_util
Mike Frysinger66e51e92014-05-03 16:52:00 -040047from chromite.lib import signals
Mike Frysinger0c0efa22014-02-09 23:32:23 -050048from chromite.lib import timeout_util
Mike Frysinger69cb41d2013-08-11 20:08:19 -040049from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040050
Mike Frysinger0c0efa22014-02-09 23:32:23 -050051# Needs to be after chromite imports.
52# TODO(build): When doing the initial buildbot bootstrap, we won't have any
53# other repos available. So ignore isolateserver imports. But buildbot will
54# re-exec itself once it has done a full repo sync and then the module will
55# be available -- it isn't needed that early. http://crbug.com/341152
56try:
57 import isolateserver
58except ImportError:
59 isolateserver = None
60
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040061
62# URLs used for uploading symbols.
63OFFICIAL_UPLOAD_URL = 'http://clients2.google.com/cr/symbol'
64STAGING_UPLOAD_URL = 'http://clients2.google.com/cr/staging_symbol'
65
66
67# The crash server rejects files that are this big.
68CRASH_SERVER_FILE_LIMIT = 350 * 1024 * 1024
69# Give ourselves a little breathing room from what the server expects.
70DEFAULT_FILE_LIMIT = CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024)
71
72
Mike Frysinger0c0efa22014-02-09 23:32:23 -050073# The batch limit when talking to the dedup server. We avoid sending one at a
74# time as the round trip overhead will dominate. Conversely, we avoid sending
75# all at once so we can start uploading symbols asap -- the symbol server is a
76# bit slow and will take longer than anything else.
77# TODO: A better algorithm would be adaptive. If we have more than one symbol
78# in the upload queue waiting, we could send more symbols to the dedupe server
79# at a time.
80DEDUPE_LIMIT = 100
81
82# How long to wait for the server to respond with the results. Note that the
83# larger the limit above, the larger this will need to be. So we give it ~1
84# second per item max.
85DEDUPE_TIMEOUT = DEDUPE_LIMIT
86
Mike Frysinger4dd462e2014-04-30 16:21:51 -040087# How long to wait for the notification to finish (in minutes). If it takes
88# longer than this, we'll stop notifiying, but that's not a big deal as we
89# will be able to recover in later runs.
90DEDUPE_NOTIFY_TIMEOUT = 20
91
Mike Frysinger0c0efa22014-02-09 23:32:23 -050092# The unique namespace in the dedupe server that only we use. Helps avoid
93# collisions with all the hashed values and unrelated content.
94OFFICIAL_DEDUPE_NAMESPACE = 'chromium-os-upload-symbols'
95STAGING_DEDUPE_NAMESPACE = '%s-staging' % OFFICIAL_DEDUPE_NAMESPACE
96
97
Mike Frysinger71046662014-09-12 18:15:15 -070098# The minimum average rate (in bytes per second) that we expect to maintain
99# when uploading symbols. This has to allow for symbols that are up to
100# CRASH_SERVER_FILE_LIMIT in size.
101UPLOAD_MIN_RATE = CRASH_SERVER_FILE_LIMIT / (30 * 60)
102
103# The lowest timeout (in seconds) we'll allow. If the server is overloaded,
104# then there might be a delay in setting up the connection, not just with the
105# transfer. So even a small file might need a larger value.
106UPLOAD_MIN_TIMEOUT = 2 * 60
Mike Frysingercd78a082013-06-26 17:13:04 -0400107
108
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400109# Sleep for 200ms in between uploads to avoid DoS'ing symbol server.
110DEFAULT_SLEEP_DELAY = 0.2
111
112
113# Number of seconds to wait before retrying an upload. The delay will double
114# for each subsequent retry of the same symbol file.
115INITIAL_RETRY_DELAY = 1
116
117# Allow up to 7 attempts to upload a symbol file (total delay may be
118# 1+2+4+8+16+32=63 seconds).
119MAX_RETRIES = 6
120
Mike Frysingereb753bf2013-11-22 16:05:35 -0500121# Number of total errors, before uploads are no longer attempted.
122# This is used to avoid lots of errors causing unreasonable delays.
123# See the related, but independent, error values below.
124MAX_TOTAL_ERRORS_FOR_RETRY = 30
125
126# A watermark of transient errors which we allow recovery from. If we hit
127# errors infrequently, overall we're probably doing fine. For example, if
128# we have one failure every 100 passes, then we probably don't want to fail
129# right away. But if we hit a string of failures in a row, we want to abort.
130#
131# The watermark starts at 0 (and can never go below that). When this error
132# level is exceeded, we stop uploading. When a failure happens, we add the
133# fail adjustment, and when an upload succeeds, we add the pass adjustment.
134# We want to penalize failures more so that we ramp up when there is a string
135# of them, but then slowly back off as things start working.
136#
137# A quick example:
138# 0.0: Starting point.
139# 0.0: Upload works, so add -0.5, and then clamp to 0.
140# 1.0: Upload fails, so add 1.0.
141# 2.0: Upload fails, so add 1.0.
142# 1.5: Upload works, so add -0.5.
143# 1.0: Upload works, so add -0.5.
144ERROR_WATERMARK = 3.0
145ERROR_ADJUST_FAIL = 1.0
146ERROR_ADJUST_PASS = -0.5
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400147
148
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700149def GetUploadTimeout(path):
150 """How long to wait for a specific file to upload to the crash server.
151
152 This is a function largely to make unittesting easier.
153
154 Args:
155 path: The path to the file to calculate the timeout for
156
157 Returns:
158 Timeout length (in seconds)
159 """
160 # Scale the timeout based on the filesize.
161 return max(os.path.getsize(path) / UPLOAD_MIN_RATE, UPLOAD_MIN_TIMEOUT)
162
163
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500164def SymUpload(upload_url, sym_item):
Mike Frysinger094a2172013-08-14 12:54:35 -0400165 """Upload a symbol file to a HTTP server
166
167 The upload is a multipart/form-data POST with the following parameters:
168 code_file: the basename of the module, e.g. "app"
169 code_identifier: the module file's identifier
170 debug_file: the basename of the debugging file, e.g. "app"
171 debug_identifier: the debug file's identifier, usually consisting of
172 the guid and age embedded in the pdb, e.g.
173 "11111111BBBB3333DDDD555555555555F"
174 version: the file version of the module, e.g. "1.2.3.4"
175 product: HTTP-friendly product name
176 os: the operating system that the module was built for
177 cpu: the CPU that the module was built for
178 symbol_file: the contents of the breakpad-format symbol file
179
180 Args:
Mike Frysinger094a2172013-08-14 12:54:35 -0400181 upload_url: The crash URL to POST the |sym_file| to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500182 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysinger094a2172013-08-14 12:54:35 -0400183 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500184 sym_header = sym_item.sym_header
185 sym_file = sym_item.sym_file
Mike Frysinger094a2172013-08-14 12:54:35 -0400186
187 fields = (
188 ('code_file', sym_header.name),
189 ('debug_file', sym_header.name),
190 ('debug_identifier', sym_header.id.replace('-', '')),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400191 # The product/version fields are used by the server only for statistic
192 # purposes. They do not impact symbolization, so they're safe to set
193 # to any value all the time.
194 # In this case, we use it to help see the load our build system is
195 # placing on the server.
196 # Not sure what to set for the version. Maybe the git sha1 of this file.
197 # Note: the server restricts this to 30 chars.
Mike Frysinger094a2172013-08-14 12:54:35 -0400198 #('version', None),
Mike Frysingerb8a966b2014-03-19 17:36:18 -0400199 ('product', 'ChromeOS'),
Mike Frysinger094a2172013-08-14 12:54:35 -0400200 ('os', sym_header.os),
201 ('cpu', sym_header.cpu),
202 poster.encode.MultipartParam.from_file('symbol_file', sym_file),
203 )
204
205 data, headers = poster.encode.multipart_encode(fields)
206 request = urllib2.Request(upload_url, data, headers)
207 request.add_header('User-agent', 'chromite.upload_symbols')
Mike Frysinger0a2fd922014-09-12 20:23:42 -0700208 urllib2.urlopen(request, timeout=GetUploadTimeout(sym_file))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400209
210
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500211def TestingSymUpload(upload_url, sym_item):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400212 """A stub version of SymUpload for --testing usage"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500213 cmd = ['sym_upload', sym_item.sym_file, upload_url]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400214 # Randomly fail 80% of the time (the retry logic makes this 80%/3 per file).
215 returncode = random.randint(1, 100) <= 80
216 cros_build_lib.Debug('would run (and return %i): %s', returncode,
Matt Tennant7feda352013-12-20 14:03:40 -0800217 cros_build_lib.CmdToStr(cmd))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400218 if returncode:
219 output = 'Failed to send the symbol file.'
220 else:
221 output = 'Successfully sent the symbol file.'
222 result = cros_build_lib.CommandResult(cmd=cmd, error=None, output=output,
223 returncode=returncode)
224 if returncode:
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500225 exceptions = (
Mike Frysingerfd355652014-01-23 02:57:48 -0500226 socket.error('[socket.error] forced test fail'),
Mike Frysingera4fa1e82014-01-15 01:45:56 -0500227 httplib.BadStatusLine('[BadStatusLine] forced test fail'),
228 urllib2.HTTPError(upload_url, 400, '[HTTPError] forced test fail',
229 {}, None),
230 urllib2.URLError('[URLError] forced test fail'),
231 )
232 raise random.choice(exceptions)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400233 else:
234 return result
235
236
Mike Frysingereb753bf2013-11-22 16:05:35 -0500237def ErrorLimitHit(num_errors, watermark_errors):
238 """See if our error limit has been hit
239
240 Args:
241 num_errors: A multiprocessing.Value of the raw number of failures.
242 watermark_errors: A multiprocessing.Value of the current rate of failures.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500243
Mike Frysingereb753bf2013-11-22 16:05:35 -0500244 Returns:
245 True if our error limits have been exceeded.
246 """
247 return ((num_errors is not None and
248 num_errors.value > MAX_TOTAL_ERRORS_FOR_RETRY) or
249 (watermark_errors is not None and
250 watermark_errors.value > ERROR_WATERMARK))
251
252
253def _UpdateCounter(counter, adj):
254 """Update |counter| by |adj|
255
256 Handle atomic updates of |counter|. Also make sure it does not
257 fall below 0.
258
259 Args:
260 counter: A multiprocessing.Value to update
261 adj: The value to add to |counter|
262 """
263 def _Update():
264 clamp = 0 if type(adj) is int else 0.0
265 counter.value = max(clamp, counter.value + adj)
266
267 if hasattr(counter, 'get_lock'):
268 with counter.get_lock():
269 _Update()
270 elif counter is not None:
271 _Update()
272
273
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500274def UploadSymbol(upload_url, sym_item, file_limit=DEFAULT_FILE_LIMIT,
Mike Frysinger02e92402013-11-22 16:22:02 -0500275 sleep=0, num_errors=None, watermark_errors=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500276 failed_queue=None, passed_queue=None):
277 """Upload |sym_item| to |upload_url|
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400278
279 Args:
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400280 upload_url: The crash server to upload things to
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500281 sym_item: A SymbolItem containing the path to the breakpad symbol to upload
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400282 file_limit: The max file size of a symbol file before we try to strip it
283 sleep: Number of seconds to sleep before running
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400284 num_errors: An object to update with the error count (needs a .value member)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500285 watermark_errors: An object to track current error behavior (needs a .value)
Mike Frysinger02e92402013-11-22 16:22:02 -0500286 failed_queue: When a symbol fails, add it to this queue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500287 passed_queue: When a symbol passes, add it to this queue
Mike Frysinger1a736a82013-12-12 01:50:59 -0500288
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400289 Returns:
290 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400291 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500292 sym_file = sym_item.sym_file
293 upload_item = sym_item
294
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400295 if num_errors is None:
296 num_errors = ctypes.c_int()
Mike Frysingereb753bf2013-11-22 16:05:35 -0500297 if ErrorLimitHit(num_errors, watermark_errors):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400298 # Abandon ship! It's on fire! NOoooooooooooOOOoooooo.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500299 if failed_queue:
300 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400301 return 0
302
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400303 if sleep:
304 # Keeps us from DoS-ing the symbol server.
305 time.sleep(sleep)
306
307 cros_build_lib.Debug('uploading %s' % sym_file)
308
309 # Ideally there'd be a tempfile.SpooledNamedTemporaryFile that we could use.
310 with tempfile.NamedTemporaryFile(prefix='upload_symbols',
311 bufsize=0) as temp_sym_file:
312 if file_limit:
313 # If the symbols size is too big, strip out the call frame info. The CFI
314 # is unnecessary for 32bit x86 targets where the frame pointer is used (as
315 # all of ours have) and it accounts for over half the size of the symbols
316 # uploaded.
317 file_size = os.path.getsize(sym_file)
318 if file_size > file_limit:
319 cros_build_lib.Warning('stripping CFI from %s due to size %s > %s',
320 sym_file, file_size, file_limit)
321 temp_sym_file.writelines([x for x in open(sym_file, 'rb').readlines()
322 if not x.startswith('STACK CFI')])
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500323
324 upload_item = FakeItem(sym_file=temp_sym_file.name,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500325 sym_header=sym_item.sym_header)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400326
327 # Hopefully the crash server will let it through. But it probably won't.
328 # Not sure what the best answer is in this case.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500329 file_size = os.path.getsize(upload_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400330 if file_size > CRASH_SERVER_FILE_LIMIT:
331 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger02e92402013-11-22 16:22:02 -0500332 cros_build_lib.Warning('upload file %s is awfully large, risking '
333 'rejection by the symbol server (%s > %s)',
334 sym_file, file_size, CRASH_SERVER_FILE_LIMIT)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400335
336 # Upload the symbol file.
Mike Frysingereb753bf2013-11-22 16:05:35 -0500337 success = False
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400338 try:
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400339 cros_build_lib.TimedCommand(
David Jamesc93e6a4d2014-01-13 11:37:36 -0800340 retry_util.RetryException,
Mike Frysinger9adcfd22013-10-24 12:01:40 -0400341 (urllib2.HTTPError, urllib2.URLError), MAX_RETRIES, SymUpload,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500342 upload_url, upload_item, sleep=INITIAL_RETRY_DELAY,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500343 timed_log_msg=('upload of %10i bytes took %%s: %s' %
344 (file_size, os.path.basename(sym_file))))
Mike Frysingereb753bf2013-11-22 16:05:35 -0500345 success = True
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500346
347 if passed_queue:
348 passed_queue.put(sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400349 except urllib2.HTTPError as e:
350 cros_build_lib.Warning('could not upload: %s: HTTP %s: %s',
351 os.path.basename(sym_file), e.code, e.reason)
Mike Frysingerfd355652014-01-23 02:57:48 -0500352 except (urllib2.URLError, httplib.HTTPException, socket.error) as e:
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400353 cros_build_lib.Warning('could not upload: %s: %s',
354 os.path.basename(sym_file), e)
Mike Frysingereb753bf2013-11-22 16:05:35 -0500355 finally:
356 if success:
357 _UpdateCounter(watermark_errors, ERROR_ADJUST_PASS)
358 else:
359 _UpdateCounter(num_errors, 1)
360 _UpdateCounter(watermark_errors, ERROR_ADJUST_FAIL)
Mike Frysinger02e92402013-11-22 16:22:02 -0500361 if failed_queue:
362 failed_queue.put(sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400363
364 return num_errors.value
365
366
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500367# A dummy class that allows for stubbing in tests and SymUpload.
368FakeItem = cros_build_lib.Collection(
369 'FakeItem', sym_file=None, sym_header=None, content=lambda x: '')
370
371
372# TODO(build): Delete this if check. http://crbug.com/341152
373if isolateserver:
374 class SymbolItem(isolateserver.BufferItem):
375 """Turn a sym_file into an isolateserver.Item"""
376
377 ALGO = hashlib.sha1
378
379 def __init__(self, sym_file):
380 sym_header = cros_generate_breakpad_symbols.ReadSymsHeader(sym_file)
381 super(SymbolItem, self).__init__(str(sym_header), self.ALGO)
382 self.sym_header = sym_header
383 self.sym_file = sym_file
384
385
386def SymbolDeduplicatorNotify(dedupe_namespace, dedupe_queue):
387 """Send a symbol file to the swarming service
388
389 Notify the swarming service of a successful upload. If the notification fails
390 for any reason, we ignore it. We don't care as it just means we'll upload it
391 again later on, and the symbol server will handle that graciously.
392
393 This func runs in a different process from the main one, so we cannot share
394 the storage object. Instead, we create our own. This func stays alive for
395 the life of the process, so we only create one here overall.
396
397 Args:
398 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
399 dedupe_queue: The queue to read SymbolItems from
400 """
401 if dedupe_queue is None:
402 return
403
404 item = None
405 try:
Mike Frysinger650e6722014-04-28 18:29:15 -0400406 with timeout_util.Timeout(DEDUPE_TIMEOUT):
407 storage = isolateserver.get_storage_api(constants.ISOLATESERVER,
408 dedupe_namespace)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500409 for item in iter(dedupe_queue.get, None):
410 with timeout_util.Timeout(DEDUPE_TIMEOUT):
Mike Frysingerefef3672014-04-20 10:06:45 -0400411 cros_build_lib.Debug('sending %s to dedupe server', item.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500412 storage.push(item, item.content(0))
Mike Frysinger66e51e92014-05-03 16:52:00 -0400413 cros_build_lib.Debug('sent %s', item.sym_file)
Mike Frysingerae298452014-03-24 22:45:23 -0400414 cros_build_lib.Info('dedupe notification finished; exiting')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500415 except Exception:
416 sym_file = item.sym_file if (item and item.sym_file) else ''
417 cros_build_lib.Warning('posting %s to dedupe server failed',
418 os.path.basename(sym_file), exc_info=True)
419
Mike Frysinger58312e92014-03-18 04:18:36 -0400420 # Keep draining the queue though so it doesn't fill up.
421 while dedupe_queue.get() is not None:
422 continue
423
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500424
425def SymbolDeduplicator(storage, sym_paths):
426 """Filter out symbol files that we've already uploaded
427
428 Using the swarming service, ask it to tell us which symbol files we've already
429 uploaded in previous runs and/or by other bots. If the query fails for any
430 reason, we'll just upload all symbols. This is fine as the symbol server will
431 do the right thing and this phase is purely an optimization.
432
433 This code runs in the main thread which is why we can re-use the existing
434 storage object. Saves us from having to recreate one all the time.
435
436 Args:
437 storage: An isolateserver.StorageApi object
438 sym_paths: List of symbol files to check against the dedupe server
439
440 Returns:
441 List of symbol files that have not been uploaded before
442 """
443 if not sym_paths:
444 return sym_paths
445
446 items = [SymbolItem(x) for x in sym_paths]
447 if storage:
448 try:
449 with timeout_util.Timeout(DEDUPE_TIMEOUT):
450 items = storage.contains(items)
451 except Exception:
452 cros_build_lib.Warning('talking to dedupe server failed', exc_info=True)
453
454 return items
455
456
Mike Frysingerd41938e2014-02-10 06:37:55 -0500457def IsTarball(path):
458 """Guess if this is a tarball based on the filename."""
459 parts = path.split('.')
460 if len(parts) <= 1:
461 return False
462
463 if parts[-1] == 'tar':
464 return True
465
466 if parts[-2] == 'tar':
467 return parts[-1] in ('bz2', 'gz', 'xz')
468
469 return parts[-1] in ('tbz2', 'tbz', 'tgz', 'txz')
470
471
472def SymbolFinder(tempdir, paths):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500473 """Locate symbol files in |paths|
474
475 Args:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500476 tempdir: Path to use for temporary files (caller will clean up).
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500477 paths: A list of input paths to walk. Files are returned w/out any checks.
Mike Frysingerd41938e2014-02-10 06:37:55 -0500478 Dirs are searched for files that end in ".sym". Urls are fetched and then
479 processed. Tarballs are unpacked and walked.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500480
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500481 Returns:
482 Yield every viable sym file.
483 """
484 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)
494 cache_dir = commandline.GetCacheDir()
495 common_path = os.path.join(cache_dir, constants.COMMON_CACHE)
496 tar_cache = cache.TarballCache(common_path)
Don Garrettf8bf7842014-03-20 17:03:42 -0700497 key = ('%s%s' % (o.netloc, o.path)).split('/') # pylint: disable=E1101
Mike Frysingerd41938e2014-02-10 06:37:55 -0500498 # The common cache will not be LRU, removing the need to hold a read
499 # lock on the cached gsutil.
500 ref = tar_cache.Lookup(key)
501 try:
502 ref.SetDefault(p)
503 except cros_build_lib.RunCommandError as e:
504 cros_build_lib.Warning('ignoring %s\n%s', p, e)
505 continue
506 for p in SymbolFinder(tempdir, [ref.path]):
507 yield p
508
509 elif os.path.isdir(p):
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500510 for root, _, files in os.walk(p):
511 for f in files:
512 if f.endswith('.sym'):
513 yield os.path.join(root, f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500514
515 elif IsTarball(p):
516 cros_build_lib.Info('processing files inside %s', p)
517 tardir = tempfile.mkdtemp(dir=tempdir)
518 cache.Untar(os.path.realpath(p), tardir)
519 for p in SymbolFinder(tardir, [tardir]):
520 yield p
521
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500522 else:
523 yield p
524
525
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500526def WriteQueueToFile(listing, queue, relpath=None):
527 """Write all the items in |queue| to the |listing|.
528
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500529 Note: The queue must have a sentinel None appended to the end.
530
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500531 Args:
532 listing: Where to write out the list of files.
533 queue: The queue of paths to drain.
534 relpath: If set, write out paths relative to this one.
535 """
536 if not listing:
Mike Frysingera0ddac62014-03-14 10:30:25 -0400537 # Still drain the queue so we make sure the producer has finished
538 # before we return. Otherwise, the queue might get destroyed too
539 # quickly which will trigger a traceback in the producer.
540 while queue.get() is not None:
541 continue
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500542 return
543
544 with cros_build_lib.Open(listing, 'wb+') as f:
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500545 while True:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500546 path = queue.get()
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500547 if path is None:
548 return
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500549 if relpath:
550 path = os.path.relpath(path, relpath)
551 f.write('%s\n' % path)
552
553
Mike Frysinger38647542014-09-12 18:15:39 -0700554def UploadSymbols(board=None, official=False, server=None, breakpad_dir=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400555 file_limit=DEFAULT_FILE_LIMIT, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500556 upload_limit=None, sym_paths=None, failed_list=None,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500557 root=None, retry=True, dedupe_namespace=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400558 """Upload all the generated symbols for |board| to the crash server
559
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400560 You can use in a few ways:
561 * pass |board| to locate all of its symbols
562 * pass |breakpad_dir| to upload all the symbols in there
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500563 * pass |sym_paths| to upload specific symbols (or dirs of symbols)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400564
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400565 Args:
566 board: The board whose symbols we wish to upload
567 official: Use the official symbol server rather than the staging one
Mike Frysinger38647542014-09-12 18:15:39 -0700568 server: Explicit server to post symbols to
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400569 breakpad_dir: The full path to the breakpad directory where symbols live
570 file_limit: The max file size of a symbol file before we try to strip it
571 sleep: How long to sleep in between uploads
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500572 upload_limit: If set, only upload this many symbols (meant for testing)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500573 sym_paths: Specific symbol files (or dirs of sym files) to upload,
574 otherwise search |breakpad_dir|
Mike Frysinger7f9be142014-01-15 02:16:42 -0500575 failed_list: Write the names of all sym files we did not upload; can be a
576 filename or file-like object.
Mike Frysinger118d2502013-08-19 03:36:56 -0400577 root: The tree to prefix to |breakpad_dir| (if |breakpad_dir| is not set)
Mike Frysinger02e92402013-11-22 16:22:02 -0500578 retry: Whether we should retry failures.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500579 dedupe_namespace: The isolateserver namespace to dedupe uploaded symbols.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500580
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400581 Returns:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400582 The number of errors that were encountered.
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400583 """
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500584 # TODO(build): Delete this assert.
585 assert isolateserver, 'Missing isolateserver import http://crbug.com/341152'
586
Mike Frysinger38647542014-09-12 18:15:39 -0700587 if server is None:
588 if official:
589 upload_url = OFFICIAL_UPLOAD_URL
590 else:
591 cros_build_lib.Warning('unofficial builds upload to the staging server')
592 upload_url = STAGING_UPLOAD_URL
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400593 else:
Mike Frysinger38647542014-09-12 18:15:39 -0700594 upload_url = server
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400595
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500596 if sym_paths:
597 cros_build_lib.Info('uploading specified symbols to %s', upload_url)
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400598 else:
599 if breakpad_dir is None:
Mike Frysinger118d2502013-08-19 03:36:56 -0400600 breakpad_dir = os.path.join(
601 root,
602 cros_generate_breakpad_symbols.FindBreakpadDir(board).lstrip('/'))
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400603 cros_build_lib.Info('uploading all symbols to %s from %s', upload_url,
604 breakpad_dir)
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500605 sym_paths = [breakpad_dir]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400606
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500607 # We use storage_query to ask the server about existing symbols. The
608 # storage_notify_proc process is used to post updates to the server. We
609 # cannot safely share the storage object between threads/processes, but
610 # we also want to minimize creating new ones as each object has to init
611 # new state (like server connections).
Mike Frysinger650e6722014-04-28 18:29:15 -0400612 storage_query = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500613 if dedupe_namespace:
614 dedupe_limit = DEDUPE_LIMIT
615 dedupe_queue = multiprocessing.Queue()
Mike Frysinger650e6722014-04-28 18:29:15 -0400616 try:
617 with timeout_util.Timeout(DEDUPE_TIMEOUT):
618 storage_query = isolateserver.get_storage_api(constants.ISOLATESERVER,
619 dedupe_namespace)
620 except Exception:
621 cros_build_lib.Warning('initializing dedupe server connection failed',
622 exc_info=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500623 else:
624 dedupe_limit = 1
Mike Frysinger650e6722014-04-28 18:29:15 -0400625 dedupe_queue = None
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500626 # Can't use parallel.BackgroundTaskRunner because that'll create multiple
627 # processes and we want only one the whole time (see comment above).
628 storage_notify_proc = multiprocessing.Process(
629 target=SymbolDeduplicatorNotify, args=(dedupe_namespace, dedupe_queue))
630
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400631 bg_errors = multiprocessing.Value('i')
Mike Frysingereb753bf2013-11-22 16:05:35 -0500632 watermark_errors = multiprocessing.Value('f')
Mike Frysinger02e92402013-11-22 16:22:02 -0500633 failed_queue = multiprocessing.Queue()
634 uploader = functools.partial(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500635 UploadSymbol, upload_url, file_limit=file_limit, sleep=sleep,
636 num_errors=bg_errors, watermark_errors=watermark_errors,
637 failed_queue=failed_queue, passed_queue=dedupe_queue)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400638
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500639 start_time = datetime.datetime.now()
640 Counters = cros_build_lib.Collection(
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500641 'Counters', upload_limit=upload_limit, uploaded_count=0, deduped_count=0)
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500642 counters = Counters()
643
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500644 def _Upload(queue, counters, files):
645 if not files:
646 return
647
648 missing_count = 0
649 for item in SymbolDeduplicator(storage_query, files):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500650 missing_count += 1
Mike Frysingerd42e5f02014-03-14 11:19:37 -0400651
652 if counters.upload_limit == 0:
653 continue
654
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500655 queue.put((item,))
656 counters.uploaded_count += 1
657 if counters.upload_limit is not None:
658 counters.upload_limit -= 1
659
660 counters.deduped_count += (len(files) - missing_count)
661
Mike Frysinger13870082014-03-14 10:41:20 -0400662 try:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500663 storage_notify_proc.start()
Mike Frysinger02e92402013-11-22 16:22:02 -0500664
Mike Frysinger13870082014-03-14 10:41:20 -0400665 with osutils.TempDir(prefix='upload_symbols.') as tempdir:
666 # For the first run, we collect the symbols that failed. If the
667 # overall failure rate was low, we'll retry them on the second run.
668 for retry in (retry, False):
669 # We need to limit ourselves to one upload at a time to avoid the server
670 # kicking in DoS protection. See these bugs for more details:
671 # http://crbug.com/209442
672 # http://crbug.com/212496
673 with parallel.BackgroundTaskRunner(uploader, processes=1) as queue:
Mike Frysingerd41938e2014-02-10 06:37:55 -0500674 dedupe_list = []
Mike Frysinger13870082014-03-14 10:41:20 -0400675 for sym_file in SymbolFinder(tempdir, sym_paths):
676 dedupe_list.append(sym_file)
677 dedupe_len = len(dedupe_list)
678 if dedupe_len < dedupe_limit:
679 if (counters.upload_limit is None or
680 dedupe_len < counters.upload_limit):
681 continue
Mike Frysinger02e92402013-11-22 16:22:02 -0500682
Mike Frysinger1010a892014-03-14 11:24:17 -0400683 # We check the counter before _Upload so that we don't keep talking
684 # to the dedupe server. Otherwise, we end up sending one symbol at
685 # a time to it and that slows things down a lot.
686 if counters.upload_limit == 0:
687 break
688
Mike Frysinger13870082014-03-14 10:41:20 -0400689 _Upload(queue, counters, dedupe_list)
690 dedupe_list = []
691 _Upload(queue, counters, dedupe_list)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500692
Mike Frysinger13870082014-03-14 10:41:20 -0400693 # See if we need to retry, and if we haven't failed too many times yet.
694 if not retry or ErrorLimitHit(bg_errors, watermark_errors):
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500695 break
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500696
Mike Frysinger13870082014-03-14 10:41:20 -0400697 sym_paths = []
698 failed_queue.put(None)
699 while True:
700 sym_path = failed_queue.get()
701 if sym_path is None:
702 break
703 sym_paths.append(sym_path)
Mike Frysinger02e92402013-11-22 16:22:02 -0500704
Mike Frysinger13870082014-03-14 10:41:20 -0400705 if sym_paths:
706 cros_build_lib.Warning('retrying %i symbols', len(sym_paths))
707 if counters.upload_limit is not None:
708 counters.upload_limit += len(sym_paths)
709 # Decrement the error count in case we recover in the second pass.
710 assert bg_errors.value >= len(sym_paths), \
711 'more failed files than errors?'
712 bg_errors.value -= len(sym_paths)
713 else:
714 # No failed symbols, so just return now.
715 break
Mike Frysinger7f9be142014-01-15 02:16:42 -0500716
Mike Frysinger13870082014-03-14 10:41:20 -0400717 # If the user has requested it, save all the symbol files that we failed to
718 # upload to a listing file. This should help with recovery efforts later.
719 failed_queue.put(None)
720 WriteQueueToFile(failed_list, failed_queue, breakpad_dir)
721
722 finally:
Mike Frysingerae298452014-03-24 22:45:23 -0400723 cros_build_lib.Info('finished uploading; joining background process')
Mike Frysinger13870082014-03-14 10:41:20 -0400724 if dedupe_queue:
725 dedupe_queue.put(None)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400726
727 # The notification might be slow going, so give it some time to finish.
728 # We have to poll here as the process monitor is watching for output and
729 # will kill us if we go silent for too long.
730 wait_minutes = DEDUPE_NOTIFY_TIMEOUT
731 while storage_notify_proc.is_alive() and wait_minutes > 0:
Aviv Keshetd1f04632014-05-09 11:33:46 -0700732 if dedupe_queue:
733 qsize = str(dedupe_queue.qsize())
734 else:
735 qsize = '[None]'
736 cros_build_lib.Info('waiting up to %i minutes for ~%s notifications',
737 wait_minutes, qsize)
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400738 storage_notify_proc.join(60)
739 wait_minutes -= 1
740
741 # The process is taking too long, so kill it and complain.
742 if storage_notify_proc.is_alive():
Mike Frysinger4dd462e2014-04-30 16:21:51 -0400743 cros_build_lib.Warning('notification process took too long')
744 cros_build_lib.PrintBuildbotStepWarnings()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500745
Mike Frysinger66e51e92014-05-03 16:52:00 -0400746 # Kill it gracefully first (traceback) before tacking it down harder.
747 pid = storage_notify_proc.pid
748 for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGKILL):
749 cros_build_lib.Warning('sending %s to %i', signals.StrSignal(sig), pid)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400750 # The process might have exited between the last check and the
751 # actual kill below, so ignore ESRCH errors.
752 try:
753 os.kill(pid, sig)
754 except OSError as e:
755 if e.errno == errno.ESRCH:
756 break
757 else:
758 raise
Mike Frysinger66e51e92014-05-03 16:52:00 -0400759 time.sleep(5)
Mike Frysingerdad2a032014-05-11 23:05:11 -0400760 if not storage_notify_proc.is_alive():
Mike Frysinger66e51e92014-05-03 16:52:00 -0400761 break
762
763 # Drain the queue so we don't hang when we finish.
764 try:
Mike Frysinger4f5ea832014-05-12 00:54:28 -0400765 cros_build_lib.Warning('draining the notify queue manually')
766 with timeout_util.Timeout(60):
767 try:
768 while dedupe_queue.get_nowait():
769 pass
770 except Queue.Empty:
771 pass
772 except timeout_util.TimeoutError:
773 cros_build_lib.Warning('draining the notify queue failed; trashing it')
774 dedupe_queue.cancel_join_thread()
Mike Frysinger66e51e92014-05-03 16:52:00 -0400775
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500776 cros_build_lib.Info('uploaded %i symbols (%i were deduped) which took: %s',
777 counters.uploaded_count, counters.deduped_count,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500778 datetime.datetime.now() - start_time)
779
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500780 return bg_errors.value
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400781
782
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400783def main(argv):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500784 # TODO(build): Delete this assert.
785 assert isolateserver, 'Missing isolateserver import http://crbug.com/341152'
786
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400787 parser = commandline.ArgumentParser(description=__doc__)
788
Mike Frysingerd41938e2014-02-10 06:37:55 -0500789 parser.add_argument('sym_paths', type='path_or_uri', nargs='*', default=None,
790 help='symbol file or directory or URL or tarball')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400791 parser.add_argument('--board', default=None,
792 help='board to build packages for')
793 parser.add_argument('--breakpad_root', type='path', default=None,
794 help='root directory for breakpad symbols')
795 parser.add_argument('--official_build', action='store_true', default=False,
796 help='point to official symbol server')
Mike Frysinger38647542014-09-12 18:15:39 -0700797 parser.add_argument('--server', type=str, default=None,
798 help='URI for custom symbol server')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400799 parser.add_argument('--regenerate', action='store_true', default=False,
800 help='regenerate all symbols')
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500801 parser.add_argument('--upload-limit', type=int, default=None,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400802 help='only upload # number of symbols')
803 parser.add_argument('--strip_cfi', type=int,
804 default=CRASH_SERVER_FILE_LIMIT - (10 * 1024 * 1024),
805 help='strip CFI data for files above this size')
Mike Frysinger7f9be142014-01-15 02:16:42 -0500806 parser.add_argument('--failed-list', type='path',
807 help='where to save a list of failed symbols')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500808 parser.add_argument('--dedupe', action='store_true', default=False,
809 help='use the swarming service to avoid re-uploading')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400810 parser.add_argument('--testing', action='store_true', default=False,
811 help='run in testing mode')
812 parser.add_argument('--yes', action='store_true', default=False,
813 help='answer yes to all prompts')
814
815 opts = parser.parse_args(argv)
Mike Frysinger90e49ca2014-01-14 14:42:07 -0500816 opts.Freeze()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400817
Mike Frysinger9b2ff5c2013-11-22 10:01:12 -0500818 if opts.sym_paths:
Mike Frysinger9dcf9ae2013-08-10 15:17:09 -0400819 if opts.regenerate:
820 cros_build_lib.Die('--regenerate may not be used with specific files')
821 else:
822 if opts.board is None:
823 cros_build_lib.Die('--board is required')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400824
825 if opts.breakpad_root and opts.regenerate:
826 cros_build_lib.Die('--regenerate may not be used with --breakpad_root')
827
828 if opts.testing:
829 # TODO(build): Kill off --testing mode once unittests are up-to-snuff.
830 cros_build_lib.Info('running in testing mode')
831 # pylint: disable=W0601,W0603
832 global INITIAL_RETRY_DELAY, SymUpload, DEFAULT_SLEEP_DELAY
833 INITIAL_RETRY_DELAY = DEFAULT_SLEEP_DELAY = 0
834 SymUpload = TestingSymUpload
835
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500836 dedupe_namespace = None
837 if opts.dedupe:
838 if opts.official_build and not opts.testing:
839 dedupe_namespace = OFFICIAL_DEDUPE_NAMESPACE
840 else:
841 dedupe_namespace = STAGING_DEDUPE_NAMESPACE
842
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400843 if not opts.yes:
Mike Frysingerc5de9602014-02-09 02:42:36 -0500844 prolog = '\n'.join(textwrap.wrap(textwrap.dedent("""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400845 Uploading symbols for an entire Chromium OS build is really only
846 necessary for release builds and in a few cases for developers
847 to debug problems. It will take considerable time to run. For
848 developer debugging purposes, consider instead passing specific
849 files to upload.
Mike Frysingerc5de9602014-02-09 02:42:36 -0500850 """), 80)).strip()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400851 if not cros_build_lib.BooleanPrompt(
852 prompt='Are you sure you want to upload all build symbols',
Mike Frysingerc5de9602014-02-09 02:42:36 -0500853 default=False, prolog=prolog):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400854 cros_build_lib.Die('better safe than sorry')
855
856 ret = 0
857 if opts.regenerate:
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400858 ret += cros_generate_breakpad_symbols.GenerateBreakpadSymbols(
859 opts.board, breakpad_dir=opts.breakpad_root)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400860
861 ret += UploadSymbols(opts.board, official=opts.official_build,
Mike Frysinger38647542014-09-12 18:15:39 -0700862 server=opts.server, breakpad_dir=opts.breakpad_root,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400863 file_limit=opts.strip_cfi, sleep=DEFAULT_SLEEP_DELAY,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500864 upload_limit=opts.upload_limit, sym_paths=opts.sym_paths,
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500865 failed_list=opts.failed_list,
866 dedupe_namespace=dedupe_namespace)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400867 if ret:
868 cros_build_lib.Error('encountered %i problem(s)', ret)
869 # Since exit(status) gets masked, clamp it to 1 so we don't inadvertently
870 # return 0 in case we are a multiple of the mask.
871 ret = 1
872
873 return ret
Mike Frysinger094a2172013-08-14 12:54:35 -0400874
875
876# We need this to run once per process. Do it at module import time as that
877# will let us avoid doing it inline at function call time (see SymUpload) as
878# that func might be called by the multiprocessing module which means we'll
879# do the opener logic multiple times overall. Plus, if you're importing this
880# module, it's a pretty good chance that you're going to need this.
881poster.streaminghttp.register_openers()