Skip to content

Commit 334fa99

Browse files
committed
Add protocol to image maps to RuntimeListings
1 parent c2bf0df commit 334fa99

1 file changed

Lines changed: 68 additions & 2 deletions

File tree

HeaderViewer/RuntimeListings.swift

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,48 @@ import Foundation
99
import Combine
1010
import ClassDump
1111
import MachO.dyld
12+
import OSLog
1213

1314
final class RuntimeListings: ObservableObject {
1415
static let shared = RuntimeListings()
1516
private static var sharedIfExists: RuntimeListings?
17+
private static let logger = Logger(subsystem: "null.leptos.HeaderViewer", category: "RuntimeListings")
1618

1719
@Published private(set) var classList: [String]
1820
@Published private(set) var protocolList: [String]
1921
@Published private(set) var imageList: [String]
2022

23+
@Published private(set) var protocolToImage: [String: String]
24+
@Published private(set) var imageToProtocols: [String: [String]]
25+
2126
private let shouldReload = PassthroughSubject<Void, Never>()
2227

2328
private var subscriptions: Set<AnyCancellable> = []
2429

2530
private init() {
26-
self.classList = CDUtilities.classNames()
27-
self.protocolList = CDUtilities.protocolNames()
31+
let classList = CDUtilities.classNames()
32+
let protocolList = CDUtilities.protocolNames()
33+
self.classList = classList
34+
self.protocolList = protocolList
2835
self.imageList = CDUtilities.imageNames()
2936

37+
let (protocolToImage, imageToProtocols) = Self.protocolImageTrackingFor(
38+
protocolList: protocolList, protocolToImage: [:], imageToProtocols: [:]
39+
) ?? ([:], [:])
40+
self.protocolToImage = protocolToImage
41+
self.imageToProtocols = imageToProtocols
42+
3043
RuntimeListings.sharedIfExists = self
3144

3245
shouldReload
3346
.debounce(for: .milliseconds(15), scheduler: DispatchQueue.main)
3447
.sink { [weak self] in
3548
guard let self else { return }
49+
Self.logger.debug("Start reload")
3650
self.classList = CDUtilities.classNames()
3751
self.protocolList = CDUtilities.protocolNames()
3852
self.imageList = CDUtilities.imageNames()
53+
Self.logger.debug("End reload")
3954
}
4055
.store(in: &subscriptions)
4156

@@ -46,13 +61,64 @@ final class RuntimeListings: ObservableObject {
4661
_dyld_register_func_for_remove_image { _, _ in
4762
RuntimeListings.sharedIfExists?.shouldReload.send()
4863
}
64+
65+
$protocolList
66+
.combineLatest($protocolToImage, $imageToProtocols)
67+
.sink { [unowned self] in
68+
guard let (protocolToImage, imageToProtocols) = Self.protocolImageTrackingFor(
69+
protocolList: $0, protocolToImage: $1, imageToProtocols: $2
70+
) else { return }
71+
self.protocolToImage = protocolToImage
72+
self.imageToProtocols = imageToProtocols
73+
}
74+
.store(in: &subscriptions)
75+
4976
}
5077

5178
func isImageLoaded(path: String) -> Bool {
5279
imageList.contains(CDUtilities.patchImagePathForDyld(path))
5380
}
5481
}
5582

83+
private extension RuntimeListings {
84+
static func protocolImageTrackingFor(
85+
protocolList: [String], protocolToImage: [String: String], imageToProtocols: [String: [String]]
86+
) -> ([String: String], [String: [String]])? {
87+
var protocolToImageCopy = protocolToImage
88+
var imageToProtocolsCopy = imageToProtocols
89+
90+
var dlInfo = dl_info()
91+
var didChange: Bool = false
92+
93+
for protocolName in protocolList {
94+
guard protocolToImageCopy[protocolName] == nil else { continue } // happy path
95+
96+
guard let prtcl = NSProtocolFromString(protocolName) else {
97+
logger.error("Failed to find protocol named '\(protocolName, privacy: .public)'")
98+
continue
99+
}
100+
101+
guard dladdr(protocol_getName(prtcl), &dlInfo) != 0 else {
102+
logger.warning("Failed to get dl_info for protocol named '\(protocolName, privacy: .public)'")
103+
continue
104+
}
105+
106+
guard let abc = dlInfo.dli_fname else {
107+
logger.error("Failed to get dli_fname for protocol named '\(protocolName, privacy: .public)'")
108+
continue
109+
}
110+
111+
let imageName = String(cString: abc)
112+
protocolToImageCopy[protocolName] = imageName
113+
imageToProtocolsCopy[imageName, default: []].append(protocolName)
114+
115+
didChange = true
116+
}
117+
guard didChange else { return nil }
118+
return (protocolToImageCopy, imageToProtocolsCopy)
119+
}
120+
}
121+
56122
extension CDUtilities {
57123
class func imageNames() -> [String] {
58124
(0...)

0 commit comments

Comments
 (0)