Back to index

apport  2.3
Public Member Functions | Private Member Functions
test_report.T Class Reference

List of all members.

Public Member Functions

def test_add_package_info
def test_add_os_info
def test_add_user_info
def test_add_proc_info
def test_add_path_classification
def test_check_interpreted
def test_check_interpreted_twistd
def test_add_gdb_info
def test_add_gdb_info_load
def test_add_zz_parse_segv_details
def test_add_gdb_info_script
def test_add_gdb_info_abort
def test_search_bug_patterns
def test_add_hooks_info
def test_ignoring
def test_blacklisting
def test_whitelisting
def test_has_useful_stacktrace
def test_standard_title
def test_obsolete_packages
def test_gen_stacktrace_top
def test_crash_signature
def test_nonascii_data
def test_address_to_offset
def test_address_to_offset_live
def test_crash_signature_addresses

Private Member Functions

def _generate_sigsegv_report
def _validate_gdb_fields

Detailed Description

Definition at line 9 of file test_report.py.


Member Function Documentation

def test_report.T._generate_sigsegv_report (   klass,
  file = None,
  signal = '11',
  code = ''' int f(x) { int* p = 0; *p = x; return x+1; } int main() { return f(42); } ''' 
) [private]
Create a test executable which will die with a SIGSEGV, generate a
core dump for it, create a problem report with those two arguments
(ExecutablePath and CoreDump) and call add_gdb_info().

If file is given, the report is written into it. Return the apport.report.Report.

Definition at line 430 of file test_report.py.

