Finally get rid of depot_tools' breakpad.
R=maruel@chromium.org BUG= Review URL: https://codereview.chromium.org/1689633002 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@298710 0039d316-1c4b-4281-b951-d872f2087c98changes/01/332501/1
parent
c1eb692f03
commit
a05c501e6c
@ -1,147 +0,0 @@
|
|||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""Breakpad for Python.
|
|
||||||
|
|
||||||
Sends a notification when a process stops on an exception.
|
|
||||||
|
|
||||||
It is only enabled when all these conditions are met:
|
|
||||||
1. hostname finishes with '.google.com' or 'chromium.org'
|
|
||||||
2. main module name doesn't contain the word 'test'
|
|
||||||
3. no NO_BREAKPAD environment variable is defined
|
|
||||||
"""
|
|
||||||
|
|
||||||
import atexit
|
|
||||||
import getpass
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
import urllib
|
|
||||||
import urllib2
|
|
||||||
|
|
||||||
|
|
||||||
# Configure these values.
|
|
||||||
DEFAULT_URL = 'https://chromium-status.appspot.com'
|
|
||||||
|
|
||||||
# Global variable to prevent double registration.
|
|
||||||
_REGISTERED = False
|
|
||||||
|
|
||||||
_TIME_STARTED = time.time()
|
|
||||||
|
|
||||||
_HOST_NAME = socket.getfqdn()
|
|
||||||
|
|
||||||
# Skip unit tests and we don't want anything from non-googler.
|
|
||||||
IS_ENABLED = (
|
|
||||||
not 'test' in getattr(sys.modules['__main__'], '__file__', '') and
|
|
||||||
not 'NO_BREAKPAD' in os.environ and
|
|
||||||
_HOST_NAME.endswith(('.google.com', '.chromium.org')))
|
|
||||||
|
|
||||||
|
|
||||||
def post(url, params):
|
|
||||||
"""HTTP POST with timeout when it's supported."""
|
|
||||||
if not IS_ENABLED:
|
|
||||||
# Make sure to not send anything for non googler.
|
|
||||||
return
|
|
||||||
kwargs = {}
|
|
||||||
if (sys.version_info[0] * 10 + sys.version_info[1]) >= 26:
|
|
||||||
kwargs['timeout'] = 4
|
|
||||||
try:
|
|
||||||
request = urllib2.urlopen(url, urllib.urlencode(params), **kwargs)
|
|
||||||
out = request.read()
|
|
||||||
request.close()
|
|
||||||
return out
|
|
||||||
except IOError:
|
|
||||||
return 'There was a failure while trying to send the stack trace. Too bad.'
|
|
||||||
|
|
||||||
|
|
||||||
def FormatException(e):
|
|
||||||
"""Returns a human readable form of an exception.
|
|
||||||
|
|
||||||
Adds the maximum number of interesting information in the safest way."""
|
|
||||||
try:
|
|
||||||
out = repr(e)
|
|
||||||
except Exception:
|
|
||||||
out = ''
|
|
||||||
try:
|
|
||||||
out = str(e)
|
|
||||||
if isinstance(e, Exception):
|
|
||||||
# urllib exceptions, usually the HTTP headers.
|
|
||||||
if hasattr(e, 'headers'):
|
|
||||||
out += '\nHeaders: %s' % e.headers
|
|
||||||
if hasattr(e, 'url'):
|
|
||||||
out += '\nUrl: %s' % e.url
|
|
||||||
if hasattr(e, 'msg'):
|
|
||||||
out += '\nMsg: %s' % e.msg
|
|
||||||
# The web page in some urllib exceptions.
|
|
||||||
if hasattr(e, 'read') and callable(e.read):
|
|
||||||
out += '\nread(): %s' % e.read()
|
|
||||||
if hasattr(e, 'info') and callable(e.info):
|
|
||||||
out += '\ninfo(): %s' % e.info()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
def SendStack(last_tb, stack, url=None, maxlen=50, verbose=True):
|
|
||||||
"""Sends the stack trace to the breakpad server."""
|
|
||||||
if not IS_ENABLED:
|
|
||||||
return
|
|
||||||
def p(o):
|
|
||||||
if verbose:
|
|
||||||
print(o)
|
|
||||||
p('Sending crash report ...')
|
|
||||||
params = {
|
|
||||||
'args': sys.argv,
|
|
||||||
'cwd': os.getcwd(),
|
|
||||||
'exception': FormatException(last_tb),
|
|
||||||
'host': _HOST_NAME,
|
|
||||||
'stack': stack[0:4096],
|
|
||||||
'user': getpass.getuser(),
|
|
||||||
'version': sys.version,
|
|
||||||
}
|
|
||||||
p('\n'.join(' %s: %s' % (k, params[k][0:maxlen]) for k in sorted(params)))
|
|
||||||
p(post(url or DEFAULT_URL + '/breakpad', params))
|
|
||||||
|
|
||||||
|
|
||||||
def SendProfiling(duration, url=None):
|
|
||||||
params = {
|
|
||||||
'argv': ' '.join(sys.argv),
|
|
||||||
# Strip the hostname.
|
|
||||||
'domain': _HOST_NAME.split('.', 1)[-1],
|
|
||||||
'duration': duration,
|
|
||||||
'platform': sys.platform,
|
|
||||||
}
|
|
||||||
post(url or DEFAULT_URL + '/profiling', params)
|
|
||||||
|
|
||||||
|
|
||||||
def CheckForException():
|
|
||||||
"""Runs at exit. Look if there was an exception active."""
|
|
||||||
last_value = getattr(sys, 'last_value', None)
|
|
||||||
if last_value:
|
|
||||||
if not isinstance(last_value, KeyboardInterrupt):
|
|
||||||
last_tb = getattr(sys, 'last_traceback', None)
|
|
||||||
if last_tb:
|
|
||||||
SendStack(last_value, ''.join(traceback.format_tb(last_tb)))
|
|
||||||
else:
|
|
||||||
duration = time.time() - _TIME_STARTED
|
|
||||||
if duration > 90:
|
|
||||||
SendProfiling(duration)
|
|
||||||
|
|
||||||
|
|
||||||
def Register():
|
|
||||||
"""Registers the callback at exit. Calling it multiple times is no-op."""
|
|
||||||
global _REGISTERED
|
|
||||||
if _REGISTERED:
|
|
||||||
return
|
|
||||||
_REGISTERED = True
|
|
||||||
atexit.register(CheckForException)
|
|
||||||
|
|
||||||
|
|
||||||
if IS_ENABLED:
|
|
||||||
Register()
|
|
||||||
|
|
||||||
# Uncomment this line if you want to test it out.
|
|
||||||
#Register()
|
|
@ -1,123 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""Unit tests for breakpad.py."""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
|
|
||||||
from testing_support.super_mox import SuperMoxTestBase
|
|
||||||
|
|
||||||
import breakpad
|
|
||||||
|
|
||||||
|
|
||||||
class Breakpad(SuperMoxTestBase):
|
|
||||||
"""Setups and tear downs the mocks but doesn't test anything as-is."""
|
|
||||||
def setUp(self):
|
|
||||||
super(Breakpad, self).setUp()
|
|
||||||
self.mox.StubOutWithMock(breakpad.atexit, 'register')
|
|
||||||
self.mox.StubOutWithMock(breakpad.getpass, 'getuser')
|
|
||||||
self.mox.StubOutWithMock(breakpad.urllib2, 'urlopen')
|
|
||||||
breakpad._HOST_NAME = 'bozo'
|
|
||||||
self.assertEquals(False, breakpad.IS_ENABLED)
|
|
||||||
breakpad.IS_ENABLED = True
|
|
||||||
self._old_sys_argv = breakpad.sys.argv
|
|
||||||
breakpad.sys.argv = ['my_test']
|
|
||||||
self._old_sys_version = breakpad.sys.version
|
|
||||||
breakpad.sys.version = 'random python'
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
breakpad.IS_ENABLED = False
|
|
||||||
breakpad.sys.version = self._old_sys_version
|
|
||||||
breakpad.sys.argv = self._old_sys_argv
|
|
||||||
super(Breakpad, self).tearDown()
|
|
||||||
|
|
||||||
def testMembersChanged(self):
|
|
||||||
members = [
|
|
||||||
'CheckForException', 'DEFAULT_URL', 'FormatException', 'IS_ENABLED',
|
|
||||||
'Register', 'SendProfiling', 'SendStack',
|
|
||||||
'atexit', 'getpass', 'os', 'post', 'socket', 'sys', 'time', 'traceback',
|
|
||||||
'urllib', 'urllib2',
|
|
||||||
]
|
|
||||||
# If this test fails, you should add the relevant test.
|
|
||||||
self.compareMembers(breakpad, members)
|
|
||||||
|
|
||||||
def _part_1_setup_mocks(self, exception):
|
|
||||||
breakpad.os.getcwd().AndReturn('/tmp/asdf')
|
|
||||||
breakpad.getpass.getuser().AndReturn('georges')
|
|
||||||
obj = self.mox.CreateMockAnything()
|
|
||||||
kwargs = {}
|
|
||||||
if (breakpad.sys.version_info[0] * 10 + breakpad.sys.version_info[1]) >= 26:
|
|
||||||
kwargs['timeout'] = 4
|
|
||||||
breakpad.urllib2.urlopen(
|
|
||||||
'https://chromium-status.appspot.com/breakpad',
|
|
||||||
breakpad.urllib.urlencode([('exception', exception)]) + (
|
|
||||||
'&args=%5B%27my_test%27%5D'
|
|
||||||
'&stack=bar'
|
|
||||||
'&host=bozo'
|
|
||||||
'&version=random+python'
|
|
||||||
'&user=georges'
|
|
||||||
'&cwd=%2Ftmp%2Fasdf'),
|
|
||||||
**kwargs).AndReturn(obj)
|
|
||||||
obj.read().AndReturn('ok')
|
|
||||||
obj.close()
|
|
||||||
|
|
||||||
def _part_2_verify_stdout(self, exception):
|
|
||||||
self.checkstdout(
|
|
||||||
( "Sending crash report ...\n"
|
|
||||||
" args: ['my_test']\n"
|
|
||||||
" cwd: /tmp/asdf\n"
|
|
||||||
" exception: %s\n"
|
|
||||||
" host: bozo\n"
|
|
||||||
" stack: bar\n"
|
|
||||||
" user: georges\n"
|
|
||||||
" version: random python\n"
|
|
||||||
"ok\n") % exception)
|
|
||||||
|
|
||||||
def _check(self, obj, result):
|
|
||||||
self._part_1_setup_mocks(result)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
breakpad.SendStack(obj, 'bar')
|
|
||||||
self._part_2_verify_stdout(result)
|
|
||||||
|
|
||||||
def testSendBase(self):
|
|
||||||
self._check('foo', 'foo')
|
|
||||||
|
|
||||||
def testSendReprThrows(self):
|
|
||||||
class Throws(object):
|
|
||||||
def __repr__(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
def __str__(self):
|
|
||||||
return '[foo]'
|
|
||||||
self._check(Throws(), '[foo]')
|
|
||||||
|
|
||||||
def testSendStrThrows(self):
|
|
||||||
class Throws(object):
|
|
||||||
def __repr__(self):
|
|
||||||
return '[foo]'
|
|
||||||
def __str__(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
self._check(Throws(), '[foo]')
|
|
||||||
|
|
||||||
def testSendBoth(self):
|
|
||||||
class Both(object):
|
|
||||||
def __repr__(self):
|
|
||||||
return '[foo]'
|
|
||||||
def __str__(self):
|
|
||||||
return '[bar]'
|
|
||||||
self._check(Both(), '[bar]')
|
|
||||||
|
|
||||||
def testSendException(self):
|
|
||||||
obj = Exception('foo')
|
|
||||||
obj.msg = 'a message'
|
|
||||||
self._check(obj, 'foo\nMsg: a message')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
import unittest
|
|
||||||
unittest.main()
|
|
Loading…
Reference in New Issue