mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Driver] Preserve filelists when a subprocess crashes. (#9849)
This should make it easier to rerun crashed jobs that use filelists; previously you'd have to run the top-level driver command again with -save-temps. I didn't want to save /all/ temporary files because that often includes things like .o files, which could fill up your disk pretty quickly. But we can always tweak this later.
This commit is contained in:
@@ -57,6 +57,13 @@ enum class OutputLevel {
|
|||||||
Parseable,
|
Parseable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Indicates whether a temporary file should always be preserved if a part of
|
||||||
|
/// the compilation crashes.
|
||||||
|
enum class PreserveOnSignal : bool {
|
||||||
|
No,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
class Compilation {
|
class Compilation {
|
||||||
friend class PerformJobsState;
|
friend class PerformJobsState;
|
||||||
private:
|
private:
|
||||||
@@ -87,8 +94,8 @@ private:
|
|||||||
|
|
||||||
/// Temporary files that should be cleaned up after the compilation finishes.
|
/// Temporary files that should be cleaned up after the compilation finishes.
|
||||||
///
|
///
|
||||||
/// These apply whether the compilation succeeds or fails.
|
/// These apply whether the compilation succeeds or fails. If the
|
||||||
std::vector<std::string> TempFilePaths;
|
llvm::StringMap<PreserveOnSignal> TempFilePaths;
|
||||||
|
|
||||||
/// Write information about this compilation to this file.
|
/// Write information about this compilation to this file.
|
||||||
///
|
///
|
||||||
@@ -174,14 +181,13 @@ public:
|
|||||||
}
|
}
|
||||||
Job *addJob(std::unique_ptr<Job> J);
|
Job *addJob(std::unique_ptr<Job> J);
|
||||||
|
|
||||||
void addTemporaryFile(StringRef file) {
|
void addTemporaryFile(StringRef file,
|
||||||
TempFilePaths.push_back(file.str());
|
PreserveOnSignal preserve = PreserveOnSignal::No) {
|
||||||
|
TempFilePaths[file] = preserve;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isTemporaryFile(StringRef file) {
|
bool isTemporaryFile(StringRef file) {
|
||||||
// TODO: Use a set instead of a linear search.
|
return TempFilePaths.count(file);
|
||||||
return std::find(TempFilePaths.begin(), TempFilePaths.end(), file) !=
|
|
||||||
TempFilePaths.end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::opt::DerivedArgList &getArgs() const { return *TranslatedArgs; }
|
const llvm::opt::DerivedArgList &getArgs() const { return *TranslatedArgs; }
|
||||||
@@ -255,9 +261,12 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// \brief Perform all jobs.
|
/// \brief Perform all jobs.
|
||||||
///
|
///
|
||||||
/// \returns exit code of the first failed Job, or 0 on success. A return
|
/// \param[out] abnormalExit Set to true if any job exits abnormally (i.e.
|
||||||
/// value of -2 indicates that a Job crashed during execution.
|
/// crashes).
|
||||||
int performJobsImpl();
|
///
|
||||||
|
/// \returns exit code of the first failed Job, or 0 on success. If a Job
|
||||||
|
/// crashes during execution, a negative value will be returned.
|
||||||
|
int performJobsImpl(bool &abnormalExit);
|
||||||
|
|
||||||
/// \brief Performs a single Job by executing in place, if possible.
|
/// \brief Performs a single Job by executing in place, if possible.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -165,6 +165,9 @@ namespace driver {
|
|||||||
/// Cumulative result of PerformJobs(), accumulated from subprocesses.
|
/// Cumulative result of PerformJobs(), accumulated from subprocesses.
|
||||||
int Result = EXIT_SUCCESS;
|
int Result = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
/// True if any Job crashed.
|
||||||
|
bool AnyAbnormalExit = false;
|
||||||
|
|
||||||
/// Timers for monitoring execution time of subprocesses.
|
/// Timers for monitoring execution time of subprocesses.
|
||||||
llvm::TimerGroup DriverTimerGroup {"driver", "Driver Compilation Time"};
|
llvm::TimerGroup DriverTimerGroup {"driver", "Driver Compilation Time"};
|
||||||
llvm::SmallDenseMap<const Job *, std::unique_ptr<llvm::Timer>, 16>
|
llvm::SmallDenseMap<const Job *, std::unique_ptr<llvm::Timer>, 16>
|
||||||
@@ -470,6 +473,7 @@ namespace driver {
|
|||||||
|
|
||||||
// Since the task signalled, unconditionally set result to -2.
|
// Since the task signalled, unconditionally set result to -2.
|
||||||
Result = -2;
|
Result = -2;
|
||||||
|
AnyAbnormalExit = true;
|
||||||
|
|
||||||
return TaskFinishedResponse::StopExecution;
|
return TaskFinishedResponse::StopExecution;
|
||||||
}
|
}
|
||||||
@@ -689,6 +693,10 @@ namespace driver {
|
|||||||
Result = Comp.Diags.hadAnyError();
|
Result = Comp.Diags.hadAnyError();
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hadAnyAbnormalExit() {
|
||||||
|
return AnyAbnormalExit;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace driver
|
} // namespace driver
|
||||||
} // namespace swift
|
} // namespace swift
|
||||||
@@ -816,8 +824,7 @@ static bool writeFilelistIfNecessary(const Job *job, DiagnosticEngine &diags) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Compilation::performJobsImpl() {
|
int Compilation::performJobsImpl(bool &abnormalExit) {
|
||||||
|
|
||||||
PerformJobsState State(*this);
|
PerformJobsState State(*this);
|
||||||
|
|
||||||
State.scheduleInitialJobs();
|
State.scheduleInitialJobs();
|
||||||
@@ -833,6 +840,7 @@ int Compilation::performJobsImpl() {
|
|||||||
InputInfo);
|
InputInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abnormalExit = State.hadAnyAbnormalExit();
|
||||||
return State.getResult();
|
return State.getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -917,15 +925,14 @@ int Compilation::performJobs() {
|
|||||||
if (!TaskQueue::supportsParallelExecution() && NumberOfParallelCommands > 1) {
|
if (!TaskQueue::supportsParallelExecution() && NumberOfParallelCommands > 1) {
|
||||||
Diags.diagnose(SourceLoc(), diag::warning_parallel_execution_not_supported);
|
Diags.diagnose(SourceLoc(), diag::warning_parallel_execution_not_supported);
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = performJobsImpl();
|
bool abnormalExit;
|
||||||
|
int result = performJobsImpl(abnormalExit);
|
||||||
|
|
||||||
if (!SaveTemps) {
|
if (!SaveTemps) {
|
||||||
// FIXME: Do we want to be deleting temporaries even when a child process
|
for (const auto &pathPair : TempFilePaths) {
|
||||||
// crashes?
|
if (!abnormalExit || pathPair.getValue() == PreserveOnSignal::No)
|
||||||
for (auto &path : TempFilePaths) {
|
(void)llvm::sys::fs::remove(pathPair.getKey());
|
||||||
// Ignore the error code for removing temporary files.
|
|
||||||
(void)llvm::sys::fs::remove(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -945,7 +952,7 @@ const char *Compilation::getAllSourcesPath() const {
|
|||||||
llvm::report_fatal_error("unable to create list of input sources");
|
llvm::report_fatal_error("unable to create list of input sources");
|
||||||
}
|
}
|
||||||
auto *mutableThis = const_cast<Compilation *>(this);
|
auto *mutableThis = const_cast<Compilation *>(this);
|
||||||
mutableThis->addTemporaryFile(Buffer.str());
|
mutableThis->addTemporaryFile(Buffer.str(), PreserveOnSignal::Yes);
|
||||||
mutableThis->AllSourceFilesPath = getArgs().MakeArgString(Buffer);
|
mutableThis->AllSourceFilesPath = getArgs().MakeArgString(Buffer);
|
||||||
}
|
}
|
||||||
return AllSourceFilesPath;
|
return AllSourceFilesPath;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ ToolChain::JobContext::getTemporaryFilePath(const llvm::Twine &name,
|
|||||||
llvm::report_fatal_error("unable to create temporary file for filelist");
|
llvm::report_fatal_error("unable to create temporary file for filelist");
|
||||||
}
|
}
|
||||||
|
|
||||||
C.addTemporaryFile(buffer.str());
|
C.addTemporaryFile(buffer.str(), PreserveOnSignal::Yes);
|
||||||
// We can't just reference the data in the TemporaryFiles vector because
|
// We can't just reference the data in the TemporaryFiles vector because
|
||||||
// that could theoretically get copied to a new address.
|
// that could theoretically get copied to a new address.
|
||||||
return C.getArgs().MakeArgString(buffer.str());
|
return C.getArgs().MakeArgString(buffer.str());
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Fails if the input file is named "bad.swift" or "crash.swift"; otherwise
|
# Fails if the input file is named "bad.swift" or "crash.swift"; otherwise
|
||||||
# dispatches to update-dependencies.py. "crash.swift" gives an exit code
|
# dispatches to update-dependencies.py. "crash.swift" results in an
|
||||||
# other than 1.
|
# exit-by-SIGKILL
|
||||||
#
|
#
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ from __future__ import print_function
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
assert sys.argv[1] == '-frontend'
|
assert sys.argv[1] == '-frontend'
|
||||||
@@ -42,7 +43,8 @@ if (os.path.basename(primaryFile) == 'bad.swift' or
|
|||||||
if os.path.basename(primaryFile) == 'bad.swift':
|
if os.path.basename(primaryFile) == 'bad.swift':
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
sys.exit(129)
|
sys.stdout.flush()
|
||||||
|
os.kill(os.getpid(), signal.SIGKILL)
|
||||||
|
|
||||||
execDir = os.path.dirname(os.path.abspath(__file__))
|
execDir = os.path.dirname(os.path.abspath(__file__))
|
||||||
exec(open(os.path.join(execDir, "update-dependencies.py")).read())
|
exec(open(os.path.join(execDir, "update-dependencies.py")).read())
|
||||||
|
|||||||
@@ -14,6 +14,6 @@
|
|||||||
|
|
||||||
|
|
||||||
// RUN: touch -t 201401240006 %t/other.swift
|
// RUN: touch -t 201401240006 %t/other.swift
|
||||||
// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/fail.py -output-file-map %t/output.json -whole-module-optimization ./main.swift ./other.swift -module-name main -j1 -v 2>&1
|
// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/../Inputs/fail.py -output-file-map %t/output.json -whole-module-optimization ./main.swift ./other.swift -module-name main -j1 -v 2>&1
|
||||||
|
|
||||||
// Just don't crash.
|
// Just don't crash.
|
||||||
|
|||||||
14
test/Driver/Inputs/crash.py
Executable file
14
test/Driver/Inputs/crash.py
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# crash.py - Sends SIGKILL to self. -*- python -*-
|
||||||
|
#
|
||||||
|
# This source file is part of the Swift.org open source project
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||||
|
# Licensed under Apache License v2.0 with Runtime Library Exception
|
||||||
|
#
|
||||||
|
# See https://swift.org/LICENSE.txt for license information
|
||||||
|
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||||
|
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
os.kill(os.getpid(), signal.SIGKILL)
|
||||||
@@ -32,6 +32,15 @@
|
|||||||
// CHECK-WMO-THREADED-NEXT: ...with output!
|
// CHECK-WMO-THREADED-NEXT: ...with output!
|
||||||
// CHECK-WMO-THREADED-NOT: Handled
|
// CHECK-WMO-THREADED-NOT: Handled
|
||||||
|
|
||||||
|
// RUN: mkdir -p %t/tmp-fail/
|
||||||
|
// RUN: (cd %t && not env TMPDIR="%t/tmp-fail/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/fail.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1)
|
||||||
|
// RUN: not ls %t/tmp-fail/sources-*
|
||||||
|
// RUN: not ls %t/tmp-fail/outputs-*
|
||||||
|
|
||||||
|
// RUN: mkdir -p %t/tmp-crash/
|
||||||
|
// RUN: (cd %t && not env TMPDIR="%t/tmp-crash/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/crash.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1)
|
||||||
|
// RUN: ls %t/tmp-crash/sources-* %t/tmp-crash/outputs-*
|
||||||
|
|
||||||
|
|
||||||
// RUN: (cd %t && env PATH=%t/bin/:$PATH %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s)
|
// RUN: (cd %t && env PATH=%t/bin/:$PATH %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s)
|
||||||
// RUN: (cd %t && env PATH=%t/bin/:$PATH %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s)
|
// RUN: (cd %t && env PATH=%t/bin/:$PATH %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/filelists/check-filelist-abc.py -emit-library ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-use-filelists -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1 2>&1 | %FileCheck -check-prefix=CHECK-LINK %s)
|
||||||
|
|||||||
Reference in New Issue
Block a user