Skip to content

Commit 0414cb2

Browse files
authored
Merge pull request #79049 from al45tair/eng/PR-136977833-6.1
[Backtracing] Support redirection to a named file.
2 parents 7bf11dc + ca4d5a4 commit 0414cb2

File tree

7 files changed

+454
-436
lines changed

7 files changed

+454
-436
lines changed

stdlib/public/libexec/swift-backtrace/Utils.swift

+21-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#if canImport(Darwin)
18-
import Darwin.C
18+
import Darwin
1919
#elseif canImport(Glibc)
2020
import Glibc
2121
#elseif canImport(Musl)
@@ -143,6 +143,22 @@ internal func spawn(_ path: String, args: [String]) throws {
143143

144144
#endif // os(macOS)
145145

146+
internal func isDir(_ path: String) -> Bool {
147+
var st = stat()
148+
guard stat(path, &st) == 0 else {
149+
return false
150+
}
151+
return (st.st_mode & S_IFMT) == S_IFDIR
152+
}
153+
154+
internal func exists(_ path: String) -> Bool {
155+
var st = stat()
156+
guard stat(path, &st) == 0 else {
157+
return false
158+
}
159+
return true
160+
}
161+
146162
struct CFileStream: TextOutputStream {
147163
var fp: UnsafeMutablePointer<FILE>
148164

@@ -153,6 +169,10 @@ struct CFileStream: TextOutputStream {
153169
public func flush() {
154170
fflush(fp)
155171
}
172+
173+
public func close() {
174+
fclose(fp)
175+
}
156176
}
157177

158178
var standardOutput = CFileStream(fp: stdout)

stdlib/public/libexec/swift-backtrace/main.swift

+87-16
Original file line numberDiff line numberDiff line change
@@ -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))
@@ -837,7 +908,7 @@ Generate a backtrace for the parent process.
837908
}
838909

839910
while true {
840-
outputStream.flush()
911+
outputStream!.flush()
841912
write(theme.prompt(">>> "), flush: true)
842913
guard let input = readLine() else {
843914
print("")

0 commit comments

Comments
 (0)