Skip to content

Commit 434c083

Browse files
Merge pull request #2 from llvm-swift/numerology
Add numbering to stack dump entries
2 parents 922a6df + eae082d commit 434c083

File tree

6 files changed

+183
-9
lines changed

6 files changed

+183
-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+
private func writeLeadingSpacesAndStackPosition(_ 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+
writeLeadingSpacesAndStackPosition(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/long-trace.swift

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// RUN: cat %S/../Sources/PrettyStackTrace/PrettyStackTrace.swift %s | swiftc -c -emit-executable -o %t - && %t 2>&1 | %FileCheck %s
2+
3+
// CHECK-DAG: Stack dump
4+
// CHECK-DAG: 1. While in frame 100
5+
// CHECK-DAG: 2. While in frame 99
6+
// CHECK-DAG: 3. While in frame 98
7+
// CHECK-DAG: 4. While in frame 97
8+
// CHECK-DAG: 5. While in frame 96
9+
// CHECK-DAG: 6. While in frame 95
10+
// CHECK-DAG: 7. While in frame 94
11+
// CHECK-DAG: 8. While in frame 93
12+
// CHECK-DAG: 9. While in frame 92
13+
// CHECK-DAG: 10. While in frame 91
14+
// CHECK-DAG: 11. While in frame 90
15+
// CHECK-DAG: 12. While in frame 89
16+
// CHECK-DAG: 13. While in frame 88
17+
// CHECK-DAG: 14. While in frame 87
18+
// CHECK-DAG: 15. While in frame 86
19+
// CHECK-DAG: 16. While in frame 85
20+
// CHECK-DAG: 17. While in frame 84
21+
// CHECK-DAG: 18. While in frame 83
22+
// CHECK-DAG: 19. While in frame 82
23+
// CHECK-DAG: 20. While in frame 81
24+
// CHECK-DAG: 21. While in frame 80
25+
// CHECK-DAG: 22. While in frame 79
26+
// CHECK-DAG: 23. While in frame 78
27+
// CHECK-DAG: 24. While in frame 77
28+
// CHECK-DAG: 25. While in frame 76
29+
// CHECK-DAG: 26. While in frame 75
30+
// CHECK-DAG: 27. While in frame 74
31+
// CHECK-DAG: 28. While in frame 73
32+
// CHECK-DAG: 29. While in frame 72
33+
// CHECK-DAG: 30. While in frame 71
34+
// CHECK-DAG: 31. While in frame 70
35+
// CHECK-DAG: 32. While in frame 69
36+
// CHECK-DAG: 33. While in frame 68
37+
// CHECK-DAG: 34. While in frame 67
38+
// CHECK-DAG: 35. While in frame 66
39+
// CHECK-DAG: 36. While in frame 65
40+
// CHECK-DAG: 37. While in frame 64
41+
// CHECK-DAG: 38. While in frame 63
42+
// CHECK-DAG: 39. While in frame 62
43+
// CHECK-DAG: 40. While in frame 61
44+
// CHECK-DAG: 41. While in frame 60
45+
// CHECK-DAG: 42. While in frame 59
46+
// CHECK-DAG: 43. While in frame 58
47+
// CHECK-DAG: 44. While in frame 57
48+
// CHECK-DAG: 45. While in frame 56
49+
// CHECK-DAG: 46. While in frame 55
50+
// CHECK-DAG: 47. While in frame 54
51+
// CHECK-DAG: 48. While in frame 53
52+
// CHECK-DAG: 49. While in frame 52
53+
// CHECK-DAG: 50. While in frame 51
54+
// CHECK-DAG: 51. While in frame 50
55+
// CHECK-DAG: 52. While in frame 49
56+
// CHECK-DAG: 53. While in frame 48
57+
// CHECK-DAG: 54. While in frame 47
58+
// CHECK-DAG: 55. While in frame 46
59+
// CHECK-DAG: 56. While in frame 45
60+
// CHECK-DAG: 57. While in frame 44
61+
// CHECK-DAG: 58. While in frame 43
62+
// CHECK-DAG: 59. While in frame 42
63+
// CHECK-DAG: 60. While in frame 41
64+
// CHECK-DAG: 61. While in frame 40
65+
// CHECK-DAG: 62. While in frame 39
66+
// CHECK-DAG: 63. While in frame 38
67+
// CHECK-DAG: 64. While in frame 37
68+
// CHECK-DAG: 65. While in frame 36
69+
// CHECK-DAG: 66. While in frame 35
70+
// CHECK-DAG: 67. While in frame 34
71+
// CHECK-DAG: 68. While in frame 33
72+
// CHECK-DAG: 69. While in frame 32
73+
// CHECK-DAG: 70. While in frame 31
74+
// CHECK-DAG: 71. While in frame 30
75+
// CHECK-DAG: 72. While in frame 29
76+
// CHECK-DAG: 73. While in frame 28
77+
// CHECK-DAG: 74. While in frame 27
78+
// CHECK-DAG: 75. While in frame 26
79+
// CHECK-DAG: 76. While in frame 25
80+
// CHECK-DAG: 77. While in frame 24
81+
// CHECK-DAG: 78. While in frame 23
82+
// CHECK-DAG: 79. While in frame 22
83+
// CHECK-DAG: 80. While in frame 21
84+
// CHECK-DAG: 81. While in frame 20
85+
// CHECK-DAG: 82. While in frame 19
86+
// CHECK-DAG: 83. While in frame 18
87+
// CHECK-DAG: 84. While in frame 17
88+
// CHECK-DAG: 85. While in frame 16
89+
// CHECK-DAG: 86. While in frame 15
90+
// CHECK-DAG: 87. While in frame 14
91+
// CHECK-DAG: 88. While in frame 13
92+
// CHECK-DAG: 89. While in frame 12
93+
// CHECK-DAG: 90. While in frame 11
94+
// CHECK-DAG: 91. While in frame 10
95+
// CHECK-DAG: 92. While in frame 9
96+
// CHECK-DAG: 93. While in frame 8
97+
// CHECK-DAG: 94. While in frame 7
98+
// CHECK-DAG: 95. While in frame 6
99+
// CHECK-DAG: 96. While in frame 5
100+
// CHECK-DAG: 97. While in frame 4
101+
// CHECK-DAG: 98. While in frame 3
102+
// CHECK-DAG: 99. While in frame 2
103+
// CHECK-DAG: 100. While in frame 1
104+
func buildTrace(_ int: Int = 1) {
105+
if int > 100 { abort() }
106+
trace("in frame \(int)") {
107+
buildTrace(int + 1)
108+
}
109+
}
110+
111+
buildTrace()

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)