@@ -56,6 +56,7 @@ internal struct SwiftBacktrace {
56
56
enum OutputTo {
57
57
case stdout
58
58
case stderr
59
+ case file
59
60
}
60
61
61
62
enum Symbolication {
@@ -81,6 +82,7 @@ internal struct SwiftBacktrace {
81
82
var cache = true
82
83
var outputTo : OutputTo = . stdout
83
84
var symbolicate : Symbolication = . full
85
+ var outputPath : String = " /tmp "
84
86
}
85
87
86
88
static var args = Arguments ( )
@@ -97,15 +99,10 @@ internal struct SwiftBacktrace {
97
99
}
98
100
}
99
101
100
- static var outputStream : CFileStream {
101
- switch args. outputTo {
102
- case . stdout: return standardOutput
103
- case . stderr: return standardError
104
- }
105
- }
102
+ static var outputStream : CFileStream ? = nil
106
103
107
104
static func write( _ string: String , flush: Bool = false ) {
108
- var stream = outputStream
105
+ var stream = outputStream!
109
106
110
107
print ( string, terminator: " " , to: & stream)
111
108
if flush {
@@ -114,7 +111,7 @@ internal struct SwiftBacktrace {
114
111
}
115
112
116
113
static func writeln( _ string: String , flush: Bool = false ) {
117
- var stream = outputStream
114
+ var stream = outputStream!
118
115
119
116
print ( string, to: & stream)
120
117
if flush {
@@ -208,6 +205,10 @@ Generate a backtrace for the parent process.
208
205
--output-to <stream> Set which output stream to use. Options are " stdout "
209
206
-o <stream> and " stderr " . The default is " stdout " .
210
207
208
+ Alternatively, you may specify a file path here. If
209
+ the path points to a directory, a unique filename will
210
+ be generated automatically.
211
+
211
212
--crashinfo <addr>
212
213
-a <addr> Provide a pointer to a platform specific CrashInfo
213
214
structure. <addr> should be in hexadecimal.
@@ -405,10 +406,8 @@ Generate a backtrace for the parent process.
405
406
case " stderr " :
406
407
args. outputTo = . stderr
407
408
default :
408
- print ( " swift-backtrace: unknown output-to setting ' \( v) ' " ,
409
- to: & standardError)
410
- usage ( )
411
- exit ( 1 )
409
+ args. outputTo = . file
410
+ args. outputPath = v
412
411
}
413
412
} else {
414
413
print ( " swift-backtrace: missing output-to value " ,
@@ -540,6 +539,71 @@ Generate a backtrace for the parent process.
540
539
currentThread = target!. crashingThreadNdx
541
540
}
542
541
542
+ // Set up the output stream
543
+ var didOpenOutput = false
544
+ switch args. outputTo {
545
+ case . stdout:
546
+ outputStream = standardOutput
547
+ case . stderr:
548
+ outputStream = standardError
549
+ case . file:
550
+ if isDir ( args. outputPath) {
551
+ // If the output path is a directory, generate a filename
552
+ let name = target!. name
553
+ let pid = target!. pid
554
+ var now = timespec ( )
555
+
556
+ if clock_gettime ( CLOCK_REALTIME, & now) != 0 {
557
+ now. tv_sec = time ( nil )
558
+ now. tv_nsec = 0
559
+ }
560
+
561
+ var filename =
562
+ " \( args. outputPath) / \( name) - \( pid) - \( now. tv_sec) . \( now. tv_nsec) .log "
563
+
564
+ var fd = open ( filename, O_RDWR|O_CREAT|O_EXCL, 0o644 )
565
+ var ndx = 1
566
+ while fd < 0 && ( errno == EEXIST || errno == EINTR) {
567
+ if errno != EINTR {
568
+ ndx += 1
569
+ }
570
+ filename = " \( args. outputPath) / \( name) - \( pid) - \( now. tv_sec) . \( now. tv_nsec) - \( ndx) .log "
571
+ fd = open ( filename, O_RDWR|O_CREAT|O_EXCL, 0o644 )
572
+ }
573
+
574
+ if fd < 0 {
575
+ print ( " swift-backtrace: unable to create \( filename) for writing " ,
576
+ to: & standardError)
577
+ outputStream = standardError
578
+ }
579
+
580
+ if let cFile = fdopen ( fd, " wt " ) {
581
+ didOpenOutput = true
582
+ outputStream = CFileStream ( fp: cFile)
583
+ } else {
584
+ close ( fd)
585
+ unlink ( filename)
586
+
587
+ print ( " swift-backtrace: unable to fdopen \( filename) for writing " ,
588
+ to: & standardError)
589
+ outputStream = standardError
590
+ }
591
+ } else if let cFile = fopen ( args. outputPath, " wt " ) {
592
+ didOpenOutput = true
593
+ outputStream = CFileStream ( fp: cFile)
594
+ } else {
595
+ print ( " swift-backtrace: unable to open \( args. outputPath) for writing " ,
596
+ to: & standardError)
597
+
598
+ outputStream = standardError
599
+ }
600
+ }
601
+ defer {
602
+ if didOpenOutput {
603
+ outputStream!. close ( )
604
+ }
605
+ }
606
+
543
607
printCrashLog ( )
544
608
545
609
writeln ( " " )
@@ -707,11 +771,18 @@ Generate a backtrace for the parent process.
707
771
description = " Program crashed: \( target. signalDescription) at \( hex ( target. faultAddress) ) "
708
772
}
709
773
710
- // Clear (or complete) the message written by the crash handler
774
+ // Clear (or complete) the message written by the crash handler; this
775
+ // is always on stdout or stderr, even if you specify a file for output.
776
+ var handlerOut : CFileStream
777
+ if args. outputTo == . stdout {
778
+ handlerOut = standardOutput
779
+ } else {
780
+ handlerOut = standardError
781
+ }
711
782
if args. color {
712
- write ( " \r \u{1b} [0K " )
783
+ print ( " \r \u{1b} [0K " , terminator : " " , to : & handlerOut )
713
784
} else {
714
- write ( " done *** \n \n " )
785
+ print ( " done *** \n \n " , terminator : " " , to : & handlerOut )
715
786
}
716
787
717
788
writeln ( theme. crashReason ( description) )
@@ -837,7 +908,7 @@ Generate a backtrace for the parent process.
837
908
}
838
909
839
910
while true {
840
- outputStream. flush ( )
911
+ outputStream! . flush ( )
841
912
write ( theme. prompt ( " >>> " ) , flush: true )
842
913
guard let input = readLine ( ) else {
843
914
print ( " " )
0 commit comments