Skip to content

Commit c5dcf01

Browse files
authored
Merge pull request #14814 from jrose-apple/dump-truck
Dump (some) filelists in the PrettyStackTrace
2 parents d138575 + 6243096 commit c5dcf01

File tree

10 files changed

+164
-48
lines changed

10 files changed

+164
-48
lines changed

include/swift/Basic/PrettyStackTrace.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
#include "llvm/Support/PrettyStackTrace.h"
1717
#include "llvm/ADT/StringRef.h"
1818

19+
namespace llvm {
20+
class MemoryBuffer;
21+
}
22+
1923
namespace swift {
2024

2125
/// A PrettyStackTraceEntry for performing an action involving a StringRef.
@@ -31,6 +35,15 @@ class PrettyStackTraceStringAction : public llvm::PrettyStackTraceEntry {
3135
void print(llvm::raw_ostream &OS) const override;
3236
};
3337

38+
/// A PrettyStackTraceEntry to dump the contents of a file.
39+
class PrettyStackTraceFileContents : public llvm::PrettyStackTraceEntry {
40+
const llvm::MemoryBuffer &Buffer;
41+
public:
42+
explicit PrettyStackTraceFileContents(const llvm::MemoryBuffer &buffer)
43+
: Buffer(buffer) {}
44+
void print(llvm::raw_ostream &OS) const override;
45+
};
46+
3447
} // end namespace swift
3548

3649
#endif // SWIFT_BASIC_PRETTYSTACKTRACE_H

include/swift/Frontend/ArgsToFrontendInputsConverter.h

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,28 @@ namespace swift {
4242
class ArgsToFrontendInputsConverter {
4343
DiagnosticEngine &Diags;
4444
const llvm::opt::ArgList &Args;
45-
FrontendInputsAndOutputs &InputsAndOutputs;
4645

4746
llvm::opt::Arg const *const FilelistPathArg;
4847
llvm::opt::Arg const *const PrimaryFilelistPathArg;
4948

50-
SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> BuffersToKeepAlive;
49+
/// A place to keep alive any buffers that are loaded as part of setting up
50+
/// the frontend inputs.
51+
SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> ConfigurationFileBuffers;
5152

5253
llvm::SetVector<StringRef> Files;
5354

5455
public:
5556
ArgsToFrontendInputsConverter(DiagnosticEngine &diags,
56-
const llvm::opt::ArgList &args,
57-
FrontendInputsAndOutputs &inputsAndOutputs);
57+
const llvm::opt::ArgList &args);
5858

59-
bool convert();
59+
/// Produces a FrontendInputsAndOutputs object with the inputs populated from
60+
/// the arguments the converter was initialized with.
61+
///
62+
/// \param buffers If present, buffers read in the processing of the frontend
63+
/// inputs will be saved here. These should only be used for debugging
64+
/// purposes.
65+
Optional<FrontendInputsAndOutputs> convert(
66+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> *buffers);
6067

6168
private:
6269
bool enforceFilelistExclusion();
@@ -66,11 +73,19 @@ class ArgsToFrontendInputsConverter {
6673
llvm::function_ref<void(StringRef)> fn);
6774
bool addFile(StringRef file);
6875
Optional<std::set<StringRef>> readPrimaryFiles();
69-
std::set<StringRef>
76+
77+
/// Returns the newly set-up FrontendInputsAndOutputs, as well as a set of
78+
/// any unused primary files (those that do not correspond to an input).
79+
std::pair<FrontendInputsAndOutputs, std::set<StringRef>>
7080
createInputFilesConsumingPrimaries(std::set<StringRef> primaryFiles);
71-
bool checkForMissingPrimaryFiles(std::set<StringRef> primaryFiles);
7281

73-
bool isSingleThreadedWMO() const;
82+
/// Emits an error for each file in \p unusedPrimaryFiles.
83+
///
84+
/// \returns true if \p unusedPrimaryFiles is non-empty.
85+
bool diagnoseUnusedPrimaryFiles(std::set<StringRef> unusedPrimaryFiles);
86+
87+
bool
88+
isSingleThreadedWMO(const FrontendInputsAndOutputs &inputsAndOutputs) const;
7489
};
7590

