1# Copyright Martin J. Bligh, Andy Whitcroft, 2006
2#
3# Shell class for a test, inherited by all individual tests
4#
5# Methods:
6#       __init__        initialise
7#       initialize      run once for each job
8#       setup           run once for each new version of the test installed
9#       run             run the test (wrapped by job.run_test())
10#
11# Data:
12#       job             backreference to the job this test instance is part of
13#       outputdir       eg. results/<job>/<testname.tag>
14#       resultsdir      eg. results/<job>/<testname.tag>/results
15#       profdir         eg. results/<job>/<testname.tag>/profiling
16#       debugdir        eg. results/<job>/<testname.tag>/debug
17#       bindir          eg. tests/<test>
18#       src             eg. tests/<test>/src
19#       tmpdir          eg. tmp/<testname.tag>
20
21import os, logging, resource, glob
22
23from autotest_lib.client.common_lib import utils
24from autotest_lib.client.common_lib import test as common_test
25from autotest_lib.client.bin import os_dep
26
27
28class test(common_test.base_test):
29    # Segmentation fault handling is something that is desirable only for
30    # client side tests.
31    def configure_crash_handler(self):
32        """
33        Configure the crash handler by:
34         * Setting up core size to unlimited
35         * Putting an appropriate crash handler on /proc/sys/kernel/core_pattern
36         * Create files that the crash handler will use to figure which tests
37           are active at a given moment
38
39        The crash handler will pick up the core file and write it to
40        self.debugdir, and perform analysis on it to generate a report. The
41        program also outputs some results to syslog.
42
43        If multiple tests are running, an attempt to verify if we still have
44        the old PID on the system process table to determine whether it is a
45        parent of the current test execution. If we can't determine it, the
46        core file and the report file will be copied to all test debug dirs.
47        """
48        self.crash_handling_enabled = False
49
50        # make sure this script will run with a new enough python to work
51        cmd = ("python -c 'import sys; "
52               "print sys.version_info[0], sys.version_info[1]'")
53        result = utils.run(cmd, ignore_status=True)
54        if result.exit_status != 0:
55            logging.warning('System python is too old, crash handling disabled')
56            return
57        major, minor = [int(x) for x in result.stdout.strip().split()]
58        if (major, minor) < (2, 4):
59            logging.warning('System python is too old, crash handling disabled')
60            return
61
62        self.pattern_file = '/proc/sys/kernel/core_pattern'
63        try:
64            # Enable core dumps
65            resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
66            # Trying to backup core pattern and register our script
67            self.core_pattern_backup = open(self.pattern_file, 'r').read()
68            pattern_file = open(self.pattern_file, 'w')
69            tools_dir = os.path.join(self.autodir, 'tools')
70            crash_handler_path = os.path.join(tools_dir, 'crash_handler.py')
71            pattern_file.write('|' + crash_handler_path + ' %p %t %u %s %h %e')
72            # Writing the files that the crash handler is going to use
73            self.debugdir_tmp_file = ('/tmp/autotest_results_dir.%s' %
74                                      os.getpid())
75            utils.open_write_close(self.debugdir_tmp_file, self.debugdir + "\n")
76        except Exception, e:
77            logging.warning('Crash handling disabled: %s', e)
78        else:
79            self.crash_handling_enabled = True
80            try:
81                os_dep.command('gdb')
82            except ValueError:
83                logging.warning('Could not find GDB installed. Crash handling '
84                                'will operate with limited functionality')
85            logging.debug('Crash handling enabled')
86
87
88    def crash_handler_report(self):
89        """
90        If core dumps are found on the debugdir after the execution of the
91        test, let the user know.
92        """
93        if self.crash_handling_enabled:
94            # Remove the debugdir info file
95            os.unlink(self.debugdir_tmp_file)
96            # Restore the core pattern backup
97            try:
98                utils.open_write_close(self.pattern_file,
99                                       self.core_pattern_backup)
100            except EnvironmentError:
101                pass
102            # Let the user know if core dumps were generated during the test
103            core_dirs = glob.glob('%s/crash.*' % self.debugdir)
104            if core_dirs:
105                logging.warning('Programs crashed during test execution')
106                for dir in core_dirs:
107                    logging.warning('Please verify %s for more info', dir)
108
109
110def runtest(job, url, tag, args, dargs):
111    # Leave some autotest bread crumbs in the system logs.
112    utils.system('logger "autotest runtest %s"' % url, ignore_status=True)
113    common_test.runtest(job, url, tag, args, dargs, locals(), globals(),
114                        job.sysinfo.log_before_each_test,
115                        job.sysinfo.log_after_each_test,
116                        job.sysinfo.log_before_each_iteration,
117                        job.sysinfo.log_after_each_iteration)
118