Skip to content

Commit f0a6b02

Browse files
committed
[swift-inspect] Change argument options to select between file or standard output
With this change we make --json flag to toggle between JSON and text output. If --output-file option is given, then the output stream is forwarded to the file specified.
1 parent d9989d3 commit f0a6b02

File tree

2 files changed

+50
-22
lines changed

2 files changed

+50
-22
lines changed

tools/swift-inspect/Sources/swift-inspect/Operations/DumpGenericMetadata.swift

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,29 @@ private struct Metadata: Encodable {
5555
}
5656
}
5757

58+
internal struct Output: TextOutputStream {
59+
let fileHandle: FileHandle
60+
init(_ outputFile: String? = nil) throws {
61+
if let outputFile {
62+
if FileManager().createFile(atPath: outputFile, contents: nil) {
63+
self.fileHandle = try FileHandle(forWritingAtPath: outputFile)!
64+
} else {
65+
print("Unable to create file \(outputFile)")
66+
exit(1)
67+
}
68+
} else {
69+
self.fileHandle = FileHandle.standardOutput
70+
}
71+
}
72+
73+
mutating func write(_ string: String) {
74+
if let encodedString = string.data(using: .utf8) {
75+
fileHandle.write(encodedString)
76+
}
77+
}
78+
79+
}
80+
5881
internal struct DumpGenericMetadata: ParsableCommand {
5982
static let configuration = CommandConfiguration(
6083
abstract: "Print the target's generic metadata allocations.")
@@ -96,56 +119,58 @@ internal struct DumpGenericMetadata: ParsableCommand {
96119
backtrace: currentBacktrace)
97120
}
98121

99-
if let outputFile = genericMetadataOptions.outputJson {
100-
try dumpToJson(process: process, generics: generics,
101-
outputPath: outputFile)
122+
if genericMetadataOptions.json {
123+
try dumpJson(process: process, generics: generics)
102124
} else {
103-
try dumpToStdout(process: process, generics: generics)
125+
try dumpText(process: process, generics: generics)
104126
}
105127
}
106128
}
107129

108-
private func dumpToStdout(process: any RemoteProcess, generics: [Metadata]) throws {
130+
private func dumpText(process: any RemoteProcess, generics: [Metadata]) throws {
109131
var errorneousMetadata: [(ptr: swift_reflection_ptr_t, name: String)] = []
110-
111-
print("Address", "Allocation", "Size", "Offset", "isArrayOfClass", "Name", separator: "\t")
132+
var output = try Output(genericMetadataOptions.outputFile)
133+
print("Address", "Allocation", "Size", "Offset", "isArrayOfClass", "Name", separator: "\t", to: &output)
112134
generics.forEach {
113-
print("\(hex: $0.ptr)", terminator: "\t")
135+
print("\(hex: $0.ptr)", terminator: "\t", to: &output)
114136
if let allocation = $0.allocation, let offset = $0.offset {
115-
print("\(hex: allocation.ptr)\t\(allocation.size)\t\(offset)", terminator: "\t")
137+
print("\(hex: allocation.ptr)\t\(allocation.size)\t\(offset)", terminator: "\t", to: &output)
116138
} else {
117139
if $0.garbage {
118140
errorneousMetadata.append((ptr: $0.ptr, name: $0.name))
119141
}
120-
print("???\t??\t???", terminator: "\t")
142+
print("???\t??\t???", terminator: "\t", to: &output)
121143
}
122-
print($0.isArrayOfClass, terminator: "\t")
123-
print($0.name)
144+
print($0.isArrayOfClass, terminator: "\t", to: &output)
145+
print($0.name, to: &output)
124146
if let _ = backtraceOptions.style, let _ = $0.allocation {
125-
print($0.backtrace ?? " No stacktrace available")
147+
print($0.backtrace ?? " No stacktrace available", to: &output)
126148
}
127149
}
128150

129151
if errorneousMetadata.count > 0 {
130-
print("Error: The following metadata was not found in any DATA or AUTH segments, may be garbage.")
152+
print("Error: The following metadata was not found in any DATA or AUTH segments, may be garbage.", to: &output)
131153
errorneousMetadata.forEach {
132-
print("\(hex: $0.ptr)\t\($0.name)")
154+
print("\(hex: $0.ptr)\t\($0.name)", to: &output)
133155
}
134156
}
135157
}
136158

137-
private func dumpToJson(process: any RemoteProcess,
138-
generics: [Metadata],
139-
outputPath: String) throws {
159+
private func dumpJson(process: any RemoteProcess,
160+
generics: [Metadata]) throws {
140161
struct AllMetadataEntries: Encodable {
141162
var metadata: [Metadata]
142163
}
143164
let allMetadataEntries = AllMetadataEntries(metadata: generics)
144165
let encoder = JSONEncoder()
145166
encoder.outputFormatting = .prettyPrinted
146167
let data = try encoder.encode(allMetadataEntries)
147-
try String(data: data, encoding: .utf8)!
148-
.write(toFile: outputPath, atomically: true, encoding: .utf8)
168+
let jsonOutput = String(data: data, encoding: .utf8)!
169+
if let outputFile = genericMetadataOptions.outputFile {
170+
try jsonOutput.write(toFile: outputFile, atomically: true, encoding: .utf8)
171+
} else {
172+
print(jsonOutput)
173+
}
149174
}
150175

151176
}

tools/swift-inspect/Sources/swift-inspect/main.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ internal struct GenericMetadataOptions: ParsableArguments {
4646
@Flag(help: "Show allocations in mangled form")
4747
var mangled: Bool = false
4848

49-
@Option(help: "Path to output JSON file")
50-
var outputJson: String? = nil
49+
@Flag(help: "Output JSON")
50+
var json: Bool = false
51+
52+
@Option(help: "Output to a file")
53+
var outputFile: String? = nil
5154
}
5255

5356
internal func inspect(options: UniversalOptions,

0 commit comments

Comments
 (0)