Skip to content

Commit 8c1852f

Browse files
Add swift crash producer support
When swift compiler crashed with swift caching enabled, create a crash reproducer automatically.
1 parent 8594e08 commit 8c1852f

File tree

5 files changed

+68
-6
lines changed

5 files changed

+68
-6
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import struct TSCBasic.AbsolutePath
2323
import struct TSCBasic.ByteString
2424
import struct TSCBasic.Diagnostic
2525
import struct TSCBasic.FileInfo
26+
import struct TSCBasic.ProcessResult
2627
import struct TSCBasic.RelativePath
2728
import var TSCBasic.localFileSystem
2829
import var TSCBasic.stderrStream
@@ -68,6 +69,7 @@ public struct Driver {
6869
case conditionalCompilationFlagIsNotValidIdentifier(String)
6970
case baselineGenerationRequiresTopLevelModule(String)
7071
case optionRequiresAnother(String, String)
72+
case unableToCreateReproducer
7173
// Explicit Module Build Failures
7274
case malformedModuleDependency(String, String)
7375
case missingModuleDependency(String)
@@ -137,6 +139,8 @@ public struct Driver {
137139
return "generating a baseline with '\(arg)' is only supported with '-emit-module' or '-emit-module-path'"
138140
case .optionRequiresAnother(let first, let second):
139141
return "'\(first)' cannot be specified if '\(second)' is not present"
142+
case .unableToCreateReproducer:
143+
return "failed to create reproducer"
140144
}
141145
}
142146
}
@@ -1978,7 +1982,8 @@ extension Driver {
19781982
buildRecordInfo: buildRecordInfo,
19791983
showJobLifecycle: showJobLifecycle,
19801984
argsResolver: executor.resolver,
1981-
diagnosticEngine: diagnosticEngine)
1985+
diagnosticEngine: diagnosticEngine,
1986+
reproducerCallback: self.generateReproducer)
19821987
}
19831988

19841989
private mutating func performTheBuild(
@@ -4044,3 +4049,34 @@ extension Driver {
40444049
return mapping
40454050
}
40464051
}
4052+
4053+
// Generate reproducer.
4054+
extension Driver {
4055+
func generateReproducer(_ job: Job, _ status: ProcessResult.ExitStatus) throws {
4056+
// If process is not terminated with certain error code, no need to create reproducer.
4057+
#if os(Windows)
4058+
guard case .abnormal = status else {
4059+
return;
4060+
}
4061+
#else
4062+
guard case .signalled = status else {
4063+
return;
4064+
}
4065+
#endif
4066+
// TODO: check flag is supported.
4067+
try withTemporaryDirectory(dir: fileSystem.tempDirectory, prefix: "swift-reproducer", removeTreeOnDeinit: false) { tempDir in
4068+
var reproJob = job
4069+
reproJob.kind = .reproducer
4070+
reproJob.commandLine.appendFlag("-gen-reproducer")
4071+
reproJob.commandLine.appendFlag("-gen-reproducer-dir")
4072+
reproJob.commandLine.appendPath(tempDir)
4073+
reproJob.outputs.removeAll()
4074+
reproJob.outputCacheKeys.removeAll()
4075+
let result = try executor.execute(job: reproJob, forceResponseFiles: false, recordedInputModificationDates: [:])
4076+
guard case .terminated(let code) = result.exitStatus, code == 0 else {
4077+
throw Error.unableToCreateReproducer
4078+
}
4079+
diagnosticEngine.emit(.note_reproducer_created(tempDir.pathString))
4080+
}
4081+
}
4082+
}

Sources/SwiftDriver/Driver/ToolExecutionDelegate.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import struct TSCBasic.Diagnostic
3030
import struct TSCBasic.ProcessResult
3131
import var TSCBasic.stderrStream
3232
import var TSCBasic.stdoutStream
33+
import class TSCBasic.Process
3334

3435
/// Delegate for printing execution information on the command-line.
3536
@_spi(Testing) public final class ToolExecutionDelegate: JobExecutionDelegate {
@@ -49,6 +50,8 @@ import var TSCBasic.stdoutStream
4950
case silent
5051
}
5152

53+
public typealias ReproducerCallback = (Job, ProcessResult.ExitStatus) throws -> Void
54+
5255
public let mode: Mode
5356
public let buildRecordInfo: BuildRecordInfo?
5457
public let showJobLifecycle: Bool
@@ -58,18 +61,21 @@ import var TSCBasic.stdoutStream
5861
private var nextBatchQuasiPID: Int
5962
private let argsResolver: ArgsResolver
6063
private var batchJobInputQuasiPIDMap = TwoLevelMap<Job, TypedVirtualPath, Int>()
64+
private let reproducerCallback: ReproducerCallback?
6165

