Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -72,6 +72,10 @@ constants that are not representable in a casted value. For example, ``(float) f == 0.1`` is always false. +- Crash diagnostics are now aggregated into a single tar file instead of + being split across many temp files. Moreover, input files are written into + the output tar file as well. + Non-comprehensive list of changes in this release ------------------------------------------------- Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -90,8 +90,10 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/TarWriter.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" +#include #include #include #include @@ -1390,6 +1392,34 @@ return false; } +static std::pair, std::string> +openReproTarFile(Driver &D, Compilation &C) { + SmallString<64> TmpName; + if (Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_dir)) + TmpName = A->getValue(); + else + llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, TmpName); + + if (!D.getVFS().exists(TmpName)) + llvm::sys::fs::create_directories(TmpName); + + llvm::sys::path::append(TmpName, "%%%%%%.tar"); + std::error_code EC = llvm::sys::fs::createUniqueFile(TmpName, TmpName); + if (EC) { + D.Diag(clang::diag::err_unable_to_make_temp) << EC.message(); + return {}; + } + + auto TarWriterOrErr = + llvm::TarWriter::create(TmpName, llvm::sys::path::stem(TmpName)); + if (!TarWriterOrErr) { + D.Diag(clang::diag::err_unable_to_make_temp) << TmpName; + return {}; + } + + return {std::move(*TarWriterOrErr), TmpName.c_str()}; +} + // 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. @@ -1504,15 +1534,46 @@ return; } - Diag(clang::diag::note_drv_command_failed_diag_msg) - << "\n********************\n\n" - "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n" - "Preprocessed source(s) and associated run script(s) are located at:"; + std::unique_ptr TarWriter; + std::string ReproFileName; + std::tie(TarWriter, ReproFileName) = openReproTarFile(*this, C); + if (!TarWriter) { + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "Error generating preprocessed source(s). - cannot open tar file"; + return; + } + + std::function WriteFileToTar = + [&WriteFileToTar, &TarWriter](StringRef FSPath, std::string TarPath) { + if (llvm::sys::fs::is_directory(FSPath)) { + using llvm::sys::fs::recursive_directory_iterator; + std::error_code EC; + for (recursive_directory_iterator I{FSPath, EC}, E; I != E && !EC; + I.increment(EC)) { + // Skip over the base path of the dir. i.e., if + // WriteFileToTar("a/b/dir", "tmp/"), where dir has "c" and "d" + // "a/b/dir/c" -> "dir/", so this will be put into the tar file at + // "tmp/dir/c" and "tmp/dir/d". + std::string Path = I->path(); + Path = Path.data() + FSPath.size() + 1; + llvm::SmallString<32> Prefix{Path.begin(), Path.end()}; + llvm::sys::path::remove_filename(Prefix); + if (I->type() == llvm::sys::fs::file_type::regular_file) + WriteFileToTar(I->path(), (TarPath + Prefix + "/").str()); + } + } + auto MBOrErr = llvm::MemoryBuffer::getFile(FSPath); + if (!MBOrErr) + return; + + TarPath += llvm::sys::path::filename(FSPath); + TarWriter->append(TarPath, MBOrErr->get()->getBuffer()); + }; SmallString<128> VFS; SmallString<128> ReproCrashFilename; for (const char *TempFile : TempFiles) { - Diag(clang::diag::note_drv_command_failed_diag_msg) << TempFile; + WriteFileToTar(TempFile, "tmp/"); if (Report) Report->TemporaryFiles.push_back(TempFile); if (ReproCrashFilename.empty()) { @@ -1527,39 +1588,40 @@ } } + for (const auto &A : C.getArgs().filtered(options::OPT_frewrite_map_file_EQ, + options::OPT_INPUT)) + WriteFileToTar(A->getValue(), "input/"); + // Assume associated files are based off of the first temporary file. CrashReportInfo CrashInfo(TempFiles[0], VFS); llvm::SmallString<128> Script(CrashInfo.Filename); - llvm::sys::path::replace_extension(Script, "sh"); - std::error_code EC; - llvm::raw_fd_ostream ScriptOS(Script, EC, llvm::sys::fs::CD_CreateNew, - llvm::sys::fs::FA_Write, - llvm::sys::fs::OF_Text); - if (EC) { - Diag(clang::diag::note_drv_command_failed_diag_msg) - << "Error generating run script: " << Script << " " << EC.message(); - } else { - ScriptOS << "# Crash reproducer for " << getClangFullVersion() << "\n" - << "# Driver args: "; - printArgList(ScriptOS, C.getInputArgs()); - ScriptOS << "# Original command: "; - Cmd.Print(ScriptOS, "\n", /*Quote=*/true); - Cmd.Print(ScriptOS, "\n", /*Quote=*/true, &CrashInfo); - if (!AdditionalInformation.empty()) - ScriptOS << "\n# Additional information: " << AdditionalInformation - << "\n"; - if (Report) - Report->TemporaryFiles.push_back(std::string(Script.str())); - Diag(clang::diag::note_drv_command_failed_diag_msg) << Script; - } + std::string Storage; + llvm::raw_string_ostream ScriptOS{Storage}; + ScriptOS << "# Crash reproducer for " << getClangFullVersion() << "\n" + << "# Driver args: "; + printArgList(ScriptOS, C.getInputArgs()); + ScriptOS << "# Original command: "; + Cmd.Print(ScriptOS, "\n", /*Quote=*/true); + Cmd.Print(ScriptOS, "\n", /*Quote=*/true, &CrashInfo); + if (!AdditionalInformation.empty()) + ScriptOS << "\n# Additional information: " << AdditionalInformation << "\n"; + if (Report) + Report->TemporaryFiles.push_back(std::string(Script.str())); + TarWriter->append("repro.sh", Storage); + + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "\n********************\n\n" + "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n" + "Preprocessed source(s) and associated run script(s) are located at:"; + + Diag(clang::diag::note_drv_command_failed_diag_msg) << ReproFileName; // 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(); + WriteFileToTar(ReproCrashFilename.str(), ""); } else { // Suggest a directory for the user to look for .crash files. llvm::sys::path::append(CrashDiagDir, Name); CrashDiagDir += "__.crash"; @@ -1572,9 +1634,6 @@ } } - for (const auto &A : C.getArgs().filtered(options::OPT_frewrite_map_file_EQ)) - Diag(clang::diag::note_drv_command_failed_diag_msg) << A->getValue(); - Diag(clang::diag::note_drv_command_failed_diag_msg) << "\n\n********************"; } Index: clang/test/Driver/crash-diagnostics-dir.c =================================================================== --- clang/test/Driver/crash-diagnostics-dir.c +++ clang/test/Driver/crash-diagnostics-dir.c @@ -2,4 +2,4 @@ // RUN: not %clang -fcrash-diagnostics-dir=%t -c %s -o - 2>&1 | FileCheck %s #pragma clang __debug parser_crash // CHECK: Preprocessed source(s) and associated run script(s) are located at: -// CHECK: diagnostic msg: {{.*}}{{/|\\}}crash-diagnostics-dir.c.tmp{{(/|\\).*}}.c +// CHECK: diagnostic msg: {{.*}}{{/|\\}}crash-diagnostics-dir.c.tmp{{(/|\\).*}}.tar Index: clang/test/Driver/crash-report-clang-cl.cpp =================================================================== --- clang/test/Driver/crash-report-clang-cl.cpp +++ clang/test/Driver/crash-report-clang-cl.cpp @@ -3,8 +3,12 @@ // RUN: not %clang_cl -fsyntax-only /Brepro /source-charset:utf-8 \ // RUN: -fcrash-diagnostics-dir=%t -- %s 2>&1 | FileCheck %s -// RUN: cat %t/crash-report-clang-cl-*.cpp | FileCheck --check-prefix=CHECKSRC %s -// RUN: cat %t/crash-report-clang-cl-*.sh | FileCheck --check-prefix=CHECKSH %s +// RUN: tar xOf %t/*.tar --wildcards "*/tmp/crash-report-clang-cl-*.cpp" \ +// RUN: | FileCheck --check-prefix=CHECKSRC %s +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" \ +// RUN: | FileCheck --check-prefix=CHECKSH %s +// RN: cat %t/crash-report-clang-cl-*.cpp | FileCheck --check-prefix=CHECKSRC %s +// RN: cat %t/crash-report-clang-cl-*.sh | FileCheck --check-prefix=CHECKSH %s // REQUIRES: crash-recovery @@ -32,7 +36,7 @@ // CHECKSRC-NEXT: {{^}}int a = 0; // CHECKSRC-NEXT: {{^}}#endif -// CHECK-NEXT: note: diagnostic msg: {{.*}}crash-report-clang-cl-{{.*}}.cpp +// CHECK-NEXT: note: diagnostic msg: {{.*}}.tar // CHECKSH: # Crash reproducer // CHECKSH-NEXT: # Driver args: {{.*}}"-fsyntax-only" // CHECKSH-SAME: /Brepro Index: clang/test/Driver/crash-report-header.h =================================================================== --- clang/test/Driver/crash-report-header.h +++ clang/test/Driver/crash-report-header.h @@ -1,13 +1,13 @@ // RUN: rm -rf %t // RUN: mkdir %t // RUN: env TMPDIR="%t" TEMP="%t" TMP="%t" RC_DEBUG_OPTIONS=1 not %clang -fsyntax-only %s 2>&1 | FileCheck %s -// RUN: cat %t/crash-report-header-*.h | FileCheck --check-prefix=CHECKSRC "%s" -// RUN: cat %t/crash-report-header-*.sh | FileCheck --check-prefix=CHECKSH "%s" +// RUN: tar xOf %t/*.tar --wildcards "*/tmp/crash-report-header-*.h" | FileCheck --check-prefix=CHECKSRC "%s" +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" | FileCheck --check-prefix=CHECKSH "%s" // REQUIRES: crash-recovery #pragma clang __debug parser_crash // CHECK: Preprocessed source(s) and associated run script(s) are located at: -// CHECK-NEXT: note: diagnostic msg: {{.*}}.h +// CHECK-NEXT: note: diagnostic msg: {{.*}}.tar FOO // CHECKSRC: FOO // CHECKSH: "-cc1" Index: clang/test/Driver/crash-report-modules.m =================================================================== --- clang/test/Driver/crash-report-modules.m +++ clang/test/Driver/crash-report-modules.m @@ -8,8 +8,12 @@ // RUN: not %clang -fsyntax-only %s -I %S/Inputs/module -isysroot %/t/i/ \ // RUN: -fmodules -fmodules-cache-path=%T/crmdir/m/ -DFOO=BAR 2>&1 | FileCheck %s -// RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %T/crmdir/crash-report-*.m -// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %T/crmdir/crash-report-*.sh +// RUN: tar tf %T/crmdir/*.tar | sort | FileCheck %s --check-prefix=CHECKTAR + +// RUN: tar xOf %T/crmdir/*.tar --wildcards "*/tmp/crash-report-modules-*.m" \ +// RUN: | FileCheck --check-prefix=CHECKSRC %s +// RUN: tar xOf %T/crmdir/*.tar --wildcards "*/repro.sh" \ +// RUN: | FileCheck --check-prefix=CHECKSH %s // REQUIRES: crash-recovery // FIXME: This test creates excessively deep directory hierarchies that cause @@ -21,8 +25,15 @@ // CHECK: PLEASE submit a bug report to {{.*}} and include the crash backtrace, preprocessed source, and associated run script. // 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: {{.*}}.tar + +// CHECKTAR: {{.*}}/repro.sh +// CHECKTAR-NEXT: {{.*}}/tmp/crash-report-modules-{{.*}}.m +// CHECKTAR-NEXT: {{.*}}/tmp/modules/{{.*}}/modules.idx +// CHECKTAR-NEXT: {{.*}}/tmp/modules/{{.*}}/simple-{{.*}}.pcm +// CHECKTAR-NEXT: {{.*}}/tmp/vfs/{{.*}}/module/module.modulemap +// CHECKTAR-NEXT: {{.*}}/tmp/vfs/{{.*}}/module/simple.h +// CHECKTAR-NEXT: {{.*}}/tmp/vfs/vfs.yaml // CHECKSRC: @import simple; // CHECKSRC: const int x = 10; Index: clang/test/Driver/crash-report-spaces.c =================================================================== --- clang/test/Driver/crash-report-spaces.c +++ clang/test/Driver/crash-report-spaces.c @@ -2,13 +2,13 @@ // RUN: mkdir "%t" // RUN: cp "%s" "%t/crash report spaces.c" // RUN: env TMPDIR="%t" TEMP="%t" TMP="%t" RC_DEBUG_OPTIONS=1 not %clang -fsyntax-only "%t/crash report spaces.c" 2>&1 | FileCheck "%s" -// RUN: cat "%t/crash report spaces"-*.c | FileCheck --check-prefix=CHECKSRC "%s" -// RUN: cat "%t/crash report spaces"-*.sh | FileCheck --check-prefix=CHECKSH "%s" +// RUN: tar xOf %t/*.tar --wildcards "*/crash report spaces-*.c" | FileCheck --check-prefix=CHECKSRC "%s" +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" | FileCheck --check-prefix=CHECKSH "%s" // REQUIRES: crash-recovery #pragma clang __debug parser_crash // CHECK: Preprocessed source(s) and associated run script(s) are located at: -// CHECK-NEXT: note: diagnostic msg: {{.*}}.c +// CHECK-NEXT: note: diagnostic msg: {{.*}}.tar FOO // CHECKSRC: FOO // CHECKSH: "-cc1" Index: clang/test/Driver/crash-report-with-asserts.c =================================================================== --- clang/test/Driver/crash-report-with-asserts.c +++ clang/test/Driver/crash-report-with-asserts.c @@ -12,14 +12,20 @@ // RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ // RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ // RUN: not %clang %s @%t.rsp -DASSERT 2>&1 | FileCheck %s -// RUN: cat %t/crash-report-*.c | FileCheck --check-prefix=CHECKSRC %s -// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s +// RUN: tar xOf %t/*.tar --wildcards "*/tmp/crash-report-*.c" \ +// RUN: | FileCheck --check-prefix=CHECKSRC %s +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" \ +// RUN: | FileCheck --check-prefix=CHECKSH %s + +// RUN: rm -f %t/*.tar // RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ // RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ // RUN: not %clang %s @%t.rsp -DUNREACHABLE 2>&1 | FileCheck %s -// RUN: cat %t/crash-report-with-asserts-*.c | FileCheck --check-prefix=CHECKSRC %s -// RUN: cat %t/crash-report-with-asserts-*.sh | FileCheck --check-prefix=CHECKSH %s +// RUN: tar xOf %t/*.tar --wildcards "*/tmp/crash-report-*.c" \ +// RUN: | FileCheck --check-prefix=CHECKSRC %s +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" \ +// RUN: | FileCheck --check-prefix=CHECKSH %s // REQUIRES: crash-recovery, asserts @@ -30,7 +36,7 @@ #endif // CHECK: Preprocessed source(s) and associated run script(s) are located at: -// CHECK-NEXT: note: diagnostic msg: {{.*}}crash-report-with-asserts-{{.*}}.c +// CHECK-NEXT: note: diagnostic msg: {{.*}}.tar FOO // CHECKSRC: FOO // CHECKSH: # Crash reproducer Index: clang/test/Driver/crash-report.cpp =================================================================== --- clang/test/Driver/crash-report.cpp +++ clang/test/Driver/crash-report.cpp @@ -12,20 +12,30 @@ // RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ // RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ // RUN: not %clang %s @%t.rsp -DPARSER 2>&1 | FileCheck %s -// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s -// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s +// RUN: tar xOf %t/*.tar --wildcards "*/tmp/crash-report-*.cpp" \ +// RUN: | FileCheck --check-prefix=CHECKSRC %s +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" \ +// RUN: | FileCheck --check-prefix=CHECKSH %s + +// RUN: rm -f %t/*.tar // RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ // RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ // RUN: not %clang %s @%t.rsp -DCRASH 2>&1 | FileCheck %s -// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s -// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s +// RUN: tar xOf %t/*.tar --wildcards "*/tmp/crash-report-*.cpp" \ +// RUN: | FileCheck --check-prefix=CHECKSRC %s +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" \ +// RUN: | FileCheck --check-prefix=CHECKSH %s + +// RUN: rm -f %t/*.tar // RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 \ // RUN: CC_PRINT_HEADERS=1 CC_LOG_DIAGNOSTICS=1 \ // RUN: not %clang %s @%t.rsp -DFATAL 2>&1 | FileCheck %s -// RUN: cat %t/crash-report-*.cpp | FileCheck --check-prefix=CHECKSRC %s -// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s +// RUN: tar xOf %t/*.tar --wildcards "*/tmp/crash-report-*.cpp" \ +// RUN: | FileCheck --check-prefix=CHECKSRC %s +// RUN: tar xOf %t/*.tar --wildcards "*/repro.sh" \ +// RUN: | FileCheck --check-prefix=CHECKSH %s // REQUIRES: crash-recovery @@ -38,7 +48,7 @@ #endif // CHECK: Preprocessed source(s) and associated run script(s) are located at: -// CHECK-NEXT: note: diagnostic msg: {{.*}}crash-report-{{.*}}.cpp +// CHECK-NEXT: note: diagnostic msg: {{.*}}.tar // __has_feature(cxx_exceptions) is default-on in the gcc-compatible driver. FOO Index: clang/test/Driver/rewrite-map-in-diagnostics.c =================================================================== --- clang/test/Driver/rewrite-map-in-diagnostics.c +++ clang/test/Driver/rewrite-map-in-diagnostics.c @@ -3,9 +3,15 @@ // RUN: env TMPDIR="%t" TEMP="%t" TMP="%t" RC_DEBUG_OPTION=1 \ // RUN: not %clang -fsyntax-only -frewrite-map-file=%p/Inputs/rewrite.map %s 2>&1 \ // RUN: | FileCheck %s +// RUN: tar tf %t/*.tar | sort | FileCheck --check-prefix=CHECKTAR %s #pragma clang __debug parser_crash -// CHECK: note: diagnostic msg: {{.*}}rewrite.map +// CHECK: note: diagnostic msg: {{.*}}.tar + +// CHECKTAR: {{.*}}/input/rewrite-map-in-diagnostics.c +// CHECKTAR-NEXT: {{.*}}/input/rewrite.map +// CHECKTAR-NEXT: {{.*}}/repro.sh +// CHECKTAR-NEXT: {{.*}}/tmp/rewrite-map-in-diagnostics-{{.*}}.c // REQUIRES: crash-recovery