00430 
00431 '''):
00432         '''Create a test executable which will die with a SIGSEGV, generate a
00433         core dump for it, create a problem report with those two arguments
00434         (ExecutablePath and CoreDump) and call add_gdb_info().
00435 
00436         If file is given, the report is written into it. Return the apport.report.Report.'''
00437 
00438         workdir = None
00439         orig_cwd = os.getcwd()
00440         pr = apport.report.Report()
00441         try:
00442             workdir = tempfile.mkdtemp()
00443             atexit.register(shutil.rmtree, workdir)
00444             os.chdir(workdir)
00445 
00446             # create a test executable
00447             with open('crash.c', 'w') as fd:
00448                 fd.write(code)
00449             assert subprocess.call(['gcc', '-g', 'crash.c', '-o', 'crash']) == 0
00450             assert os.path.exists('crash')
00451 
00452             # call it through gdb and dump core
00453             subprocess.call(['gdb', '--batch', '--ex', 'run', '--ex',
00454                              'generate-core-file core', './crash'], stdout=subprocess.PIPE)
00455             assert os.path.exists('core')
00456             assert subprocess.call(['readelf', '-n', 'core'],
00457                                    stdout=subprocess.PIPE) == 0
00458 
00459             pr['ExecutablePath'] = os.path.join(workdir, 'crash')
00460             pr['CoreDump'] = (os.path.join(workdir, 'core'),)
00461             pr['Signal'] = signal
00462 
00463             pr.add_gdb_info()
00464             if file:
00465                 pr.write(file)
00466                 file.flush()
00467         finally:
00468             os.chdir(orig_cwd)
00469 
00470         return pr

Here is the caller graph for this function:

def test_report.T._validate_gdb_fields (   self,
  pr 
) [private]

Definition at line 471 of file test_report.py.

00471 
00472     def _validate_gdb_fields(self, pr):
00473         self.assertTrue('Stacktrace' in pr)
00474         self.assertTrue('ThreadStacktrace' in pr)
00475         self.assertTrue('StacktraceTop' in pr)
00476         self.assertTrue('Registers' in pr)
00477         self.assertTrue('Disassembly' in pr)
00478         self.assertTrue('(no debugging symbols found)' not in pr['Stacktrace'])
00479         self.assertTrue('Core was generated by' not in pr['Stacktrace'], pr['Stacktrace'])
00480         self.assertTrue(not re.match(r'(?s)(^|.*\n)#0  [^\n]+\n#0  ',
00481                                      pr['Stacktrace']))
00482         self.assertTrue('#0  0x' in pr['Stacktrace'])
00483         self.assertTrue('#1  0x' in pr['Stacktrace'])
00484         self.assertTrue('#0  0x' in pr['ThreadStacktrace'])
00485         self.assertTrue('#1  0x' in pr['ThreadStacktrace'])
00486         self.assertTrue('Thread 1 (' in pr['ThreadStacktrace'])
00487         self.assertTrue(len(pr['StacktraceTop'].splitlines()) <= 5)

add_gdb_info() with core dump file reference.

Definition at line 488 of file test_report.py.

00488 
00489     def test_add_gdb_info(self):
00490         '''add_gdb_info() with core dump file reference.'''
00491 
00492         pr = apport.report.Report()
00493         # should not throw an exception for missing fields
00494         pr.add_gdb_info()
00495 
00496         # normal crash
00497         pr = self._generate_sigsegv_report()
00498         self._validate_gdb_fields(pr)
00499         self.assertEqual(pr['StacktraceTop'], 'f (x=42) at crash.c:3\nmain () at crash.c:6', pr['StacktraceTop'])
00500         self.assertFalse('AssertionMessage' in pr)
00501 
00502         # crash where gdb generates output on stderr
00503         pr = self._generate_sigsegv_report(code='''
00504 int main() {
00505     void     (*function)(void);
00506     function = 0;
00507     function();
00508 }
00509 ''')
00510         self._validate_gdb_fields(pr)
00511         self.assertTrue('Cannot access memory at address 0x0' in pr['Disassembly'], pr['Disassembly'])
00512         self.assertFalse('AssertionMessage' in pr)

Here is the call graph for this function:

add_gdb_info() with SIGABRT/assert()

If these come from an assert(), the report should have the assertion
message. Otherwise it should be marked as not reportable.

Definition at line 593 of file test_report.py.

00593 
00594     def test_add_gdb_info_abort(self):
00595         '''add_gdb_info() with SIGABRT/assert()
00596 
00597         If these come from an assert(), the report should have the assertion
00598         message. Otherwise it should be marked as not reportable.
00599         '''
00600         # abort with assert
00601         (fd, script) = tempfile.mkstemp()
00602         assert not os.path.exists('core')
00603         try:
00604             os.close(fd)
00605 
00606             # create a test script which produces a core dump for us
00607             with open(script, 'w') as fd:
00608                 fd.write('''#!/bin/sh
00609 gcc -o $0.bin -x c - <<EOF
00610 #include <assert.h>
00611 int main() { assert(1 < 0); }
00612 EOF
00613 ulimit -c unlimited
00614 $0.bin 2>/dev/null
00615 ''')
00616             os.chmod(script, 0o755)
00617 
00618             # call script and verify that it gives us a proper ELF core dump
00619             assert subprocess.call([script]) != 0
00620             subprocess.check_call(['sync'])
00621             assert subprocess.call(['readelf', '-n', 'core'],
00622                                    stdout=subprocess.PIPE) == 0
00623 
00624             pr = apport.report.Report()
00625             pr['ExecutablePath'] = script + '.bin'
00626             pr['CoreDump'] = ('core',)
00627             pr.add_gdb_info()
00628         finally:
00629             os.unlink(script)
00630             os.unlink(script + '.bin')
00631             os.unlink('core')
00632 
00633         self._validate_gdb_fields(pr)
00634         self.assertTrue("<stdin>:2: main: Assertion `1 < 0' failed." in
00635                         pr['AssertionMessage'], pr['AssertionMessage'])
00636         self.assertFalse(pr['AssertionMessage'].startswith('$'), pr['AssertionMessage'])
00637         self.assertFalse('= 0x' in pr['AssertionMessage'], pr['AssertionMessage'])
00638         self.assertFalse(pr['AssertionMessage'].endswith('\\n'), pr['AssertionMessage'])
00639 
00640         # abort with internal error
00641         (fd, script) = tempfile.mkstemp()
00642         assert not os.path.exists('core')
00643         try:
00644             os.close(fd)
00645 
00646             # create a test script which produces a core dump for us
00647             with open(script, 'w') as fd:
00648                 fd.write('''#!/bin/sh
00649 gcc -O2 -D_FORTIFY_SOURCE=2 -o $0.bin -x c - <<EOF
00650 #include <string.h>
00651 int main(int argc, char *argv[]) {
00652     char buf[8];
00653     strcpy(buf, argv[1]);
00654     return 0;
00655 }
00656 EOF
00657 ulimit -c unlimited
00658 LIBC_FATAL_STDERR_=1 $0.bin aaaaaaaaaaaaaaaa 2>/dev/null
00659 ''')
00660             os.chmod(script, 0o755)
00661 
00662             # call script and verify that it gives us a proper ELF core dump
00663             assert subprocess.call([script]) != 0
00664             subprocess.check_call(['sync'])
00665             assert subprocess.call(['readelf', '-n', 'core'],
00666                                    stdout=subprocess.PIPE) == 0
00667 
00668             pr = apport.report.Report()
00669             pr['ExecutablePath'] = script + '.bin'
00670             pr['CoreDump'] = ('core',)
00671             pr.add_gdb_info()
00672         finally:
00673             os.unlink(script)
00674             os.unlink(script + '.bin')
00675             os.unlink('core')
00676 
00677         self._validate_gdb_fields(pr)
00678         self.assertTrue("** buffer overflow detected ***: %s.bin terminated" % (script) in
00679                         pr['AssertionMessage'], pr['AssertionMessage'])
00680         self.assertFalse(pr['AssertionMessage'].startswith('$'), pr['AssertionMessage'])
00681         self.assertFalse('= 0x' in pr['AssertionMessage'], pr['AssertionMessage'])
00682         self.assertFalse(pr['AssertionMessage'].endswith('\\n'), pr['AssertionMessage'])
00683 
00684         # abort without assertion
00685         (fd, script) = tempfile.mkstemp()
00686         assert not os.path.exists('core')
00687         try:
00688             os.close(fd)
00689 
00690             # create a test script which produces a core dump for us
00691             with open(script, 'w') as fd:
00692                 fd.write('''#!/bin/sh
00693 gcc -o $0.bin -x c - <<EOF
00694 #include <stdlib.h>
00695 int main() { abort(); }
00696 EOF
00697 ulimit -c unlimited
00698 $0.bin 2>/dev/null
00699 ''')
00700             os.chmod(script, 0o755)
00701 
00702             # call script and verify that it gives us a proper ELF core dump
00703             assert subprocess.call([script]) != 0
00704             subprocess.check_call(['sync'])
00705             assert subprocess.call(['readelf', '-n', 'core'],
00706                                    stdout=subprocess.PIPE) == 0
00707 
00708             pr = apport.report.Report()
00709             pr['ExecutablePath'] = script + '.bin'
00710             pr['CoreDump'] = ('core',)
00711             pr.add_gdb_info()
00712         finally:
00713             os.unlink(script)
00714             os.unlink(script + '.bin')
00715             os.unlink('core')
00716 
00717         self._validate_gdb_fields(pr)
00718         self.assertFalse('AssertionMessage' in pr, pr.get('AssertionMessage'))

Here is the call graph for this function:

add_gdb_info() with inline core dump.

Definition at line 513 of file test_report.py.

00513 
00514     def test_add_gdb_info_load(self):
00515         '''add_gdb_info() with inline core dump.'''
00516 
00517         rep = tempfile.NamedTemporaryFile()
00518         self._generate_sigsegv_report(rep)
00519         rep.seek(0)
00520 
00521         pr = apport.report.Report()
00522         with open(rep.name, 'rb') as f:
00523             pr.load(f)
00524         pr.add_gdb_info()
00525 
00526         self._validate_gdb_fields(pr)

Here is the call graph for this function:

add_gdb_info() with a script.

Definition at line 557 of file test_report.py.

00557 
00558     def test_add_gdb_info_script(self):
00559         '''add_gdb_info() with a script.'''
00560 
00561         (fd, script) = tempfile.mkstemp()
00562         coredump = os.path.join(os.path.dirname(script), 'core')
00563         assert not os.path.exists(coredump)
00564         try:
00565             os.close(fd)
00566 
00567             # create a test script which produces a core dump for us
00568             with open(script, 'w') as fd:
00569                 fd.write('''#!/bin/bash
00570 cd `dirname $0`
00571 ulimit -c unlimited
00572 kill -SEGV $$
00573 ''')
00574             os.chmod(script, 0o755)
00575 
00576             # call script and verify that it gives us a proper ELF core dump
00577             assert subprocess.call([script]) != 0
00578             subprocess.check_call(['sync'])
00579             assert subprocess.call(['readelf', '-n', coredump],
00580                                    stdout=subprocess.PIPE) == 0
00581 
00582             pr = apport.report.Report()
00583             pr['InterpreterPath'] = '/bin/bash'
00584             pr['ExecutablePath'] = script
00585             pr['CoreDump'] = (coredump,)
00586             pr.add_gdb_info()
00587         finally:
00588             os.unlink(coredump)
00589             os.unlink(script)
00590 
00591         self._validate_gdb_fields(pr)
00592         self.assertTrue('libc.so' in pr['Stacktrace'] or 'in execute_command' in pr['Stacktrace'])

Here is the call graph for this function:

add_hooks_info().

Definition at line 829 of file test_report.py.

00829 
00830     def test_add_hooks_info(self):
00831         '''add_hooks_info().'''
00832 
00833         orig_hook_dir = apport.report._hook_dir
00834         apport.report._hook_dir = tempfile.mkdtemp()
00835         orig_common_hook_dir = apport.report._common_hook_dir
00836         apport.report._common_hook_dir = tempfile.mkdtemp()
00837         try:
00838             with open(os.path.join(apport.report._hook_dir, 'foo.py'), 'w') as fd:
00839                 fd.write('''
00840 import sys
00841 def add_info(report):
00842     report['Field1'] = 'Field 1'
00843     report['Field2'] = 'Field 2\\nBla'
00844     if 'Spethial' in report:
00845         raise StopIteration
00846 ''')
00847 
00848             with open(os.path.join(apport.report._common_hook_dir, 'foo1.py'), 'w') as fd:
00849                 fd.write('''
00850 def add_info(report):
00851     report['CommonField1'] = 'CommonField 1'
00852     if report['Package'] == 'commonspethial':
00853         raise StopIteration
00854 ''')
00855             with open(os.path.join(apport.report._common_hook_dir, 'foo2.py'), 'w') as fd:
00856                 fd.write('''
00857 def add_info(report):
00858     report['CommonField2'] = 'CommonField 2'
00859 ''')
00860             with open(os.path.join(apport.report._common_hook_dir, 'foo3.py'), 'w') as fd:
00861                 fd.write('''
00862 def add_info(report, ui):
00863     report['CommonField3'] = str(ui)
00864 ''')
00865 
00866             # should only catch .py files
00867             with open(os.path.join(apport.report._common_hook_dir, 'notme'), 'w') as fd:
00868                 fd.write('''
00869 def add_info(report):
00870     report['BadField'] = 'XXX'
00871 ''')
00872             r = apport.report.Report()
00873             r['Package'] = 'bar'
00874             # should not throw any exceptions
00875             self.assertEqual(r.add_hooks_info('fake_ui'), False)
00876             self.assertEqual(set(r.keys()),
00877                              set(['ProblemType', 'Date', 'Package',
00878                                   'CommonField1', 'CommonField2',
00879                                   'CommonField3']),
00880                              'report has required fields')
00881 
00882             r = apport.report.Report()
00883             r['Package'] = 'baz 1.2-3'
00884             # should not throw any exceptions
00885             self.assertEqual(r.add_hooks_info('fake_ui'), False)
00886             self.assertEqual(set(r.keys()),
00887                              set(['ProblemType', 'Date', 'Package',
00888                                   'CommonField1', 'CommonField2',
00889                                   'CommonField3']),
00890                              'report has required fields')
00891 
00892             r = apport.report.Report()
00893             r['Package'] = 'foo'
00894             self.assertEqual(r.add_hooks_info('fake_ui'), False)
00895             self.assertEqual(set(r.keys()),
00896                              set(['ProblemType', 'Date', 'Package', 'Field1',
00897                                   'Field2', 'CommonField1', 'CommonField2',
00898                                   'CommonField3']),
00899                              'report has required fields')
00900             self.assertEqual(r['Field1'], 'Field 1')
00901             self.assertEqual(r['Field2'], 'Field 2\nBla')
00902             self.assertEqual(r['CommonField1'], 'CommonField 1')
00903             self.assertEqual(r['CommonField2'], 'CommonField 2')
00904             self.assertEqual(r['CommonField3'], 'fake_ui')
00905 
00906             r = apport.report.Report()
00907             r['Package'] = 'foo 4.5-6'
00908             self.assertEqual(r.add_hooks_info('fake_ui'), False)
00909             self.assertEqual(set(r.keys()),
00910                              set(['ProblemType', 'Date', 'Package', 'Field1',
00911                                   'Field2', 'CommonField1', 'CommonField2',
00912                                   'CommonField3']),
00913                              'report has required fields')
00914             self.assertEqual(r['Field1'], 'Field 1')
00915             self.assertEqual(r['Field2'], 'Field 2\nBla')
00916             self.assertEqual(r['CommonField1'], 'CommonField 1')
00917             self.assertEqual(r['CommonField2'], 'CommonField 2')
00918 
00919             # test hook abort
00920             r['Spethial'] = '1'
00921             self.assertEqual(r.add_hooks_info('fake_ui'), True)
00922             r = apport.report.Report()
00923             r['Package'] = 'commonspethial'
00924             self.assertEqual(r.add_hooks_info('fake_ui'), True)
00925 
00926             # source package hook
00927             with open(os.path.join(apport.report._hook_dir, 'source_foo.py'), 'w') as fd:
00928                 fd.write('''
00929 def add_info(report, ui):
00930     report['Field1'] = 'Field 1'
00931     report['Field2'] = 'Field 2\\nBla'
00932     if report['Package'] == 'spethial':
00933         raise StopIteration
00934 ''')
00935             r = apport.report.Report()
00936             r['SourcePackage'] = 'foo'
00937             r['Package'] = 'libfoo 3'
00938             self.assertEqual(r.add_hooks_info('fake_ui'), False)
00939             self.assertEqual(set(r.keys()),
00940                              set(['ProblemType', 'Date', 'Package',
00941                                   'SourcePackage', 'Field1', 'Field2',
00942                                   'CommonField1', 'CommonField2',
00943                                   'CommonField3']),
00944                              'report has required fields')
00945             self.assertEqual(r['Field1'], 'Field 1')
00946             self.assertEqual(r['Field2'], 'Field 2\nBla')
00947             self.assertEqual(r['CommonField1'], 'CommonField 1')
00948             self.assertEqual(r['CommonField2'], 'CommonField 2')
00949             self.assertEqual(r['CommonField3'], 'fake_ui')
00950 
00951             # test hook abort
00952             r['Package'] = 'spethial'
00953             self.assertEqual(r.add_hooks_info('fake_ui'), True)
00954 
00955         finally:
00956             shutil.rmtree(apport.report._hook_dir)
00957             shutil.rmtree(apport.report._common_hook_dir)
00958             apport.report._hook_dir = orig_hook_dir
00959             apport.report._common_hook_dir = orig_common_hook_dir

Here is the call graph for this function:

add_os_info().

Definition at line 41 of file test_report.py.

00041 
00042     def test_add_os_info(self):
00043         '''add_os_info().'''
00044 
00045         pr = apport.report.Report()
00046         pr.add_os_info()
00047         self.assertTrue(pr['Uname'].startswith('Linux'))
00048         self.assertTrue(hasattr(pr['DistroRelease'], 'startswith'))
00049         self.assertGreater(len(pr['DistroRelease']), 5)
00050         self.assertTrue(pr['Architecture'])

add_package_info().

Definition at line 10 of file test_report.py.

00010 
00011     def test_add_package_info(self):
00012         '''add_package_info().'''
00013 
00014         # determine bash version
00015         bashversion = apport.packaging.get_version('bash')
00016 
00017         pr = apport.report.Report()
00018         self.assertRaises(ValueError, pr.add_package_info, 'nonexistant_package')
00019 
00020         pr.add_package_info('bash')
00021         self.assertEqual(pr['Package'], 'bash ' + bashversion.strip())
00022         self.assertEqual(pr['SourcePackage'], 'bash')
00023         self.assertTrue('libc' in pr['Dependencies'])
00024 
00025         # test without specifying a package, but with ExecutablePath
00026         pr = apport.report.Report()
00027         self.assertRaises(KeyError, pr.add_package_info)
00028         pr['ExecutablePath'] = '/bin/bash'
00029         pr.add_package_info()
00030         self.assertEqual(pr['Package'], 'bash ' + bashversion.strip())
00031         self.assertEqual(pr['SourcePackage'], 'bash')
00032         self.assertTrue('libc' in pr['Dependencies'])
00033         # check for stray empty lines
00034         self.assertTrue('\n\n' not in pr['Dependencies'])
00035         self.assertTrue('PackageArchitecture' in pr)
00036 
00037         pr = apport.report.Report()
00038         pr['ExecutablePath'] = '/nonexisting'
00039         pr.add_package_info()
00040         self.assertTrue('Package' not in pr)

classification of $PATH.

Definition at line 197 of file test_report.py.

00197 
00198     def test_add_path_classification(self):
00199         '''classification of $PATH.'''
00200 
00201         # system default
00202         p = subprocess.Popen(['cat'], stdin=subprocess.PIPE,
00203                              env={'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games'})
00204         time.sleep(0.1)
00205         r = apport.report.Report()
00206         r.add_proc_environ(pid=p.pid)
00207         p.communicate(b'')
00208         self.assertFalse('PATH' in r['ProcEnviron'],
00209                          'system default $PATH should be filtered out')
00210 
00211         # no user paths
00212         p = subprocess.Popen(['cat'], stdin=subprocess.PIPE,
00213                              env={'PATH': '/usr/sbin:/usr/bin:/sbin:/bin'})
00214         time.sleep(0.1)
00215         r = apport.report.Report()
00216         r.add_proc_environ(pid=p.pid)
00217         p.communicate(b'')
00218         self.assertTrue('PATH=(custom, no user)' in r['ProcEnviron'],
00219                         'PATH is customized without user paths')
00220 
00221         # user paths
00222         p = subprocess.Popen(['cat'], stdin=subprocess.PIPE,
00223                              env={'PATH': '/home/pitti:/usr/sbin:/usr/bin:/sbin:/bin'})
00224         time.sleep(0.1)
00225         r = apport.report.Report()
00226         r.add_proc_environ(pid=p.pid)
00227         p.communicate(b'')
00228         self.assertTrue('PATH=(custom, user)' in r['ProcEnviron'],
00229                         'PATH is customized with user paths')

add_proc_info().

Definition at line 63 of file test_report.py.

00063 
00064     def test_add_proc_info(self):
00065         '''add_proc_info().'''
00066 
00067         # set test environment
00068         assert 'LANG' in os.environ, 'please set $LANG for this test'
00069 
00070         # check without additional safe environment variables
00071         pr = apport.report.Report()
00072         self.assertEqual(pr.pid, None)
00073         pr.add_proc_info()
00074         self.assertEqual(pr.pid, os.getpid())
00075         self.assertTrue(set(['ProcEnviron', 'ProcMaps', 'ProcCmdline',
00076                              'ProcMaps']).issubset(set(pr.keys())), 'report has required fields')
00077         self.assertTrue('LANG=' + os.environ['LANG'] in pr['ProcEnviron'])
00078         self.assertTrue('USER' not in pr['ProcEnviron'])
00079         self.assertTrue('PWD' not in pr['ProcEnviron'])
00080         self.assertTrue('report.py' in pr['ExecutablePath'])
00081         self.assertEqual(int(pr['ExecutableTimestamp']),
00082                          int(os.stat(__file__).st_mtime))
00083 
00084         # check with one additional safe environment variable
00085         pr = apport.report.Report()
00086         pr.add_proc_info(extraenv=['PWD'])
00087         self.assertTrue('USER' not in pr['ProcEnviron'])
00088         if 'PWD' in os.environ:
00089             self.assertTrue('PWD=' + os.environ['PWD'] in pr['ProcEnviron'])
00090 
00091         # check process from other user
00092         restore_root = False
00093         if os.getuid() == 0:
00094             # temporarily drop to normal user "mail"
00095             os.setresuid(8, 8, -1)
00096             restore_root = True
00097         pr = apport.report.Report()
00098         self.assertRaises(OSError, pr.add_proc_info, 1)  # EPERM for init process
00099         if restore_root:
00100             os.setresuid(0, 0, -1)
00101 
00102         self.assertEqual(pr.pid, 1)
00103         self.assertTrue('init' in pr['ProcStatus'], pr['ProcStatus'])
00104         self.assertTrue(pr['ProcEnviron'].startswith('Error:'), pr['ProcEnviron'])
00105         self.assertTrue('InterpreterPath' not in pr)
00106 
00107         # check escaping of ProcCmdline
00108         p = subprocess.Popen(['cat', '/foo bar', '\\h', '\\ \\', '-'],
00109                              stdin=subprocess.PIPE, stdout=subprocess.PIPE,
00110                              stderr=subprocess.PIPE)
00111         assert p.pid
00112         # wait until /proc/pid/cmdline exists
00113         while True:
00114             with open('/proc/%i/cmdline' % p.pid) as fd:
00115                 if fd.read():
00116                     break
00117                 time.sleep(0.1)
00118         pr = apport.report.Report()
00119         pr.add_proc_info(pid=p.pid)
00120         self.assertEqual(pr.pid, p.pid)
00121         p.communicate(b'\n')
00122         self.assertEqual(pr['ProcCmdline'], 'cat /foo\ bar \\\\h \\\\\\ \\\\ -')
00123         self.assertEqual(pr['ExecutablePath'], '/bin/cat')
00124         self.assertTrue('InterpreterPath' not in pr)
00125         self.assertTrue('/bin/cat' in pr['ProcMaps'])
00126         self.assertTrue('[stack]' in pr['ProcMaps'])
00127 
00128         # check correct handling of executable symlinks
00129         assert os.path.islink('/bin/sh'), '/bin/sh needs to be a symlink for this test'
00130         p = subprocess.Popen(['sh'], stdin=subprocess.PIPE)
00131         assert p.pid
00132         # wait until /proc/pid/cmdline exists
00133         while True:
00134             with open('/proc/%i/cmdline' % p.pid) as fd:
00135                 if fd.read():
00136                     break
00137                 time.sleep(0.1)
00138         pr = apport.report.Report()
00139         pr.pid = p.pid
00140         pr.add_proc_info()
00141         p.communicate(b'exit\n')
00142         self.assertFalse('InterpreterPath' in pr, pr.get('InterpreterPath'))
00143         self.assertEqual(pr['ExecutablePath'], os.path.realpath('/bin/sh'))
00144         self.assertEqual(int(pr['ExecutableTimestamp']),
00145                          int(os.stat(os.path.realpath('/bin/sh')).st_mtime))
00146 
00147         # check correct handling of interpreted executables: shell
00148         p = subprocess.Popen(['zgrep', 'foo'], stdin=subprocess.PIPE)
00149         assert p.pid
00150         # wait until /proc/pid/cmdline exists
00151         while True:
00152             with open('/proc/%i/cmdline' % p.pid) as fd:
00153                 if fd.read():
00154                     break
00155                 time.sleep(0.1)
00156         pr = apport.report.Report()
00157         pr.add_proc_info(pid=p.pid)
00158         p.communicate(b'\n')
00159         self.assertTrue(pr['ExecutablePath'].endswith('bin/zgrep'))
00160         with open(pr['ExecutablePath']) as fd:
00161             self.assertEqual(pr['InterpreterPath'],
00162                              os.path.realpath(fd.readline().strip()[2:]))
00163         self.assertEqual(int(pr['ExecutableTimestamp']),
00164                          int(os.stat(pr['ExecutablePath']).st_mtime))
00165         self.assertTrue('[stack]' in pr['ProcMaps'])
00166 
00167         # check correct handling of interpreted executables: python
00168         (fd, testscript) = tempfile.mkstemp()
00169         os.write(fd, ('''#!/usr/bin/%s
00170 import sys
00171 sys.stdin.readline()
00172 ''' % os.getenv('PYTHON', 'python3')).encode('ascii'))
00173         os.close(fd)
00174         os.chmod(testscript, 0o755)
00175         p = subprocess.Popen([testscript], stdin=subprocess.PIPE,
00176                              stderr=subprocess.PIPE)
00177         assert p.pid
00178         # wait until /proc/pid/cmdline exists
00179         while True:
00180             with open('/proc/%i/cmdline' % p.pid) as fd:
00181                 if fd.read():
00182                     break
00183                 time.sleep(0.1)
00184         pr = apport.report.Report()
00185         pr.add_proc_info(pid=p.pid)
00186         p.communicate(b'\n')
00187         self.assertEqual(pr['ExecutablePath'], testscript)
00188         self.assertEqual(int(pr['ExecutableTimestamp']),
00189                          int(os.stat(testscript).st_mtime))
00190         os.unlink(testscript)
00191         self.assertTrue('python' in pr['InterpreterPath'])
00192         self.assertTrue('python' in pr['ProcMaps'])
00193         self.assertTrue('[stack]' in pr['ProcMaps'])
00194 
00195         # test process is gone, should complain about nonexisting PID
00196         self.assertRaises(ValueError, pr.add_proc_info, p.pid)

add_user_info().

Definition at line 51 of file test_report.py.

00051 
00052     def test_add_user_info(self):
00053         '''add_user_info().'''
00054 
00055         pr = apport.report.Report()
00056         pr.add_user_info()
00057         self.assertTrue('UserGroups' in pr)
00058 
00059         # double-check that user group names are removed
00060         for g in pr['UserGroups'].split():
00061             self.assertTrue(grp.getgrnam(g).gr_gid < 1000)
00062         self.assertTrue(grp.getgrgid(os.getgid()).gr_name not in pr['UserGroups'])

parse-segv produces sensible results

Definition at line 527 of file test_report.py.

00527 
00528     def test_add_zz_parse_segv_details(self):
00529         '''parse-segv produces sensible results'''
00530         rep = tempfile.NamedTemporaryFile()
00531         self._generate_sigsegv_report(rep)
00532         rep.seek(0)
00533 
00534         pr = apport.report.Report()
00535         with open(rep.name, 'rb') as f:
00536             pr.load(f)
00537         pr['Signal'] = '1'
00538         pr.add_hooks_info('fake_ui')
00539         self.assertTrue('SegvAnalysis' not in pr.keys())
00540 
00541         pr = apport.report.Report()
00542         with open(rep.name, 'rb') as f:
00543             pr.load(f)
00544         pr.add_hooks_info('fake_ui')
00545         self.assertTrue('Skipped: missing required field "Architecture"' in pr['SegvAnalysis'],
00546                         pr['SegvAnalysis'])
00547 
00548         pr.add_os_info()
00549         pr.add_hooks_info('fake_ui')
00550         self.assertTrue('Skipped: missing required field "ProcMaps"' in pr['SegvAnalysis'],
00551                         pr['SegvAnalysis'])
00552 
00553         pr.add_proc_info()
00554         pr.add_hooks_info('fake_ui')
00555         self.assertTrue('not located in a known VMA region' in pr['SegvAnalysis'],
00556                         pr['SegvAnalysis'])

Here is the call graph for this function:

_address_to_offset()

Definition at line 1696 of file test_report.py.

01696 
01697     def test_address_to_offset(self):
01698         '''_address_to_offset()'''
01699 
01700         pr = apport.report.Report()
01701 
01702         self.assertRaises(AssertionError, pr._address_to_offset, 0)
01703 
01704         pr['ProcMaps'] = '''
01705 00400000-004df000 r-xp 00000000 08:02 1044485                            /bin/bash
01706 006de000-006df000 r--p 000de000 08:02 1044485                            /bin/bash
01707 01596000-01597000 rw-p 00000000 00:00 0
01708 01597000-015a4000 rw-p 00000000 00:00 0                                  [heap]
01709 7f491f868000-7f491f88a000 r-xp 00000000 08:02 526219                     /lib/x86_64-linux-gnu/libtinfo.so.5.9
01710 7f491fa8f000-7f491fc24000 r-xp 00000000 08:02 522605                     /lib/x86_64-linux-gnu/libc-2.13.so
01711 7f491fc24000-7f491fe23000 ---p 00195000 08:02 522605                     /lib/with spaces !/libfoo.so
01712 7fff6e57b000-7fff6e59c000 rw-p 00000000 00:00 0                          [stack]
01713 7fff6e5ff000-7fff6e600000 r-xp 00000000 00:00 0                          [vdso]
01714 ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
01715 '''
01716 
01717         self.assertEqual(pr._address_to_offset(0x41d703), '/bin/bash+1d703')
01718         self.assertEqual(pr._address_to_offset(0x00007f491fac5687),
01719                          '/lib/x86_64-linux-gnu/libc-2.13.so+36687')
01720 
01721         self.assertEqual(pr._address_to_offset(0x006ddfff), None)
01722         self.assertEqual(pr._address_to_offset(0x006de000), '/bin/bash+0')
01723         self.assertEqual(pr._address_to_offset(0x006df000), '/bin/bash+1000')
01724         self.assertEqual(pr._address_to_offset(0x006df001), None)
01725 
01726         self.assertEqual(pr._address_to_offset(0x7f491fc24010),
01727                          '/lib/with spaces !/libfoo.so+10')

_address_to_offset() for current /proc/pid/maps

Definition at line 1728 of file test_report.py.

01728 
01729     def test_address_to_offset_live(self):
01730         '''_address_to_offset() for current /proc/pid/maps'''
01731 
01732         # this primarily checks that the parser actually gets along with the
01733         # real /proc/pid/maps and not just with our static test case above
01734         pr = apport.report.Report()
01735         pr.add_proc_info()
01736         self.assertEqual(pr._address_to_offset(0), None)
01737         res = pr._address_to_offset(int(pr['ProcMaps'].split('-', 1)[0], 16) + 5)
01738         self.assertEqual(res.split('+', 1)[1], '5')
01739         self.assertTrue('python' in res.split('+', 1)[0])

check_ignored() for system-wise blacklist.

Definition at line 1020 of file test_report.py.

01020 
01021     def test_blacklisting(self):
01022         '''check_ignored() for system-wise blacklist.'''
01023 
01024         orig_blacklist_dir = apport.report._blacklist_dir
01025         apport.report._blacklist_dir = tempfile.mkdtemp()
01026         orig_ignore_file = apport.report._ignore_file
01027         apport.report._ignore_file = '/nonexistant'
01028         try:
01029             bash_rep = apport.report.Report()
01030             bash_rep['ExecutablePath'] = '/bin/bash'
01031             crap_rep = apport.report.Report()
01032             crap_rep['ExecutablePath'] = '/bin/crap'
01033 
01034             # no ignores initially
01035             self.assertEqual(bash_rep.check_ignored(), False)
01036             self.assertEqual(crap_rep.check_ignored(), False)
01037 
01038             # should not stumble over comments
01039             with open(os.path.join(apport.report._blacklist_dir, 'README'), 'w') as fd:
01040                 fd.write('# Ignore file\n#/bin/bash\n')
01041 
01042             # no ignores on nonmatching paths
01043             with open(os.path.join(apport.report._blacklist_dir, 'bl1'), 'w') as fd:
01044                 fd.write('/bin/bas\n/bin/bashh\nbash\nbin/bash\n')
01045             self.assertEqual(bash_rep.check_ignored(), False)
01046             self.assertEqual(crap_rep.check_ignored(), False)
01047 
01048             # ignore crap now
01049             with open(os.path.join(apport.report._blacklist_dir, 'bl_2'), 'w') as fd:
01050                 fd.write('/bin/crap\n')
01051             self.assertEqual(bash_rep.check_ignored(), False)
01052             self.assertEqual(crap_rep.check_ignored(), True)
01053 
01054             # ignore bash now
01055             with open(os.path.join(apport.report._blacklist_dir, 'bl1'), 'a') as fd:
01056                 fd.write('/bin/bash\n')
01057             self.assertEqual(bash_rep.check_ignored(), True)
01058             self.assertEqual(crap_rep.check_ignored(), True)
01059         finally:
01060             shutil.rmtree(apport.report._blacklist_dir)
01061             apport.report._blacklist_dir = orig_blacklist_dir
01062             apport.report._ignore_file = orig_ignore_file

_check_interpreted().

Definition at line 230 of file test_report.py.

00230 
00231     def test_check_interpreted(self):
00232         '''_check_interpreted().'''
00233 
00234         restore_root = False
00235         if os.getuid() == 0:
00236             # temporarily drop to normal user "mail"
00237             os.setresuid(8, 8, -1)
00238             restore_root = True
00239 
00240         try:
00241             # standard ELF binary
00242             f = tempfile.NamedTemporaryFile()
00243             pr = apport.report.Report()
00244             pr['ExecutablePath'] = '/usr/bin/gedit'
00245             pr['ProcStatus'] = 'Name:\tgedit'
00246             pr['ProcCmdline'] = 'gedit\0/' + f.name
00247             pr._check_interpreted()
00248             self.assertEqual(pr['ExecutablePath'], '/usr/bin/gedit')
00249             self.assertFalse('InterpreterPath' in pr)
00250             f.close()
00251 
00252             # bogus argv[0]
00253             pr = apport.report.Report()
00254             pr['ExecutablePath'] = '/bin/dash'
00255             pr['ProcStatus'] = 'Name:\tznonexisting'
00256             pr['ProcCmdline'] = 'nonexisting\0/foo'
00257             pr._check_interpreted()
00258             self.assertEqual(pr['ExecutablePath'], '/bin/dash')
00259             self.assertFalse('InterpreterPath' in pr)
00260 
00261             # standard sh script
00262             pr = apport.report.Report()
00263             pr['ExecutablePath'] = '/bin/dash'
00264             pr['ProcStatus'] = 'Name:\tzgrep'
00265             pr['ProcCmdline'] = '/bin/sh\0/bin/zgrep\0foo'
00266             pr._check_interpreted()
00267             self.assertEqual(pr['ExecutablePath'], '/bin/zgrep')
00268             self.assertEqual(pr['InterpreterPath'], '/bin/dash')
00269 
00270             # standard sh script when being called explicitly with interpreter
00271             pr = apport.report.Report()
00272             pr['ExecutablePath'] = '/bin/dash'
00273             pr['ProcStatus'] = 'Name:\tdash'
00274             pr['ProcCmdline'] = '/bin/sh\0/bin/zgrep\0foo'
00275             pr._check_interpreted()
00276             self.assertEqual(pr['ExecutablePath'], '/bin/zgrep')
00277             self.assertEqual(pr['InterpreterPath'], '/bin/dash')
00278 
00279             # special case mono scheme: beagled-helper (use zgrep to make the test
00280             # suite work if mono or beagle are not installed)
00281             pr = apport.report.Report()
00282             pr['ExecutablePath'] = '/usr/bin/mono'
00283             pr['ProcStatus'] = 'Name:\tzgrep'
00284             pr['ProcCmdline'] = 'zgrep\0--debug\0/bin/zgrep'
00285             pr._check_interpreted()
00286             self.assertEqual(pr['ExecutablePath'], '/bin/zgrep')
00287             self.assertEqual(pr['InterpreterPath'], '/usr/bin/mono')
00288 
00289             # special case mono scheme: banshee (use zgrep to make the test
00290             # suite work if mono or beagle are not installed)
00291             pr = apport.report.Report()
00292             pr['ExecutablePath'] = '/usr/bin/mono'
00293             pr['ProcStatus'] = 'Name:\tzgrep'
00294             pr['ProcCmdline'] = 'zgrep\0/bin/zgrep'
00295             pr._check_interpreted()
00296             self.assertEqual(pr['ExecutablePath'], '/bin/zgrep')
00297             self.assertEqual(pr['InterpreterPath'], '/usr/bin/mono')
00298 
00299             # fail on files we shouldn't have access to when name!=argv[0]
00300             pr = apport.report.Report()
00301             pr['ExecutablePath'] = '/usr/bin/python'
00302             pr['ProcStatus'] = 'Name:\tznonexisting'
00303             pr['ProcCmdline'] = 'python\0/etc/shadow'
00304             pr._check_interpreted()
00305             self.assertEqual(pr['ExecutablePath'], '/usr/bin/python')
00306             self.assertFalse('InterpreterPath' in pr)
00307 
00308             # succeed on files we should have access to when name!=argv[0]
00309             pr = apport.report.Report()
00310             pr['ExecutablePath'] = '/usr/bin/python'
00311             pr['ProcStatus'] = 'Name:\tznonexisting'
00312             pr['ProcCmdline'] = 'python\0/etc/passwd'
00313             pr._check_interpreted()
00314             self.assertEqual(pr['InterpreterPath'], '/usr/bin/python')
00315             self.assertEqual(pr['ExecutablePath'], '/etc/passwd')
00316 
00317             # fail on files we shouldn't have access to when name==argv[0]
00318             pr = apport.report.Report()
00319             pr['ExecutablePath'] = '/usr/bin/python'
00320             pr['ProcStatus'] = 'Name:\tshadow'
00321             pr['ProcCmdline'] = '../etc/shadow'
00322             pr._check_interpreted()
00323             self.assertEqual(pr['ExecutablePath'], '/usr/bin/python')
00324             self.assertFalse('InterpreterPath' in pr)
00325 
00326             # succeed on files we should have access to when name==argv[0]
00327             pr = apport.report.Report()
00328             pr['ExecutablePath'] = '/usr/bin/python'
00329             pr['ProcStatus'] = 'Name:\tpasswd'
00330             pr['ProcCmdline'] = '../etc/passwd'
00331             pr._check_interpreted()
00332             self.assertEqual(pr['InterpreterPath'], '/usr/bin/python')
00333             self.assertEqual(pr['ExecutablePath'], '/bin/../etc/passwd')
00334 
00335             # interactive python process
00336             pr = apport.report.Report()
00337             pr['ExecutablePath'] = '/usr/bin/python'
00338             pr['ProcStatus'] = 'Name:\tpython'
00339             pr['ProcCmdline'] = 'python'
00340             pr._check_interpreted()
00341             self.assertEqual(pr['ExecutablePath'], '/usr/bin/python')
00342             self.assertFalse('InterpreterPath' in pr)
00343 
00344             # python script (abuse /bin/bash since it must exist)
00345             pr = apport.report.Report()
00346             pr['ExecutablePath'] = '/usr/bin/python'
00347             pr['ProcStatus'] = 'Name:\tbash'
00348             pr['ProcCmdline'] = 'python\0/bin/bash'
00349             pr._check_interpreted()
00350             self.assertEqual(pr['InterpreterPath'], '/usr/bin/python')
00351             self.assertEqual(pr['ExecutablePath'], '/bin/bash')
00352 
00353             # python script with options (abuse /bin/bash since it must exist)
00354             pr = apport.report.Report()
00355             pr['ExecutablePath'] = '/usr/bin/python'
00356             pr['ProcStatus'] = 'Name:\tbash'
00357             pr['ProcCmdline'] = 'python\0-OO\0/bin/bash'
00358             pr._check_interpreted()
00359             self.assertEqual(pr['InterpreterPath'], '/usr/bin/python')
00360             self.assertEqual(pr['ExecutablePath'], '/bin/bash')
00361 
00362             # python script with a versioned interpreter
00363             pr = apport.report.Report()
00364             pr['ExecutablePath'] = '/usr/bin/python2.7'
00365             pr['ProcStatus'] = 'Name:\tbash'
00366             pr['ProcCmdline'] = '/usr/bin/python\0/bin/bash'
00367             pr._check_interpreted()
00368             self.assertEqual(pr['InterpreterPath'], '/usr/bin/python2.7')
00369             self.assertEqual(pr['ExecutablePath'], '/bin/bash')
00370 
00371             # python script through -m
00372             pr = apport.report.Report()
00373             pr['ExecutablePath'] = '/usr/bin/python2.7'
00374             pr['ProcStatus'] = 'Name:\tpython'
00375             pr['ProcCmdline'] = 'python\0-tt\0-m\0apport/report\0-v'
00376             pr._check_interpreted()
00377             self.assertEqual(pr['InterpreterPath'], '/usr/bin/python2.7')
00378             self.assertTrue('report' in pr['ExecutablePath'],
00379                             'expecting "report" in ExecutablePath "%s"' % pr['ExecutablePath'])
00380         finally:
00381             if restore_root:
00382                 os.setresuid(0, 0, -1)

_check_interpreted() for programs ran through twistd

Definition at line 383 of file test_report.py.

00383 
00384     def test_check_interpreted_twistd(self):
00385         '''_check_interpreted() for programs ran through twistd'''
00386 
00387         # LP#761374
00388         pr = apport.report.Report()
00389         pr['ExecutablePath'] = '/usr/bin/python2.7'
00390         pr['ProcStatus'] = 'Name:\ttwistd'
00391         pr['ProcCmdline'] = '/usr/bin/python\0/usr/bin/twistd\0--uid\0root\0--gid\0root\0--pidfile\0/var/run/nanny.pid\0-r\0glib2\0--logfile\0/var/log/nanny.log\0-y\0/usr/share/nanny/daemon/nanny.tap'
00392         pr._check_interpreted()
00393         self.assertEqual(pr['ExecutablePath'], '/usr/share/nanny/daemon/nanny.tap')
00394         self.assertEqual(pr['InterpreterPath'], '/usr/bin/twistd')
00395 
00396         # LP#625039
00397         pr = apport.report.Report()
00398         pr['ExecutablePath'] = '/usr/bin/python2.7'
00399         pr['ProcStatus'] = 'Name:\ttwistd'
00400         pr['ProcCmdline'] = '/usr/bin/python\0/usr/bin/twistd\0--pidfile=/var/run/apt-p2p//apt-p2p.pid\0--rundir=/var/run/apt-p2p/\0--python=/usr/sbin/apt-p2p\0--logfile=/var/log/apt-p2p.log\0--no_save'
00401         pr._check_interpreted()
00402         self.assertEqual(pr['ExecutablePath'], '/usr/sbin/apt-p2p')
00403         self.assertEqual(pr['InterpreterPath'], '/usr/bin/twistd')
00404 
00405         # somewhere from LP#755025
00406         pr = apport.report.Report()
00407         pr['ExecutablePath'] = '/usr/bin/python2.7'
00408         pr['ProcStatus'] = 'Name:\ttwistd'
00409         pr['ProcCmdline'] = '/usr/bin/python\0/usr/bin/twistd\0-r\0gtk2\0--pidfile\0/tmp/vmc.pid\0-noy\0/usr/share/vodafone-mobile-connect/gtk-tap.py\0-l\0/dev/null'
00410         pr._check_interpreted()
00411         self.assertEqual(pr['ExecutablePath'], '/usr/share/vodafone-mobile-connect/gtk-tap.py')
00412         self.assertEqual(pr['InterpreterPath'], '/usr/bin/twistd')
00413 
00414         # LP#725383 -> not practical to determine file here
00415         pr = apport.report.Report()
00416         pr['ExecutablePath'] = '/usr/bin/python2.7'
00417         pr['ProcStatus'] = 'Name:\ttwistd'
00418         pr['ProcCmdline'] = '/usr/bin/python\0/usr/bin/twistd\0--pidfile=/var/run/poker-network-server.pid\0--logfile=/var/log/poker-network-server.log\0--no_save\0--reactor=poll\0pokerserver'
00419         pr._check_interpreted()
00420         self.assertTrue('ExecutablePath' in pr)
00421         self.assertTrue('UnreportableReason' in pr)
00422         self.assertEqual(pr['InterpreterPath'], '/usr/bin/twistd')

Here is the call graph for this function:

crash_signature().

Definition at line 1522 of file test_report.py.

01522 
01523     def test_crash_signature(self):
01524         '''crash_signature().'''
01525 
01526         r = apport.report.Report()
01527         self.assertEqual(r.crash_signature(), None)
01528 
01529         # signal crashes
01530         r['Signal'] = '42'
01531         r['ExecutablePath'] = '/bin/crash'
01532 
01533         r['StacktraceTop'] = '''foo_bar (x=1) at crash.c:28
01534 d01 (x=1) at crash.c:29
01535 raise () from /lib/libpthread.so.0
01536 <signal handler called>
01537 __frob::~frob (x=1) at crash.c:30'''
01538 
01539         self.assertEqual(r.crash_signature(), '/bin/crash:42:foo_bar:d01:raise:<signal handler called>:__frob::~frob')
01540 
01541         r['StacktraceTop'] = '''foo_bar (x=1) at crash.c:28
01542 ??
01543 raise () from /lib/libpthread.so.0
01544 <signal handler called>
01545 __frob (x=1) at crash.c:30'''
01546         self.assertEqual(r.crash_signature(), None)
01547 
01548         r['StacktraceTop'] = ''
01549         self.assertEqual(r.crash_signature(), None)
01550 
01551         # Python crashes
01552         del r['Signal']
01553         r['Traceback'] = '''Traceback (most recent call last):
01554   File "test.py", line 7, in <module>
01555     print(_f(5))
01556   File "test.py", line 5, in _f
01557     return g_foo00(x+1)
01558   File "test.py", line 2, in g_foo00
01559     return x/0
01560 ZeroDivisionError: integer division or modulo by zero'''
01561         self.assertEqual(r.crash_signature(), '/bin/crash:ZeroDivisionError:test.py@7:_f:g_foo00')
01562 
01563         # sometimes Python traces do not have file references
01564         r['Traceback'] = 'TypeError: function takes exactly 0 arguments (1 given)'
01565         self.assertEqual(r.crash_signature(), '/bin/crash:TypeError')
01566 
01567         r['Traceback'] = 'FooBar'
01568         self.assertEqual(r.crash_signature(), None)
01569 
01570         # kernel
01571         r['ProblemType'] = 'KernelCrash'
01572         r['Stacktrace'] = '''
01573 crash 4.0-8.9
01574 GNU gdb 6.1
01575 GDB is free software, covered by the GNU General Public License, and you are
01576 welcome to change it and/or distribute copies of it under certain conditions.
01577 Type "show copying" to see the conditions.
01578 There is absolutely no warranty for GDB.  Type "show warranty" for details.
01579 This GDB was configured as "i686-pc-linux-gnu"...
01580 
01581       KERNEL: /usr/lib/debug/boot/vmlinux-2.6.31-2-generic
01582     DUMPFILE: /tmp/tmpRJZy_O
01583         CPUS: 1
01584         DATE: Thu Jul  9 12:58:08 2009
01585       UPTIME: 00:00:57
01586 LOAD AVERAGE: 0.15, 0.05, 0.02
01587        TASKS: 173
01588     NODENAME: egon-desktop
01589      RELEASE: 2.6.31-2-generic
01590      VERSION: #16-Ubuntu SMP Mon Jul 6 20:38:51 UTC 2009
01591      MACHINE: i686  (2137 Mhz)
01592       MEMORY: 2 GB
01593        PANIC: "[   57.879776] Oops: 0002 [#1] SMP " (check log for details)
01594          PID: 0
01595      COMMAND: "swapper"
01596         TASK: c073c180  [THREAD_INFO: c0784000]
01597          CPU: 0
01598        STATE: TASK_RUNNING (PANIC)
01599 
01600 PID: 0      TASK: c073c180  CPU: 0   COMMAND: "swapper"
01601  #0 [c0785ba0] sysrq_handle_crash at c03917a3
01602     [RA: c03919c6  SP: c0785ba0  FP: c0785ba0  SIZE: 4]
01603     c0785ba0: c03919c6
01604  #1 [c0785ba0] __handle_sysrq at c03919c4
01605     [RA: c0391a91  SP: c0785ba4  FP: c0785bc8  SIZE: 40]
01606     c0785ba4: c06d4bab  c06d42d2  f6534000  00000004
01607     c0785bb4: 00000086  0000002e  00000001  f6534000
01608     c0785bc4: c0785bcc  c0391a91
01609  #2 [c0785bc8] handle_sysrq at c0391a8c
01610     [RA: c0389961  SP: c0785bcc  FP: c0785bd0  SIZE: 8]
01611     c0785bcc: c0785c0c  c0389961
01612  #3 [c0785bd0] kbd_keycode at c038995c
01613     [RA: c0389b8b  SP: c0785bd4  FP: c0785c10  SIZE: 64]
01614     c0785bd4: c056f96a  c0785be4  00000096  c07578c0
01615     c0785be4: 00000001  f6ac6e00  f6ac6e00  00000001
01616     c0785bf4: 00000000  00000000  0000002e  0000002e
01617     c0785c04: 00000001  f70d6850  c0785c1c  c0389b8b
01618  #4 [c0785c10] kbd_event at c0389b86
01619     [RA: c043140c  SP: c0785c14  FP: c0785c20  SIZE: 16]
01620     c0785c14: c0758040  f6910900  c0785c3c  c043140c
01621  #5 [c0785c20] input_pass_event at c0431409
01622     [RA: c04332ce  SP: c0785c24  FP: c0785c40  SIZE: 32]
01623     c0785c24: 00000001  0000002e  00000001  f70d6000
01624     c0785c34: 00000001  0000002e  c0785c64  c04332ce
01625  #6 [c0785c40] input_handle_event at c04332c9
01626     [RA: c0433ac6  SP: c0785c44  FP: c0785c68  SIZE: 40]
01627     c0785c44: 00000001  ffff138d  0000003d  00000001
01628     c0785c54: f70d6000  00000001  f70d6000  0000002e
01629     c0785c64: c0785c84  c0433ac6
01630  #7 [c0785c68] input_event at c0433ac1
01631     [RA: c0479806  SP: c0785c6c  FP: c0785c88  SIZE: 32]
01632     c0785c6c: 00000001  00000092  f70d677c  f70d70b4
01633     c0785c7c: 0000002e  f70d7000  c0785ca8  c0479806
01634  #8 [c0785c88] hidinput_hid_event at c0479801
01635     [RA: c0475b31  SP: c0785c8c  FP: c0785cac  SIZE: 36]
01636     c0785c8c: 00000001  00000007  c0785c00  f70d6000
01637     c0785c9c: f70d70b4  f70d5000  f70d7000  c0785cc4
01638     c0785cac: c0475b31
01639     [RA: 0  SP: c0785ffc  FP: c0785ffc  SIZE: 0]
01640    PID    PPID  CPU   TASK    ST  %MEM     VSZ    RSS  COMM
01641 >     0      0   0  c073c180  RU   0.0       0      0  [swapper]
01642       1      0   1  f7038000  IN   0.1    3096   1960  init
01643       2      0   0  f7038c90  IN   0.0       0      0  [kthreadd]
01644     271      2   1  f72bf110  IN   0.0       0      0  [bluetooth]
01645     325      2   1  f71c25b0  IN   0.0       0      0  [khungtaskd]
01646    1404      2   0  f6b5bed0  IN   0.0       0      0  [kpsmoused]
01647    1504      2   1  f649cb60  IN   0.0       0      0  [hd-audio0]
01648    2055      1   0  f6a18000  IN   0.0    1824    536  getty
01649    2056      1   0  f6a1d7f0  IN   0.0    1824    536  getty
01650    2061      1   0  f6a1f110  IN   0.1    3132   1604  login
01651    2062      1   1  f6a18c90  IN   0.0    1824    540  getty
01652    2063      1   1  f6b58c90  IN   0.0    1824    540  getty
01653    2130      1   0  f6b5f110  IN   0.0    2200   1032  acpid
01654    2169      1   0  f69ebed0  IN   0.0    2040    664  syslogd
01655    2192      1   1  f65b3ed0  IN   0.0    1976    532  dd
01656    2194      1   1  f6b5a5b0  IN   0.1    3996   2712  klogd
01657    2217      1   0  f6b74b60  IN   0.1    3008   1120  dbus-daemon
01658    2248      1   0  f65b7110  IN   0.2    6896   4304  hald
01659    2251      1   1  f65b3240  IN   0.1   19688   2604  console-kit-dae
01660 RUNQUEUES[0]: c6002320
01661  RT PRIO_ARRAY: c60023c0
01662  CFS RB_ROOT: c600237c
01663   PID: 9      TASK: f703f110  CPU: 0   COMMAND: "events/0"
01664 '''
01665         self.assertEqual(r.crash_signature(), 'kernel:sysrq_handle_crash:__handle_sysrq:handle_sysrq:kbd_keycode:kbd_event:input_pass_event:input_handle_event:input_event:hidinput_hid_event')
01666 
01667         # assertion failures
01668         r = apport.report.Report()
01669         r['Signal'] = '6'
01670         r['ExecutablePath'] = '/bin/bash'
01671         r['AssertionMessage'] = 'foo.c:42 main: i > 0'
01672         self.assertEqual(r.crash_signature(), '/bin/bash:foo.c:42 main: i > 0')

crash_signature_addresses()

Definition at line 1740 of file test_report.py.

01740 
01741     def test_crash_signature_addresses(self):
01742         '''crash_signature_addresses()'''
01743 
01744         pr = apport.report.Report()
01745         self.assertEqual(pr.crash_signature_addresses(), None)
01746 
01747         pr['ExecutablePath'] = '/bin/bash'
01748         pr['Signal'] = '42'
01749         pr['ProcMaps'] = '''
01750 00400000-004df000 r-xp 00000000 08:02 1044485                            /bin/bash
01751 006de000-006df000 r--p 000de000 08:02 1044485                            /bin/bash
01752 01596000-01597000 rw-p 00000000 00:00 0
01753 01597000-015a4000 rw-p 00000000 00:00 0                                  [heap]
01754 7f491f868000-7f491f88a000 r-xp 00000000 08:02 526219                     /lib/x86_64-linux-gnu/libtinfo.so.5.9
01755 7f491fa8f000-7f491fc24000 r-xp 00000000 08:02 522605                     /lib/x86_64-linux-gnu/libc-2.13.so
01756 7f491fc24000-7f491fe23000 ---p 00195000 08:02 522605                     /lib/with spaces !/libfoo.so
01757 7fff6e57b000-7fff6e59c000 rw-p 00000000 00:00 0                          [stack]
01758 7fff6e5ff000-7fff6e600000 r-xp 00000000 00:00 0                          [vdso]
01759 ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
01760 '''
01761 
01762         # no Stacktrace field
01763         self.assertEqual(pr.crash_signature_addresses(), None)
01764 
01765         # good stack trace
01766         pr['Stacktrace'] = '''
01767 #0  0x00007f491fac5687 in kill () at ../sysdeps/unix/syscall-template.S:82
01768 No locals.
01769 #1  0x000000000043fd51 in kill_pid ()
01770 #2  g_main_context_iterate (context=0x1731680) at gmain.c:3068
01771 #3  0x000000000042eb76 in ?? ()
01772 #4  0x00000000004324d8 in ??
01773 No symbol table info available.
01774 #5  0x00000000004707e3 in parse_and_execute ()
01775 #6  0x000000000041d703 in _start ()
01776 '''
01777         self.assertEqual(pr.crash_signature_addresses(),
01778                          '/bin/bash:42:%s:/lib/x86_64-linux-gnu/libc-2.13.so+36687:/bin/bash+3fd51:/bin/bash+2eb76:/bin/bash+324d8:/bin/bash+707e3:/bin/bash+1d703' % os.uname()[4])
01779 
01780         # all resolvable, but too short
01781         pr['Stacktrace'] = '#0  0x00007f491fac5687 in kill () at ../sysdeps/unix/syscall-template.S:82'
01782         self.assertEqual(pr.crash_signature_addresses(), None)
01783 
01784         # one unresolvable, but long enough
01785         pr['Stacktrace'] = '''
01786 #0  0x00007f491fac5687 in kill () at ../sysdeps/unix/syscall-template.S:82
01787 No locals.
01788 #1  0x000001000043fd51 in kill_pid ()
01789 #2  g_main_context_iterate (context=0x1731680) at gmain.c:3068
01790 #3  0x000000000042eb76 in ?? ()
01791 #4  0x00000000004324d8 in ??
01792 No symbol table info available.
01793 #5  0x00000000004707e3 in parse_and_execute ()
01794 #6  0x000000000041d715 in main ()
01795 #7  0x000000000041d703 in _start ()
01796 '''
01797         self.assertNotEqual(pr.crash_signature_addresses(), None)
01798 
01799         # two unresolvables, 2/7 is too much
01800         pr['Stacktrace'] = '''
01801 #0  0x00007f491fac5687 in kill () at ../sysdeps/unix/syscall-template.S:82
01802 No locals.
01803 #1  0x000001000043fd51 in kill_pid ()
01804 #2  g_main_context_iterate (context=0x1731680) at gmain.c:3068
01805 #3  0x000001000042eb76 in ?? ()
01806 #4  0x00000000004324d8 in ??
01807 No symbol table info available.
01808 #5  0x00000000004707e3 in parse_and_execute ()
01809 #6  0x000000000041d715 in main ()
01810 #7  0x000000000041d703 in _start ()
01811 '''
01812         self.assertEqual(pr.crash_signature_addresses(), None)

_gen_stacktrace_top().

Definition at line 1326 of file test_report.py.

01326 
01327     def test_gen_stacktrace_top(self):
01328         '''_gen_stacktrace_top().'''
01329 
01330         # nothing to chop off
01331         r = apport.report.Report()
01332         r['Stacktrace'] = '''#0  0x10000488 in h (p=0x0) at crash.c:25
01333 #1  0x100004c8 in g (x=1, y=42) at crash.c:26
01334 #2  0x10000514 in f (x=1) at crash.c:27
01335 #3  0x10000530 in e (x=1) at crash.c:28
01336 #4  0x10000530 in d (x=1) at crash.c:29
01337 #5  0x10000530 in c (x=1) at crash.c:30
01338 #6  0x10000550 in main () at crash.c:31
01339 '''
01340         r._gen_stacktrace_top()
01341         self.assertEqual(r['StacktraceTop'], '''h (p=0x0) at crash.c:25
01342 g (x=1, y=42) at crash.c:26
01343 f (x=1) at crash.c:27
01344 e (x=1) at crash.c:28
01345 d (x=1) at crash.c:29''')
01346 
01347         # nothing to chop off: some addresses missing (LP #269133)
01348         r = apport.report.Report()
01349         r['Stacktrace'] = '''#0 h (p=0x0) at crash.c:25
01350 #1  0x100004c8 in g (x=1, y=42) at crash.c:26
01351 #2 f (x=1) at crash.c:27
01352 #3  0x10000530 in e (x=1) at crash.c:28
01353 #4  0x10000530 in d (x=1) at crash.c:29
01354 #5  0x10000530 in c (x=1) at crash.c:30
01355 #6  0x10000550 in main () at crash.c:31
01356 '''
01357         r._gen_stacktrace_top()
01358         self.assertEqual(r['StacktraceTop'], '''h (p=0x0) at crash.c:25
01359 g (x=1, y=42) at crash.c:26
01360 f (x=1) at crash.c:27
01361 e (x=1) at crash.c:28
01362 d (x=1) at crash.c:29''')
01363 
01364         # single signal handler invocation
01365         r = apport.report.Report()
01366         r['Stacktrace'] = '''#0  0x10000488 in raise () from /lib/libpthread.so.0
01367 #1  0x100004c8 in ??
01368 #2  <signal handler called>
01369 #3  0x10000530 in e (x=1) at crash.c:28
01370 #4  0x10000530 in d (x=1) at crash.c:29
01371 #5  0x10000530 in c (x=1) at crash.c:30
01372 #6  0x10000550 in main () at crash.c:31
01373 '''
01374         r._gen_stacktrace_top()
01375         self.assertEqual(r['StacktraceTop'], '''e (x=1) at crash.c:28
01376 d (x=1) at crash.c:29
01377 c (x=1) at crash.c:30
01378 main () at crash.c:31''')
01379 
01380         # single signal handler invocation: some addresses missing
01381         r = apport.report.Report()
01382         r['Stacktrace'] = '''#0  0x10000488 in raise () from /lib/libpthread.so.0
01383 #1  ??
01384 #2  <signal handler called>
01385 #3  0x10000530 in e (x=1) at crash.c:28
01386 #4  d (x=1) at crash.c:29
01387 #5  0x10000530 in c (x=1) at crash.c:30
01388 #6  0x10000550 in main () at crash.c:31
01389 '''
01390         r._gen_stacktrace_top()
01391         self.assertEqual(r['StacktraceTop'], '''e (x=1) at crash.c:28
01392 d (x=1) at crash.c:29
01393 c (x=1) at crash.c:30
01394 main () at crash.c:31''')
01395 
01396         # stacked signal handler; should only cut the first one
01397         r = apport.report.Report()
01398         r['Stacktrace'] = '''#0  0x10000488 in raise () from /lib/libpthread.so.0
01399 #1  0x100004c8 in ??
01400 #2  <signal handler called>
01401 #3  0x10000530 in e (x=1) at crash.c:28
01402 #4  0x10000530 in d (x=1) at crash.c:29
01403 #5  0x10000123 in raise () from /lib/libpthread.so.0
01404 #6  <signal handler called>
01405 #7  0x10000530 in c (x=1) at crash.c:30
01406 #8  0x10000550 in main () at crash.c:31
01407 '''
01408         r._gen_stacktrace_top()
01409         self.assertEqual(r['StacktraceTop'], '''e (x=1) at crash.c:28
01410 d (x=1) at crash.c:29
01411 raise () from /lib/libpthread.so.0
01412 <signal handler called>
01413 c (x=1) at crash.c:30''')
01414 
01415         # Gnome assertion; should unwind the logs and assert call
01416         r = apport.report.Report()
01417         r['Stacktrace'] = '''#0  0xb7d39cab in IA__g_logv (log_domain=<value optimized out>, log_level=G_LOG_LEVEL_ERROR,
01418     format=0xb7d825f0 "file %s: line %d (%s): assertion failed: (%s)", args1=0xbfee8e3c "xxx") at /build/buildd/glib2.0-2.13.5/glib/gmessages.c:493
01419 #1  0xb7d39f29 in IA__g_log (log_domain=0xb7edbfd0 "libgnomevfs", log_level=G_LOG_LEVEL_ERROR,
01420     format=0xb7d825f0 "file %s: line %d (%s): assertion failed: (%s)") at /build/buildd/glib2.0-2.13.5/glib/gmessages.c:517
01421 #2  0xb7d39fa6 in IA__g_assert_warning (log_domain=0xb7edbfd0 "libgnomevfs", file=0xb7ee1a26 "gnome-vfs-volume.c", line=254,
01422     pretty_function=0xb7ee1920 "gnome_vfs_volume_unset_drive_private", expression=0xb7ee1a39 "volume->priv->drive == drive")
01423     at /build/buildd/glib2.0-2.13.5/glib/gmessages.c:552
01424 No locals.
01425 #3  0xb7ec6c11 in gnome_vfs_volume_unset_drive_private (volume=0x8081a30, drive=0x8078f00) at gnome-vfs-volume.c:254
01426         __PRETTY_FUNCTION__ = "gnome_vfs_volume_unset_drive_private"
01427 #4  0x08054db8 in _gnome_vfs_volume_monitor_disconnected (volume_monitor=0x8070400, drive=0x8078f00) at gnome-vfs-volume-monitor.c:963
01428         vol_list = (GList *) 0x8096d30
01429         current_vol = (GList *) 0x8097470
01430 #5  0x0805951e in _hal_device_removed (hal_ctx=0x8074da8, udi=0x8093be4 "/org/freedesktop/Hal/devices/volume_uuid_92FC9DFBFC9DDA35")
01431     at gnome-vfs-hal-mounts.c:1316
01432         backing_udi = <value optimized out>
01433 #6  0xb7ef1ead in filter_func (connection=0x8075288, message=0x80768d8, user_data=0x8074da8) at libhal.c:820
01434         udi = <value optimized out>
01435         object_path = 0x8076d40 "/org/freedesktop/Hal/Manager"
01436         error = {name = 0x0, message = 0x0, dummy1 = 1, dummy2 = 0, dummy3 = 0, dummy4 = 1, dummy5 = 0, padding1 = 0xb7e50c00}
01437 #7  0xb7e071d2 in dbus_connection_dispatch (connection=0x8075288) at dbus-connection.c:4267
01438 #8  0xb7e33dfd in ?? () from /usr/lib/libdbus-glib-1.so.2'''
01439         r._gen_stacktrace_top()
01440         self.assertEqual(r['StacktraceTop'], '''gnome_vfs_volume_unset_drive_private (volume=0x8081a30, drive=0x8078f00) at gnome-vfs-volume.c:254
01441 _gnome_vfs_volume_monitor_disconnected (volume_monitor=0x8070400, drive=0x8078f00) at gnome-vfs-volume-monitor.c:963
01442 _hal_device_removed (hal_ctx=0x8074da8, udi=0x8093be4 "/org/freedesktop/Hal/devices/volume_uuid_92FC9DFBFC9DDA35")
01443 filter_func (connection=0x8075288, message=0x80768d8, user_data=0x8074da8) at libhal.c:820
01444 dbus_connection_dispatch (connection=0x8075288) at dbus-connection.c:4267''')
01445 
01446         # XError (taken from LP#848808)
01447         r = apport.report.Report()
01448         r['Stacktrace'] = '''#0  0x007cf416 in __kernel_vsyscall ()
01449 No symbol table info available.
01450 #1  0x01017c8f in __GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
01451 #2  0x0101b2b5 in __GI_abort () at abort.c:92
01452 #3  0x0807daab in meta_bug (format=0x80b0c60 "Unexpected X error: %s serial %ld error_code %d request_code %d minor_code %d)\n") at core/util.c:398
01453 #4  0x0806989c in x_error_handler (error=0xbf924acc, xdisplay=0x9104b88) at core/errors.c:247
01454 #5  x_error_handler (xdisplay=0x9104b88, error=0xbf924acc) at core/errors.c:203
01455 #6  0x00e97d3b in _XError (dpy=0x9104b88, rep=0x9131840) at ../../src/XlibInt.c:1583
01456 #7  0x00e9490d in handle_error (dpy=0x9104b88, err=0x9131840, in_XReply=0) at ../../src/xcb_io.c:212
01457 #8  0x00e94967 in handle_response (dpy=0x9104b88, response=0x9131840, in_XReply=0) at ../../src/xcb_io.c:324
01458 #9  0x00e952fe in _XReadEvents (dpy=0x9104b88) at ../../src/xcb_io.c:425
01459 #10 0x00e93663 in XWindowEvent (dpy=0x9104b88, w=16777220, mask=4194304, event=0xbf924c6c) at ../../src/WinEvent.c:79
01460 #11 0x0806071c in meta_display_get_current_time_roundtrip (display=0x916d7d0) at core/display.c:1217
01461 #12 0x08089f64 in meta_window_show (window=0x91ccfc8) at core/window.c:2165
01462 #13 implement_showing (window=0x91ccfc8, showing=1) at core/window.c:1583
01463 #14 0x080879cc in meta_window_flush_calc_showing (window=0x91ccfc8) at core/window.c:1806'''
01464         r._gen_stacktrace_top()
01465         self.assertEqual(r['StacktraceTop'], '''meta_display_get_current_time_roundtrip (display=0x916d7d0) at core/display.c:1217
01466 meta_window_show (window=0x91ccfc8) at core/window.c:2165
01467 implement_showing (window=0x91ccfc8, showing=1) at core/window.c:1583
01468 meta_window_flush_calc_showing (window=0x91ccfc8) at core/window.c:1806''')
01469 
01470         # another XError (taken from LP#834403)
01471         r = apport.report.Report()
01472         r['Stacktrace'] = '''#0  g_logv (log_domain=0x7fd41db08a46 "Gdk", log_level=<optimized out>, format=0x7fd41db12e87 "%s", args1=0x7fff50bf0c18) at /build/buildd/glib2.0-2.29.16/./glib/gmessages.c:577
01473 #1  0x00007fd42006bb92 in g_log (log_domain=<optimized out>, log_level=<optimized out>, format=<optimized out>) at /build/buildd/glib2.0-2.29.16/./glib/gmessages.c:591
01474 #2  0x00007fd41dae86f3 in _gdk_x11_display_error_event (display=<optimized out>, error=<optimized out>) at /build/buildd/gtk+3.0-3.1.12/./gdk/x11/gdkdisplay-x11.c:2374
01475 #3  0x00007fd41daf5647 in gdk_x_error (error=0x7fff50bf0dc0, xdisplay=<optimized out>) at /build/buildd/gtk+3.0-3.1.12/./gdk/x11/gdkmain-x11.c:312
01476 #4  gdk_x_error (xdisplay=<optimized out>, error=0x7fff50bf0dc0) at /build/buildd/gtk+3.0-3.1.12/./gdk/x11/gdkmain-x11.c:275
01477 #5  0x00007fd41d5a301f in _XError (dpy=0x2425370, rep=<optimized out>) at ../../src/XlibInt.c:1583
01478 #6  0x00007fd41d59fdd1 in handle_error (dpy=0x2425370, err=0x7fd408707980, in_XReply=<optimized out>) at ../../src/xcb_io.c:212
01479 #7  0x00007fd41d5a0d27 in _XReply (dpy=0x2425370, rep=0x7fff50bf0f60, extra=0, discard=0) at ../../src/xcb_io.c:698
01480 #8  0x00007fd41d5852fb in XGetWindowProperty (dpy=0x2425370, w=0, property=348, offset=0, length=2, delete=<optimized out>, req_type=348, actual_type=0x7fff50bf1038, actual_format=0x7fff50bf105c, nitems=0x7fff50bf1040, bytesafter=0x7fff50bf1048, prop=0x7fff50bf1050) at ../../src/GetProp.c:61
01481 #9  0x00007fd41938269e in window_is_xembed (w=<optimized out>, d=<optimized out>) at canberra-gtk-module.c:373
01482 #10 dispatch_sound_event (d=0x32f6a30) at canberra-gtk-module.c:454
01483 #11 dispatch_queue () at canberra-gtk-module.c:815'''
01484         r._gen_stacktrace_top()
01485         self.assertEqual(r['StacktraceTop'], '''XGetWindowProperty (dpy=0x2425370, w=0, property=348, offset=0, length=2, delete=<optimized out>, req_type=348, actual_type=0x7fff50bf1038, actual_format=0x7fff50bf105c, nitems=0x7fff50bf1040, bytesafter=0x7fff50bf1048, prop=0x7fff50bf1050) at ../../src/GetProp.c:61
01486 window_is_xembed (w=<optimized out>, d=<optimized out>) at canberra-gtk-module.c:373
01487 dispatch_sound_event (d=0x32f6a30) at canberra-gtk-module.c:454
01488 dispatch_queue () at canberra-gtk-module.c:815''')
01489 
01490         # problem with too old gdb, only assertion, nothing else
01491         r = apport.report.Report()
01492         r['Stacktrace'] = '''#0  0x00987416 in __kernel_vsyscall ()
01493 No symbol table info available.
01494 #1  0x00ebecb1 in *__GI_raise (sig=6)
01495         selftid = 945
01496 #2  0x00ec218e in *__GI_abort () at abort.c:59
01497         save_stage = Unhandled dwarf expression opcode 0x9f
01498 '''
01499         r._gen_stacktrace_top()
01500         self.assertEqual(r['StacktraceTop'], '')
01501 
01502         # ignore uninteresting frames
01503         r = apport.report.Report()
01504         r['Stacktrace'] = '''#0  0x00987416 in __kernel_vsyscall ()
01505 #1  __strchr_sse42 () at strchr.S:97
01506 #2 h (p=0x0) at crash.c:25
01507 #3  0x100004c8 in g (x=1, y=42) at crash.c:26
01508 #4  0x10000999 in __memmove_ssse3 ()
01509 #5 f (x=1) at crash.c:27
01510 #6  0x10000530 in e (x=1) at crash.c:28
01511 #7  0x10000999 in __strlen_sse2_back () at strchr.S:42
01512 #8  0x10000530 in d (x=1) at crash.c:29
01513 #9  0x10000530 in c (x=1) at crash.c:30
01514 #10 0x10000550 in main () at crash.c:31
01515 '''
01516         r._gen_stacktrace_top()
01517         self.assertEqual(r['StacktraceTop'], '''h (p=0x0) at crash.c:25
01518 g (x=1, y=42) at crash.c:26
01519 f (x=1) at crash.c:27
01520 e (x=1) at crash.c:28
01521 d (x=1) at crash.c:29''')