6266
@_spi(Testing) public init(mode: ToolExecutionDelegate.Mode,
6367
buildRecordInfo: BuildRecordInfo?,
6468
showJobLifecycle: Bool,
6569
argsResolver: ArgsResolver,
66-
diagnosticEngine: DiagnosticsEngine) {
70+
diagnosticEngine: DiagnosticsEngine,
71+
reproducerCallback: ReproducerCallback? = nil) {
6772
self.mode = mode
6873
self.buildRecordInfo = buildRecordInfo
6974
self.showJobLifecycle = showJobLifecycle
7075
self.diagnosticEngine = diagnosticEngine
7176
self.argsResolver = argsResolver
7277
self.nextBatchQuasiPID = ToolExecutionDelegate.QUASI_PID_START
78+
self.reproducerCallback = reproducerCallback
7379
}
7480

7581
public func jobStarted(job: Job, arguments: [String], pid: Int) {
@@ -107,6 +113,14 @@ import var TSCBasic.stdoutStream
107113
}
108114
#endif
109115

116+
if let reproducerCallback = reproducerCallback {
117+
do {
118+
try reproducerCallback(job, result.exitStatus)
119+
} catch {
120+
diagnosticEngine.emit(.error_failed_to_create_reproducer)
121+
}
122+
}
123+
110124
switch mode {
111125
case .silent:
112126
break

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ extension Driver {
104104
.help, .link, .verifyDebugInfo, .scanDependencies,
105105
.emitSupportedFeatures, .moduleWrap,
106106
.generateAPIBaseline, .generateABIBaseline, .compareAPIBaseline,
107-
.compareABIBaseline, .printSupportedFeatures:
107+
.compareABIBaseline, .printSupportedFeatures, .reproducer:
108108
jobNeedPathRemap = false
109109
}
110110
} else {

Sources/SwiftDriver/Jobs/Job.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public struct Job: Codable, Equatable, Hashable {
2525
case emitModule = "emit-module"
2626
case generatePCH = "generate-pch"
2727
case moduleWrap = "module-wrap"
28+
case reproducer = "gen-reproducer"
2829

2930
/// Generate a compiled Clang module.
3031
case generatePCM = "generate-pcm"
@@ -255,6 +256,9 @@ extension Job : CustomStringConvertible {
255256

256257
case .printSupportedFeatures:
257258
return "Print supported upcoming and experimental features"
259+
260+
case .reproducer:
261+
return "Generate reproducer for the crash"
258262
}
259263
}
260264

@@ -273,7 +277,7 @@ extension Job.Kind {
273277
public var isSwiftFrontend: Bool {
274278
switch self {
275279
case .backend, .compile, .mergeModule, .emitModule, .compileModuleFromInterface, .generatePCH,
276-
.generatePCM, .dumpPCM, .interpret, .repl, .printTargetInfo,
280+
.generatePCM, .dumpPCM, .interpret, .repl, .printTargetInfo, .reproducer,
277281
.versionRequest, .emitSupportedFeatures, .scanDependencies, .verifyModuleInterface, .printSupportedFeatures:
278282
return true
279283

@@ -294,7 +298,7 @@ extension Job.Kind {
294298
.help, .link, .verifyDebugInfo, .scanDependencies,
295299
.emitSupportedFeatures, .moduleWrap, .verifyModuleInterface,
296300
.generateAPIBaseline, .generateABIBaseline, .compareAPIBaseline,
297-
.compareABIBaseline, .printSupportedFeatures:
301+
.compareABIBaseline, .printSupportedFeatures, .reproducer:
298302
return false
299303
}
300304
}
@@ -309,7 +313,7 @@ extension Job.Kind {
309313
.versionRequest, .autolinkExtract, .generateDSYM, .help, .link,
310314
.verifyDebugInfo, .scanDependencies, .emitSupportedFeatures, .moduleWrap,
311315
.generateAPIBaseline, .generateABIBaseline, .compareAPIBaseline,
312-
.compareABIBaseline, .printSupportedFeatures:
316+
.compareABIBaseline, .printSupportedFeatures, .reproducer:
313317
return false
314318
}
315319
}

Sources/SwiftDriver/Utilities/Diagnostics.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,12 @@ extension Diagnostic.Message {
182182
static var error_no_objc_interop_embedded: Diagnostic.Message {
183183
.error("Objective-C interop cannot be enabled with embedded Swift.")
184184
}
185+
186+
static var error_failed_to_create_reproducer: Diagnostic.Message {
187+
.error("failed to create crash reproducer")
188+
}
189+
190+
static func note_reproducer_created(_ path: String) -> Diagnostic.Message {
191+
.note("crash reproducer is created at: \(path)")
192+
}
185193
}

0 commit comments

Comments
 (0)