Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Side by Side Diff: client/site_tests/logging_UserCrash/logging_UserCrash.py

Issue 3454023: autotest: Test new meta files in crash directory (Closed) Base URL: http://git.chromium.org/git/autotest.git
Patch Set: Fix bug Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 1 # Copyright (c) 2010 The Chromium OS 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 import grp, logging, os, pwd, re, stat, subprocess 5 import grp, logging, os, pwd, re, stat, subprocess
6 from signal import SIGSEGV 6 from signal import SIGSEGV
7 from autotest_lib.client.bin import site_crash_test, site_utils, test 7 from autotest_lib.client.bin import site_crash_test, site_utils, test
8 from autotest_lib.client.common_lib import error, utils 8 from autotest_lib.client.common_lib import error, utils
9 9
10 _CORE_PATTERN = '/proc/sys/kernel/core_pattern' 10 _CORE_PATTERN = '/proc/sys/kernel/core_pattern'
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 # Should identify recursion line which is on the stack 114 # Should identify recursion line which is on the stack
115 # for 15 levels 115 # for 15 levels
116 if not ('15 %s!recbomb(int) [bomb.cc : 12 ' % basename) in stack: 116 if not ('15 %s!recbomb(int) [bomb.cc : 12 ' % basename) in stack:
117 raise error.TestFail('Did not show recursion line on stack') 117 raise error.TestFail('Did not show recursion line on stack')
118 118
119 # Should identify main line 119 # Should identify main line
120 if not ('16 %s!main [crasher.cc : 21 ' % basename) in stack: 120 if not ('16 %s!main [crasher.cc : 21 ' % basename) in stack:
121 raise error.TestFail('Did not show main on stack') 121 raise error.TestFail('Did not show main on stack')
122 122
123 123
124 def _run_crasher_process(self, username, cause_crash=True): 124 def _run_crasher_process(self, username, cause_crash=True, consent=True):
125 """Runs the crasher process. 125 """Runs the crasher process.
126 126
127 Args: 127 Args:
128 username: runs as given user 128 username: runs as given user
129 extra_args: additional parameters to pass to crasher process 129 extra_args: additional parameters to pass to crasher process
130 130
131 Returns: 131 Returns:
132 A dictionary with keys: 132 A dictionary with keys:
133 returncode: return code of the crasher 133 returncode: return code of the crasher
134 crashed: did the crasher return segv error code 134 crashed: did the crasher return segv error code
135 crash_reporter_caught: did crash_reporter catch a segv 135 crash_reporter_caught: did crash_reporter catch a segv
136 output: stderr/stdout output of the crasher process 136 output: stderr/stdout output of the crasher process
137 """ 137 """
138 self._prepare_crasher() 138 self._prepare_crasher()
139 self._populate_symbols() 139 self._populate_symbols()
140 140
141 if username != 'root': 141 if username != 'root':
142 crasher_command = ['su', username, '-c'] 142 crasher_command = ['su', username, '-c']
143 expected_result = 128 + SIGSEGV 143 expected_result = 128 + SIGSEGV
144 else: 144 else:
145 crasher_command = [] 145 crasher_command = []
146 expected_result = -SIGSEGV 146 expected_result = -SIGSEGV
147 147
148 crasher_command.append(self._crasher_path) 148 crasher_command.append(self._crasher_path)
149 basename = os.path.basename(self._crasher_path) 149 basename = os.path.basename(self._crasher_path)
150 if not cause_crash: 150 if not cause_crash:
151 crasher_command.append('--nocrash') 151 crasher_command.append('--nocrash')
152 self._set_consent(consent)
152 crasher = subprocess.Popen(crasher_command, 153 crasher = subprocess.Popen(crasher_command,
153 stdout=subprocess.PIPE, 154 stdout=subprocess.PIPE,
154 stderr=subprocess.PIPE) 155 stderr=subprocess.PIPE)
155 output = crasher.communicate()[1] 156 output = crasher.communicate()[1]
156 logging.debug('Output from %s: %s' % 157 logging.debug('Output from %s: %s' %
157 (self._crasher_path, output)) 158 (self._crasher_path, output))
158 159
159 # Grab the pid from the process output. We can't just use 160 # Grab the pid from the process output. We can't just use
160 # crasher.pid unfortunately because that may be the PID of su. 161 # crasher.pid unfortunately because that may be the PID of su.
161 match = re.search(r'pid=(\d+)', output) 162 match = re.search(r'pid=(\d+)', output)
162 if not match: 163 if not match:
163 raise error.TestFail('Could not find pid output from crasher: %s' % 164 raise error.TestFail('Could not find pid output from crasher: %s' %
164 output) 165 output)
165 pid = int(match.group(1)) 166 pid = int(match.group(1))
166 167
167 expected_message = ('Received crash notification for ' 168 if consent:
168 '%s[%d] sig 11' % (basename, pid)) 169 handled_string = 'handling'
170 else:
171 handled_string = 'ignoring'
172 expected_message = (
173 'Received crash notification for %s[%d] sig 11 (%s)' %
174 (basename, pid, handled_string))
169 175
170 # Wait until no crash_reporter is running. 176 # Wait until no crash_reporter is running.
171 site_utils.poll_for_condition( 177 site_utils.poll_for_condition(
172 lambda: utils.system('pgrep crash_reporter', 178 lambda: utils.system('pgrep crash_reporter',
173 ignore_status=True) != 0, 179 ignore_status=True) != 0,
174 timeout=10, 180 timeout=10,
175 exception=error.TestError( 181 exception=error.TestError(
176 'Timeout waiting for crash_reporter to finish: ' + 182 'Timeout waiting for crash_reporter to finish: ' +
177 self._log_reader.get_logs())) 183 self._log_reader.get_logs()))
178 184
179 logging.debug('crash_reporter_caught message: ' + expected_message) 185 logging.debug('crash_reporter_caught message: ' + expected_message)
180 crash_reporter_caught = self._log_reader.can_find(expected_message) 186 is_caught = self._log_reader.can_find(expected_message)
181 187
182 result = {'crashed': crasher.returncode == expected_result, 188 result = {'crashed': crasher.returncode == expected_result,
183 'crash_reporter_caught': crash_reporter_caught, 189 'crash_reporter_caught': is_caught,
184 'output': output, 190 'output': output,
185 'returncode': crasher.returncode} 191 'returncode': crasher.returncode}
186 logging.debug('Crasher process result: %s' % result) 192 logging.debug('Crasher process result: %s' % result)
187 return result 193 return result
188 194
189 195
190 def _check_crash_directory_permissions(self, crash_dir): 196 def _check_crash_directory_permissions(self, crash_dir):
191 stat_info = os.stat(crash_dir) 197 stat_info = os.stat(crash_dir)
192 user = pwd.getpwuid(stat_info.st_uid)[0] 198 user = pwd.getpwuid(stat_info.st_uid)[0]
193 group = grp.getgrgid(stat_info.st_gid)[0] 199 group = grp.getgrgid(stat_info.st_gid)[0]
(...skipping 19 matching lines...) Expand all
213 219
214 220
215 def _check_minidump_stackwalk(self, minidump_path, basename, 221 def _check_minidump_stackwalk(self, minidump_path, basename,
216 from_crash_reporter): 222 from_crash_reporter):
217 # Now stackwalk the minidump 223 # Now stackwalk the minidump
218 stack = utils.system_output('/usr/bin/minidump_stackwalk %s %s' % 224 stack = utils.system_output('/usr/bin/minidump_stackwalk %s %s' %
219 (minidump_path, self._symbol_dir)) 225 (minidump_path, self._symbol_dir))
220 self._verify_stack(stack, basename, from_crash_reporter) 226 self._verify_stack(stack, basename, from_crash_reporter)
221 227
222 228
223 def _check_generated_minidump_sending(self, minidump_path, 229 def _check_generated_minidump_sending(self, meta_path, minidump_path,
224 username, crasher_basename, 230 username, crasher_basename,
225 will_syslog_give_name): 231 will_syslog_give_name):
226 # Now check that the sending works 232 # Now check that the sending works
227 self._set_sending(True) 233 self._set_sending(True)
228 result = self._call_sender_one_crash( 234 result = self._call_sender_one_crash(
229 username=username, 235 username=username,
230 report=os.path.basename(minidump_path)) 236 report=os.path.basename(minidump_path))
231 if (not result['send_attempt'] or not result['send_success'] or 237 if (not result['send_attempt'] or not result['send_success'] or
232 result['report_exists']): 238 result['report_exists']):
233 raise error.TestFail('Minidump not sent properly') 239 raise error.TestFail('Minidump not sent properly')
234 if will_syslog_give_name: 240 if will_syslog_give_name:
235 if result['exec_name'] != crasher_basename: 241 if result['exec_name'] != crasher_basename:
236 raise error.TestFail('Executable name incorrect') 242 raise error.TestFail('Executable name incorrect')
237 if result['report_kind'] != 'minidump': 243 if result['report_kind'] != 'minidump':
238 raise error.TestFail('Expected a minidump report') 244 raise error.TestFail('Expected a minidump report')
239 if result['report_name'] != minidump_path: 245 if result['report_payload'] != minidump_path:
240 raise error.TestFail('Sent the wrong minidump report') 246 raise error.TestFail('Sent the wrong minidump payload')
247 if result['meta_path'] != meta_path:
248 raise error.TestFail('Used the wrong meta file')
249
250 # Check version matches.
251 lsb_release = utils.read_file('/etc/lsb-release')
252 version_match = re.search(r'CHROMEOS_RELEASE_VERSION=(.*)', lsb_release)
253 if not ('Version: %s' % version_match.group(1)) in result['output']:
254 raise error.TestFail('Did not find version %s in log output' %
255 version_match.group(1))
241 256
242 257
243 def _check_crashing_process(self, username): 258 def _check_crashing_process(self, username, consent=True):
244 self._log_reader.set_start_by_current() 259 self._log_reader.set_start_by_current()
245 260
246 result = self._run_crasher_process(username) 261 result = self._run_crasher_process(username, consent=consent)
247 262
248 if not result['crashed']: 263 if not result['crashed']:
249 raise error.TestFail('crasher did not do its job of crashing: %d' % 264 raise error.TestFail('crasher did not do its job of crashing: %d' %
250 result['returncode']) 265 result['returncode'])
251 266
252 if not result['crash_reporter_caught']: 267 if not result['crash_reporter_caught']:
253 logging.debug('Messages that should have included segv: %s' % 268 logging.debug('Messages that should have included segv: %s' %
254 self._log_reader.get_logs()) 269 self._log_reader.get_logs())
255 raise error.TestFail('Did not find segv message') 270 raise error.TestFail('Did not find segv message')
256 271
257 crash_dir = self._get_crash_dir(username) 272 crash_dir = self._get_crash_dir(username)
273
274 if not consent:
275 if os.path.exists(crash_dir):
276 raise error.TestFail('Crash directory should not exist')
277 return
278
258 crash_contents = os.listdir(crash_dir) 279 crash_contents = os.listdir(crash_dir)
259 basename = os.path.basename(self._crasher_path) 280 basename = os.path.basename(self._crasher_path)
260 281
261 breakpad_minidump = None 282 breakpad_minidump = None
262 crash_reporter_minidump = None 283 crash_reporter_minidump = None
284 crash_reporter_meta = None
263 285
264 self._check_crash_directory_permissions(crash_dir) 286 self._check_crash_directory_permissions(crash_dir)
265 287
266 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents)) 288 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents))
267 289
268 for filename in crash_contents: 290 for filename in crash_contents:
269 if filename.endswith('.core'): 291 if filename.endswith('.core'):
270 # Ignore core files. We'll test them later. 292 # Ignore core files. We'll test them later.
271 pass 293 pass
272 elif filename.startswith(basename): 294 elif (filename.startswith(basename) and
295 filename.endswith('.dmp')):
273 # This appears to be a minidump created by the crash reporter. 296 # This appears to be a minidump created by the crash reporter.
274 if not crash_reporter_minidump is None: 297 if not crash_reporter_minidump is None:
275 raise error.TestFail('Crash reporter wrote multiple ' 298 raise error.TestFail('Crash reporter wrote multiple '
276 'minidumps') 299 'minidumps')
277 crash_reporter_minidump = os.path.join(crash_dir, filename) 300 crash_reporter_minidump = os.path.join(crash_dir, filename)
301 elif (filename.startswith(basename) and
302 filename.endswith('.meta')):
303 if not crash_reporter_meta is None:
304 raise error.TestFail('Crash reported wrote multiple '
305 'meta files')
306 crash_reporter_meta = os.path.join(crash_dir, filename)
278 else: 307 else:
279 # This appears to be a breakpad created minidump. 308 # This appears to be a breakpad created minidump.
280 if not breakpad_minidump is None: 309 if not breakpad_minidump is None:
281 raise error.TestFail('Breakpad wrote multimpe minidumps') 310 raise error.TestFail('Breakpad wrote multimpe minidumps')
282 breakpad_minidump = os.path.join(crash_dir, filename) 311 breakpad_minidump = os.path.join(crash_dir, filename)
283 312
284 if breakpad_minidump: 313 if breakpad_minidump:
285 raise error.TestFail('%s did generate breakpad minidump' % basename) 314 raise error.TestFail('%s did generate breakpad minidump' % basename)
286 315
287 if not crash_reporter_minidump: 316 if not crash_reporter_minidump:
288 raise error.TestFail('crash reporter did not generate minidump') 317 raise error.TestFail('crash reporter did not generate minidump')
289 318
319 if not crash_reporter_meta:
320 raise error.TestFail('crash reporter did not generate meta')
321
290 if not self._log_reader.can_find('Stored minidump to ' + 322 if not self._log_reader.can_find('Stored minidump to ' +
291 crash_reporter_minidump): 323 crash_reporter_minidump):
292 raise error.TestFail('crash reporter did not announce minidump') 324 raise error.TestFail('crash reporter did not announce minidump')
293 325
294 # By default test sending the crash_reporter minidump unless there
295 # is a breakpad minidump, and then we test sending it instead.
296 send_minidump = crash_reporter_minidump
297
298 if crash_reporter_minidump: 326 if crash_reporter_minidump:
299 self._check_minidump_stackwalk(crash_reporter_minidump, 327 self._check_minidump_stackwalk(crash_reporter_minidump,
300 basename, 328 basename,
301 from_crash_reporter=True) 329 from_crash_reporter=True)
302 will_syslog_give_name = True 330 will_syslog_give_name = True
303 331
304 if breakpad_minidump: 332 self._check_generated_minidump_sending(crash_reporter_meta,
305 self._check_minidump_stackwalk(breakpad_minidump, 333 crash_reporter_minidump,
306 basename,
307 from_crash_reporter=False)
308 send_minidump = breakpad_minidump
309 os.unlink(crash_reporter_minidump)
310 # If you link against -lcrash, upon sending the syslog will
311 # just say exec_name: <very-long-guid>, where that GUID is the
312 # GUID generated during breakpad. Since -lcrash is going away,
313 # it seems ok to have the syslog be a little more opaque for
314 # these crashes. They'll be next to sends for the real crash
315 # anyway.
316 will_syslog_give_name = False
317
318 self._check_generated_minidump_sending(send_minidump,
319 username, 334 username,
320 basename, 335 basename,
321 will_syslog_give_name) 336 will_syslog_give_name)
322 337
323 def _test_no_crash(self): 338 def _test_no_crash(self):
324 """Test a program linked against libcrash_dumper can exit normally.""" 339 """Test a program linked against libcrash_dumper can exit normally."""
325 self._log_reader.set_start_by_current() 340 self._log_reader.set_start_by_current()
326 result = self._run_crasher_process(username='root', 341 result = self._run_crasher_process(username='root',
327 cause_crash=False) 342 cause_crash=False)
328 if (result['crashed'] or 343 if (result['crashed'] or
329 result['crash_reporter_caught'] or 344 result['crash_reporter_caught'] or
330 result['returncode'] != 0): 345 result['returncode'] != 0):
331 raise error.TestFail('Normal exit of program with dumper failed') 346 raise error.TestFail('Normal exit of program with dumper failed')
332 347
333 348
334 def _test_chronos_nobreakpad_crasher(self): 349 def _test_chronos_crasher(self):
335 """Test a user space crash when running as chronos is handled.""" 350 """Test a user space crash when running as chronos is handled."""
336 self._check_crashing_process('chronos') 351 self._check_crashing_process('chronos')
337 352
338 353
339 def _test_root_nobreakpad_crasher(self): 354 def _test_chronos_crasher_no_consent(self):
355 """Test that without consent no files are stored."""
356 results = self._check_crashing_process('chronos', consent=False)
357
358
359 def _test_root_crasher(self):
340 """Test a user space crash when running as root is handled.""" 360 """Test a user space crash when running as root is handled."""
341 self._check_crashing_process('root') 361 self._check_crashing_process('root')
342 362
343 363
364 def _test_root_crasher_no_consent(self):
365 """Test that without consent no files are stored."""
366 results = self._check_crashing_process('root', consent=False)
367
368
344 def _test_max_enqueued_crashes(self): 369 def _test_max_enqueued_crashes(self):
345 """Test that _MAX_CRASH_DIRECTORY_SIZE is enforced.""" 370 """Test that _MAX_CRASH_DIRECTORY_SIZE is enforced."""
346 self._log_reader.set_start_by_current() 371 self._log_reader.set_start_by_current()
347 username = 'root' 372 username = 'root'
348 373
349 crash_dir = self._get_crash_dir(username) 374 crash_dir = self._get_crash_dir(username)
350 full_message = ('Crash directory %s already full with %d pending ' 375 full_message = ('Crash directory %s already full with %d pending '
351 'reports' % (crash_dir, _MAX_CRASH_DIRECTORY_SIZE)) 376 'reports' % (crash_dir, _MAX_CRASH_DIRECTORY_SIZE))
352 377
353 # Fill up the queue. 378 # Fill up the queue.
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
443 os.system('umount /root') 468 os.system('umount /root')
444 469
445 470
446 # TODO(kmixter): Test crashing a process as ntp or some other 471 # TODO(kmixter): Test crashing a process as ntp or some other
447 # non-root, non-chronos user. 472 # non-root, non-chronos user.
448 473
449 def run_once(self): 474 def run_once(self):
450 self.run_crash_tests(['reporter_startup', 475 self.run_crash_tests(['reporter_startup',
451 'reporter_shutdown', 476 'reporter_shutdown',
452 'no_crash', 477 'no_crash',
453 'chronos_nobreakpad_crasher', 478 'chronos_crasher',
454 'root_nobreakpad_crasher', 479 'chronos_crasher_no_consent',
480 'root_crasher',
481 'root_crasher_no_consent',
455 'max_enqueued_crashes', 482 'max_enqueued_crashes',
456 'core_file_persists_in_debug', 483 'core_file_persists_in_debug',
457 'core_file_removed_in_production'], 484 'core_file_removed_in_production'],
458 initialize_crash_reporter = True) 485 initialize_crash_reporter = True)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698