has_useful_stacktrace().

Definition at line 1106 of file test_report.py.

01106 
01107     def test_has_useful_stacktrace(self):
01108         '''has_useful_stacktrace().'''
01109 
01110         r = apport.report.Report()
01111         self.assertFalse(r.has_useful_stacktrace())
01112 
01113         r['StacktraceTop'] = ''
01114         self.assertFalse(r.has_useful_stacktrace())
01115 
01116         r['StacktraceTop'] = '?? ()'
01117         self.assertFalse(r.has_useful_stacktrace())
01118 
01119         r['StacktraceTop'] = '?? ()\n?? ()'
01120         self.assertFalse(r.has_useful_stacktrace())
01121 
01122         r['StacktraceTop'] = 'read () from /lib/libc.6.so\n?? ()'
01123         self.assertFalse(r.has_useful_stacktrace())
01124 
01125         r['StacktraceTop'] = 'read () from /lib/libc.6.so\n?? ()\n?? ()\n?? ()'
01126         self.assertFalse(r.has_useful_stacktrace())
01127 
01128         r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
01129         self.assertTrue(r.has_useful_stacktrace())
01130 
01131         r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()'
01132         self.assertTrue(r.has_useful_stacktrace())
01133 
01134         r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()\n?? ()'
01135         self.assertTrue(r.has_useful_stacktrace())
01136 
01137         r['StacktraceTop'] = 'read () from /lib/libc.6.so\n?? ()\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()\n?? ()'
01138         self.assertFalse(r.has_useful_stacktrace())

