Skip to content
This repository has been archived by the owner on Oct 28, 2024. It is now read-only.

Commit

Permalink
trap SIGSEGV, SIGBUS and SIGFPE by default, in addition to SIGILL (#58)
Browse files Browse the repository at this point in the history
motivation: print crash trace on segmentation faults and other common failures beyond illegal instructions

changes:
* include SIGSEGV, SIGBUS and SIGFPE in the default signals trapped
* update and improve tests
  • Loading branch information
tomerd authored Jun 8, 2022
1 parent 7ba397e commit baf9597
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 18 deletions.
4 changes: 2 additions & 2 deletions Sources/Backtrace/Backtrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ private func printBacktrace(signal: CInt) {
}

public enum Backtrace {
/// Install the backtrace handler on `SIGILL`.
/// Install the backtrace handler on default signals: `SIGILL`, `SIGSEGV`, `SIGBUS`, `SIGFPE`.
public static func install() {
Backtrace.install(signals: [SIGILL])
Backtrace.install(signals: [SIGILL, SIGSEGV, SIGBUS, SIGFPE])
}

/// Install the backtrace handler when any of `signals` happen.
Expand Down
25 changes: 23 additions & 2 deletions Sources/Sample/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,28 @@
//===----------------------------------------------------------------------===//

import Backtrace
#if canImport(Darwin)
import Darwin
#elseif os(Linux)
import Glibc
#endif

let reason = CommandLine.arguments.count == 2 ? CommandLine.arguments[1] : "unknown"
Backtrace.install()
fatalError(reason)

func raiseSignal(_ signal: Int32) {
raise(signal)
}

let reason = CommandLine.arguments.count == 2 ? CommandLine.arguments[1] : "unknown"
switch reason.uppercased() {
case "SIGILL":
raiseSignal(SIGILL)
case "SIGSEGV":
raiseSignal(SIGSEGV)
case "SIGBUS":
raiseSignal(SIGBUS)
case "SIGFPE":
raiseSignal(SIGFPE)
default:
fatalError(reason)
}
6 changes: 5 additions & 1 deletion Tests/BacktraceTests/BacktraceTests+XCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ import XCTest
extension BacktraceTests {
public static var allTests: [(String, (BacktraceTests) -> () throws -> Void)] {
return [
("testBacktrace", testBacktrace),
("testFatalError", testFatalError),
("testSIGILL", testSIGILL),
("testSIGSEGV", testSIGSEGV),
("testSIGBUS", testSIGBUS),
("testSIGFPE", testSIGFPE),
]
}
}
78 changes: 66 additions & 12 deletions Tests/BacktraceTests/BacktraceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,76 @@
import XCTest

public final class BacktraceTests: XCTestCase {
func testBacktrace() {
#if os(Linux)
func testFatalError() throws {
#if !os(Linux)
try XCTSkipIf(true, "test is only supported on Linux")
#endif

let expectedError = UUID().uuidString
let pipe = Pipe()
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/swift")
process.arguments = ["run", "Sample", expectedError]
process.standardError = pipe
XCTAssertNoThrow(try process.run())
if process.isRunning {
process.waitUntilExit()
}
let stderr = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
let stderr = try runSample(reason: expectedError)
print(stderr)

XCTAssert(stderr.contains("Received signal 4. Backtrace:"))
XCTAssert(stderr.contains("Current stack trace:"), "expected stanard error to include backtrace")
XCTAssert(stderr.contains("Fatal error: \(expectedError)"), "expected stanard error to include error information")
}

func testSIGILL() throws {
#if !os(Linux)
try XCTSkipIf(true, "test is only supported on Linux")
#endif

let stderr = try runSample(reason: "SIGILL")
print(stderr)

XCTAssert(stderr.contains("Received signal \(SIGILL). Backtrace:"))
XCTAssert(stderr.contains("Sample.raiseSignal"))
}

func testSIGSEGV() throws {
#if !os(Linux)
try XCTSkipIf(true, "test is only supported on Linux")
#endif

let stderr = try runSample(reason: "SIGSEGV")
print(stderr)

XCTAssert(stderr.contains("Received signal \(SIGSEGV). Backtrace:"))
XCTAssert(stderr.contains("Sample.raiseSignal"))
}

func testSIGBUS() throws {
#if !os(Linux)
try XCTSkipIf(true, "test is only supported on Linux")
#endif

let stderr = try runSample(reason: "SIGBUS")
print(stderr)

XCTAssert(stderr.contains("Received signal \(SIGBUS). Backtrace:"))
XCTAssert(stderr.contains("Sample.raiseSignal"))
}

func testSIGFPE() throws {
#if !os(Linux)
try XCTSkipIf(true, "test is only supported on Linux")
#endif

let stderr = try runSample(reason: "SIGFPE")
print(stderr)

XCTAssert(stderr.contains("Received signal \(SIGFPE). Backtrace:"))
XCTAssert(stderr.contains("Sample.raiseSignal"))
}

func runSample(reason: String) throws -> String {
let pipe = Pipe()
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/swift")
process.arguments = ["run", "Sample", reason]
process.standardError = pipe
try process.run()
process.waitUntilExit()
return String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
}
}
2 changes: 1 addition & 1 deletion docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ services:

shell:
<<: *common
entrypoint: /bin/bash
entrypoint: /bin/bash -l

0 comments on commit baf9597

Please sign in to comment.