@@ -56,6 +56,7 @@ internal struct SwiftBacktrace {
5656 enum OutputTo {
5757 case stdout
5858 case stderr
59+ case file
5960 }
6061
6162 enum Symbolication {
@@ -81,6 +82,7 @@ internal struct SwiftBacktrace {
8182 var cache = true
8283 var outputTo : OutputTo = . stdout
8384 var symbolicate : Symbolication = . full
85+ var outputPath : String = " /tmp "
8486 }
8587
8688 static var args = Arguments ( )
@@ -97,15 +99,10 @@ internal struct SwiftBacktrace {
9799 }
98100 }
99101
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
106103
107104 static func write( _ string: String , flush: Bool = false ) {
108- var stream = outputStream
105+ var stream = outputStream!
109106
110107 print ( string, terminator: " " , to: & stream)
111108 if flush {
@@ -114,7 +111,7 @@ internal struct SwiftBacktrace {
114111 }
115112
116113 static func writeln( _ string: String , flush: Bool = false ) {
117- var stream = outputStream
114+ var stream = outputStream!
118115
119116 print ( string, to: & stream)
120117 if flush {
@@ -208,6 +205,10 @@ Generate a backtrace for the parent process.
208205--output-to <stream> Set which output stream to use. Options are " stdout "
209206-o <stream> and " stderr " . The default is " stdout " .
210207
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+
211212--crashinfo <addr>
212213-a <addr> Provide a pointer to a platform specific CrashInfo
213214 structure. <addr> should be in hexadecimal.
@@ -405,10 +406,8 @@ Generate a backtrace for the parent process.
405406 case " stderr " :
406407 args. outputTo = . stderr
407408 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
412411 }
413412 } else {
414413 print ( " swift-backtrace: missing output-to value " ,
@@ -540,6 +539,69 @@ Generate a backtrace for the parent process.
540539 currentThread = target!. crashingThreadNdx
541540 }
542541
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+
543605 printCrashLog ( )
544606
545607 writeln ( " " )
@@ -707,11 +769,18 @@ Generate a backtrace for the parent process.
707769 description = " Program crashed: \( target. signalDescription) at \( hex ( target. faultAddress) ) "
708770 }
709771
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+ }
711780 if args. color {
712- write ( " \r \u{1b} [0K " )
781+ print ( " \r \u{1b} [0K " , terminator : " " , to : & handlerOut )
713782 } else {
714- write ( " done *** \n \n " )
783+ print ( " done *** \n \n " , terminator : " " , to : & handlerOut )
715784 }
716785
717786 writeln ( theme. crashReason ( description) )
@@ -839,7 +908,7 @@ Generate a backtrace for the parent process.
839908 }
840909
841910 while true {
842- outputStream. flush ( )
911+ outputStream! . flush ( )
843912 write ( theme. prompt ( " >>> " ) , flush: true )
844913 guard let input = readLine ( ) else {
845914 print ( " " )
0 commit comments