This repository has been archived by the owner on Jan 30, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 39
/
breakpad.py
147 lines (121 loc) · 3.85 KB
/
breakpad.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# 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()