mark_ignore() and check_ignored().

Definition at line 960 of file test_report.py.

00960 
00961     def test_ignoring(self):
00962         '''mark_ignore() and check_ignored().'''
00963 
00964         orig_ignore_file = apport.report.apport.report._ignore_file
00965         workdir = tempfile.mkdtemp()
00966         apport.report.apport.report._ignore_file = os.path.join(workdir, 'ignore.xml')
00967         try:
00968             with open(os.path.join(workdir, 'bash'), 'w') as fd:
00969                 fd.write('bash')
00970             with open(os.path.join(workdir, 'crap'), 'w') as fd:
00971                 fd.write('crap')
00972 
00973             bash_rep = apport.report.Report()
00974             bash_rep['ExecutablePath'] = os.path.join(workdir, 'bash')
00975             crap_rep = apport.report.Report()
00976             crap_rep['ExecutablePath'] = os.path.join(workdir, 'crap')
00977             # must be able to deal with executables that do not exist any more
00978             cp_rep = apport.report.Report()
00979             cp_rep['ExecutablePath'] = os.path.join(workdir, 'cp')
00980 
00981             # no ignores initially
00982             self.assertEqual(bash_rep.check_ignored(), False)
00983             self.assertEqual(crap_rep.check_ignored(), False)
00984             self.assertEqual(cp_rep.check_ignored(), False)
00985 
00986             # ignore crap now
00987             crap_rep.mark_ignore()
00988             self.assertEqual(bash_rep.check_ignored(), False)
00989             self.assertEqual(crap_rep.check_ignored(), True)
00990             self.assertEqual(cp_rep.check_ignored(), False)
00991 
00992             # ignore bash now
00993             bash_rep.mark_ignore()
00994             self.assertEqual(bash_rep.check_ignored(), True)
00995             self.assertEqual(crap_rep.check_ignored(), True)
00996             self.assertEqual(cp_rep.check_ignored(), False)
00997 
00998             # poke crap so that it has a newer timestamp
00999             time.sleep(1)
01000             with open(os.path.join(workdir, 'crap'), 'w') as fd:
01001                 fd.write('crapnew')
01002             self.assertEqual(bash_rep.check_ignored(), True)
01003             self.assertEqual(crap_rep.check_ignored(), False)
01004             self.assertEqual(cp_rep.check_ignored(), False)
01005 
01006             # do not complain about an empty ignore file
01007             with open(apport.report.apport.report._ignore_file, 'w') as fd:
01008                 fd.write('')
01009             self.assertEqual(bash_rep.check_ignored(), False)
01010             self.assertEqual(crap_rep.check_ignored(), False)
01011             self.assertEqual(cp_rep.check_ignored(), False)
01012 
01013             # does not crash if the executable went away under our feet
01014             crap_rep['ExecutablePath'] = '/non existing'
01015             crap_rep.mark_ignore()
01016             self.assertEqual(os.path.getsize(apport.report.apport.report._ignore_file), 0)
01017         finally:
01018             shutil.rmtree(workdir)
01019             apport.report.apport.report._ignore_file = orig_ignore_file

