@@ -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,71 @@ 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 || 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+
543607 printCrashLog ( )
544608
545609 writeln ( " " )
@@ -707,11 +771,18 @@ Generate a backtrace for the parent process.
707771 description = " Program crashed: \( target. signalDescription) at \( hex ( target. faultAddress) ) "
708772 }
709773
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+ }
711782 if args. color {
712- write ( " \r \u{1b} [0K " )
783+ print ( " \r \u{1b} [0K " , terminator : " " , to : & handlerOut )
713784 } else {
714- write ( " done *** \n \n " )
785+ print ( " done *** \n \n " , terminator : " " , to : & handlerOut )
715786 }
716787
717788 writeln ( theme. crashReason ( description) )
@@ -838,7 +909,7 @@ Generate a backtrace for the parent process.
838909 }
839910
840911 while true {
841- outputStream. flush ( )
912+ outputStream! . flush ( )
842913 write ( theme. prompt ( " >>> " ) , flush: true )
843914 guard let input = readLine ( ) else {
844915 print ( " " )
0 commit comments