Skip to content

Commit 6b320ee

Browse files
committed
Add numbering to stack dump entries
1 parent 922a6df commit 6b320ee

File tree

5 files changed

+72
-9
lines changed

5 files changed

+72
-9
lines changed

Sources/PrettyStackTrace/PrettyStackTrace.swift

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ private struct TraceEntry: CustomStringConvertible {
4646
var description: String {
4747
let base = URL(fileURLWithPath: file.description).lastPathComponent
4848
return """
49-
-> While \(action) (func \(function), in file \(base), line \(line))
49+
While \(action) (func \(function), in file \(base), line \(line))
5050
"""
5151
}
5252
}
@@ -59,6 +59,63 @@ var registeredSignalInfo =
5959
count: killSigs.count)
6060
var numRegisteredSignalInfo = 0
6161

62+
/// 8 digits + 2 spaces + 1 period + 1 space + 1 NULL terminator
63+
/// ought to be enough for anyone...
64+
let numberBuffer: UnsafeMutablePointer<Int8> = {
65+
let count = 13
66+
let ptr = UnsafeMutablePointer<Int8>.allocate(capacity: count)
67+
68+
/// NUL terminator
69+
ptr[count - 1] = 0
70+
71+
return ptr
72+
}()
73+
74+
/// Fills a pre-allocated 13-byte buffer with the following:
75+
///
76+
/// [' ', ' ', '1', '2', '3', '4', '5', '6', '7', '8', '.', ' ', '\0']
77+
///
78+
/// ensuring that the buffer only uses the exact digits from the integer and
79+
/// doesn't write outside the bounds of the buffer.
80+
/// It then writes the contents of this buffer, starting at the first space at
81+
/// the beginning, to stderr.
82+
func writeInt(_ int: UInt) {
83+
// We can't write more than 8 digits.
84+
guard int <= 99_999_999 else { return }
85+
var int = int
86+
87+
// Fill the buffer from right to left, decrementing the pointer as we add
88+
// characters and digits.
89+
90+
// First, add the '. ' at the end.
91+
var end = numberBuffer.advanced(by: 12)
92+
end.pointee = 32 /// (ascii ' ')
93+
end = end.predecessor()
94+
end.pointee = 46 /// (ascii '.')
95+
end = end.predecessor()
96+
97+
// Next, pop successive digits off the end of the integer and add them to
98+
// the current 'start' of the buffer.
99+
while int > 0 {
100+
let remInt = int.remainderReportingOverflow(dividingBy: 10).partialValue
101+
let remInt8 = Int8(truncatingIfNeeded: remInt)
102+
int = int.unsafeDivided(by: 10)
103+
end.pointee = remInt8 &+ 48 /// (ascii '0')
104+
end = end.predecessor()
105+
}
106+
107+
// Add the ' ' at the current 'start' of the buffer.
108+
end.pointee = 32 /// (ascii ' ')
109+
end = end.predecessor()
110+
end.pointee = 32 /// (ascii ' ')
111+
// Don't move to the predecessor -- we're at the beginning of the string now.
112+
113+
// Find the distance between the end of the buffer and the beginning of our
114+
// string.
115+
let dist = abs(numberBuffer.advanced(by: 13).distance(to: end))
116+
write(STDERR_FILENO, end, dist)
117+
}
118+
62119
/// A class managing a stack of trace entries. When a particular thread gets
63120
/// a kill signal, this handler will dump all the entries in the tack trace and
64121
/// end the process.
@@ -84,7 +141,7 @@ private class PrettyStackTraceManager {
84141

85142
/// Pushes the description of a trace entry to the stack.
86143
func push(_ entry: TraceEntry) {
87-
let str = " \(entry.description)\n"
144+
let str = "\(entry.description)\n"
88145
let newEntry = StackEntry(prev: stack,
89146
data: strndup(str, str.count),
90147
count: str.count)
@@ -106,11 +163,14 @@ private class PrettyStackTraceManager {
106163
/// recent entry.
107164
func dump(_ signal: Int32) {
108165
write(STDERR_FILENO, stackDumpMsg.data, stackDumpMsg.count)
166+
var i: UInt = 1
109167
var cur = stack
110168
while cur != nil {
169+
writeInt(i)
111170
let entry = cur.unsafelyUnwrapped
112171
write(STDERR_FILENO, entry.pointee.data, entry.pointee.count)
113172
cur = entry.pointee.prev
173+
i += 1
114174
}
115175
}
116176
}
@@ -215,6 +275,9 @@ private let __setupStackOnce: Void = {
215275
#else
216276
typealias SSSize = Int
217277
#endif
278+
279+
_ = numberBuffer
280+
218281
let altStackSize = SSSize(MINSIGSTKSZ) + (SSSize(64) * 1024)
219282

220283
/// Make sure we're not currently executing on an alternate stack already.

Tests/abort.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Glibc
99
// CHECK-DAG: in first task!
1010
// CHECK-DAG: in second task!
1111
// CHECK-DAG: Stack dump
12-
// CHECK-DAG: -> While doing first task
12+
// CHECK-DAG: 1. While doing first task
1313
trace("doing first task") {
1414
print("in first task!")
1515
trace("doing second task") {

Tests/fatal-error.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
// CHECK-DAG: in second task!
55
// CHECK-DAG: {{[fF]}}atal error: second task failed
66
// CHECK-DAG: Stack dump
7-
// CHECK-DAG: -> While doing second task
8-
// CHECK-DAG: -> While doing first task
7+
// CHECK-DAG: 1. While doing second task
8+
// CHECK-DAG: 2. While doing first task
99
trace("doing first task") {
1010
print("in first task!")
1111
trace("doing second task") {

Tests/nsexception.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import Foundation
66
// CHECK-DAG: about to raise!
77
// CHECK-DAG: Terminating app due to uncaught exception 'NSGenericException', reason: 'You failed'
88
// CHECK-DAG: Stack dump:
9-
// CHECK-DAG: -> While raising an exception
10-
// CHECK-DAG: -> While doing first task
9+
// CHECK-DAG: 1. While raising an exception
10+
// CHECK-DAG: 2. While doing first task
1111
trace("doing first task") {
1212
print("in first task!")
1313
trace("raising an exception") {

Tests/stack-overflow.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ func overflow() {
88
// CHECK-DAG: in first task!
99
// CHECK-DAG: in second task!
1010
// CHECK-DAG: Stack dump
11-
// CHECK-DAG: -> While doing second task
12-
// CHECK-DAG: -> While doing first task
11+
// CHECK-DAG: 1. While doing second task
12+
// CHECK-DAG: 2. While doing first task
1313
trace("doing first task") {
1414
print("in first task!")
1515
trace("doing second task") {

0 commit comments

Comments
 (0)