Skip to content

Commit

Permalink
The swift-backtrace library is no longer needed in 5.9 (#3064)
Browse files Browse the repository at this point in the history
* The swift-backtrace library is no longer needed in 5.9
* Deprecate StackTrace and immediately remove all its functionality.
* Clear up a whole pile of stray print()s in the tests (they'll run faster and actually be testing something)
  • Loading branch information
gwynne committed Sep 6, 2023
1 parent 546dac1 commit 03a08f6
Show file tree
Hide file tree
Showing 20 changed files with 53 additions and 254 deletions.
4 changes: 0 additions & 4 deletions Package@swift-5.9.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ let package = Package(
// 🚍 High-performance trie-node router.
.package(url: "https://github.com/vapor/routing-kit.git", from: "4.5.0"),

// 💥 Backtraces for Swift on Linux
.package(url: "https://github.com/swift-server/swift-backtrace.git", from: "1.1.1"),

// Event-driven network application framework for high performance protocol servers & clients, non-blocking.
.package(url: "https://github.com/apple/swift-nio.git", from: "2.56.0"),

Expand Down Expand Up @@ -71,7 +68,6 @@ let package = Package(
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "AsyncKit", package: "async-kit"),
.product(name: "Backtrace", package: "swift-backtrace"),
.target(name: "CVaporBcrypt"),
.target(name: "CVaporURLParser"),
.product(name: "ConsoleKit", package: "console-kit"),
Expand Down
5 changes: 1 addition & 4 deletions Sources/Development/routes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -315,15 +315,13 @@ struct TestError: AbortError, DebuggableError {
}

var source: ErrorSource?
var stackTrace: StackTrace?

init(
file: String = #fileID,
function: String = #function,
line: UInt = #line,
column: UInt = #column,
range: Range<UInt>? = nil,
stackTrace: StackTrace? = .capture(skip: 1)
range: Range<UInt>? = nil
) {
self.source = .init(
file: file,
Expand All @@ -332,7 +330,6 @@ struct TestError: AbortError, DebuggableError {
column: column,
range: range
)
self.stackTrace = stackTrace
}
}

Expand Down
4 changes: 4 additions & 0 deletions Sources/Vapor/Application.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#if swift(<5.9)
import Backtrace
#endif
import NIOConcurrencyHelpers
import NIOCore
import Logging
Expand Down Expand Up @@ -113,7 +115,9 @@ public final class Application: Sendable {
_ environment: Environment = .development,
_ eventLoopGroupProvider: EventLoopGroupProvider = .createNew
) {
#if swift(<5.9)
Backtrace.install()
#endif
self._environment = .init(environment)
self.eventLoopGroupProvider = eventLoopGroupProvider
switch eventLoopGroupProvider {
Expand Down
29 changes: 28 additions & 1 deletion Sources/Vapor/Error/Abort.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,36 @@ public struct Abort: AbortError, DebuggableError {
public var source: ErrorSource?

/// Stack trace at point of error creation.
@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
public var stackTrace: StackTrace?

/// Create a new `Abort`, capturing current source location info.
public init(
_ status: HTTPResponseStatus,
headers: HTTPHeaders = [:],
reason: String? = nil,
identifier: String? = nil,
suggestedFixes: [String] = [],
file: String = #fileID,
function: String = #function,
line: UInt = #line,
column: UInt = #column,
range: Range<UInt>? = nil
) {
self.identifier = identifier ?? status.code.description
self.headers = headers
self.status = status
self.reason = reason ?? status.reasonPhrase
self.source = ErrorSource(
file: file,
function: function,
line: line,
column: column,
range: range
)
}

@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
public init(
_ status: HTTPResponseStatus,
headers: HTTPHeaders = [:],
Expand All @@ -69,7 +96,7 @@ public struct Abort: AbortError, DebuggableError {
line: UInt = #line,
column: UInt = #column,
range: Range<UInt>? = nil,
stackTrace: StackTrace? = .capture(skip: 1)
stackTrace: StackTrace?
) {
self.identifier = identifier ?? status.code.description
self.headers = headers
Expand Down
5 changes: 2 additions & 3 deletions Sources/Vapor/Error/DebuggableError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public protocol DebuggableError: LocalizedError, CustomDebugStringConvertible, C
var source: ErrorSource? { get }

/// Stack trace from which this error originated (must set this from the error's init)
@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
var stackTrace: StackTrace? { get }

/// A `String` array describing the possible causes of the error.
Expand Down Expand Up @@ -102,6 +103,7 @@ extension DebuggableError {
nil
}

@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
public var stackTrace: StackTrace? {
nil
}
Expand Down Expand Up @@ -188,9 +190,6 @@ extension DebuggableError {
if !self.gitHubIssues.isEmpty {
print.append("See these GitHub issues for discussion on this topic:\(self.gitHubIssues.bulletedList)")
}
if let stackTrace = self.stackTrace {
print.append("Stack trace:\n\(stackTrace)")
}
case .short:
if self.possibleCauses.count > 0 {
print.append("[Possible causes: \(self.possibleCauses.joined(separator: " "))]")
Expand Down
116 changes: 15 additions & 101 deletions Sources/Vapor/Error/StackTrace.swift
Original file line number Diff line number Diff line change
@@ -1,123 +1,37 @@
import Foundation
#if os(Linux)
import Backtrace
import CBacktrace
#endif
import NIOConcurrencyHelpers

@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
extension Optional where Wrapped == StackTrace {
public static func capture(skip: Int = 0) -> Self {
StackTrace.capture(skip: 1 + skip)
}
public static func capture(skip: Int = 0) -> Self { StackTrace() }
}

@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
public struct StackTrace: Sendable {
public static var isCaptureEnabled: Bool {
get {
_isCaptureEnabled.withLockedValue {
$0
}
}
set {
_isCaptureEnabled.withLockedValue {
$0 = newValue
}
}
}
private static let _isCaptureEnabled: NIOLockedValueBox<Bool> = .init(true)

public static func capture(skip: Int = 0) -> Self? {
guard Self.isCaptureEnabled else {
return nil
}
let frames = Self.captureRaw().dropFirst(1 + skip)
return .init(rawFrames: .init(frames))
}

#if os(Linux)
private static let state = backtrace_create_state(CommandLine.arguments[0], /* supportThreading: */ 1, nil, nil)
#endif

static func captureRaw() -> [RawFrame] {
#if os(Linux)
final class Context {
var frames: [RawFrame] = []
}
let context = Context()
backtrace_full(self.state, /* skip: */ 1, { data, pc, filename, lineno, function in
let frame = RawFrame(
file: filename.flatMap { String(cString: $0) } ?? "unknown",
mangledFunction: function.flatMap { String(cString: $0) } ?? "unknown"
)
Unmanaged<Context>.fromOpaque(data!).takeUnretainedValue().frames.append(frame)
return 0
}, { _, cMessage, _ in
let message = cMessage.flatMap { String(cString: $0) } ?? "unknown"
fatalError("Failed to capture Linux stacktrace: \(message)")
}, Unmanaged.passUnretained(context).toOpaque())
return context.frames
#else
return Thread.callStackSymbols.dropFirst(1).map { line in
let parts = line.split(
separator: " ",
maxSplits: 3,
omittingEmptySubsequences: true
)
let file = parts.count > 1 ? String(parts[1]) : "unknown"
let functionPart = parts.count > 3 ? (parts[3].split(separator: "+").first.map({ String($0) }) ?? "unknown") : "unknown"
let mangledFunction = functionPart.trimmingCharacters(in: .whitespaces)
return .init(file: file, mangledFunction: mangledFunction)
}
#endif
}

public struct Frame: Sendable {
public var file: String
public var function: String
}

public var frames: [Frame] {
self.rawFrames.map { frame in
Frame(
file: frame.file,
function: _stdlib_demangleName(frame.mangledFunction)
)
}
}

struct RawFrame: Sendable {
var file: String
var mangledFunction: String
}

let rawFrames: [RawFrame]

public func description(max: Int = 16) -> String {
return self.frames[..<min(self.frames.count, max)].readable
public static var isCaptureEnabled: Bool {
get { false }
set { }
}
public static func capture(skip: Int = 0) -> Self? { nil }
public var frames: [Frame] { [] }
public func description(max: Int = 16) -> String { "" }
}

@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
extension StackTrace: CustomStringConvertible {
public var description: String {
self.description()
}
public var description: String { self.description() }
}

@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
extension StackTrace.Frame: CustomStringConvertible {
public var description: String {
"\(self.file) \(self.function)"
}
public var description: String { "\(self.file) \(self.function)" }
}

@available(*, deprecated, message: "Captured stack traces are no longer supported by Vapor")
extension Collection where Element == StackTrace.Frame, Index: BinaryInteger {
var readable: String {
let maxIndexWidth = self.indices.max(by: { String($0).count < String($1).count }).map { String($0).count } ?? 0
let maxFileWidth = self.max(by: { $0.file.count < $1.file.count })?.file.count ?? 0
return self.enumerated().map { i, frame in
let indexPad = String(repeating: " ", count: Swift.max(0, maxIndexWidth - String(i).count))
let filePad = String(repeating: " ", count: Swift.max(0, maxFileWidth - frame.file.count))

return "\(i)\(indexPad) \(frame.file)\(filePad) \(frame.function)"
}.joined(separator: "\n")
}
var readable: String { "" }
}
5 changes: 0 additions & 5 deletions Sources/Vapor/Logging/LoggingSystem+Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ extension LoggingSystem {
public static func bootstrap(from environment: inout Environment, _ factory: (Logger.Level) -> (String) -> LogHandler) throws {
let level = try Logger.Level.detect(from: &environment)

// Disable stack traces if log level > trace.
if level > .trace {
StackTrace.isCaptureEnabled = false
}

// Bootstrap logger with a factory created by the factoryfactory.
return LoggingSystem.bootstrap(factory(level))
}
Expand Down
8 changes: 1 addition & 7 deletions Sources/Vapor/Responder/DefaultResponder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,7 @@ private struct NotFoundResponder: Responder {
}
}

struct RouteNotFound: Error {
let stackTrace: StackTrace?

init() {
self.stackTrace = StackTrace.capture(skip: 1)
}
}
struct RouteNotFound: Error {}

extension RouteNotFound: AbortError {
var status: HTTPResponseStatus {
Expand Down
2 changes: 0 additions & 2 deletions Tests/AsyncTests/AsyncMiddlewareTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ final class AsyncMiddlewareTests: XCTestCase {
XCTAssertEqual(res.headers[.vary], ["origin"])
XCTAssertEqual(res.headers[.accessControlAllowOrigin], ["foo"])
XCTAssertEqual(res.headers[.accessControlAllowHeaders], ["origin"])
print(res.headers)
}
}

Expand All @@ -105,7 +104,6 @@ final class AsyncMiddlewareTests: XCTestCase {
XCTAssertEqual(res.headers[.vary], [])
XCTAssertEqual(res.headers[.accessControlAllowOrigin], [""])
XCTAssertEqual(res.headers[.accessControlAllowHeaders], [""])
print(res.headers)
}
}
}
7 changes: 2 additions & 5 deletions Tests/VaporTests/BcryptTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ final class BcryptTests: XCTestCase {
}

func testInvalidSalt() throws {
do {
_ = try Bcrypt.verify("", created: "foo")
XCTFail("Should have failed")
} catch let error as BcryptError {
print(error)
XCTAssertThrowsError(try Bcrypt.verify("", created: "foo")) {
XCTAssert($0 is BcryptError)
}
}

Expand Down
1 change: 0 additions & 1 deletion Tests/VaporTests/ContentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,6 @@ final class ContentTests: XCTestCase {
return User(name: "Vapor", age: 3, luckyNumbers: [5, 7])
}
try app.testable().test(.GET, "/urlencodedform") { res in
debugPrint(res)
XCTAssertEqual(res.status.code, 200)
XCTAssertEqual(res.headers.contentType, .urlEncodedForm)
XCTAssertContains(res.body.string, "luckyNumbers[]=5")
Expand Down
Loading

0 comments on commit 03a08f6

Please sign in to comment.