Skip to content

Commit cf48deb

Browse files
authored
Merge pull request #81186 from jabrylg/add-json-summary-to-swift-inspect-dump-raw-metadata
[swift-inspect] Add json and summary options to DumpRawMetadata
2 parents 7db6c5d + b9fa070 commit cf48deb

File tree

3 files changed

+85
-27
lines changed

3 files changed

+85
-27
lines changed

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

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ internal struct Output: TextOutputStream {
8888
}
8989
}
9090

91+
internal func dumpJson(of: (any Encodable), outputFile: String?) throws {
92+
let encoder = JSONEncoder()
93+
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
94+
let data = try encoder.encode(of)
95+
let jsonOutput = String(data: data, encoding: .utf8)!
96+
if let outputFile = outputFile {
97+
try jsonOutput.write(toFile: outputFile, atomically: true, encoding: .utf8)
98+
} else {
99+
print(jsonOutput)
100+
}
101+
}
102+
91103
internal struct DumpGenericMetadata: ParsableCommand {
92104
static let configuration = CommandConfiguration(
93105
abstract: "Print the target's generic metadata allocations.")
@@ -99,7 +111,10 @@ internal struct DumpGenericMetadata: ParsableCommand {
99111
var backtraceOptions: BacktraceOptions
100112

101113
@OptionGroup()
102-
var genericMetadataOptions: GenericMetadataOptions
114+
var metadataOptions: MetadataOptions
115+
116+
@Flag(help: "Show allocations in mangled form")
117+
var mangled: Bool = false
103118

104119
func run() throws {
105120
disableStdErrBuffer()
@@ -126,7 +141,7 @@ internal struct DumpGenericMetadata: ParsableCommand {
126141

127142
return Metadata(ptr: pointer,
128143
allocation: allocation,
129-
name: process.context.name(type: pointer, mangled: genericMetadataOptions.mangled) ?? "<unknown>",
144+
name: process.context.name(type: pointer, mangled: mangled) ?? "<unknown>",
130145
isArrayOfClass: process.context.isArrayOfClass(pointer),
131146
garbage: garbage,
132147
backtrace: currentBacktrace)
@@ -146,30 +161,30 @@ internal struct DumpGenericMetadata: ParsableCommand {
146161
}
147162
}
148163

149-
if genericMetadataOptions.json {
164+
if metadataOptions.json {
150165
let processMetadata = ProcessMetadata(name: process.processName,
151166
pid: process.processIdentifier as! ProcessIdentifier,
152167
metadata: generics)
153168
allProcesses.append(processMetadata)
154-
} else if !genericMetadataOptions.summary {
169+
} else if !metadataOptions.summary {
155170
try dumpText(process: process, generics: generics)
156171
}
157172
} // inspect
158173

159-
if genericMetadataOptions.json {
160-
if genericMetadataOptions.summary {
161-
try dumpJson(of: metadataSummary)
174+
if metadataOptions.json {
175+
if metadataOptions.summary {
176+
try dumpJson(of: metadataSummary, outputFile: metadataOptions.outputFile)
162177
} else {
163-
try dumpJson(of: allProcesses)
178+
try dumpJson(of: allProcesses, outputFile: metadataOptions.outputFile)
164179
}
165-
} else if genericMetadataOptions.summary {
180+
} else if metadataOptions.summary {
166181
try dumpTextSummary(of: metadataSummary)
167182
}
168183
}
169184

170185
private func dumpText(process: any RemoteProcess, generics: [Metadata]) throws {
171186
var erroneousMetadata: [(ptr: swift_reflection_ptr_t, name: String)] = []
172-
var output = try Output(genericMetadataOptions.outputFile)
187+
var output = try Output(metadataOptions.outputFile)
173188
print("\(process.processName)(\(process.processIdentifier)):\n", to: &output)
174189
print("Address", "Allocation", "Size", "Offset", "isArrayOfClass", "Name", separator: "\t", to: &output)
175190
generics.forEach {
@@ -198,20 +213,8 @@ internal struct DumpGenericMetadata: ParsableCommand {
198213
print("", to: &output)
199214
}
200215

201-
private func dumpJson(of: (any Encodable)) throws {
202-
let encoder = JSONEncoder()
203-
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
204-
let data = try encoder.encode(of)
205-
let jsonOutput = String(data: data, encoding: .utf8)!
206-
if let outputFile = genericMetadataOptions.outputFile {
207-
try jsonOutput.write(toFile: outputFile, atomically: true, encoding: .utf8)
208-
} else {
209-
print(jsonOutput)
210-
}
211-
}
212-
213216
private func dumpTextSummary(of: [String: MetadataSummary]) throws {
214-
var output = try Output(genericMetadataOptions.outputFile)
217+
var output = try Output(metadataOptions.outputFile)
215218
print("Size", "Owners", "Name", separator: "\t", to: &output)
216219
var totalSize = 0
217220
var unknownSize = 0

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@
1212

1313
import ArgumentParser
1414
import SwiftRemoteMirror
15+
import Foundation
16+
17+
private struct AllocatorTagTotal: Encodable {
18+
let name: String
19+
let tag: Int
20+
var totalBytes: Int
21+
}
22+
23+
private struct Summary: Encodable {
24+
let totalBytesAllocated: Int
25+
let allocatorTags: [AllocatorTagTotal]
26+
}
27+
28+
private struct RawMetadataOutput: Encodable {
29+
let allocationList: [swift_metadata_allocation_t]?
30+
let summary: Summary?
31+
}
1532

1633
internal struct DumpRawMetadata: ParsableCommand {
1734
static let configuration = CommandConfiguration(
@@ -22,8 +39,15 @@ internal struct DumpRawMetadata: ParsableCommand {
2239

2340
@OptionGroup()
2441
var backtraceOptions: BacktraceOptions
42+
43+
@OptionGroup()
44+
var metadataOptions: MetadataOptions
2545

2646
func run() throws {
47+
var allocatorTagTotals = [Int: AllocatorTagTotal]()
48+
var total: Int = 0
49+
var allocationList: [swift_metadata_allocation_t] = []
50+
2751
try inspect(options: options) { process in
2852
let stacks: [swift_reflection_ptr_t:[swift_reflection_ptr_t]]? =
2953
backtraceOptions.style == nil
@@ -33,6 +57,18 @@ internal struct DumpRawMetadata: ParsableCommand {
3357
try process.context.allocations.forEach { allocation in
3458
let name: String = process.context.name(allocation: allocation.tag) ?? "<unknown>"
3559
print("Metadata allocation at: \(hex: allocation.ptr) size: \(allocation.size) tag: \(allocation.tag) (\(name))")
60+
61+
if metadataOptions.summary {
62+
if var allocatorTagTotal = allocatorTagTotals[Int(allocation.tag)] {
63+
allocatorTagTotal.totalBytes += allocation.size
64+
allocatorTagTotals[Int(allocation.tag)] = allocatorTagTotal
65+
} else {
66+
allocatorTagTotals[Int(allocation.tag)] = AllocatorTagTotal(name: name, tag: Int(allocation.tag), totalBytes: allocation.size)
67+
}
68+
69+
total += allocation.size
70+
}
71+
allocationList.append(allocation)
3672
if let style = backtraceOptions.style {
3773
if let stack = stacks?[allocation.ptr] {
3874
print(backtrace(stack, style: style, process.symbolicate))
@@ -42,5 +78,27 @@ internal struct DumpRawMetadata: ParsableCommand {
4278
}
4379
}
4480
}
81+
82+
if metadataOptions.json {
83+
let jsonStruct: RawMetadataOutput
84+
let allocatorTagArray = Array(allocatorTagTotals.values).sorted(by: {$0.totalBytes > $1.totalBytes})
85+
86+
if metadataOptions.summary {
87+
let summaryStruct = Summary(totalBytesAllocated: total, allocatorTags: allocatorTagArray)
88+
jsonStruct = RawMetadataOutput(allocationList: allocationList, summary: summaryStruct)
89+
} else {
90+
jsonStruct = RawMetadataOutput(allocationList: allocationList, summary: nil)
91+
}
92+
try dumpJson(of: jsonStruct, outputFile: metadataOptions.outputFile)
93+
} else if metadataOptions.summary {
94+
let allocatorTagArray = Array(allocatorTagTotals.values).sorted(by: {$0.totalBytes > $1.totalBytes})
95+
96+
print("Metadata allocation summary:")
97+
for tag in allocatorTagArray {
98+
print("Tag: \(tag.tag) (\(tag.name)) Size: \(tag.totalBytes) bytes")
99+
}
100+
101+
print("\nTotal bytes allocated: \(total)")
102+
}
45103
}
46104
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ internal struct BacktraceOptions: ParsableArguments {
6262
}
6363
}
6464

65-
internal struct GenericMetadataOptions: ParsableArguments {
66-
@Flag(help: "Show allocations in mangled form")
67-
var mangled: Bool = false
68-
65+
internal struct MetadataOptions: ParsableArguments {
6966
@Flag(help: "Output JSON")
7067
var json: Bool = false
7168

0 commit comments

Comments
 (0)