@@ -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,69 @@ 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 {
567
+ ndx += 1
568
+ filename = " \( args. outputPath) / \( name) - \( pid) - \( now. tv_sec) . \( now. tv_nsec) - \( ndx) .log "
569
+ fd = open ( filename, O_RDWR|O_CREAT|O_EXCL, 0o644 )
570
+ }
571
+
572
+ if fd < 0 {
573
+ print ( " swift-backtrace: unable to create \( filename) for writing " ,
574
+ to: & standardError)
575
+ outputStream = standardError
576
+ }
577
+
578
+ if let cFile = fdopen ( fd, " wt " ) {
579
+ didOpenOutput = true
580
+ outputStream = CFileStream ( fp: cFile)
581
+ } else {
582
+ close ( fd)
583
+ unlink ( filename)
584
+
585
+ print ( " swift-backtrace: unable to fdopen \( filename) for writing " ,
586
+ to: & standardError)
587
+ outputStream = standardError
588
+ }
589
+ } else if let cFile = fopen ( args. outputPath, " wt " ) {
590
+ didOpenOutput = true
591
+ outputStream = CFileStream ( fp: cFile)
592
+ } else {
593
+ print ( " swift-backtrace: unable to open \( args. outputPath) for writing " ,
594
+ to: & standardError)
595
+
596
+ outputStream = standardError
597
+ }
598
+ }
599
+ defer {
600
+ if didOpenOutput {
601
+ outputStream!. close ( )
602
+ }
603
+ }
604
+
543
605
printCrashLog ( )
544
606
545
607
writeln ( " " )
@@ -707,11 +769,18 @@ Generate a backtrace for the parent process.
707
769
description = " Program crashed: \( target. signalDescription) at \( hex ( target. faultAddress) ) "
708
770
}
709
771
710
- // Clear (or complete) the message written by the crash handler
772
+ // Clear (or complete) the message written by the crash handler; this
773
+ // is always on stdout or stderr, even if you specify a file for output.
774
+ var handlerOut : CFileStream
775
+ if args. outputTo == . stdout {
776
+ handlerOut = standardOutput
777
+ } else {
778
+ handlerOut = standardError
779
+ }
711
780
if args. color {
712
- write ( " \r \u{1b} [0K " )
781
+ print ( " \r \u{1b} [0K " , terminator : " " , to : & handlerOut )
713
782
} else {
714
- write ( " done *** \n \n " )
783
+ print ( " done *** \n \n " , terminator : " " , to : & handlerOut )
715
784
}
716
785
717
786
writeln ( theme. crashReason ( description) )
@@ -837,7 +906,7 @@ Generate a backtrace for the parent process.
837
906
}
838
907
839
908
while true {
840
- outputStream. flush ( )
909
+ outputStream! . flush ( )
841
910
write ( theme. prompt ( " >>> " ) , flush: true )
842
911
guard let input = readLine ( ) else {
843
912
print ( " " )
0 commit comments