Skip to content

Commit be66c84

Browse files
committed
[Clang][Driver] Create crash reproducers for IR inputs
This patch makes Clang produce the crash reproducer shell script for IR inputs as well.
1 parent 92caf1a commit be66c84

File tree

2 files changed

+86
-33
lines changed

2 files changed

+86
-33
lines changed

clang/lib/Driver/Driver.cpp

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#include "llvm/ADT/ArrayRef.h"
7171
#include "llvm/ADT/STLExtras.h"
7272
#include "llvm/ADT/SmallSet.h"
73+
#include "llvm/ADT/SmallVector.h"
7374
#include "llvm/ADT/StringExtras.h"
7475
#include "llvm/ADT/StringRef.h"
7576
#include "llvm/ADT/StringSet.h"
@@ -103,6 +104,7 @@
103104
#include <memory>
104105
#include <optional>
105106
#include <set>
107+
#include <string>
106108
#include <utility>
107109
#if LLVM_ON_UNIX
108110
#include <unistd.h> // getpid
@@ -2041,12 +2043,17 @@ void Driver::generateCompilationDiagnostics(
20412043
InputList Inputs;
20422044
BuildInputs(C.getDefaultToolChain(), C.getArgs(), Inputs);
20432045

2046+
ArgStringList IRInputs;
20442047
for (InputList::iterator it = Inputs.begin(), ie = Inputs.end(); it != ie;) {
20452048
bool IgnoreInput = false;
20462049

2047-
// Ignore input from stdin or any inputs that cannot be preprocessed.
2048-
// Check type first as not all linker inputs have a value.
2049-
if (types::getPreprocessedType(it->first) == types::TY_INVALID) {
2050+
// Save IR inputs separately, ignore input from stdin or any other inputs
2051+
// that cannot be preprocessed. Check type first as not all linker inputs
2052+
// have a value.
2053+
if (types::isLLVMIR(it->first)) {
2054+
IRInputs.push_back(it->second->getValue());
2055+
IgnoreInput = true;
2056+
} else if (types::getPreprocessedType(it->first) == types::TY_INVALID) {
20502057
IgnoreInput = true;
20512058
} else if (!strcmp(it->second->getValue(), "-")) {
20522059
Diag(clang::diag::note_drv_command_failed_diag_msg)
@@ -2063,7 +2070,7 @@ void Driver::generateCompilationDiagnostics(
20632070
}
20642071
}
20652072

2066-
if (Inputs.empty()) {
2073+
if (Inputs.empty() && IRInputs.empty()) {
20672074
Diag(clang::diag::note_drv_command_failed_diag_msg)
20682075
<< "Error generating preprocessed source(s) - "
20692076
"no preprocessable inputs.";
@@ -2086,46 +2093,77 @@ void Driver::generateCompilationDiagnostics(
20862093
return;
20872094
}
20882095

2089-
// Construct the list of abstract actions to perform for this compilation. On
2090-
// Darwin OSes this uses the driver-driver and builds universal actions.
2091-
const ToolChain &TC = C.getDefaultToolChain();
2092-
if (TC.getTriple().isOSBinFormatMachO())
2093-
BuildUniversalActions(C, TC, Inputs);
2094-
else
2095-
BuildActions(C, C.getArgs(), Inputs, C.getActions());
2096+
// If we only have IR inputs there's no need for preprocessing.
2097+
if (!Inputs.empty()) {
2098+
// Construct the list of abstract actions to perform for this compilation.
2099+
// On
2100+
// Darwin OSes this uses the driver-driver and builds universal actions.
2101+
const ToolChain &TC = C.getDefaultToolChain();
2102+
if (TC.getTriple().isOSBinFormatMachO())
2103+
BuildUniversalActions(C, TC, Inputs);
2104+
else
2105+
BuildActions(C, C.getArgs(), Inputs, C.getActions());
20962106

2097-
BuildJobs(C);
2107+
BuildJobs(C);
20982108

2099-
// If there were errors building the compilation, quit now.
2100-
if (Trap.hasErrorOccurred()) {
2101-
Diag(clang::diag::note_drv_command_failed_diag_msg)
2102-
<< "Error generating preprocessed source(s).";
2103-
return;
2104-
}
2109+
// If there were errors building the compilation, quit now.
2110+
if (Trap.hasErrorOccurred()) {
2111+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2112+
<< "Error generating preprocessed source(s).";
2113+
return;
2114+
}
2115+
// Generate preprocessed output.
2116+
SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
2117+
C.ExecuteJobs(C.getJobs(), FailingCommands);
21052118

2106-
// Generate preprocessed output.
2107-
SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
2108-
C.ExecuteJobs(C.getJobs(), FailingCommands);
2119+
// If any of the preprocessing commands failed, clean up and exit.
2120+
if (!FailingCommands.empty()) {
2121+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2122+
<< "Error generating preprocessed source(s).";
2123+
return;
2124+
}
21092125

2110-
// If any of the preprocessing commands failed, clean up and exit.
2111-
if (!FailingCommands.empty()) {
2112-
Diag(clang::diag::note_drv_command_failed_diag_msg)
2113-
<< "Error generating preprocessed source(s).";
2114-
return;
2126+
const ArgStringList &TempFiles = C.getTempFiles();
2127+
if (TempFiles.empty()) {
2128+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2129+
<< "Error generating preprocessed source(s).";
2130+
return;
2131+
}
21152132
}
21162133

2117-
const ArgStringList &TempFiles = C.getTempFiles();
2118-
if (TempFiles.empty()) {
2119-
Diag(clang::diag::note_drv_command_failed_diag_msg)
2120-
<< "Error generating preprocessed source(s).";
2121-
return;
2134+
// Copying filenames due to ownership.
2135+
const ArgStringList &Files = C.getTempFiles();
2136+
SmallVector<std::string> TempFiles(Files.begin(), Files.end());
2137+
2138+
// We'd like to copy the IR input file into our own temp file
2139+
// because the build system might try to clean-up after itself.
2140+
for (auto const *Input : IRInputs) {
2141+
int FD;
2142+
llvm::SmallVector<char, 64> Path;
2143+
2144+
StringRef extension = llvm::sys::path::extension(Input);
2145+
if (!extension.empty())
2146+
extension = extension.drop_front();
2147+
2148+
std::error_code EC = llvm::sys::fs::createTemporaryFile(
2149+
llvm::sys::path::stem(Input), extension, FD, Path);
2150+
2151+
if (EC) {
2152+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2153+
<< "Error generating run script: " << "Failed copying IR input files"
2154+
<< " " << EC.message();
2155+
return;
2156+
}
2157+
llvm::sys::fs::copy_file(Input, FD);
2158+
2159+
TempFiles.push_back(std::string(Path.begin(), Path.end()));
21222160
}
21232161

21242162
Diag(clang::diag::note_drv_command_failed_diag_msg) << BugReporMsg;
21252163

21262164
SmallString<128> VFS;
21272165
SmallString<128> ReproCrashFilename;
2128-
for (const char *TempFile : TempFiles) {
2166+
for (std::string &TempFile : TempFiles) {
21292167
Diag(clang::diag::note_drv_command_failed_diag_msg) << TempFile;
21302168
if (Report)
21312169
Report->TemporaryFiles.push_back(TempFile);
@@ -2142,7 +2180,7 @@ void Driver::generateCompilationDiagnostics(
21422180
}
21432181

21442182
for (const char *TempFile : SavedTemps)
2145-
C.addTempFile(TempFile);
2183+
TempFiles.push_back(TempFile);
21462184

21472185
// Assume associated files are based off of the first temporary file.
21482186
CrashReportInfo CrashInfo(TempFiles[0], VFS);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %clang -S -emit-llvm -o %t.ll %s
2+
// RUN: not %clang -DCRASH %s %t.ll 2>&1 | FileCheck %s
3+
4+
// CHECK: Preprocessed source(s) and associated run script(s) are located at:
5+
// CHECK-NEXT: clang: note: diagnostic msg: {{.*}}.cpp
6+
// CHECK-NEXT: clang: note: diagnostic msg: {{.*}}.ll
7+
// CHECK-NEXT: clang: note: diagnostic msg: {{.*}}.sh
8+
9+
#ifdef CRASH
10+
#pragma clang __debug parser_crash
11+
#endif
12+
13+
int main() {
14+
return 0;
15+
}

0 commit comments

Comments
 (0)