7691
} // namespace swift

include/swift/Frontend/ArgsToFrontendOptionsConverter.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,13 @@ class ArgsToFrontendOptionsConverter {
6969
FrontendOptions &Opts)
7070
: Diags(Diags), Args(Args), Opts(Opts) {}
7171

72-
bool convert();
72+
/// Populates the FrontendOptions the converter was initialized with.
73+
///
74+
/// \param buffers If present, buffers read in the processing of the frontend
75+
/// options will be saved here. These should only be used for debugging
76+
/// purposes.
77+
bool convert(
78+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> *buffers);
7379

7480
static FrontendOptions::ActionType
7581
determineRequestedAction(const llvm::opt::ArgList &);

include/swift/Frontend/Frontend.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,19 @@ class CompilerInvocation {
8686
/// default values given the /absence/ of a flag. This is because \c parseArgs
8787
/// may be used to modify an already partially configured invocation.
8888
///
89+
/// Any configuration files loaded as a result of parsing arguments will be
90+
/// stored in \p ConfigurationFileBuffers, if non-null. The contents of these
91+
/// buffers should \e not be interpreted by the caller; they are only present
92+
/// in order to make it possible to reproduce how these arguments were parsed
93+
/// if the compiler ends up crashing or exhibiting other bad behavior.
94+
///
8995
/// If non-empty, relative search paths are resolved relative to
9096
/// \p workingDirectory.
9197
///
9298
/// \returns true if there was an error, false on success.
9399
bool parseArgs(ArrayRef<const char *> Args, DiagnosticEngine &Diags,
100+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>>
101+
*ConfigurationFileBuffers = nullptr,
94102
StringRef workingDirectory = {});
95103

96104
/// Sets specific options based on the given serialized Swift binary data.

lib/Basic/PrettyStackTrace.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,19 @@
1717

1818
#include "swift/Basic/PrettyStackTrace.h"
1919
#include "swift/Basic/QuotedString.h"
20+
#include "llvm/Support/MemoryBuffer.h"
2021
#include "llvm/Support/raw_ostream.h"
2122

2223
using namespace swift;
2324

2425
void PrettyStackTraceStringAction::print(llvm::raw_ostream &out) const {
2526
out << "While " << Action << ' ' << QuotedString(TheString) << '\n';
2627
}
28+
29+
void PrettyStackTraceFileContents::print(llvm::raw_ostream &out) const {
30+
out << "Contents of " << Buffer.getBufferIdentifier() << ":\n---\n"
31+
<< Buffer.getBuffer();
32+
if (!Buffer.getBuffer().endswith("\n"))
33+
out << '\n';
34+
out << "---\n";
35+
}

lib/Frontend/ArgsToFrontendInputsConverter.cpp

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/Frontend/ArgsToFrontendInputsConverter.h"
1414

1515
#include "swift/AST/DiagnosticsFrontend.h"
16+
#include "swift/Basic/Defer.h"
1617
#include "swift/Frontend/ArgsToFrontendOutputsConverter.h"
1718
#include "swift/Frontend/FrontendOptions.h"
1819
#include "swift/Option/Options.h"
@@ -30,33 +31,47 @@ using namespace swift;
3031
using namespace llvm::opt;
3132

3233
ArgsToFrontendInputsConverter::ArgsToFrontendInputsConverter(
33-
DiagnosticEngine &diags, const ArgList &args,
34-
FrontendInputsAndOutputs &inputsAndOutputs)
35-
: Diags(diags), Args(args), InputsAndOutputs(inputsAndOutputs),
34+
DiagnosticEngine &diags, const ArgList &args)
35+
: Diags(diags), Args(args),
3636
FilelistPathArg(args.getLastArg(options::OPT_filelist)),
3737
PrimaryFilelistPathArg(args.getLastArg(options::OPT_primary_filelist)) {}
3838

