77
88import Foundation
99
10+ /// A set of signals that would normally kill the program. We handle these
11+ /// signals by dumping the pretty stack trace description.
12+ let killSigs = [
13+ SIGILL, SIGABRT, SIGTRAP, SIGFPE,
14+ SIGBUS, SIGSEGV, SIGSYS, SIGQUIT
15+ ]
16+
17+ /// Needed because the type `sigaction` conflicts with the function `sigaction`.
18+ typealias SigAction = sigaction
19+
20+ /// A wrapper that associates a signal handler with the number it handles.
21+ struct SigHandler {
22+ /// The signal action, called when the handler is called.
23+ var action : SigAction
24+
25+ /// The signal number this will fire on.
26+ var signalNumber : Int32
27+ }
28+
1029/// Represents an entry in a stack trace. Contains information necessary to
1130/// reconstruct what was happening at the time this function was executed.
1231private struct TraceEntry : CustomStringConvertible {
@@ -32,36 +51,65 @@ private struct TraceEntry: CustomStringConvertible {
3251 }
3352}
3453
35- var stderr = FileHandle . standardError
54+ // HACK: This array must be pre-allocated and contains functionally immortal
55+ // C-strings because String may allocate when passed to write(1).
56+ var registeredSignalInfo =
57+ UnsafeMutableBufferPointer < SigHandler > ( start:
58+ UnsafeMutablePointer . allocate ( capacity: killSigs. count) ,
59+ count: killSigs. count)
60+ var numRegisteredSignalInfo = 0
3661
3762/// A class managing a stack of trace entries. When a particular thread gets
3863/// a kill signal, this handler will dump all the entries in the tack trace and
3964/// end the process.
4065private class PrettyStackTraceManager {
66+ struct StackEntry {
67+ var prev : UnsafeMutablePointer < StackEntry > ?
68+ let data : UnsafeMutablePointer < Int8 >
69+ let count : Int
70+ }
71+
4172 /// Keeps a stack of serialized trace entries in reverse order.
42- /// - Note: This keeps strings, because it's not particularly safe to
73+ /// - Note: This keeps strings, because it's not safe to
4374 /// construct the strings in the signal handler directly.
44- var stack = [ Data] ( )
45- let stackDumpMsgData = " Stack dump: \n " . data ( using: . utf8) !
75+ var stack : UnsafeMutablePointer < StackEntry > ? = nil
76+
77+ private let stackDumpMsg : StackEntry
78+ init ( ) {
79+ let msg = " Stack dump: \n "
80+ stackDumpMsg = StackEntry ( prev: nil ,
81+ data: strndup ( msg, msg. count) ,
82+ count: msg. count)
83+ }
4684
4785 /// Pushes the description of a trace entry to the stack.
4886 func push( _ entry: TraceEntry ) {
4987 let str = " \( entry. description) \n "
50- stack. insert ( str. data ( using: . utf8) !, at: 0 )
88+ let newEntry = StackEntry ( prev: stack,
89+ data: strndup ( str, str. count) ,
90+ count: str. count)
91+ let newStack = UnsafeMutablePointer< StackEntry> . allocate( capacity: 1 )
92+ newStack. pointee = newEntry
93+ stack = newStack
5194 }
5295
5396 /// Pops the latest trace entry off the stack.
5497 func pop( ) {
55- guard !stack. isEmpty else { return }
56- stack. removeFirst ( )
98+ guard let stack = stack else { return }
99+ let prev = stack. pointee. prev
100+ stack. deallocate ( capacity: 1 )
101+ self . stack = prev
57102 }
58103
59104 /// Dumps the stack entries to standard error, starting with the most
60105 /// recent entry.
61106 func dump( _ signal: Int32 ) {
62- stderr. write ( stackDumpMsgData)
63- for entry in stack {
64- stderr. write ( entry)
107+ write ( STDERR_FILENO, stackDumpMsg. data, stackDumpMsg. count)
108+ var cur = stack
109+ while cur != nil {
110+ let entry = cur. unsafelyUnwrapped
111+ write ( STDERR_FILENO, entry. pointee. data, entry. pointee. count)
112+ cur = entry. pointee. prev
65113 }
66114 }
67115}
@@ -73,6 +121,11 @@ private var __stackContextKey = pthread_key_t()
73121/// Creates a key for a thread-local reference to a PrettyStackTraceHandler.
74122private var stackContextKey : pthread_key_t = {
75123 pthread_key_create ( & __stackContextKey) { ptr in
124+ #if os(Linux)
125+ guard let ptr = ptr else {
126+ return
127+ }
128+ #endif
76129 let unmanaged = Unmanaged< PrettyStackTraceManager> . fromOpaque( ptr)
77130 unmanaged. release ( )
78131 }
@@ -93,57 +146,58 @@ private func threadLocalHandler() -> PrettyStackTraceManager {
93146 return unmanaged. takeUnretainedValue ( )
94147}
95148
96- /// A set of signals that would normally kill the program. We handle these
97- /// signals by dumping the pretty stack trace description.
98- let killSigs = [
99- SIGILL, SIGABRT, SIGTRAP, SIGFPE,
100- SIGBUS, SIGSEGV, SIGSYS, SIGQUIT
101- ]
102-
103- /// Needed because the type `sigaction` conflicts with the function `sigaction`.
104- typealias SigAction = sigaction
105-
106- /// A wrapper that associates a signal handler with the number it handles.
107- struct SigHandler {
108- /// The signal action, called when the handler is called.
109- var action : SigAction
110-
111- /// The signal number this will fire on.
112- var signalNumber : Int32
149+ extension Int32 {
150+ /// HACK: Just for compatibility's sake on Linux.
151+ public init ( bitPattern: Int32 ) { self = bitPattern }
113152}
114153
115- /// The currently registered set of signal handlers.
116- private var handlers = [ SigHandler] ( )
117-
118154/// Registers the pretty stack trace signal handlers.
119155private func registerHandler( signal: Int32 ) {
120156 var newHandler = SigAction ( )
121- newHandler . __sigaction_u . __sa_handler = {
157+ let cHandler : @ convention ( c ) ( Int32 ) -> Swift . Void = { signalNumber in
122158 unregisterHandlers ( )
123159
124160 // Unblock all potentially blocked kill signals
125161 var sigMask = sigset_t ( )
126162 sigfillset ( & sigMask)
127163 sigprocmask ( SIG_UNBLOCK, & sigMask, nil )
128164
129- threadLocalHandler ( ) . dump ( $0 )
130- exit ( - 1 )
165+ threadLocalHandler ( ) . dump ( signalNumber )
166+ exit ( signalNumber )
131167 }
132- newHandler. sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK
168+ #if os(macOS)
169+ newHandler. __sigaction_u. __sa_handler = cHandler
170+ #elseif os(Linux)
171+ newHandler. __sigaction_handler = . init( sa_handler: cHandler)
172+ #else
173+ fatalError ( " Cannot register signal action handler on this platform " )
174+ #endif
175+ newHandler. sa_flags = Int32 ( bitPattern: SA_NODEFER) |
176+ Int32 ( bitPattern: SA_RESETHAND) |
177+ Int32 ( bitPattern: SA_ONSTACK)
133178 sigemptyset ( & newHandler. sa_mask)
134179
135180 var handler = SigAction ( )
136181 if sigaction ( signal, & newHandler, & handler) != 0 {
137182 let sh = SigHandler ( action: handler, signalNumber: signal)
138- handlers. append ( sh)
183+ registeredSignalInfo [ numRegisteredSignalInfo] = sh
184+ numRegisteredSignalInfo += 1
139185 }
140186}
141187
142188/// Unregisters all pretty stack trace signal handlers.
143189private func unregisterHandlers( ) {
144- while var handler = handlers. popLast ( ) {
145- sigaction ( handler. signalNumber, & handler. action, nil )
190+ var i = 0
191+ while i < killSigs. count {
192+ sigaction ( registeredSignalInfo [ i] . signalNumber,
193+ & registeredSignalInfo[ i] . action, nil )
194+ i += 1
146195 }
196+
197+ // HACK: Must leak the old registerdSignalInfo because we cannot safely
198+ // free inside a signal handler.
199+ // cannot: free(registeredSignalInfo)
200+ numRegisteredSignalInfo = 0
147201}
148202
149203/// A reference to the previous alternate stack, if any.
@@ -155,11 +209,16 @@ private var newAltStackPointer: UnsafeMutableRawPointer?
155209/// Sets up an alternate stack and registers all signal handlers with the
156210/// system.
157211private let __setupStackOnce : Void = {
158- let altStackSize = UInt ( MINSIGSTKSZ) + ( UInt ( 64 ) * 1024 )
212+ #if os(macOS)
213+ typealias SSSize = UInt
214+ #else
215+ typealias SSSize = Int
216+ #endif
217+ let altStackSize = SSSize ( MINSIGSTKSZ) + ( SSSize ( 64 ) * 1024 )
159218
160219 /// Make sure we're not currently executing on an alternate stack already.
161220 guard sigaltstack ( nil , & oldAltStack) == 0 else { return }
162- guard oldAltStack. ss_flags & SS_ONSTACK == 0 else { return }
221+ guard Int ( oldAltStack. ss_flags) & Int ( SS_ONSTACK) == 0 else { return }
163222 guard oldAltStack. ss_sp == nil || oldAltStack. ss_size < altStackSize else {
164223 return
165224 }
@@ -199,4 +258,3 @@ public func trace<T>(_ action: String, file: StaticString = #file,
199258 defer { h. pop ( ) }
200259 return try actions ( )
201260}
202-
0 commit comments