12
12
13
13
import ArgumentParser
14
14
import SwiftRemoteMirror
15
+ import Foundation
15
16
16
- private struct Metadata {
17
+ private struct Metadata : Encodable {
17
18
let ptr : swift_reflection_ptr_t
18
19
var allocation : swift_metadata_allocation_t ?
19
-
20
20
let name : String
21
21
let isArrayOfClass : Bool
22
-
22
+ var garbage : Bool = false
23
23
var offset : Int ? { allocation. map { Int ( self . ptr - $0. ptr) } }
24
+ var backtrace : String ?
25
+
26
+ enum CodingKeys : String , CodingKey {
27
+ case ptr = " address "
28
+ case allocation
29
+ case name
30
+ case isArrayOfClass
31
+ case garbage
32
+ case offset
33
+ case backtrace
34
+ }
35
+
36
+ func encode( to encoder: Encoder ) throws {
37
+ var container = encoder. container ( keyedBy: CodingKeys . self)
38
+ try container. encode ( ptr, forKey: . ptr)
39
+ try container. encode ( name, forKey: . name)
40
+ if isArrayOfClass {
41
+ try container. encode ( isArrayOfClass, forKey: . isArrayOfClass)
42
+ }
43
+ if garbage {
44
+ try container. encode ( garbage, forKey: . garbage)
45
+ }
46
+ if let offset {
47
+ try container. encode ( offset, forKey: . offset)
48
+ }
49
+ if let backtrace {
50
+ try container. encode ( backtrace, forKey: . backtrace)
51
+ }
52
+ if let allocation {
53
+ try container. encode ( allocation, forKey: . allocation)
54
+ }
55
+ }
24
56
}
25
57
26
58
internal struct DumpGenericMetadata : ParsableCommand {
@@ -41,52 +73,79 @@ internal struct DumpGenericMetadata: ParsableCommand {
41
73
let allocations : [ swift_metadata_allocation_t ] =
42
74
try process. context. allocations. sorted ( )
43
75
44
- let generics : [ Metadata ] = allocations. compactMap { allocation -> Metadata ? in
76
+ let stacks : [ swift_reflection_ptr_t : [ swift_reflection_ptr_t ] ] =
77
+ backtraceOptions. style == nil
78
+ ? [ swift_reflection_ptr_t: [ swift_reflection_ptr_t] ] ( )
79
+ : try process. context. allocationStacks
80
+
81
+ let generics : [ Metadata ] = allocations. compactMap { allocation in
45
82
let pointer = swift_reflection_allocationMetadataPointer ( process. context, allocation)
46
83
if pointer == 0 { return nil }
84
+ let allocation = allocations. last ( where: { pointer >= $0. ptr && pointer < $0. ptr + UInt64( $0. size) } )
85
+ let garbage = ( allocation == nil && swift_reflection_ownsAddressStrict ( process. context, UInt ( pointer) ) == 0 )
86
+ var currentBacktrace : String ?
87
+ if let style = backtraceOptions. style, let allocation, let stack = stacks [ allocation. ptr] {
88
+ currentBacktrace = backtrace ( stack, style: style, process. symbolicate)
89
+ }
47
90
48
91
return Metadata ( ptr: pointer,
49
- allocation: allocations. last ( where: { pointer >= $0. ptr && pointer < $0. ptr + swift_reflection_ptr_t( $0. size) } ) ,
50
- name: ( process. context. name ( type: pointer, mangled: genericMetadataOptions. mangled) ?? " <unknown> " ) ,
51
- isArrayOfClass: process. context. isArrayOfClass ( pointer) )
92
+ allocation: allocation,
93
+ name: process. context. name ( type: pointer, mangled: genericMetadataOptions. mangled) ?? " <unknown> " ,
94
+ isArrayOfClass: process. context. isArrayOfClass ( pointer) ,
95
+ garbage: garbage,
96
+ backtrace: currentBacktrace)
52
97
}
53
98
54
- let stacks : [ swift_reflection_ptr_t : [ swift_reflection_ptr_t ] ] ? =
55
- backtraceOptions. style == nil
56
- ? nil
57
- : try process. context. allocationStacks
58
-
59
- var errorneousMetadata : [ ( ptr: swift_reflection_ptr_t , name: String ) ] = [ ]
60
-
61
- print ( " Address " , " Allocation " , " Size " , " Offset " , " isArrayOfClass " , " Name " , separator: " \t " )
62
- generics. forEach {
63
- print ( " \( hex: $0. ptr) " , terminator: " \t " )
64
- if let allocation = $0. allocation, let offset = $0. offset {
65
- print ( " \( hex: allocation. ptr) \t \( allocation. size) \t \( offset) " , terminator: " \t " )
66
- } else {
67
- if ( swift_reflection_ownsAddressStrict ( process. context, UInt ( $0. ptr) ) ) == 0 {
68
- errorneousMetadata. append ( ( ptr: $0. ptr, name: $0. name) )
69
- }
70
- print ( " ??? \t ?? \t ??? " , terminator: " \t " )
71
- }
72
- print ( $0. isArrayOfClass, terminator: " \t " )
73
- print ( $0. name)
74
- if let style = backtraceOptions. style, let allocation = $0. allocation {
75
- if let stack = stacks ? [ allocation. ptr] {
76
- print ( backtrace ( stack, style: style, process. symbolicate) )
77
- } else {
78
- print ( " No stacktrace available " )
79
- }
80
- }
99
+ if let outputFile = genericMetadataOptions. outputJson {
100
+ try dumpToJson ( process: process, generics: generics,
101
+ outputPath: outputFile)
102
+ } else {
103
+ try dumpToStdout ( process: process, generics: generics)
81
104
}
105
+ }
106
+ }
82
107
83
- if errorneousMetadata. count > 0 {
84
- print ( " Error: The following metadata was not found in any DATA or AUTH segments, may be garbage. " )
85
- errorneousMetadata. forEach {
86
- print ( " \( hex: $0. ptr) \t \( $0. name) " )
108
+ private func dumpToStdout( process: any RemoteProcess , generics: [ Metadata ] ) throws {
109
+ var errorneousMetadata : [ ( ptr: swift_reflection_ptr_t , name: String ) ] = [ ]
110
+
111
+ print ( " Address " , " Allocation " , " Size " , " Offset " , " isArrayOfClass " , " Name " , separator: " \t " )
112
+ generics. forEach {
113
+ print ( " \( hex: $0. ptr) " , terminator: " \t " )
114
+ if let allocation = $0. allocation, let offset = $0. offset {
115
+ print ( " \( hex: allocation. ptr) \t \( allocation. size) \t \( offset) " , terminator: " \t " )
116
+ } else {
117
+ if $0. garbage {
118
+ errorneousMetadata. append ( ( ptr: $0. ptr, name: $0. name) )
87
119
}
120
+ print ( " ??? \t ?? \t ??? " , terminator: " \t " )
121
+ }
122
+ print ( $0. isArrayOfClass, terminator: " \t " )
123
+ print ( $0. name)
124
+ if let _ = backtraceOptions. style, let _ = $0. allocation {
125
+ print ( $0. backtrace ?? " No stacktrace available " )
88
126
}
127
+ }
128
+
129
+ if errorneousMetadata. count > 0 {
130
+ print ( " Error: The following metadata was not found in any DATA or AUTH segments, may be garbage. " )
131
+ errorneousMetadata. forEach {
132
+ print ( " \( hex: $0. ptr) \t \( $0. name) " )
133
+ }
134
+ }
135
+ }
89
136
137
+ private func dumpToJson( process: any RemoteProcess ,
138
+ generics: [ Metadata ] ,
139
+ outputPath: String ) throws {
140
+ struct AllMetadataEntries : Encodable {
141
+ var metadata : [ Metadata ]
90
142
}
143
+ let allMetadataEntries = AllMetadataEntries ( metadata: generics)
144
+ let encoder = JSONEncoder ( )
145
+ encoder. outputFormatting = . prettyPrinted
146
+ let data = try encoder. encode ( allMetadataEntries)
147
+ try String ( data: data, encoding: . utf8) !
148
+ . write ( toFile: outputPath, atomically: true , encoding: . utf8)
91
149
}
150
+
92
151
}
0 commit comments