diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_mac.h b/compiler-rt/lib/sanitizer_common/sanitizer_mac.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_mac.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_mac.h @@ -64,21 +64,8 @@ } // namespace __sanitizer -extern "C" { -static char __crashreporter_info_buff__[__sanitizer::kErrorMessageBufferSize] = - {}; -static const char *__crashreporter_info__ __attribute__((__used__)) = - &__crashreporter_info_buff__[0]; -asm(".desc ___crashreporter_info__, 0x10"); -} // extern "C" - namespace __sanitizer { -static BlockingMutex crashreporter_info_mutex(LINKER_INITIALIZED); - -inline void CRAppendCrashLogMessage(const char *msg) { - BlockingMutexLock l(&crashreporter_info_mutex); - internal_strlcat(__crashreporter_info_buff__, msg, - sizeof(__crashreporter_info_buff__)); } +inline void CRAppendCrashLogMessage(const char *msg); } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp @@ -779,6 +779,48 @@ #endif } +static char __crashreporter_info_buff__[__sanitizer::kErrorMessageBufferSize] = + {}; +// Integrate with crash reporter libraries. +#if defined(__has_include) && __has_include() +#define HAVE_CRASHREPORTERCLIENT_H 1 +#include +extern "C" { +CRASH_REPORTER_CLIENT_HIDDEN +struct crashreporter_annotations_t gCRAnnotations + __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) = { + CRASHREPORTER_ANNOTATIONS_VERSION, + 0, + 0, + 0, + 0, + 0, + 0, +#if CRASHREPORTER_ANNOTATIONS_VERSION > 4 + 0, +#endif +}; +} +#else +// fall back to old crashreporter api +extern "C" { +static const char *__crashreporter_info__ __attribute__((__used__)) = + &__crashreporter_info_buff__[0]; +asm(".desc ___crashreporter_info__, 0x10"); +} // extern "C" + +#endif + +inline void CRAppendCrashLogMessage(const char *msg) { + static BlockingMutex crashreporter_info_mutex(LINKER_INITIALIZED); +#ifdef HAVE_CRASHREPORTERCLIENT_H + (void)CRSetCrashLogMessage(__crashreporter_info_buff__); +#endif + BlockingMutexLock l(&crashreporter_info_mutex); + internal_strlcat(__crashreporter_info_buff__, msg, + sizeof(__crashreporter_info_buff__)); +} + void LogMessageOnPrintf(const char *str) { // Log all printf output to CrashLog. if (common_flags()->abort_on_error) diff --git a/compiler-rt/test/asan/TestCases/Darwin/asan_log_to_crashreporter.cpp b/compiler-rt/test/asan/TestCases/Darwin/asan_log_to_crashreporter.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Darwin/asan_log_to_crashreporter.cpp @@ -0,0 +1,22 @@ +// Check that with empty ASAN_OPTIONS, ASan reports on OS X actually crash +// the process (abort_on_error=1). See also Linux/abort_on_error.cpp. + +// RUN: %clangxx_asan %s -o %t + +// crash hard so the crashlog is created. +// RUN: %env_asan_opts=abort_on_error=1 not --crash %run %t > %t.process_output.txt 2>&1 +// RUN: %ios_command get_pid_from_output.py --infile=%t.process_output.txt \ +// RUN: | %ios_command print_crashreport_for_pid.py --filename=%basename_t.tmp \ +// RUN: | FileCheck %s --check-prefixes CHECK-CRASHLOG + +#include +int main() { + char *x = (char *)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // needs to crash hard so the crashlog exists... + // CHECK-CRASHLOG: {{.*Application Specific Information:}} + // CHECK-CRASHLOG-NEXT: {{=====}} + // CHECK-CRASHLOG-NEXT: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK-CRASHLOG: {{abort()}} +} diff --git a/compiler-rt/test/lit.common.cfg.py b/compiler-rt/test/lit.common.cfg.py --- a/compiler-rt/test/lit.common.cfg.py +++ b/compiler-rt/test/lit.common.cfg.py @@ -134,6 +134,9 @@ emulator = get_lit_conf('emulator', None) +def get_ios_commands_dir(): + return os.path.join(config.compiler_rt_src_root, "test", "sanitizer_common", "ios_commands") + # Allow tests to be executed on a simulator or remotely. if emulator: config.substitutions.append( ('%run', emulator) ) @@ -173,7 +176,7 @@ if config.apple_platform != "ios" and config.apple_platform != "iossim": config.available_features.add(config.apple_platform) - ios_commands_dir = os.path.join(config.compiler_rt_src_root, "test", "sanitizer_common", "ios_commands") + ios_commands_dir = get_ios_commands_dir() run_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_run.py") env_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_env.py") @@ -591,3 +594,6 @@ config.clang = " " + " ".join(run_wrapper + [config.compile_wrapper, config.clang]) + " " config.target_cflags = " " + " ".join(target_cflags + extra_cflags) + " " + +if config.host_os == 'Darwin': + config.substitutions.append(("%ios_command ", "python {}/".format(get_ios_commands_dir()))) \ No newline at end of file diff --git a/compiler-rt/test/sanitizer_common/ios_commands/get_pid_from_output.py b/compiler-rt/test/sanitizer_common/ios_commands/get_pid_from_output.py new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/ios_commands/get_pid_from_output.py @@ -0,0 +1,34 @@ +import sys, argparse, re + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help='The sanitizer output to get the pid from') + parser.add_argument('--outfile', nargs='?', type=argparse.FileType('r'), default=sys.stdout, help='Where to write the result') + args = parser.parse_args() + + pid = process_file(args.infile) + args.outfile.write(pid) + args.infile.close() + args.outfile.close() + + + +def process_file(infile): + # check first line is just ==== divider + first_line_pattern = re.compile(r'=*') + assert first_line_pattern.match(infile.readline()) + + # parse out pid from 2nd line + # `==PID==ERROR: SanitizerName: error-type on address...` + pid_pattern = re.compile(r'==([0-9]*)==ERROR:') + pid = pid_pattern.search(infile.readline()).group(1) + + # ignore the rest + + assert pid and pid.isdigit() + + return pid + +if __name__ == '__main__': + main() diff --git a/compiler-rt/test/sanitizer_common/ios_commands/print_crashreport_for_pid.py b/compiler-rt/test/sanitizer_common/ios_commands/print_crashreport_for_pid.py new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/ios_commands/print_crashreport_for_pid.py @@ -0,0 +1,48 @@ +import sys, os, argparse, re, glob, shutil, time + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--pid', type=str, nargs='?', default=None, help='The process id of the process that crashed') + parser.add_argument('--filename', type=str, help='The name of the file that crashed') + parser.add_argument('--dir', nargs='?', type=str, default="~/Library/Logs/DiagnosticReports", help='The directory to look for the crash report') + parser.add_argument('--outfile', nargs='?', type=argparse.FileType('r'), default=sys.stdout, help='Where to write the result') + args = parser.parse_args() + + os.chdir(os.path.expanduser(args.dir)) + if args.pid: + pid = args.pid + else: + pid = sys.stdin.read() + + output_report_with_retries(args.outfile, pid.strip(), args.filename) + +def output_report_with_retries(outfile, pid, filename, retry_count=10): + report_name = find_report_in_cur_dir(pid, filename) + if report_name: + with open(report_name, "r") as f: + shutil.copyfileobj(f, outfile) + return + elif(retry_count > 0): + time.sleep(2.0 / retry_count) + output_report_with_retries(outfile, pid, filename, retry_count - 1) + else: + raise RuntimeError("Report not found for ({}, {}).".format(filename, pid)) + +def find_report_in_cur_dir(pid, filename): + for report_name in sorted(glob.glob("{}_*.crash".format(filename)), reverse=True): + # parse out pid from first line of report + # `Process: filename [pid]`` + with open(report_name) as cur_report: + pattern = re.compile(r'Process: *{} \[([0-9]*)\]'.format(filename)) + cur_report_pid = pattern.search(cur_report.readline()).group(1) + + assert cur_report_pid and cur_report_pid.isdigit() + if cur_report_pid == pid: + return report_name + + # did not find the crash report + return None + + +if __name__ == '__main__': + main()