Skip to content

Commit 782187b

Browse files
committed
SwiftDriver: address TODO about abnormal process termination
This adds the handling for abnormal process termination on Windows. With this, we can now track processes which exit abnormally, indicating failure. This was identified by the saveTemps test which now passes on Windows. Add an additional test case for the new message type to differentiate the abnormal exit due to an exception from a signalled exit which is limited to Unixish platforms.
1 parent 708caad commit 782187b

File tree

6 files changed

+94
-11
lines changed

6 files changed

+94
-11
lines changed

Sources/SwiftDriver/Driver/ToolExecutionDelegate.swift

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,15 @@ import Glibc
9191

9292
buildRecordInfo?.jobFinished(job: job, result: result)
9393

94-
// FIXME: Currently, TSCBasic.Process uses NSProcess on Windows and discards
95-
// the bits of the exit code used to differentiate between normal and abnormal
96-
// termination.
97-
#if !os(Windows)
94+
#if os(Windows)
95+
if case .abnormal = result.exitStatus {
96+
anyJobHadAbnormalExit = true
97+
}
98+
#else
9899
if case .signalled = result.exitStatus {
99100
anyJobHadAbnormalExit = true
100101
}
101-
#endif
102+
#endif
102103

103104
switch mode {
104105
case .silent:
@@ -123,7 +124,13 @@ import Glibc
123124
pid: pid).map {
124125
ParsableMessage(name: job.kind.rawValue, kind: .finished($0))
125126
}
126-
#if !os(Windows)
127+
#if os(Windows)
128+
case .abnormal(let exception):
129+
messages = constructAbnormalExitMessage(job: job, output: output,
130+
exception: exception, pid: pid).map {
131+
ParsableMessage(name: job.kind.rawValue, kind: .abnormal($0))
132+
}
133+
#else
127134
case .signalled(let signal):
128135
let errorMessage = strsignal(signal).map { String(cString: $0) } ?? ""
129136
messages = constructJobSignalledMessages(job: job, error: errorMessage, output: output,
@@ -132,6 +139,7 @@ import Glibc
132139
}
133140
#endif
134141
}
142+
135143
for message in messages {
136144
emit(message)
137145
}
@@ -265,6 +273,22 @@ private extension ToolExecutionDelegate {
265273
return FinishedMessage(exitStatus: Int(exitCode), output: output, pid: pid, realPid: realPid)
266274
}
267275

276+
// MARK: - Abnormal Exit
277+
func constructAbnormalExitMessage(job: Job, output: String?, exception: UInt32, pid: Int) -> [AbnormalExitMessage] {
278+
let result: [AbnormalExitMessage]
279+
if job.kind == .compile, job.primaryInputs.count > 1 {
280+
result = job.primaryInputs.map {
281+
guard let quasiPid = batchJobInputQuasiPIDMap[(job, $0)] else {
282+
fatalError("Parsable-Output batch sub-job abnormal exit with no matching started message: \(job.description): \($0.file.description)")
283+
}
284+
return AbnormalExitMessage(pid: quasiPid, realPid: pid, output: output, exception: exception)
285+
}
286+
} else {
287+
result = [AbnormalExitMessage(pid: pid, realPid: pid, output: output, exception: exception)]
288+
}
289+
return result
290+
}
291+
268292
// MARK: - Job Signalled
269293
func constructJobSignalledMessages(job: Job, error: String, output: String?,
270294
signal: Int32, pid: Int) -> [SignalledMessage] {

Sources/SwiftDriver/Execution/DriverExecutor.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,13 @@ extension DriverExecutor {
123123
switch exitStatus {
124124
case .terminated(let code):
125125
returnCode = Int(code)
126-
#if !os(Windows)
126+
#if os(Windows)
127+
case .abnormal(let exception):
128+
returnCode = Int(exception)
129+
#else
127130
case .signalled(let signal):
128131
returnCode = Int(signal)
129-
#endif
132+
#endif
130133
}
131134
return returnCode
132135
}

Sources/SwiftDriver/Execution/ParsableOutput.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Foundation
1616
public enum Kind {
1717
case began(BeganMessage)
1818
case finished(FinishedMessage)
19+
case abnormal(AbnormalExitMessage)
1920
case signalled(SignalledMessage)
2021
case skipped(SkippedMessage)
2122
}
@@ -133,6 +134,27 @@ import Foundation
133134
}
134135
}
135136

137+
@_spi(Testing) public struct AbnormalExitMessage: Encodable {
138+
let pid: Int
139+
let process: ActualProcess
140+
let output: String?
141+
let exception: UInt32
142+
143+
public init(pid: Int, realPid: Int, output: String?, exception: UInt32) {
144+
self.pid = pid
145+
self.process = ActualProcess(realPid: realPid)
146+
self.output = output
147+
self.exception = exception
148+
}
149+
150+
private enum CodingKeys: String, CodingKey {
151+
case pid
152+
case process
153+
case output
154+
case exception
155+
}
156+
}
157+
136158
@_spi(Testing) public struct SignalledMessage: Encodable {
137159
let pid: Int
138160
let process: ActualProcess
@@ -174,6 +196,9 @@ import Foundation
174196
case .finished(let msg):
175197
try container.encode("finished", forKey: .kind)
176198
try msg.encode(to: encoder)
199+
case .abnormal(let msg):
200+
try container.encode("abnormal-exit", forKey: .kind)
201+
try msg.encode(to: encoder)
177202
case .signalled(let msg):
178203
try container.encode("signalled", forKey: .kind)
179204
try msg.encode(to: encoder)

Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ fileprivate class ModuleCompileDelegate: JobExecutionDelegate {
122122
stderrStream.flush()
123123
}
124124
}
125-
#if !os(Windows)
125+
#if os(Windows)
126+
case .abnormal(let exception):
127+
diagnosticsEngine.emit(.remark("\(job.moduleName) exception: \(exception)"))
128+
#else
126129
case .signalled:
127130
diagnosticsEngine.emit(.remark("\(job.moduleName) interrupted"))
128131
#endif

Sources/SwiftDriverExecution/MultiJobExecutor.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,13 @@ public final class MultiJobExecutor {
211211
switch (result.exitStatus, continueBuildingAfterErrors) {
212212
case (.terminated(let code), false) where code != EXIT_SUCCESS:
213213
isBuildCancelled = true
214-
#if !os(Windows)
214+
#if os(Windows)
215+
case (.abnormal, false):
216+
isBuildCancelled = true
217+
#else
215218
case (.signalled, _):
216219
isBuildCancelled = true
217-
#endif
220+
#endif
218221
default:
219222
break
220223
}
@@ -614,6 +617,8 @@ class ExecuteJobRule: LLBuildRule {
614617
if !job.kind.isCompile || code != EXIT_FAILURE {
615618
context.diagnosticsEngine.emit(.error_command_failed(kind: job.kind, code: code))
616619
}
620+
case let .abnormal(exception):
621+
context.diagnosticsEngine.emit(.error_command_exception(kind: job.kind, exception: exception))
617622
#if !os(Windows)
618623
case let .signalled(signal):
619624
// An interrupt of an individual compiler job means it was deliberatly cancelled,
@@ -667,4 +672,8 @@ private extension TSCBasic.Diagnostic.Message {
667672
static func error_command_signalled(kind: Job.Kind, signal: Int32) -> TSCBasic.Diagnostic.Message {
668673
.error("\(kind.rawValue) command failed due to signal \(signal) (use -v to see invocation)")
669674
}
675+
676+
static func error_command_exception(kind: Job.Kind, exception: UInt32) -> TSCBasic.Diagnostic.Message {
677+
.error("\(kind.rawValue) command failed due to exception \(exception) (use -v to see invocation)")
678+
}
670679
}

Tests/SwiftDriverTests/ParsableMessageTests.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,25 @@ final class ParsableMessageTests: XCTestCase {
118118
""")
119119
}
120120

121+
func testAbnormalExitMessage() throws {
122+
let exit = AbnormalExitMessage(pid: 1024, realPid: 1024, output: nil, exception: 0x8000_0003)
123+
let message = ParsableMessage(name: "compile", kind: .abnormal(exit))
124+
let encoded = try message.toJSON()
125+
let string = String(data: encoded, encoding: .utf8)!
126+
127+
XCTAssertEqual(string, """
128+
{
129+
"exception" : 2147483651,
130+
"kind" : "abnormal-exit",
131+
"name" : "compile",
132+
"pid" : 1024,
133+
"process" : {
134+
"real_pid" : 1024
135+
}
136+
}
137+
""")
138+
}
139+
121140
func testBeganBatchMessages() throws {
122141
do {
123142
try withTemporaryDirectory { path in

0 commit comments

Comments
 (0)