methods get along with non-ASCII data

Definition at line 1673 of file test_report.py.

01673 
01674     def test_nonascii_data(self):
01675         '''methods get along with non-ASCII data'''
01676 
01677         # fake os.uname() into reporting a non-ASCII name
01678         uname = os.uname()
01679         uname = (uname[0], b't\xe2\x99\xaax'.decode('UTF-8'), uname[2], uname[3], uname[4])
01680         orig_uname = os.uname
01681         os.uname = lambda: uname
01682 
01683         try:
01684             pr = apport.report.Report()
01685             utf8_val = b'\xc3\xa4 ' + uname[1].encode('UTF-8') + b' \xe2\x99\xa5 '
01686             pr['ProcUnicodeValue'] = utf8_val.decode('UTF-8')
01687             pr['ProcByteArrayValue'] = utf8_val
01688 
01689             pr.anonymize()
01690 
01691             exp_utf8 = b'\xc3\xa4 hostname \xe2\x99\xa5 '
01692             self.assertEqual(pr['ProcUnicodeValue'], exp_utf8.decode('UTF-8'))
01693             self.assertEqual(pr['ProcByteArrayValue'], exp_utf8)
01694         finally:
01695             os.uname = orig_uname

obsolete_packages().

Definition at line 1300 of file test_report.py.

