7
7
8
8
import Foundation
9
9
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
+
10
29
/// Represents an entry in a stack trace. Contains information necessary to
11
30
/// reconstruct what was happening at the time this function was executed.
12
31
private struct TraceEntry : CustomStringConvertible {
@@ -32,36 +51,65 @@ private struct TraceEntry: CustomStringConvertible {
32
51
}
33
52
}
34
53
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
36
61
37
62
/// A class managing a stack of trace entries. When a particular thread gets
38
63
/// a kill signal, this handler will dump all the entries in the tack trace and
39
64
/// end the process.
40
65
private class PrettyStackTraceManager {
66
+ struct StackEntry {
67
+ var prev : UnsafeMutablePointer < StackEntry > ?
68
+ let data : UnsafeMutablePointer < Int8 >
69
+ let count : Int
70
+ }
71
+
41
72
/// 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
43
74
/// 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
+ }
46
84
47
85
/// Pushes the description of a trace entry to the stack.
48
86
func push( _ entry: TraceEntry ) {
49
87
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
51
94
}
52
95
53
96
/// Pops the latest trace entry off the stack.
54
97
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
57
102
}
58
103
59
104
/// Dumps the stack entries to standard error, starting with the most
60
105
/// recent entry.
61
106
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
65
113
}
66
114
}
67
115
}
@@ -73,6 +121,11 @@ private var __stackContextKey = pthread_key_t()
73
121
/// Creates a key for a thread-local reference to a PrettyStackTraceHandler.
74
122
private var stackContextKey : pthread_key_t = {
75
123
pthread_key_create ( & __stackContextKey) { ptr in
124
+ #if os(Linux)
125
+ guard let ptr = ptr else {
126
+ return
127
+ }
128
+ #endif
76
129
let unmanaged = Unmanaged< PrettyStackTraceManager> . fromOpaque( ptr)
77
130
unmanaged. release ( )
78
131
}
@@ -93,57 +146,58 @@ private func threadLocalHandler() -> PrettyStackTraceManager {
93
146
return unmanaged. takeUnretainedValue ( )
94
147
}
95
148
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 }
113
152
}
114
153
115
- /// The currently registered set of signal handlers.
116
- private var handlers = [ SigHandler] ( )
117
-
118
154
/// Registers the pretty stack trace signal handlers.
119
155
private func registerHandler( signal: Int32 ) {
120
156
var newHandler = SigAction ( )
121
- newHandler . __sigaction_u . __sa_handler = {
157
+ let cHandler : @ convention ( c ) ( Int32 ) -> Swift . Void = { signalNumber in
122
158
unregisterHandlers ( )
123
159
124
160
// Unblock all potentially blocked kill signals
125
161
var sigMask = sigset_t ( )
126
162
sigfillset ( & sigMask)
127
163
sigprocmask ( SIG_UNBLOCK, & sigMask, nil )
128
164
129
- threadLocalHandler ( ) . dump ( $0 )
130
- exit ( - 1 )
165
+ threadLocalHandler ( ) . dump ( signalNumber )
166
+ exit ( signalNumber )
131
167
}
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)
133
178
sigemptyset ( & newHandler. sa_mask)
134
179
135
180
var handler = SigAction ( )
136
181
if sigaction ( signal, & newHandler, & handler) != 0 {
137
182
let sh = SigHandler ( action: handler, signalNumber: signal)
138
- handlers. append ( sh)
183
+ registeredSignalInfo [ numRegisteredSignalInfo] = sh
184
+ numRegisteredSignalInfo += 1
139
185
}
140
186
}
141
187
142
188
/// Unregisters all pretty stack trace signal handlers.
143
189
private 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
146
195
}
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
147
201
}
148
202
149
203
/// A reference to the previous alternate stack, if any.
@@ -155,11 +209,16 @@ private var newAltStackPointer: UnsafeMutableRawPointer?
155
209
/// Sets up an alternate stack and registers all signal handlers with the
156
210
/// system.
157
211
private 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 )
159
218
160
219
/// Make sure we're not currently executing on an alternate stack already.
161
220
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 }
163
222
guard oldAltStack. ss_sp == nil || oldAltStack. ss_size < altStackSize else {
164
223
return
165
224
}
@@ -199,4 +258,3 @@ public func trace<T>(_ action: String, file: StaticString = #file,
199
258
defer { h. pop ( ) }
200
259
return try actions ( )
201
260
}
202
-
0 commit comments