* Fixed a bug on get_results_dir list (wrong indent level of a return statement) * Use GDB also to determine information about the core dump * Keep a copy of the core file on a temp directory for safety * After all reporting is done, compress core files generated to save space (they are usually pretty large)
Signed-off-by: Lucas Meneghel Rodrigues <[email protected]> --- client/tools/crash_handler.py | 141 +++++++++++++++++++++++------------------ 1 files changed, 80 insertions(+), 61 deletions(-) diff --git a/client/tools/crash_handler.py b/client/tools/crash_handler.py index 9a94510..7ee9a78 100755 --- a/client/tools/crash_handler.py +++ b/client/tools/crash_handler.py @@ -5,7 +5,7 @@ Simple crash handling application for autotest @copyright Red Hat Inc 2009 @author Lucas Meneghel Rodrigues <[email protected]> """ -import sys, os, commands, glob, tempfile, shutil, syslog +import sys, os, commands, glob, tempfile, shutil, syslog, re, time def get_parent_pid(pid): @@ -24,17 +24,30 @@ def get_parent_pid(pid): return ppid -def write_to_file(file_path, contents): +def write_to_file(filename, data, compress=False): """ Write contents to a given file path specified. If not specified, the file - will be created. + will be created. Optionally, compress the destination file. @param file_path: Path to a given file. - @param contents: File contents. + @param data: File contents. + @param compress: Whether the file is going to be compressed at the end of + the data write process. """ - file_object = open(file_path, 'w') - file_object.write(contents) - file_object.close() + f = open(filename, 'w') + try: + f.write(data) + finally: + f.close() + + if compress: + s, o = commands.getstatusoutput('bzip2 %s' % filename) + if s: + syslog.syslog("File %s compression failed: %s" % (filename, o)) + else: + filename += '.bz2' + + return filename def get_results_dir_list(pid, core_dir_basename): @@ -68,9 +81,9 @@ def get_results_dir_list(pid, core_dir_basename): else: results_dir_list = pid_dir_dict.values() - return (results_dir_list or - pid_dir_dict.values() or - [os.path.join("/tmp", core_dir_basename)]) + return (results_dir_list or + pid_dir_dict.values() or + [os.path.join("/tmp", core_dir_basename)]) def get_info_from_core(path): @@ -80,17 +93,22 @@ def get_info_from_core(path): @param path: Path to core file. """ - # Here we are getting the executable full path in a very inelegant way :( - # Since the 'right' solution for it is to make a library to get information - # from core dump files, properly written, I'll leave this as it is for now. - full_exe_path = commands.getoutput('strings %s | grep "_="' % - path).strip("_=") - if full_exe_path.startswith("./"): - pwd = commands.getoutput('strings %s | grep "^PWD="' % - path).strip("PWD=") - full_exe_path = os.path.join(pwd, full_exe_path.strip("./")) + full_exe_path = None + output = commands.getoutput('gdb -c %s batch' % path) + path_pattern = re.compile("Core was generated by `([^\0]+)'", re.IGNORECASE) + match = re.findall(path_pattern, output) + for m in match: + # Sometimes the command line args come with the core, so get rid of them + m = m.split(" ")[0] + if os.path.isfile(m): + full_exe_path = m + break - return {'core_file': path, 'full_exe_path': full_exe_path} + if full_exe_path is None: + syslog.syslog("Could not determine from which application core file %s " + "is from" % path) + + return {'full_exe_path': full_exe_path} if __name__ == "__main__": @@ -99,7 +117,7 @@ if __name__ == "__main__": try: full_functionality = False try: - (crashed_pid, time, uid, signal, hostname, exe) = sys.argv[1:] + (crashed_pid, crash_time, uid, signal, hostname, exe) = sys.argv[1:] full_functionality = True except ValueError, e: # Probably due a kernel bug, we can't exactly map the parameters @@ -128,74 +146,75 @@ if __name__ == "__main__": # Write the core file to the appropriate directory # (we are piping it to this script) core_file = sys.stdin.read() - # Write the core file to its temporary location - write_to_file(core_tmp_path, core_file) + # Write the core file to its temporary location, let's keep it + # there in case something goes wrong + core_tmp_path = write_to_file(core_tmp_path, core_file) + processing_succeed = False if not full_functionality: - syslog.syslog(syslog.LOG_INFO, "Writing core files to %s" % + syslog.syslog("Writing core files to %s" % current_results_dir_list) for result_dir in current_results_dir_list: if not os.path.isdir(result_dir): os.makedirs(result_dir) core_path = os.path.join(result_dir, 'core') - write_to_file(core_path, core_file) + core_path = write_to_file(core_path, core_file, + compress=True) + processing_succeed = True raise ValueError("Incorrect params passed to handler " "script: %s." % sys.argv[1:]) - # Write a command file for GDB - gdb_command = 'bt full\n' - write_to_file(gdb_command_path, gdb_command) - # Get full command path exe_path = get_info_from_core(core_tmp_path)['full_exe_path'] - # Take a backtrace from the running program - gdb_cmd = ('gdb -e %s -c %s -x %s -n -batch -quiet' % - (exe_path, core_tmp_path, gdb_command_path)) - backtrace = commands.getoutput(gdb_cmd) - # Sanitize output before passing it to the report - backtrace = backtrace.decode('utf-8', 'ignore') + if exe_path is not None: + # Write a command file for GDB + gdb_command = 'bt full\n' + write_to_file(gdb_command_path, gdb_command) + + # Take a backtrace from the running program + gdb_cmd = ('gdb -e %s -c %s -x %s -n -batch -quiet' % + (exe_path, core_tmp_path, gdb_command_path)) + backtrace = commands.getoutput(gdb_cmd) + # Sanitize output before passing it to the report + backtrace = backtrace.decode('utf-8', 'ignore') + else: + exe_path = "Unknown" + backtrace = ("Could not determine backtrace for core file %s" % + core_tmp_path) # Composing the format_dict - format_dict = {} - format_dict['program'] = exe_path - format_dict['pid'] = crashed_pid - format_dict['signal'] = signal - format_dict['hostname'] = hostname - format_dict['time'] = time - format_dict['backtrace'] = backtrace - - report = """Autotest crash report - -Program: %(program)s -PID: %(pid)s -Signal: %(signal)s -Hostname: %(hostname)s -Time of the crash: %(time)s -Program backtrace: -%(backtrace)s -""" % format_dict - - syslog.syslog(syslog.LOG_INFO, - "Application %s, PID %s crashed" % + report = "Program: %s\n" % exe_path + report += "PID: %s\n" % crashed_pid + report += "Signal: %s\n" % signal + report += "Hostname: %s\n" % hostname + report += "Time of the crash: %s\n" % time.ctime(float(crash_time)) + report += "Program backtrace:\n%s\n" % backtrace + + syslog.syslog("Application %s, PID %s crashed" % (exe_path, crashed_pid)) # Now, for all results dir, let's create the directory if it doesn't # exist, and write the core file and the report to it. - syslog.syslog(syslog.LOG_INFO, - "Writing core files and reports to %s" % + syslog.syslog("Writing core files and reports to %s" % current_results_dir_list) for result_dir in current_results_dir_list: if not os.path.isdir(result_dir): os.makedirs(result_dir) core_path = os.path.join(result_dir, 'core') - write_to_file(core_path, core_file) + core_path = write_to_file(core_path, core_file, compress=True) report_path = os.path.join(result_dir, 'report') write_to_file(report_path, report) + processing_succeed = True except Exception, e: syslog.syslog("Crash handler had a problem: %s" % e) finally: - if os.path.isdir(core_tmp_dir): - shutil.rmtree(core_tmp_dir) + if processing_succeed: + if os.path.isdir(core_tmp_dir): + shutil.rmtree(core_tmp_dir) + else: + syslog.syslog("Crash handler failed to process the core file. " + "A copy of the file was kept at %s" % + core_tmp_path) -- 1.7.0.1 _______________________________________________ Autotest mailing list [email protected] http://test.kernel.org/cgi-bin/mailman/listinfo/autotest