01300 
01301     def test_obsolete_packages(self):
01302         '''obsolete_packages().'''
01303 
01304         report = apport.report.Report()
01305         self.assertEqual(report.obsolete_packages(), [])
01306 
01307         # should work without Dependencies
01308         report['Package'] = 'bash 0'
01309         self.assertEqual(report.obsolete_packages(), ['bash'])
01310         report['Package'] = 'bash 0 [modified: /bin/bash]'
01311         self.assertEqual(report.obsolete_packages(), ['bash'])
01312         report['Package'] = 'bash ' + apport.packaging.get_available_version('bash')
01313         self.assertEqual(report.obsolete_packages(), [])
01314 
01315         report['Dependencies'] = 'coreutils 0\ncron 0\n'
01316         self.assertEqual(report.obsolete_packages(), ['coreutils', 'cron'])
01317 
01318         report['Dependencies'] = 'coreutils %s [modified: /bin/mount]\ncron 0\n' % \
01319             apport.packaging.get_available_version('coreutils')
01320         self.assertEqual(report.obsolete_packages(), ['cron'])
01321 
01322         report['Dependencies'] = 'coreutils %s\ncron %s\n' % (
01323             apport.packaging.get_available_version('coreutils'),
01324             apport.packaging.get_available_version('cron'))
01325         self.assertEqual(report.obsolete_packages(), [])

