From 02681c4af6a802e12fe4dcfc8026a8942b7042c8 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Thu, 17 Nov 2016 21:41:22 +0000 Subject: [PATCH] [CrashReproducer][Darwin] Suggest attaching .crash diagnostic file In addition to the preprocessed sources file and reproducer script, also point to the .crash diagnostic files on Darwin. Example: PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT: Preprocessed source(s) and associated run script(s) are located at: clang-4.0: note: diagnostic msg: /var/folders/bk/1hj20g8j4xvdj5gd25ywhd3m0000gq/T/RegAllocGreedy-238f28.cpp clang-4.0: note: diagnostic msg: /var/folders/bk/1hj20g8j4xvdj5gd25ywhd3m0000gq/T/RegAllocGreedy-238f28.cache clang-4.0: note: diagnostic msg: /var/folders/bk/1hj20g8j4xvdj5gd25ywhd3m0000gq/T/RegAllocGreedy-238f28.sh clang-4.0: note: diagnostic msg: /var/folders/bk/1hj20g8j4xvdj5gd25ywhd3m0000gq/T/RegAllocGreedy-238f28.crash When no match is found for the .crash, point the user to a directory where those can be found. Example: clang-4.0: note: diagnostic msg: Crash backtrace is located in clang-4.0: note: diagnostic msg: /Users/bruno/Library/Logs/DiagnosticReports/clang-4.0__.crash clang-4.0: note: diagnostic msg: (choose the .crash file that corresponds to your crash) rdar://problem/27286266 llvm-svn: 287262 --- clang/include/clang/Driver/Driver.h | 14 ++++ clang/lib/Driver/Driver.cpp | 115 +++++++++++++++++++++++++++++ clang/test/Driver/crash-report-crashfile.m | 17 +++++ 3 files changed, 146 insertions(+) create mode 100644 clang/test/Driver/crash-report-crashfile.m diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 9104658..e60be4f 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -246,6 +246,20 @@ private: void generatePrefixedToolNames(StringRef Tool, const ToolChain &TC, SmallVectorImpl &Names) const; + /// \brief Find the appropriate .crash diagonostic file for the child crash + /// under this driver and copy it out to a temporary destination with the + /// other reproducer related files (.sh, .cache, etc). If not found, suggest a + /// directory for the user to look at. + /// + /// \param The file path to copy the .crash to. + /// \param The suggested directory for the user to look at in case the search + /// or copy fails. + /// + /// \returns If the .crash is found and successfully copied return true, + /// otherwise false and return the suggested directory in \p CrashDiagDir. + bool getCrashDiagnosticFile(StringRef ReproCrashFilename, + SmallString<128> &CrashDiagDir); + public: Driver(StringRef ClangExecutable, StringRef DefaultTargetTriple, DiagnosticsEngine &Diags, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 2e76623..ffecccc 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -42,6 +42,9 @@ #include #include #include +#if LLVM_ON_UNIX +#include // getpid +#endif using namespace clang::driver; using namespace clang; @@ -695,6 +698,95 @@ static void printArgList(raw_ostream &OS, const llvm::opt::ArgList &Args) { OS << '\n'; } +bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename, + SmallString<128> &CrashDiagDir) { + using namespace llvm::sys; + assert(llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin() && + "Only knows about .crash files on Darwin"); + + // The .crash file can be found on at ~/Library/Logs/DiagnosticReports/ + // (or /Library/Logs/DiagnosticReports for root) and has the filename pattern + // clang-__.crash. + path::home_directory(CrashDiagDir); + if (CrashDiagDir.startswith("/var/root")) + CrashDiagDir = "/"; + path::append(CrashDiagDir, "Library/Logs/DiagnosticReports"); + int PID = +#if LLVM_ON_UNIX + getpid(); +#else + 0; +#endif + std::error_code EC; + fs::file_status FileStatus; + TimePoint<> LastAccessTime; + SmallString<128> CrashFilePath; + // Lookup the .crash files and get the one generated by a subprocess spawned + // by this driver invocation. + for (fs::directory_iterator File(CrashDiagDir, EC), FileEnd; + File != FileEnd && !EC; File.increment(EC)) { + StringRef FileName = path::filename(File->path()); + if (!FileName.startswith(Name)) + continue; + if (fs::status(File->path(), FileStatus)) + continue; + llvm::ErrorOr> CrashFile = + llvm::MemoryBuffer::getFile(File->path()); + if (!CrashFile) + continue; + // The first line should start with "Process:", otherwise this isn't a real + // .crash file. + StringRef Data = CrashFile.get()->getBuffer(); + if (!Data.startswith("Process:")) + continue; + // Parse parent process pid line, e.g: "Parent Process: clang-4.0 [79141]" + size_t ParentProcPos = Data.find("Parent Process:"); + if (ParentProcPos == StringRef::npos) + continue; + size_t LineEnd = Data.find_first_of("\n", ParentProcPos); + if (LineEnd == StringRef::npos) + continue; + StringRef ParentProcess = Data.slice(ParentProcPos+15, LineEnd).trim(); + int OpenBracket = -1, CloseBracket = -1; + for (size_t i = 0, e = ParentProcess.size(); i < e; ++i) { + if (ParentProcess[i] == '[') + OpenBracket = i; + if (ParentProcess[i] == ']') + CloseBracket = i; + } + // Extract the parent process PID from the .crash file and check whether + // it matches this driver invocation pid. + int CrashPID; + if (OpenBracket < 0 || CloseBracket < 0 || + ParentProcess.slice(OpenBracket + 1, CloseBracket) + .getAsInteger(10, CrashPID) || CrashPID != PID) { + continue; + } + + // Found a .crash file matching the driver pid. To avoid getting an older + // and misleading crash file, continue looking for the most recent. + // FIXME: the driver can dispatch multiple cc1 invocations, leading to + // multiple crashes poiting to the same parent process. Since the driver + // does not collect pid information for the dispatched invocation there's + // currently no way to distinguish among them. + const auto FileAccessTime = FileStatus.getLastModificationTime(); + if (FileAccessTime > LastAccessTime) { + CrashFilePath.assign(File->path()); + LastAccessTime = FileAccessTime; + } + } + + // If found, copy it over to the location of other reproducer files. + if (!CrashFilePath.empty()) { + EC = fs::copy_file(CrashFilePath, ReproCrashFilename); + if (EC) + return false; + return true; + } + + return false; +} + // When clang crashes, produce diagnostic information including the fully // preprocessed source file(s). Request that the developer attach the // diagnostic information to a bug report. @@ -822,8 +914,13 @@ void Driver::generateCompilationDiagnostics(Compilation &C, "Preprocessed source(s) and associated run script(s) are located at:"; SmallString<128> VFS; + SmallString<128> ReproCrashFilename; for (const char *TempFile : TempFiles) { Diag(clang::diag::note_drv_command_failed_diag_msg) << TempFile; + if (ReproCrashFilename.empty()) { + ReproCrashFilename = TempFile; + llvm::sys::path::replace_extension(ReproCrashFilename, ".crash"); + } if (StringRef(TempFile).endswith(".cache")) { // In some cases (modules) we'll dump extra data to help with reproducing // the crash into a directory next to the output. @@ -851,6 +948,24 @@ void Driver::generateCompilationDiagnostics(Compilation &C, Diag(clang::diag::note_drv_command_failed_diag_msg) << Script; } + // On darwin, provide information about the .crash diagnostic report. + if (llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin()) { + SmallString<128> CrashDiagDir; + if (getCrashDiagnosticFile(ReproCrashFilename, CrashDiagDir)) { + Diag(clang::diag::note_drv_command_failed_diag_msg) + << ReproCrashFilename.str(); + } else { // Suggest a directory for the user to look for .crash files. + llvm::sys::path::append(CrashDiagDir, Name); + CrashDiagDir += "__.crash"; + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "Crash backtrace is located in"; + Diag(clang::diag::note_drv_command_failed_diag_msg) + << CrashDiagDir.str(); + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "(choose the .crash file that corresponds to your crash)"; + } + } + for (const auto &A : C.getArgs().filtered(options::OPT_frewrite_map_file, options::OPT_frewrite_map_file_EQ)) Diag(clang::diag::note_drv_command_failed_diag_msg) << A->getValue(); diff --git a/clang/test/Driver/crash-report-crashfile.m b/clang/test/Driver/crash-report-crashfile.m new file mode 100644 index 0000000..71f6e7a --- /dev/null +++ b/clang/test/Driver/crash-report-crashfile.m @@ -0,0 +1,17 @@ +// REQUIRES: crash-recovery, shell, system-darwin +// RUN: rm -rf %t +// RUN: mkdir -p %t/i %t/m %t + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -I %S/Inputs/module -isysroot %/t/i/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ -DFOO=BAR 2>&1 | FileCheck %s + +@import simple; +const int x = MODULE_MACRO; + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m +// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache +// CHECK-NEXT: note: diagnostic msg: {{.*}}.sh +// CHECK-NEXT: note: diagnostic msg: Crash backtrace is located in +// CHECK-NEXT: note: diagnostic msg: {{.*}}Library/Logs/DiagnosticReports{{.*}} -- 2.7.4