OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Breakpad for Python. | 5 """Breakpad for Python. |
6 | 6 |
7 Sends a notification when a process stops on an exception. | 7 Sends a notification when a process stops on an exception. |
8 | 8 |
9 It is only enabled when all these conditions are met: | 9 It is only enabled when all these conditions are met: |
10 1. hostname finishes with '.google.com' or 'chromium.org' | 10 1. hostname finishes with '.google.com' or 'chromium.org' |
(...skipping 24 matching lines...) Expand all Loading... |
35 | 35 |
36 # Skip unit tests and we don't want anything from non-googler. | 36 # Skip unit tests and we don't want anything from non-googler. |
37 IS_ENABLED = ( | 37 IS_ENABLED = ( |
38 not 'test' in getattr(sys.modules['__main__'], '__file__', '') and | 38 not 'test' in getattr(sys.modules['__main__'], '__file__', '') and |
39 not 'NO_BREAKPAD' in os.environ and | 39 not 'NO_BREAKPAD' in os.environ and |
40 _HOST_NAME.endswith(('.google.com', '.chromium.org'))) | 40 _HOST_NAME.endswith(('.google.com', '.chromium.org'))) |
41 | 41 |
42 | 42 |
43 def post(url, params): | 43 def post(url, params): |
44 """HTTP POST with timeout when it's supported.""" | 44 """HTTP POST with timeout when it's supported.""" |
| 45 if not IS_ENABLED: |
| 46 # Make sure to not send anything for non googler. |
| 47 return |
45 kwargs = {} | 48 kwargs = {} |
46 if (sys.version_info[0] * 10 + sys.version_info[1]) >= 26: | 49 if (sys.version_info[0] * 10 + sys.version_info[1]) >= 26: |
47 kwargs['timeout'] = 4 | 50 kwargs['timeout'] = 4 |
48 request = urllib2.urlopen(url, urllib.urlencode(params), **kwargs) | 51 try: |
49 out = request.read() | 52 request = urllib2.urlopen(url, urllib.urlencode(params), **kwargs) |
50 request.close() | 53 out = request.read() |
51 return out | 54 request.close() |
| 55 return out |
| 56 except IOError: |
| 57 return 'There was a failure while trying to send the stack trace. Too bad.' |
52 | 58 |
53 | 59 |
54 def FormatException(e): | 60 def FormatException(e): |
55 """Returns a human readable form of an exception. | 61 """Returns a human readable form of an exception. |
56 | 62 |
57 Adds the maximum number of interesting information in the safest way.""" | 63 Adds the maximum number of interesting information in the safest way.""" |
58 try: | 64 try: |
59 out = repr(e) | 65 out = repr(e) |
60 except Exception: | 66 except Exception: |
61 out = '' | 67 out = '' |
(...skipping 10 matching lines...) Expand all Loading... |
72 # The web page in some urllib exceptions. | 78 # The web page in some urllib exceptions. |
73 if hasattr(e, 'read') and callable(e.read): | 79 if hasattr(e, 'read') and callable(e.read): |
74 out += '\nread(): %s' % e.read() | 80 out += '\nread(): %s' % e.read() |
75 if hasattr(e, 'info') and callable(e.info): | 81 if hasattr(e, 'info') and callable(e.info): |
76 out += '\ninfo(): %s' % e.info() | 82 out += '\ninfo(): %s' % e.info() |
77 except Exception: | 83 except Exception: |
78 pass | 84 pass |
79 return out | 85 return out |
80 | 86 |
81 | 87 |
82 def SendStack(last_tb, stack, url=None, maxlen=50): | 88 def SendStack(last_tb, stack, url=None, maxlen=50, verbose=True): |
83 """Sends the stack trace to the breakpad server.""" | 89 """Sends the stack trace to the breakpad server.""" |
84 if not IS_ENABLED: | 90 if not IS_ENABLED: |
85 # Make sure to not send anything for non googler. | |
86 return | 91 return |
87 if not url: | 92 def p(o): |
88 url = DEFAULT_URL + '/breakpad' | 93 if verbose: |
89 print 'Sending crash report ...' | 94 print(o) |
90 try: | 95 p('Sending crash report ...') |
91 params = { | 96 params = { |
92 'args': sys.argv, | 97 'args': sys.argv, |
93 'stack': stack[0:4096], | 98 'cwd': os.getcwd(), |
94 'user': getpass.getuser(), | 99 'exception': FormatException(last_tb), |
95 'exception': FormatException(last_tb), | 100 'host': _HOST_NAME, |
96 'host': _HOST_NAME, | 101 'stack': stack[0:4096], |
97 'cwd': os.getcwd(), | 102 'user': getpass.getuser(), |
98 'version': sys.version, | 103 'version': sys.version, |
99 } | 104 } |
100 # pylint: disable=W0702 | 105 p('\n'.join(' %s: %s' % (k, params[k][0:maxlen]) for k in sorted(params))) |
101 print('\n'.join(' %s: %s' % (k, params[k][0:maxlen]) | 106 p(post(url or DEFAULT_URL + '/breakpad', params)) |
102 for k in sorted(params))) | |
103 print(post(url, params)) | |
104 except IOError: | |
105 print('There was a failure while trying to send the stack trace. Too bad.') | |
106 | 107 |
107 | 108 |
108 def SendProfiling(url=None): | 109 def SendProfiling(url=None): |
109 try: | 110 params = { |
110 if not url: | 111 'argv': ' '.join(sys.argv), |
111 url = DEFAULT_URL + '/profiling' | 112 # Strip the hostname. |
112 params = { | 113 'domain': _HOST_NAME.split('.', 1)[-1], |
113 'argv': ' '.join(sys.argv), | 114 'duration': time.time() - _TIME_STARTED, |
114 # Strip the hostname. | 115 'platform': sys.platform, |
115 'domain': _HOST_NAME.split('.', 1)[-1], | 116 } |
116 'duration': time.time() - _TIME_STARTED, | 117 post(url or DEFAULT_URL + '/profiling', params) |
117 'platform': sys.platform, | |
118 } | |
119 post(url, params) | |
120 except IOError: | |
121 pass | |
122 | 118 |
123 | 119 |
124 def CheckForException(): | 120 def CheckForException(): |
125 """Runs at exit. Look if there was an exception active.""" | 121 """Runs at exit. Look if there was an exception active.""" |
126 last_value = getattr(sys, 'last_value', None) | 122 last_value = getattr(sys, 'last_value', None) |
127 if last_value: | 123 if last_value: |
128 if not isinstance(last_value, KeyboardInterrupt): | 124 if not isinstance(last_value, KeyboardInterrupt): |
129 last_tb = getattr(sys, 'last_traceback', None) | 125 last_tb = getattr(sys, 'last_traceback', None) |
130 if last_tb: | 126 if last_tb: |
131 SendStack(last_value, ''.join(traceback.format_tb(last_tb))) | 127 SendStack(last_value, ''.join(traceback.format_tb(last_tb))) |
132 else: | 128 else: |
133 SendProfiling() | 129 SendProfiling() |
134 | 130 |
135 | 131 |
136 def Register(): | 132 def Register(): |
137 """Registers the callback at exit. Calling it multiple times is no-op.""" | 133 """Registers the callback at exit. Calling it multiple times is no-op.""" |
138 global _REGISTERED | 134 global _REGISTERED |
139 if _REGISTERED: | 135 if _REGISTERED: |
140 return | 136 return |
141 _REGISTERED = True | 137 _REGISTERED = True |
142 atexit.register(CheckForException) | 138 atexit.register(CheckForException) |
143 | 139 |
144 | 140 |
145 if IS_ENABLED: | 141 if IS_ENABLED: |
146 Register() | 142 Register() |
147 | 143 |
148 # Uncomment this line if you want to test it out. | 144 # Uncomment this line if you want to test it out. |
149 #Register() | 145 #Register() |
OLD | NEW |