search_bug_patterns().

Definition at line 719 of file test_report.py.

00719 
00720     def test_search_bug_patterns(self):
00721         '''search_bug_patterns().'''
00722 
00723         patterns = tempfile.NamedTemporaryFile(prefix='apport-')
00724         # create some test patterns
00725         patterns.write(b'''<?xml version="1.0"?>
00726 <patterns>
00727     <pattern url="http://bugtracker.net/bugs/1">
00728         <re key="Package">^bash </re>
00729         <re key="Foo">ba.*r</re>
00730     </pattern>
00731     <pattern url="http://bugtracker.net/bugs/2">
00732         <re key="Package">^bash 1-2$</re>
00733         <re key="Foo">write_(hello|goodbye)</re>
00734     </pattern>
00735     <pattern url="http://bugtracker.net/bugs/3">
00736         <re key="Package">^coreutils </re>
00737         <re key="Bar">^1$</re>
00738     </pattern>
00739     <pattern url="http://bugtracker.net/bugs/4">
00740         <re key="Package">^coreutils </re>
00741         <re></re>
00742         <re key="Bar">*</re> <!-- invalid RE -->
00743         <re key="broken">+[1^</re>
00744     </pattern>
00745     <pattern url="http://bugtracker.net/bugs/5">
00746         <re key="SourcePackage">^bazaar$</re>
00747         <re key="LogFile">AssertionError</re>
00748     </pattern>
00749 </patterns>''')
00750         patterns.flush()
00751 
00752         # invalid XML
00753         invalid = tempfile.NamedTemporaryFile(prefix='apport-')
00754         invalid.write(b'''<?xml version="1.0"?>
00755 </patterns>''')
00756         invalid.flush()
00757 
00758         # create some reports
00759         r_bash = apport.report.Report()
00760         r_bash['Package'] = 'bash 1-2'
00761         r_bash['Foo'] = 'bazaar'
00762 
00763         r_bazaar = apport.report.Report()
00764         r_bazaar['Package'] = 'bazaar 2-1'
00765         r_bazaar['SourcePackage'] = 'bazaar'
00766         r_bazaar['LogFile'] = 'AssertionError'
00767 
00768         r_coreutils = apport.report.Report()
00769         r_coreutils['Package'] = 'coreutils 1'
00770         r_coreutils['Bar'] = '1'
00771 
00772         r_invalid = apport.report.Report()
00773         r_invalid['Package'] = 'invalid 1'
00774 
00775         pattern_url = 'file://' + patterns.name
00776 
00777         # positive match cases
00778         self.assertEqual(r_bash.search_bug_patterns(pattern_url),
00779                          'http://bugtracker.net/bugs/1')
00780         r_bash['Foo'] = 'write_goodbye'
00781         self.assertEqual(r_bash.search_bug_patterns(pattern_url),
00782                          'http://bugtracker.net/bugs/2')
00783         self.assertEqual(r_coreutils.search_bug_patterns(pattern_url),
00784                          'http://bugtracker.net/bugs/3')
00785         self.assertEqual(r_bazaar.search_bug_patterns(pattern_url),
00786                          'http://bugtracker.net/bugs/5')
00787 
00788         # also works for CompressedValues
00789         r_bash_compressed = r_bash.copy()
00790         r_bash_compressed['Foo'] = problem_report.CompressedValue(b'bazaar')
00791         self.assertEqual(r_bash_compressed.search_bug_patterns(pattern_url),
00792                          'http://bugtracker.net/bugs/1')
00793 
00794         # negative match cases
00795         r_bash['Package'] = 'bash-static 1-2'
00796         self.assertEqual(r_bash.search_bug_patterns(pattern_url), None)
00797         r_bash['Package'] = 'bash 1-21'
00798         self.assertEqual(r_bash.search_bug_patterns(pattern_url), None,
00799                          'does not match on wrong bash version')
00800         r_bash['Foo'] = 'zz'
00801         self.assertEqual(r_bash.search_bug_patterns(pattern_url), None,
00802                          'does not match on wrong Foo value')
00803         r_coreutils['Bar'] = '11'
00804         self.assertEqual(r_coreutils.search_bug_patterns(pattern_url), None,
00805                          'does not match on wrong Bar value')
00806         r_bazaar['SourcePackage'] = 'launchpad'
00807         self.assertEqual(r_bazaar.search_bug_patterns(pattern_url), None,
00808                          'does not match on wrong source package')
00809         r_bazaar['LogFile'] = ''
00810         self.assertEqual(r_bazaar.search_bug_patterns(pattern_url), None,
00811                          'does not match on empty attribute')
00812 
00813         # various errors to check for robustness (no exceptions, just None
00814         # return value)
00815         del r_coreutils['Bar']
00816         self.assertEqual(r_coreutils.search_bug_patterns(pattern_url), None,
00817                          'does not match on nonexisting key')
00818         self.assertEqual(r_invalid.search_bug_patterns('file://' + invalid.name), None,
00819                          'gracefully handles invalid XML')
00820         r_coreutils['Package'] = 'other 2'
00821         self.assertEqual(r_bash.search_bug_patterns('file:///nonexisting/directory/'), None,
00822                          'gracefully handles nonexisting base path')
00823         # existing host, but no bug patterns
00824         self.assertEqual(r_bash.search_bug_patterns('http://security.ubuntu.com/'), None,
00825                          'gracefully handles base path without bug patterns')
00826         # nonexisting host
00827         self.assertEqual(r_bash.search_bug_patterns('http://nonexisting.domain/'), None,
00828                          'gracefully handles nonexisting URL domain')

