Skip to content

Commit 83d27f6

Browse files
authored
[Clang][Driver] Create crash reproducers for IR inputs (#165572)
This patch makes Clang produce the crash reproducer shell script for IR inputs as well.
1 parent 47d9d73 commit 83d27f6

File tree

2 files changed

+91
-33
lines changed

2 files changed

+91
-33
lines changed

clang/lib/Driver/Driver.cpp

Lines changed: 76 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
@@ -2050,12 +2052,17 @@ void Driver::generateCompilationDiagnostics(
20502052
InputList Inputs;
20512053
BuildInputs(C.getDefaultToolChain(), C.getArgs(), Inputs);
20522054

2055+
ArgStringList IRInputs;
20532056
for (InputList::iterator it = Inputs.begin(), ie = Inputs.end(); it != ie;) {
20542057
bool IgnoreInput = false;
20552058

2056-
// Ignore input from stdin or any inputs that cannot be preprocessed.
2057-
// Check type first as not all linker inputs have a value.
2058-
if (types::getPreprocessedType(it->first) == types::TY_INVALID) {
2059+
// Save IR inputs separately, ignore input from stdin or any other inputs
2060+
// that cannot be preprocessed. Check type first as not all linker inputs
2061+
// have a value.
2062+
if (types::isLLVMIR(it->first)) {
2063+
IRInputs.push_back(it->second->getValue());
2064+
IgnoreInput = true;
2065+
} else if (types::getPreprocessedType(it->first) == types::TY_INVALID) {
20592066
IgnoreInput = true;
20602067
} else if (!strcmp(it->second->getValue(), "-")) {
20612068
Diag(clang::diag::note_drv_command_failed_diag_msg)
@@ -2072,7 +2079,7 @@ void Driver::generateCompilationDiagnostics(
20722079
}
20732080
}
20742081

2075-
if (Inputs.empty()) {
2082+
if (Inputs.empty() && IRInputs.empty()) {
20762083
Diag(clang::diag::note_drv_command_failed_diag_msg)
20772084
<< "Error generating preprocessed source(s) - "
20782085
"no preprocessable inputs.";
@@ -2095,46 +2102,82 @@ void Driver::generateCompilationDiagnostics(
20952102
return;
20962103
}
20972104

2098-
// Construct the list of abstract actions to perform for this compilation. On
2099-
// Darwin OSes this uses the driver-driver and builds universal actions.
2100-
const ToolChain &TC = C.getDefaultToolChain();
2101-
if (TC.getTriple().isOSBinFormatMachO())
2102-
BuildUniversalActions(C, TC, Inputs);
2103-
else
2104-
BuildActions(C, C.getArgs(), Inputs, C.getActions());
2105+
// If we only have IR inputs there's no need for preprocessing.
2106+
if (!Inputs.empty()) {
2107+
// Construct the list of abstract actions to perform for this compilation.
2108+
// On Darwin OSes this uses the driver-driver and builds universal actions.
2109+
const ToolChain &TC = C.getDefaultToolChain();
2110+
if (TC.getTriple().isOSBinFormatMachO())
2111+
BuildUniversalActions(C, TC, Inputs);
2112+
else
2113+
BuildActions(C, C.getArgs(), Inputs, C.getActions());
21052114

2106-
BuildJobs(C);
2115+
BuildJobs(C);
21072116

2108-
// If there were errors building the compilation, quit now.
2109-
if (Trap.hasErrorOccurred()) {
2110-
Diag(clang::diag::note_drv_command_failed_diag_msg)
2111-
<< "Error generating preprocessed source(s).";
2112-
return;
2113-
}
2117+
// If there were errors building the compilation, quit now.
2118+
if (Trap.hasErrorOccurred()) {
2119+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2120+
<< "Error generating preprocessed source(s).";
2121+
return;
2122+
}
2123+
// Generate preprocessed output.
2124+
SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
2125+
C.ExecuteJobs(C.getJobs(), FailingCommands);
21142126

2115-
// Generate preprocessed output.
2116-
SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
2117-
C.ExecuteJobs(C.getJobs(), FailingCommands);
2127+
// If any of the preprocessing commands failed, clean up and exit.
2128+
if (!FailingCommands.empty()) {
2129+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2130+
<< "Error generating preprocessed source(s).";
2131+
return;
2132+
}
21182133

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;
2134+
const ArgStringList &TempFiles = C.getTempFiles();
2135+
if (TempFiles.empty()) {
2136+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2137+
<< "Error generating preprocessed source(s).";
2138+
return;
2139+
}
21242140
}
21252141

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;
2142+
// Copying filenames due to ownership.
2143+
const ArgStringList &Files = C.getTempFiles();
2144+
SmallVector<std::string> TempFiles(Files.begin(), Files.end());
2145+
2146+
// We'd like to copy the IR input file into our own temp file
2147+
// because the build system might try to clean-up after itself.
2148+
for (auto const *Input : IRInputs) {
2149+
int FD;
2150+
llvm::SmallVector<char, 64> Path;
2151+
2152+
StringRef extension = llvm::sys::path::extension(Input);
2153+
if (!extension.empty())
2154+
extension = extension.drop_front();
2155+
2156+
std::error_code EC = llvm::sys::fs::createTemporaryFile(
2157+
llvm::sys::path::stem(Input), extension, FD, Path);
2158+
if (EC) {
2159+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2160+
<< "Error generating run script: " << "Failed copying IR input files"
2161+
<< " " << EC.message();
2162+
return;
2163+
}
2164+
2165+
EC = llvm::sys::fs::copy_file(Input, FD);
2166+
if (EC) {
2167+
Diag(clang::diag::note_drv_command_failed_diag_msg)
2168+
<< "Error generating run script: " << "Failed copying IR input files"
2169+
<< " " << EC.message();
2170+
return;
2171+
}
2172+
2173+
TempFiles.push_back(std::string(Path.begin(), Path.end()));
21312174
}
21322175

21332176
Diag(clang::diag::note_drv_command_failed_diag_msg) << BugReporMsg;
21342177

21352178
SmallString<128> VFS;
21362179
SmallString<128> ReproCrashFilename;
2137-
for (const char *TempFile : TempFiles) {
2180+
for (std::string &TempFile : TempFiles) {
21382181
Diag(clang::diag::note_drv_command_failed_diag_msg) << TempFile;
21392182
if (Report)
21402183
Report->TemporaryFiles.push_back(TempFile);
@@ -2151,7 +2194,7 @@ void Driver::generateCompilationDiagnostics(
21512194
}
21522195

21532196
for (const char *TempFile : SavedTemps)
2154-
C.addTempFile(TempFile);
2197+
TempFiles.push_back(TempFile);
21552198

21562199
// Assume associated files are based off of the first temporary file.
21572200
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 -S -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)