Skip to content

Commit 4375ebb

Browse files
authored
pass -fno-omit-frame-pointer in support of new backtracer (#7042)
motivation: new backtracer relies on frame pointers. make sure we do not omit them. this can be removed once the backtracer uses DWARF instead of frame pointers changes: * introduce new hidden "omitFramePointers" build option to explicit omit frame pointers * unless user specifies omitFramePointers/noOmitFramePointers, pass -fno-omit-frame-pointer to the compiler on Linux only * add and adjust tests rdar://117578677
1 parent c9bf5b3 commit 4375ebb

File tree

8 files changed

+137
-9
lines changed

8 files changed

+137
-9
lines changed

Sources/Build/BuildDescription/ClangTargetBuildDescription.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,17 @@ public final class ClangTargetBuildDescription {
301301
args += ["-flto=thin"]
302302
}
303303

304+
// rdar://117578677
305+
// Pass -fno-omit-frame-pointer to support backtraces
306+
// this can be removed once the backtracer uses DWARF instead of frame pointers
307+
if let omitFramePointers = self.buildParameters.debuggingParameters.omitFramePointers {
308+
if omitFramePointers {
309+
args += ["-fomit-frame-pointer"]
310+
} else {
311+
args += ["-fno-omit-frame-pointer"]
312+
}
313+
}
314+
304315
// Pass default include paths from the toolchain.
305316
for includeSearchPath in self.buildParameters.toolchain.includeSearchPaths {
306317
args += ["-I", includeSearchPath.pathString]

Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,17 @@ public final class SwiftTargetBuildDescription {
589589

590590
args += self.packageNameArgumentIfSupported(with: self.package, packageAccess: self.target.packageAccess)
591591
args += try self.macroArguments()
592+
593+
// rdar://117578677
594+
// Pass -fno-omit-frame-pointer to support backtraces
595+
// this can be removed once the backtracer uses DWARF instead of frame pointers
596+
if let omitFramePointers = self.buildParameters.debuggingParameters.omitFramePointers {
597+
if omitFramePointers {
598+
args += ["-Xcc", "-fomit-frame-pointer"]
599+
} else {
600+
args += ["-Xcc", "-fno-omit-frame-pointer"]
601+
}
602+
}
592603

593604
return args
594605
}

Sources/CoreCommands/Options.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,11 @@ public struct BuildOptions: ParsableArguments {
480480
@Flag(inversion: .prefixedEnableDisable, help: .hidden)
481481
public var getTaskAllowEntitlement: Bool? = nil
482482

483+
// Whether to omit frame pointers
484+
// this can be removed once the backtracer uses DWARF instead of frame pointers
485+
@Flag(inversion: .prefixedNo, help: .hidden)
486+
public var omitFramePointers: Bool? = nil
487+
483488
// @Flag works best when there is a default value present
484489
// if true, false aren't enough and a third state is needed
485490
// nil should not be the goto. Instead create an enum

Sources/CoreCommands/SwiftTool.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,8 @@ public final class SwiftTool {
703703
debugInfoFormat: options.build.debugInfoFormat.buildParameter,
704704
targetTriple: targetTriple,
705705
shouldEnableDebuggingEntitlement:
706-
options.build.getTaskAllowEntitlement ?? (options.build.configuration == .debug)
706+
options.build.getTaskAllowEntitlement ?? (options.build.configuration == .debug),
707+
omitFramePointers: options.build.omitFramePointers
707708
),
708709
driverParameters: .init(
709710
canRenameEntrypointFunctionName: driverSupport.checkSupportedFrontendFlags(

Sources/SPMBuildCore/BuildParameters/BuildParameters+Debugging.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,36 @@ extension BuildParameters {
1818
public init(
1919
debugInfoFormat: DebugInfoFormat = .dwarf,
2020
targetTriple: Triple,
21-
shouldEnableDebuggingEntitlement: Bool
21+
shouldEnableDebuggingEntitlement: Bool,
22+
omitFramePointers: Bool?
2223
) {
2324
self.debugInfoFormat = debugInfoFormat
2425

2526
// Per rdar://112065568 for backtraces to work on macOS a special entitlement needs to be granted on the final
2627
// executable.
2728
self.shouldEnableDebuggingEntitlement = targetTriple.isMacOSX && shouldEnableDebuggingEntitlement
29+
// rdar://117578677: frame-pointer to support backtraces
30+
// this can be removed once the backtracer uses DWARF instead of frame pointers
31+
if let omitFramePointers {
32+
// if set, we respect user's preference
33+
self.omitFramePointers = omitFramePointers
34+
} else if targetTriple.isLinux() {
35+
// on Linux we preserve frame pointers by default
36+
self.omitFramePointers = false
37+
} else {
38+
// otherwise, use the platform default
39+
self.omitFramePointers = nil
40+
}
2841
}
2942

3043
public var debugInfoFormat: DebugInfoFormat
3144

3245
/// Whether the produced executable should be codesigned with the debugging entitlement, enabling enhanced
3346
/// backtraces on macOS.
3447
public var shouldEnableDebuggingEntitlement: Bool
48+
49+
/// Whether to omit frame pointers
50+
public var omitFramePointers: Bool?
3551
}
3652

3753
/// Represents the debugging strategy.

Sources/SPMBuildCore/BuildParameters/BuildParameters.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ public struct BuildParameters: Encodable {
141141
let targetTriple = try targetTriple ?? .getHostTriple(usingSwiftCompiler: toolchain.swiftCompilerPath)
142142
self.debuggingParameters = debuggingParameters ?? .init(
143143
targetTriple: targetTriple,
144-
shouldEnableDebuggingEntitlement: configuration == .debug
144+
shouldEnableDebuggingEntitlement: configuration == .debug,
145+
omitFramePointers: nil
145146
)
146147

147148
self.dataPath = dataPath

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,10 @@ final class BuildPlanTests: XCTestCase {
11891189
#endif
11901190

11911191
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
1192+
1193+
if hostTriple.isLinux() {
1194+
args += ["-fno-omit-frame-pointer"]
1195+
}
11921196

11931197
XCTAssertEqual(try ext.basicArguments(isCXX: false), args)
11941198
XCTAssertEqual(try ext.objects, [buildPath.appending(components: "extlib.build", "extlib.c.o")])
@@ -1219,6 +1223,11 @@ final class BuildPlanTests: XCTestCase {
12191223
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
12201224
#endif
12211225
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
1226+
1227+
if hostTriple.isLinux() {
1228+
args += ["-fno-omit-frame-pointer"]
1229+
}
1230+
12221231
XCTAssertEqual(try exe.basicArguments(isCXX: false), args)
12231232
XCTAssertEqual(try exe.objects, [buildPath.appending(components: "exe.build", "main.c.o")])
12241233
XCTAssertEqual(exe.moduleMap, nil)
@@ -1514,6 +1523,11 @@ final class BuildPlanTests: XCTestCase {
15141523
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
15151524
#endif
15161525
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
1526+
1527+
if hostTriple.isLinux() {
1528+
args += ["-fno-omit-frame-pointer"]
1529+
}
1530+
15171531
XCTAssertEqual(try lib.basicArguments(isCXX: false), args)
15181532
XCTAssertEqual(try lib.objects, [buildPath.appending(components: "lib.build", "lib.c.o")])
15191533
XCTAssertEqual(lib.moduleMap, buildPath.appending(components: "lib.build", "module.modulemap"))
@@ -2509,6 +2523,11 @@ final class BuildPlanTests: XCTestCase {
25092523
#endif
25102524

25112525
expectedExeBasicArgs += [triple.isWindows() ? "-gdwarf" : "-g"]
2526+
2527+
if triple.isLinux() {
2528+
expectedExeBasicArgs += ["-fno-omit-frame-pointer"]
2529+
}
2530+
25122531
XCTAssertEqual(try exe.basicArguments(isCXX: false), expectedExeBasicArgs)
25132532
XCTAssertEqual(try exe.objects, [buildPath.appending(components: "exe.build", "main.c.o")])
25142533
XCTAssertEqual(exe.moduleMap, nil)
@@ -2530,6 +2549,11 @@ final class BuildPlanTests: XCTestCase {
25302549
triple.isWindows() ? "-gdwarf" : "-g",
25312550
triple.isWindows() ? "-gdwarf" : "-g",
25322551
]
2552+
2553+
if triple.isLinux() {
2554+
expectedLibBasicArgs += ["-fno-omit-frame-pointer"]
2555+
}
2556+
25332557
XCTAssertEqual(try lib.basicArguments(isCXX: true), expectedLibBasicArgs)
25342558

25352559
XCTAssertEqual(try lib.objects, [buildPath.appending(components: "lib.build", "lib.cpp.o")])
@@ -3059,7 +3083,7 @@ final class BuildPlanTests: XCTestCase {
30593083
"-fblocks", "-fmodules", "-fmodule-name=lib",
30603084
"-I", Pkg.appending(components: "Sources", "lib", "include").pathString,
30613085
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))",
3062-
"-g",
3086+
"-g"
30633087
]
30643088
XCTAssertEqual(try lib.basicArguments(isCXX: false), args)
30653089
XCTAssertEqual(try lib.objects, [buildPath.appending(components: "lib.build", "lib.c.o")])
@@ -3136,7 +3160,10 @@ final class BuildPlanTests: XCTestCase {
31363160

31373161
func createResult(for triple: Basics.Triple) throws -> BuildPlanResult {
31383162
try BuildPlanResult(plan: BuildPlan(
3139-
buildParameters: mockBuildParameters(canRenameEntrypointFunctionName: true, targetTriple: triple),
3163+
buildParameters: mockBuildParameters(
3164+
canRenameEntrypointFunctionName: true,
3165+
targetTriple: triple
3166+
),
31403167
graph: graph,
31413168
fileSystem: fs,
31423169
observabilityScope: observability.topScope
@@ -3511,18 +3538,68 @@ final class BuildPlanTests: XCTestCase {
35113538
XCTAssertMatch(dep, [.anySequence, "-DDEP", .anySequence])
35123539

35133540
let cbar = try result.target(for: "cbar").clangTarget().basicArguments(isCXX: false)
3514-
XCTAssertMatch(cbar, [.anySequence, "-DCCC=2", "-I\(A.appending(components: "Sources", "cbar", "Sources", "headers"))", "-I\(A.appending(components: "Sources", "cbar", "Sources", "cppheaders"))", "-Icfoo", "-L", "cbar", "-Icxxfoo", "-L", "cxxbar", "-g", .end])
3541+
XCTAssertMatch(cbar, [.anySequence, "-DCCC=2", "-I\(A.appending(components: "Sources", "cbar", "Sources", "headers"))", "-I\(A.appending(components: "Sources", "cbar", "Sources", "cppheaders"))", "-Icfoo", "-L", "cbar", "-Icxxfoo", "-L", "cxxbar", "-g", "-fno-omit-frame-pointer", .end])
35153542

35163543
let bar = try result.target(for: "bar").swiftTarget().compileArguments()
3517-
XCTAssertMatch(bar, [.anySequence, "-DLINUX", "-Isfoo", "-L", "sbar", "-cxx-interoperability-mode=default", "-enable-upcoming-feature", "BestFeature", "-g", "-Xcc", "-g", .end])
3544+
XCTAssertMatch(bar, [.anySequence, "-DLINUX", "-Isfoo", "-L", "sbar", "-cxx-interoperability-mode=default", "-enable-upcoming-feature", "BestFeature", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end])
35183545

35193546
let exe = try result.target(for: "exe").swiftTarget().compileArguments()
3520-
XCTAssertMatch(exe, [.anySequence, "-DFOO", "-g", "-Xcc", "-g", .end])
3547+
XCTAssertMatch(exe, [.anySequence, "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end])
35213548

35223549
let linkExe = try result.buildProduct(for: "exe").linkArguments()
35233550
XCTAssertMatch(linkExe, [.anySequence, "-lsqlite3", "-llibz", "-Ilfoo", "-L", "lbar", "-g", .end])
35243551
}
35253552

3553+
// omit frame pointers explicitly set to true
3554+
do {
3555+
let result = try BuildPlanResult(plan: BuildPlan(
3556+
buildParameters: mockBuildParameters(
3557+
targetTriple: .x86_64Linux,
3558+
omitFramePointers: true
3559+
),
3560+
graph: graph,
3561+
fileSystem: fs,
3562+
observabilityScope: observability.topScope
3563+
))
3564+
3565+
let dep = try result.target(for: "t1").swiftTarget().compileArguments()
3566+
XCTAssertMatch(dep, [.anySequence, "-DDEP", .anySequence])
3567+
3568+
let cbar = try result.target(for: "cbar").clangTarget().basicArguments(isCXX: false)
3569+
XCTAssertMatch(cbar, [.anySequence, "-DCCC=2", "-I\(A.appending(components: "Sources", "cbar", "Sources", "headers"))", "-I\(A.appending(components: "Sources", "cbar", "Sources", "cppheaders"))", "-Icfoo", "-L", "cbar", "-Icxxfoo", "-L", "cxxbar", "-g", "-fomit-frame-pointer", .end])
3570+
3571+
let bar = try result.target(for: "bar").swiftTarget().compileArguments()
3572+
XCTAssertMatch(bar, [.anySequence, "-DLINUX", "-Isfoo", "-L", "sbar", "-cxx-interoperability-mode=default", "-enable-upcoming-feature", "BestFeature", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", .end])
3573+
3574+
let exe = try result.target(for: "exe").swiftTarget().compileArguments()
3575+
XCTAssertMatch(exe, [.anySequence, "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", .end])
3576+
}
3577+
3578+
// omit frame pointers explicitly set to false
3579+
do {
3580+
let result = try BuildPlanResult(plan: BuildPlan(
3581+
buildParameters: mockBuildParameters(
3582+
targetTriple: .x86_64Linux,
3583+
omitFramePointers: false
3584+
),
3585+
graph: graph,
3586+
fileSystem: fs,
3587+
observabilityScope: observability.topScope
3588+
))
3589+
3590+
let dep = try result.target(for: "t1").swiftTarget().compileArguments()
3591+
XCTAssertMatch(dep, [.anySequence, "-DDEP", .anySequence])
3592+
3593+
let cbar = try result.target(for: "cbar").clangTarget().basicArguments(isCXX: false)
3594+
XCTAssertMatch(cbar, [.anySequence, "-DCCC=2", "-I\(A.appending(components: "Sources", "cbar", "Sources", "headers"))", "-I\(A.appending(components: "Sources", "cbar", "Sources", "cppheaders"))", "-Icfoo", "-L", "cbar", "-Icxxfoo", "-L", "cxxbar", "-g", "-fno-omit-frame-pointer", .end])
3595+
3596+
let bar = try result.target(for: "bar").swiftTarget().compileArguments()
3597+
XCTAssertMatch(bar, [.anySequence, "-DLINUX", "-Isfoo", "-L", "sbar", "-cxx-interoperability-mode=default", "-enable-upcoming-feature", "BestFeature", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end])
3598+
3599+
let exe = try result.target(for: "exe").swiftTarget().compileArguments()
3600+
XCTAssertMatch(exe, [.anySequence, "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end])
3601+
}
3602+
35263603
do {
35273604
let result = try createResult(for: .x86_64MacOS)
35283605

Tests/BuildTests/MockBuildTestHelper.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ func mockBuildParameters(
7878
indexStoreMode: BuildParameters.IndexStoreMode = .off,
7979
useExplicitModuleBuild: Bool = false,
8080
linkerDeadStrip: Bool = true,
81-
linkTimeOptimizationMode: BuildParameters.LinkTimeOptimizationMode? = nil
81+
linkTimeOptimizationMode: BuildParameters.LinkTimeOptimizationMode? = nil,
82+
omitFramePointers: Bool? = nil
8283
) -> BuildParameters {
8384
return try! BuildParameters(
8485
dataPath: buildPath,
@@ -90,6 +91,11 @@ func mockBuildParameters(
9091
pkgConfigDirectories: [],
9192
workers: 3,
9293
indexStoreMode: indexStoreMode,
94+
debuggingParameters: .init(
95+
targetTriple: targetTriple,
96+
shouldEnableDebuggingEntitlement: config == .debug,
97+
omitFramePointers: omitFramePointers
98+
),
9399
driverParameters: .init(
94100
canRenameEntrypointFunctionName: canRenameEntrypointFunctionName,
95101
useExplicitModuleBuild: useExplicitModuleBuild

0 commit comments

Comments
 (0)