standard_title().

Definition at line 1139 of file test_report.py.

01139 
01140     def test_standard_title(self):
01141         '''standard_title().'''
01142 
01143         report = apport.report.Report()
01144         self.assertEqual(report.standard_title(), None)
01145 
01146         # named signal crash
01147         report['Signal'] = '11'
01148         report['ExecutablePath'] = '/bin/bash'
01149         report['StacktraceTop'] = '''foo()
01150 bar(x=3)
01151 baz()
01152 '''
01153         self.assertEqual(report.standard_title(),
01154                          'bash crashed with SIGSEGV in foo()')
01155 
01156         # unnamed signal crash
01157         report['Signal'] = '42'
01158         self.assertEqual(report.standard_title(),
01159                          'bash crashed with signal 42 in foo()')
01160 
01161         # do not crash on empty StacktraceTop
01162         report['StacktraceTop'] = ''
01163         self.assertEqual(report.standard_title(),
01164                          'bash crashed with signal 42')
01165 
01166         # do not create bug title with unknown function name
01167         report['StacktraceTop'] = '??()\nfoo()'
01168         self.assertEqual(report.standard_title(),
01169                          'bash crashed with signal 42 in foo()')
01170 
01171         # if we do not know any function name, don't mention ??
01172         report['StacktraceTop'] = '??()\n??()'
01173         self.assertEqual(report.standard_title(),
01174                          'bash crashed with signal 42')
01175 
01176         # assertion message
01177         report['Signal'] = '6'
01178         report['ExecutablePath'] = '/bin/bash'
01179         report['AssertionMessage'] = 'foo.c:42 main: i > 0'
01180         self.assertEqual(report.standard_title(),
01181                          'bash assert failure: foo.c:42 main: i > 0')
01182 
01183         # Python crash
01184         report = apport.report.Report()
01185         report['ExecutablePath'] = '/usr/share/apport/apport-gtk'
01186         report['Traceback'] = '''Traceback (most recent call last):
01187 File "/usr/share/apport/apport-gtk", line 202, in <module>
01188 app.run_argv()
01189 File "/var/lib/python-support/python2.5/apport/ui.py", line 161, in run_argv
01190 self.run_crashes()
01191 File "/var/lib/python-support/python2.5/apport/ui.py", line 104, in run_crashes
01192 self.run_crash(f)
01193 File "/var/lib/python-support/python2.5/apport/ui.py", line 115, in run_crash
01194 response = self.ui_present_crash(desktop_entry)
01195 File "/usr/share/apport/apport-gtk", line 67, in ui_present_crash
01196 subprocess.call(['pgrep', '-x',
01197 NameError: global name 'subprocess' is not defined'''
01198         self.assertEqual(report.standard_title(),
01199                          "apport-gtk crashed with NameError in ui_present_crash(): global name 'subprocess' is not defined")
01200 
01201         # slightly weird Python crash
01202         report = apport.report.Report()
01203         report['ExecutablePath'] = '/usr/share/apport/apport-gtk'
01204         report['Traceback'] = '''TypeError: Cannot create a consistent method resolution
01205 order (MRO) for bases GObject, CanvasGroupableIface, CanvasGroupable'''
01206         self.assertEqual(report.standard_title(),
01207                          'apport-gtk crashed with TypeError: Cannot create a consistent method resolution')
01208 
01209         # Python crash with custom message
01210         report = apport.report.Report()
01211         report['ExecutablePath'] = '/usr/share/apport/apport-gtk'
01212         report['Traceback'] = '''Traceback (most recent call last):
01213   File "/x/foo.py", line 242, in setup_chooser
01214     raise "Moo"
01215 Mo?o[a-1]'''
01216 
01217         self.assertEqual(report.standard_title(), 'apport-gtk crashed with Mo?o[a-1] in setup_chooser()')
01218 
01219         # Python crash with custom message with newlines (LP #190947)
01220         report = apport.report.Report()
01221         report['ExecutablePath'] = '/usr/share/apport/apport-gtk'
01222         report['Traceback'] = '''Traceback (most recent call last):
01223   File "/x/foo.py", line 242, in setup_chooser
01224     raise "\nKey: "+key+" isn't set.\nRestarting AWN usually solves this issue\n"
01225 
01226 Key: /apps/avant-window-navigator/app/active_png isn't set.
01227 Restarting AWN usually solves this issue'''
01228 
01229         t = report.standard_title()
01230         self.assertTrue(t.startswith('apport-gtk crashed with'))
01231         self.assertTrue(t.endswith('setup_chooser()'))
01232 
01233         # Python crash at top level in module
01234         report = apport.report.Report()
01235         report['ExecutablePath'] = '/usr/bin/gnome-about'
01236         report['Traceback'] = '''Traceback (most recent call last):
01237   File "/usr/bin/gnome-about", line 30, in <module>
01238     import pygtk
01239   File "/usr/lib/pymodules/python2.6/pygtk.py", line 28, in <module>
01240     import nonexistent
01241 ImportError: No module named nonexistent
01242 '''
01243         self.assertEqual(report.standard_title(),
01244                          "gnome-about crashed with ImportError in /usr/lib/pymodules/python2.6/pygtk.py: No module named nonexistent")
01245 
01246         # Python crash at top level in main program
01247         report = apport.report.Report()
01248         report['ExecutablePath'] = '/usr/bin/dcut'
01249         report['Traceback'] = '''Traceback (most recent call last):
01250   File "/usr/bin/dcut", line 28, in <module>
01251     import nonexistent
01252 ImportError: No module named nonexistent
01253 '''
01254         self.assertEqual(report.standard_title(),
01255                          "dcut crashed with ImportError in __main__: No module named nonexistent")
01256 
01257         # package install problem
01258         report = apport.report.Report('Package')
01259         report['Package'] = 'bash'
01260 
01261         # no ErrorMessage
01262         self.assertEqual(report.standard_title(),
01263                          'package bash failed to install/upgrade')
01264 
01265         # empty ErrorMessage
01266         report['ErrorMessage'] = ''
01267         self.assertEqual(report.standard_title(),
01268                          'package bash failed to install/upgrade')
01269 
01270         # nonempty ErrorMessage
01271         report['ErrorMessage'] = 'botched\nnot found\n'
01272         self.assertEqual(report.standard_title(),
01273                          'package bash failed to install/upgrade: not found')
01274 
01275         # matching package/system architectures
01276         report['Signal'] = '11'
01277         report['ExecutablePath'] = '/bin/bash'
01278         report['StacktraceTop'] = '''foo()
01279 bar(x=3)
01280 baz()
01281 '''
01282         report['PackageArchitecture'] = 'amd64'
01283         report['Architecture'] = 'amd64'
01284         self.assertEqual(report.standard_title(),
01285                          'bash crashed with SIGSEGV in foo()')
01286 
01287         # non-native package (on multiarch)
01288         report['PackageArchitecture'] = 'i386'
01289         self.assertEqual(report.standard_title(),
01290                          'bash crashed with SIGSEGV in foo() [non-native i386 package]')
01291 
01292         # Arch: all package (matches every system architecture)
01293         report['PackageArchitecture'] = 'all'
01294         self.assertEqual(report.standard_title(),
01295                          'bash crashed with SIGSEGV in foo()')
01296 
01297         report = apport.report.Report('KernelOops')
01298         report['OopsText'] = '------------[ cut here ]------------\nkernel BUG at /tmp/oops.c:5!\ninvalid opcode: 0000 [#1] SMP'
01299         self.assertEqual(report.standard_title(), 'kernel BUG at /tmp/oops.c:5!')

Here is the call graph for this function:

check_ignored() for system-wise whitelist.

Definition at line 1063 of file test_report.py.

01063 
01064     def test_whitelisting(self):
01065         '''check_ignored() for system-wise whitelist.'''
01066 
01067         orig_whitelist_dir = apport.report._whitelist_dir
01068         apport.report._whitelist_dir = tempfile.mkdtemp()
01069         orig_ignore_file = apport.report.apport.report._ignore_file
01070         apport.report.apport.report._ignore_file = '/nonexistant'
01071         try:
01072             bash_rep = apport.report.Report()
01073             bash_rep['ExecutablePath'] = '/bin/bash'
01074             crap_rep = apport.report.Report()
01075             crap_rep['ExecutablePath'] = '/bin/crap'
01076 
01077             # no ignores without any whitelist
01078             self.assertEqual(bash_rep.check_ignored(), False)
01079             self.assertEqual(crap_rep.check_ignored(), False)
01080 
01081             # should not stumble over comments
01082             with open(os.path.join(apport.report._whitelist_dir, 'README'), 'w') as fd:
01083                 fd.write('# Ignore file\n#/bin/bash\n')
01084 
01085             # accepts matching paths
01086             with open(os.path.join(apport.report._whitelist_dir, 'wl1'), 'w') as fd:
01087                 fd.write('/bin/bash\n')
01088             self.assertEqual(bash_rep.check_ignored(), False)
01089             self.assertEqual(crap_rep.check_ignored(), True)
01090 
01091             # also accept crap now
01092             with open(os.path.join(apport.report._whitelist_dir, 'wl_2'), 'w') as fd:
01093                 fd.write('/bin/crap\n')
01094             self.assertEqual(bash_rep.check_ignored(), False)
01095             self.assertEqual(crap_rep.check_ignored(), False)
01096 
01097             # only complete matches accepted
01098             with open(os.path.join(apport.report._whitelist_dir, 'wl1'), 'w') as fd:
01099                 fd.write('/bin/bas\n/bin/bashh\nbash\n')
01100             self.assertEqual(bash_rep.check_ignored(), True)
01101             self.assertEqual(crap_rep.check_ignored(), False)
01102         finally:
01103             shutil.rmtree(apport.report._whitelist_dir)
01104             apport.report._whitelist_dir = orig_whitelist_dir
01105             apport.report.apport.report._ignore_file = orig_ignore_file


The documentation for this class was generated from the following file: