Skip to content

Commit 7836ba6

Browse files
committed
Always record backtraces when checking for null builds
This should hopefully give us more insight into flaky tests.
1 parent caa4820 commit 7836ba6

File tree

1 file changed

+44
-16
lines changed

1 file changed

+44
-16
lines changed

Sources/SWBTestSupport/BuildOperationTester.swift

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package import SWBBuildSystem
1919
@_spi(Testing) package import SWBCore
2020
package import SWBTaskConstruction
2121
package import SWBTaskExecution
22-
package import SWBUtil
22+
@_spi(Testing) package import SWBUtil
2323

2424
private import class SWBLLBuild.BuildDB
2525
private import class SWBLLBuild.BuildKey
@@ -713,6 +713,18 @@ package final class BuildOperationTester {
713713

714714
}
715715

716+
package func checkNoTaskWithBacktraces(_ conditions: TaskCondition..., sourceLocation: SourceLocation = #_sourceLocation) {
717+
for matchedTask in findMatchingTasks(conditions) {
718+
Issue.record("found unexpected task matching conditions '\(conditions)', found: \(matchedTask)", sourceLocation: sourceLocation)
719+
720+
if let frameID = getBacktraceID(matchedTask, sourceLocation: sourceLocation) {
721+
enumerateBacktraces(frameID) { _, category, description in
722+
Issue.record("...<category='\(category)' description='\(description)'>", sourceLocation: sourceLocation)
723+
}
724+
}
725+
}
726+
}
727+
716728
/// Check whether the results contains a dependency cycle error. If so, then consume the error and create a `CycleChecking` object and pass it to the block. Otherwise fail.
717729
package func checkDependencyCycle(_ pattern: StringPattern, kind: DiagnosticKind = .error, failIfNotFound: Bool = true, sourceLocation: SourceLocation = #_sourceLocation, body: (CycleChecker) async throws -> Void) async throws {
718730
guard let message = getDiagnosticMessage(pattern, kind: kind, checkDiagnostic: { _ in true }) else {
@@ -1011,8 +1023,21 @@ package final class BuildOperationTester {
10111023
startedTasks.remove(task)
10121024
}
10131025

1014-
package func checkBacktrace(_ identifier: BuildOperationBacktraceFrameEmitted.Identifier, _ patterns: [StringPattern], sourceLocation: SourceLocation = #_sourceLocation) {
1015-
var frameDescriptions: [String] = []
1026+
private func getBacktraceID(_ task: Task, sourceLocation: SourceLocation = #_sourceLocation) -> BuildOperationBacktraceFrameEmitted.Identifier? {
1027+
guard let frameID: BuildOperationBacktraceFrameEmitted.Identifier = events.compactMap ({ (event) -> BuildOperationBacktraceFrameEmitted.Identifier? in
1028+
guard case .emittedBuildBacktraceFrame(identifier: let identifier, previousFrameIdentifier: _, category: _, description: _) = event, case .task(let signature) = identifier, BuildOperationTaskSignature.taskIdentifier(ByteString(encodingAsUTF8: task.identifier.rawValue)) == signature else {
1029+
return nil
1030+
}
1031+
return identifier
1032+
// Iff the task is a dynamic task, there may be more than one corresponding frame if it was requested multiple times, in which case we choose the first. Non-dynamic tasks always have a 1-1 relationship with frames.
1033+
}).sorted().first else {
1034+
Issue.record("Did not find a single build backtrace frame for task: \(task.identifier)", sourceLocation: sourceLocation)
1035+
return nil
1036+
}
1037+
return frameID
1038+
}
1039+
1040+
private func enumerateBacktraces(_ identifier: BuildOperationBacktraceFrameEmitted.Identifier, _ handleFrameInfo: (_ identifier: BuildOperationBacktraceFrameEmitted.Identifier?, _ category: BuildOperationBacktraceFrameEmitted.Category, _ description: String) -> ()) {
10161041
var currentFrameID: BuildOperationBacktraceFrameEmitted.Identifier? = identifier
10171042
while let id = currentFrameID {
10181043
if let frameInfo: (BuildOperationBacktraceFrameEmitted.Identifier?, BuildOperationBacktraceFrameEmitted.Category, String) = events.compactMap({ (event) -> (BuildOperationBacktraceFrameEmitted.Identifier?, BuildOperationBacktraceFrameEmitted.Category, String)? in
@@ -1022,28 +1047,29 @@ package final class BuildOperationTester {
10221047
return (previousFrameIdentifier, category, description)
10231048
// Iff the task is a dynamic task, there may be more than one corresponding frame if it was requested multiple times, in which case we choose the first. Non-dynamic tasks always have a 1-1 relationship with frames.
10241049
}).sorted(by: { $0.0 }).first {
1025-
frameDescriptions.append("<category='\(frameInfo.1)' description='\(frameInfo.2)'>")
1050+
handleFrameInfo(frameInfo.0, frameInfo.1, frameInfo.2)
10261051
currentFrameID = frameInfo.0
10271052
} else {
10281053
currentFrameID = nil
10291054
}
10301055
}
1056+
}
1057+
1058+
package func checkBacktrace(_ identifier: BuildOperationBacktraceFrameEmitted.Identifier, _ patterns: [StringPattern], sourceLocation: SourceLocation = #_sourceLocation) {
1059+
var frameDescriptions: [String] = []
1060+
enumerateBacktraces(identifier) { (_, category, description) in
1061+
frameDescriptions.append("<category='\(category)' description='\(description)'>")
1062+
}
10311063

10321064
XCTAssertMatch(frameDescriptions, patterns, sourceLocation: sourceLocation)
10331065
}
10341066

10351067
package func checkBacktrace(_ task: Task, _ patterns: [StringPattern], sourceLocation: SourceLocation = #_sourceLocation) {
1036-
guard let frameID: BuildOperationBacktraceFrameEmitted.Identifier = events.compactMap ({ (event) -> BuildOperationBacktraceFrameEmitted.Identifier? in
1037-
guard case .emittedBuildBacktraceFrame(identifier: let identifier, previousFrameIdentifier: _, category: _, description: _) = event, case .task(let signature) = identifier, BuildOperationTaskSignature.taskIdentifier(ByteString(encodingAsUTF8: task.identifier.rawValue)) == signature else {
1038-
return nil
1039-
}
1040-
return identifier
1041-
// Iff the task is a dynamic task, there may be more than one corresponding frame if it was requested multiple times, in which case we choose the first. Non-dynamic tasks always have a 1-1 relationship with frames.
1042-
}).sorted().first else {
1043-
Issue.record("Did not find a single build backtrace frame for task: \(task.identifier)", sourceLocation: sourceLocation)
1044-
return
1068+
if let frameID = getBacktraceID(task, sourceLocation: sourceLocation) {
1069+
checkBacktrace(frameID, patterns, sourceLocation: sourceLocation)
1070+
} else {
1071+
// already recorded an issue
10451072
}
1046-
checkBacktrace(frameID, patterns, sourceLocation: sourceLocation)
10471073
}
10481074

10491075
private class TaskDependencyResolver {
@@ -1520,7 +1546,7 @@ package final class BuildOperationTester {
15201546

15211547
func body(results: BuildResults) throws -> Void {
15221548
results.consumeTasksMatchingRuleTypes(excludedTasks)
1523-
results.checkNoTask(sourceLocation: sourceLocation)
1549+
results.checkNoTaskWithBacktraces(sourceLocation: sourceLocation)
15241550

15251551
results.checkNote(.equal("Building targets in dependency order"), failIfNotFound: false)
15261552
results.checkNote(.prefix("Target dependency graph"), failIfNotFound: false)
@@ -1546,7 +1572,9 @@ package final class BuildOperationTester {
15461572
}
15471573
}
15481574

1549-
try await checkBuild(name, parameters: parameters, runDestination: runDestination, buildRequest: inputBuildRequest, buildCommand: buildCommand, schemeCommand: schemeCommand, persistent: persistent, serial: serial, buildOutputMap: buildOutputMap, signableTargets: signableTargets, signableTargetInputs: signableTargetInputs, clientDelegate: clientDelegate, sourceLocation: sourceLocation, body: body)
1575+
try await UserDefaults.withEnvironment(["EnableBuildBacktraceRecording": "true"]) {
1576+
try await checkBuild(name, parameters: parameters, runDestination: runDestination, buildRequest: inputBuildRequest, buildCommand: buildCommand, schemeCommand: schemeCommand, persistent: persistent, serial: serial, buildOutputMap: buildOutputMap, signableTargets: signableTargets, signableTargetInputs: signableTargetInputs, clientDelegate: clientDelegate, sourceLocation: sourceLocation, body: body)
1577+
}
15501578
}
15511579

15521580
package static func buildRequestForIndexOperation(

0 commit comments

Comments
 (0)