#include <map>
#include <memory>
#include <utility>
+#if LLVM_ON_UNIX
+#include <unistd.h> // getpid
+#endif
using namespace clang::driver;
using namespace clang;
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-<VERSION>_<YYYY-MM-DD-HHMMSS>_<hostname>.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<std::unique_ptr<llvm::MemoryBuffer>> 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.
"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.
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 += "_<YYYY-MM-DD-HHMMSS>_<hostname>.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();