39-
bool ArgsToFrontendInputsConverter::convert() {
39+
Optional<FrontendInputsAndOutputs> ArgsToFrontendInputsConverter::convert(
40+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> *buffers) {
41+
SWIFT_DEFER {
42+
if (buffers) {
43+
std::move(ConfigurationFileBuffers.begin(),
44+
ConfigurationFileBuffers.end(),
45+
std::back_inserter(*buffers));
46+
// Clearing the original list of buffers isn't strictly necessary, but
47+
// makes the behavior more sensible if we were to call convert() again.
48+
ConfigurationFileBuffers.clear();
49+
}
50+
};
51+
4052
if (enforceFilelistExclusion())
41-
return true;
53+
return None;
54+
4255
if (FilelistPathArg ? readInputFilesFromFilelist()
4356
: readInputFilesFromCommandLine())
44-
return true;
57+
return None;
4558
Optional<std::set<StringRef>> primaryFiles = readPrimaryFiles();
4659
if (!primaryFiles)
47-
return true;
48-
std::set<StringRef> unusedPrimaryFiles =
60+
return None;
61+
62+
FrontendInputsAndOutputs result;
63+
std::set<StringRef> unusedPrimaryFiles;
64+
std::tie(result, unusedPrimaryFiles) =
4965
createInputFilesConsumingPrimaries(*primaryFiles);
5066

51-
if (checkForMissingPrimaryFiles(unusedPrimaryFiles))
52-
return true;
67+
if (diagnoseUnusedPrimaryFiles(unusedPrimaryFiles))
68+
return None;
5369

5470
// Must be set before iterating over inputs needing outputs.
55-
InputsAndOutputs.setIsSingleThreadedWMO(isSingleThreadedWMO());
56-
57-
InputsAndOutputs.setBypassBatchModeChecks(
71+
result.setBypassBatchModeChecks(
5872
Args.hasArg(options::OPT_bypass_batch_mode_checks));
59-
return false;
73+
74+
return std::move(result);
6075
}
6176

6277
bool ArgsToFrontendInputsConverter::enforceFilelistExclusion() {
@@ -114,7 +129,7 @@ bool ArgsToFrontendInputsConverter::forAllFilesInFilelist(
114129
llvm::make_range(llvm::line_iterator(*filelistBufferOrError->get()),
115130
llvm::line_iterator()))
116131
fn(file);
117-
BuffersToKeepAlive.push_back(std::move(*filelistBufferOrError));
132+
ConfigurationFileBuffers.push_back(std::move(*filelistBufferOrError));
118133
return false;
119134
}
120135

@@ -137,34 +152,38 @@ ArgsToFrontendInputsConverter::readPrimaryFiles() {
137152
return primaryFiles;
138153
}
139154

140-
std::set<StringRef>
155+
std::pair<FrontendInputsAndOutputs, std::set<StringRef>>
141156
ArgsToFrontendInputsConverter::createInputFilesConsumingPrimaries(
142157
std::set<StringRef> primaryFiles) {
158+
bool hasAnyPrimaryFiles = !primaryFiles.empty();
159+
160+
FrontendInputsAndOutputs result;
143161
for (auto &file : Files) {
144162
bool isPrimary = primaryFiles.count(file) > 0;
145-
InputsAndOutputs.addInput(InputFile(file, isPrimary));
163+
result.addInput(InputFile(file, isPrimary));
146164
if (isPrimary)
147165
primaryFiles.erase(file);
148166
}
149-
return primaryFiles;
167+
168+
if (!Files.empty() && !hasAnyPrimaryFiles) {
169+
Optional<std::vector<std::string>> userSuppliedNamesOrErr =
170+
OutputFilesComputer::getOutputFilenamesFromCommandLineOrFilelist(Args,
171+
Diags);
172+
if (userSuppliedNamesOrErr && userSuppliedNamesOrErr->size() == 1)
173+
result.setIsSingleThreadedWMO(true);
174+
}
175+
176+
return {std::move(result), std::move(primaryFiles)};
150177
}
151178

152-
bool ArgsToFrontendInputsConverter::checkForMissingPrimaryFiles(
179+
bool ArgsToFrontendInputsConverter::diagnoseUnusedPrimaryFiles(
153180
std::set<StringRef> primaryFiles) {
154181
for (auto &file : primaryFiles) {
155182
// Catch "swiftc -frontend -c -filelist foo -primary-file
156183
// some-file-not-in-foo".
157-
assert(FilelistPathArg && "Missing primary with no filelist");
184+
assert(FilelistPathArg && "Unused primary with no filelist");
158185
Diags.diagnose(SourceLoc(), diag::error_primary_file_not_found, file,
159186
FilelistPathArg->getValue());
160187
}
161188
return !primaryFiles.empty();
162189
}
163-
164-
bool ArgsToFrontendInputsConverter::isSingleThreadedWMO() const {
165-
Optional<std::vector<std::string>> userSuppliedNamesOrErr =
166-
OutputFilesComputer::getOutputFilenamesFromCommandLineOrFilelist(Args,
167-
Diags);
168-
return InputsAndOutputs.hasInputs() && !InputsAndOutputs.hasPrimaryInputs() &&
169-
userSuppliedNamesOrErr && userSuppliedNamesOrErr->size() == 1;
170-
}

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ static void debugFailWithAssertion() {
4545
LLVM_ATTRIBUTE_NOINLINE
4646
static void debugFailWithCrash() { LLVM_BUILTIN_TRAP; }
4747

48-
bool ArgsToFrontendOptionsConverter::convert() {
48+
bool ArgsToFrontendOptionsConverter::convert(
49+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> *buffers) {
4950
using namespace options;
5051

5152
handleDebugCrashGroupArguments();
@@ -93,9 +94,11 @@ bool ArgsToFrontendOptionsConverter::convert() {
9394

9495
computeDumpScopeMapLocations();
9596

96-
if (ArgsToFrontendInputsConverter(Diags, Args, Opts.InputsAndOutputs)
97-
.convert())
97+
Optional<FrontendInputsAndOutputs> inputsAndOutputs =
98+
ArgsToFrontendInputsConverter(Diags, Args).convert(buffers);
99+
if (!inputsAndOutputs)
98100
return true;
101+
Opts.InputsAndOutputs = std::move(inputsAndOutputs).getValue();
99102

100103
Opts.RequestedAction = determineRequestedAction(Args);
101104

lib/Frontend/CompilerInvocation.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ SourceFileKind CompilerInvocation::getSourceFileKind() const {
8181
llvm_unreachable("Unhandled InputFileKind in switch.");
8282
}
8383

84-
static bool ParseFrontendArgs(FrontendOptions &opts, ArgList &args,
85-
DiagnosticEngine &diags) {
86-
return ArgsToFrontendOptionsConverter(diags, args, opts).convert();
84+
static bool ParseFrontendArgs(
85+
FrontendOptions &opts, ArgList &args, DiagnosticEngine &diags,
86+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> *buffers) {
87+
ArgsToFrontendOptionsConverter converter(diags, args, opts);
88+
return converter.convert(buffers);
8789
}
8890

8991
static void diagnoseSwiftVersion(Optional<version::Version> &vers, Arg *verArg,
@@ -978,9 +980,12 @@ bool ParseMigratorArgs(MigratorOptions &Opts,
978980
return false;
979981
}
980982

981-
bool CompilerInvocation::parseArgs(ArrayRef<const char *> Args,
982-
DiagnosticEngine &Diags,
983-
StringRef workingDirectory) {
983+
bool CompilerInvocation::parseArgs(
984+
ArrayRef<const char *> Args,
985+
DiagnosticEngine &Diags,
986+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>>
987+
*ConfigurationFileBuffers,
988+
StringRef workingDirectory) {
984989
using namespace options;
985990

986991
if (Args.empty())
@@ -1006,7 +1011,8 @@ bool CompilerInvocation::parseArgs(ArrayRef<const char *> Args,
10061011
return true;
10071012
}
10081013

1009-
if (ParseFrontendArgs(FrontendOpts, ParsedArgs, Diags)) {
1014+
if (ParseFrontendArgs(FrontendOpts, ParsedArgs, Diags,
1015+
ConfigurationFileBuffers)) {
10101016
return true;
10111017
}
10121018

lib/FrontendTool/FrontendTool.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "swift/Basic/JSONSerialization.h"
4242
#include "swift/Basic/LLVMContext.h"
4343
#include "swift/Basic/LLVMInitialize.h"
44+
#include "swift/Basic/PrettyStackTrace.h"
4445
#include "swift/Basic/SourceManager.h"
4546
#include "swift/Basic/Statistic.h"
4647
#include "swift/Basic/Timer.h"
@@ -1661,10 +1662,32 @@ int swift::performFrontend(ArrayRef<const char *> Args,
16611662
llvm::sys::fs::current_path(workingDirectory);
16621663

16631664
// Parse arguments.
1664-
if (Invocation.parseArgs(Args, Instance->getDiags(), workingDirectory)) {
1665+
SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> configurationFileBuffers;
1666+
if (Invocation.parseArgs(Args, Instance->getDiags(),
1667+
&configurationFileBuffers, workingDirectory)) {
16651668
return finishDiagProcessing(1);
16661669
}
16671670

1671+
// Make an array of PrettyStackTrace objects to dump the configuration files
1672+
// we used to parse the arguments. These are RAII objects, so they and the
1673+
// buffers they refer to must be kept alive in order to be useful. (That is,
1674+
// we want them to be alive for the entire rest of performFrontend.)
1675+
//
1676+
// This can't be a SmallVector or similar because PrettyStackTraces can't be
1677+
// moved (or copied)...and it can't be an array of non-optionals because
1678+
// PrettyStackTraces can't be default-constructed. So we end up with a
1679+
// dynamically-sized array of optional PrettyStackTraces, which get
1680+
// initialized by iterating over the buffers we collected above.
1681+
auto configurationFileStackTraces =
1682+
llvm::make_unique<Optional<PrettyStackTraceFileContents>[]>(
1683+
configurationFileBuffers.size());
1684+
for_each(configurationFileBuffers.begin(), configurationFileBuffers.end(),
1685+
&configurationFileStackTraces[0],
1686+
[](const std::unique_ptr<llvm::MemoryBuffer> &buffer,
1687+
Optional<PrettyStackTraceFileContents> &trace) {
1688+
trace.emplace(*buffer);
1689+
});
1690+
16681691
// Setting DWARF Version depend on platform
16691692
IRGenOptions &IRGenOpts = Invocation.getIRGenOptions();
16701693
IRGenOpts.DWARFVersion = swift::DWARFVersion;

test/Frontend/crash.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: echo %s > %t.filelist.txt
2+
// RUN: not --crash %target-swift-frontend -typecheck -debug-crash-after-parse -filelist %t.filelist.txt 2>&1 | %FileCheck %s
3+
4+
// Check that we see the contents of the input file list in the crash log.
5+
// CHECK-LABEL: Stack dump
6+
// CHECK-NEXT: Program arguments: {{.*swift(c?)}} -frontend
7+
// CHECK-NEXT: Contents of {{.*}}.filelist.txt:
8+
// CHECK-NEXT: ---
9+
// CHECK-NEXT: test/Frontend/crash.swift{{$}}
10+
// CHECK-NEXT: ---
11+
12+
func anchor() {}
13+
anchor()
14+

0 commit comments

Comments
 (0)