Skip to content

Commit

Permalink
Afterthoughts
Browse files Browse the repository at this point in the history
  • Loading branch information
johnno1962 committed Oct 22, 2020
1 parent ef268bc commit b68e55a
Show file tree
Hide file tree
Showing 6 changed files with 481 additions and 208 deletions.
48 changes: 33 additions & 15 deletions SwiftTrace/SwiftInterpose.swift
Expand Up @@ -5,7 +5,7 @@
// Created by John Holdsworth on 23/09/2020.
// Copyright © 2020 John Holdsworth. All rights reserved.
//
// $Id: //depot/SwiftTrace/SwiftTrace/SwiftInterpose.swift#21 $
// $Id: //depot/SwiftTrace/SwiftTrace/SwiftInterpose.swift#22 $
//
// Extensions to SwiftTrace using dyld_dynamic_interpose
// =====================================================
Expand All @@ -23,8 +23,8 @@ extension SwiftTrace {
public static var swiftFunctionSuffixes = ["fC", "yF", "lF", "tF", "Qrvg"]

/// Regexp pattern for functions to exclude from interposing
public static var excludeFunction = NSRegularExpression(regexp: "^(\\w+\\.\\w+\\()")

public static var excludeFunction = NSRegularExpression(regexp:
"^\\w+\\.\\w+\\(|SwiftTrace|\\.getter : (?!some)")

/// "interpose" aspects onto Swift function name.
/// If the symbol is not in a different framework
Expand Down Expand Up @@ -104,9 +104,7 @@ extension SwiftTrace {
symval, symname, _, _ in
if let methodName = demangle(symbol: symname),
excludeFunction.firstMatch(in: methodName, options: [],
range: NSMakeRange(0, methodName.utf16.count)) == nil &&
!methodName.contains("SwiftTrace") &&
!(methodName.contains(".getter :") && !methodName.hasSuffix("some")),
range: NSMakeRange(0, methodName.utf16.count)) == nil,
let factory = methodFilter(methodName),
let method = factory.init(name: methodName,
original: OpaquePointer(symval)) {
Expand All @@ -117,31 +115,51 @@ extension SwiftTrace {
// print(interposes.count, methodName)
interposes.append(dyld_interpose_tuple(
replacement: hook, replacee: current))
interposed[current] = hook
symbols.append(methodName)
}
}
}

apply(interposes: interposes, symbols: symbols)

bundlesInterposed.insert(String(cString: inBundlePath))
}

/// Use interposing to trace all methods in main bundle
@objc open class func traceMainBundleMethods() {
interposeMethods(inBundlePath: Bundle.main.executablePath!)
}

open class func apply(interposes: [dyld_interpose_tuple],
symbols: [String]? = nil) {
interposes.withUnsafeBufferPointer { interposes in
let debugInterpose = getenv("DEBUG_INTERPOSE") != nil
appBundleImages { (imageName, header) in
for symno in 0..<interposes.count {
if debugInterpose {
for symno in 0 ..< interposes.count {
if debugInterpose, let symbols = symbols {
print("Interposing: \(symbols[symno])")
}
dyld_dynamic_interpose(header,
interposes.baseAddress!+symno, 1)
let interpose = interposes.baseAddress![symno]
interposed[interpose.replacee] = interpose.replacement
}
}
}

bundlesInterposed.insert(String(cString: inBundlePath))
}

/// Use interposing to trace all methods in main bundle
@objc open class func traceMainBundleMethods() {
interposeMethods(inBundlePath: Bundle.main.executablePath!)
@objc open class func revertInterposes() {
var interposes = [dyld_interpose_tuple]()
for (replacee, replacement) in interposed {
interposes.append(dyld_interpose_tuple(
replacement: replacee, replacee: replacement))
}
interposes.withUnsafeBufferPointer { interposes in
appBundleImages { (imageName, header) in
dyld_dynamic_interpose(header,
interposes.baseAddress!, interposes.count)
}
}
}

/// Use interposing to trace all methods in a framework
Expand All @@ -154,7 +172,7 @@ extension SwiftTrace {

/// Apply a trace to all methods in framesworks in app bundle
/// - Parameter subLevels: levels of unqualified traces to show
@objc class func traceFrameworkMethods() {
@objc open class func traceFrameworkMethods() {
appBundleImages { imageName, _ in
if strstr(imageName, ".framework") != nil {
interposeMethods(inBundlePath: imageName)
Expand Down
8 changes: 3 additions & 5 deletions SwiftTrace/SwiftSwizzle.swift
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2020 John Holdsworth. All rights reserved.
//
// Repo: https://github.com/johnno1962/SwiftTrace
// $Id: //depot/SwiftTrace/SwiftTrace/SwiftSwizzle.swift#35 $
// $Id: //depot/SwiftTrace/SwiftTrace/SwiftSwizzle.swift#36 $
//
// Mechanics of Swizzling Swift
// ============================
Expand Down Expand Up @@ -269,10 +269,8 @@ extension SwiftTrace {
guard objcMethod != nil && !isReturn else { return false }
let returnType = methodSignature == nil ? "UNDECODABLE" :
String(cString: sig_returnType(methodSignature!))
let isStret = returnType.hasPrefix("{") &&
!returnType.hasSuffix("=ff}") &&
!returnType.hasSuffix("=dd}") &&
!returnType.hasSuffix("=QQ}")
let isStret = returnType.hasPrefix("{") && "=" !=
returnType[returnType.index(returnType.endIndex, offsetBy: -4)]
if isStret && !isReturn {
invocation.swiftSelf = intArgs[1]
}
Expand Down
44 changes: 17 additions & 27 deletions SwiftTrace/SwiftTrace.swift
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2016 John Holdsworth. All rights reserved.
//
// Repo: https://github.com/johnno1962/SwiftTrace
// $Id: //depot/SwiftTrace/SwiftTrace/SwiftTrace.swift#247 $
// $Id: //depot/SwiftTrace/SwiftTrace/SwiftTrace.swift#248 $
//

import Foundation
Expand Down Expand Up @@ -61,11 +61,11 @@ open class SwiftTrace: NSObject {
public static var lastSwiftTrace = SwiftTrace(previous: nil, subLevels: 0)

/// Previous interposes need to be tracked
public static var interposed = [UnsafeMutableRawPointer: UnsafeMutableRawPointer]()
public static var interposed = [UnsafeRawPointer: UnsafeRawPointer]()

static var bundlesInterposed = Set<String>()

@objc class var isTracing: Bool {
public class var isTracing: Bool {
return lastSwiftTrace.previousSwiftTrace != nil
}

Expand Down Expand Up @@ -112,27 +112,16 @@ open class SwiftTrace: NSObject {
Default pattern of common/problematic symbols to be excluded from tracing
*/
open class var defaultMethodExclusions: String {
#if !os(macOS)
return """
\\.getter| (?:retain|_tryRetain|release|autorelease|_isDeallocating|.cxx_destruct|_?dealloc|description| debugDescription|contextID)]|initWithCoder|\
\\.getter : (?!some)| (?:retain(?:Count)?|_tryRetain|release|autorelease|_isDeallocating|.cxx_destruct|_?dealloc|class|description|\
debugDescription|contextID|undoManager|_animatorClassForTargetClass|cursorUpdate|_isTrackingAreaObject)]|initWithCoder|\
^\\+\\[(?:Reader_Base64|UI(?:NibStringIDTable|NibDecoder|CollectionViewData|WebTouchEventsGestureRecognizer)) |\
^.\\[(?:__NSAtom|NSView|UIView|RemoteCapture|BCEvent) |UIDeviceWhiteColor initWithWhite:alpha:|UIButton _defaultBackgroundImageForType:andState:|\
UIImage _initWithCompositedSymbolImageLayers:name:alignUsingBaselines:|\
_UIWindowSceneDeviceOrientationSettingsDiffAction _updateDeviceOrientationWithSettingObserverContext:windowScene:transitionContext:|\
UIColorEffect colorEffectSaturate:|UIWindow _windowWithContextId:|RxSwift.ScheduledDisposable.dispose| ns(?:li|is)_
"""
#else
return """
\\.getter| (?:retain(?:Count)?|_tryRetain|release|autorelease|_isDeallocating|.cxx_destruct|_?dealloc|class|description| debugDescription|\
contextID!undoManager|_animatorClassForTargetClass|cursorUpdate|_isTrackingAreaObject)]|initWithCoder|\
^\\+\\[(?:Reader_Base64|UI(?:NibStringIDTable|NibDecoder|CollectionViewData|WebTouchEventsGestureRecognizer)) |\
^.\\[(?:__NSAtom|NS(?:View|Appearance|AnimationContext|Segment|KVONotifying__)|_NSViewAnimator|UIView|RemoteCapture|BCEvent) |\
^.\\[(?:__NSAtom|NS(?:View|Appearance|AnimationContext|Segment|KVONotifying_\\S+)|_NSViewAnimator|UIView|RemoteCapture|BCEvent) |\
_TtGC7SwiftUI|NSTheme|NSTracking|UIDeviceWhiteColor initWithWhite:alpha:|UIButton _defaultBackgroundImageForType:andState:|\
UIImage _initWithCompositedSymbolImageLayers:name:alignUsingBaselines:|\
_UIWindowSceneDeviceOrientationSettingsDiffAction _updateDeviceOrientationWithSettingObserverContext:windowScene:transitionContext:|\
UIColorEffect colorEffectSaturate:|UIWindow _windowWithContextId:|RxSwift.ScheduledDisposable.dispose| ns(?:li|is)_
"""
#endif
}

static var exclusionRegexp: NSRegularExpression? =
Expand Down Expand Up @@ -254,7 +243,7 @@ open class SwiftTrace: NSObject {
- parameter bundlePath: Path to bundle to trace
- parameter subLevels: levels of unqualified traces to show
*/
@objc class func trace(bundlePath: UnsafePointer<Int8>?, subLevels: Int = 0) {
@objc open class func trace(bundlePath: UnsafePointer<Int8>?, subLevels: Int = 0) {
startNewTrace(subLevels: subLevels)
forAllClasses(bundlePath: bundlePath) {
(aClass, stop) in
Expand All @@ -265,7 +254,7 @@ open class SwiftTrace: NSObject {
/**
Lists Swift classes not inheriting from NSObject in an app or framework.
*/
open class func swiftClassList(bundlePath: UnsafePointer<Int8>? = nil) -> [AnyClass] {
@objc open class func swiftClassList(bundlePath: UnsafePointer<Int8>? = nil) -> [AnyClass] {
var classes = [AnyClass]()
findSwiftSymbols(bundlePath, "CN", { aClass, _, _, _ in
classes.append(autoBitCast(aClass))
Expand All @@ -278,7 +267,7 @@ open class SwiftTrace: NSObject {
- parameter pattern: regexp patten to specify classes to trace
- parameter subLevels: levels of unqualified traces to show
*/
open class func traceClasses(matchingPattern pattern: String, subLevels: Int = 0) {
@objc open class func traceClasses(matchingPattern pattern: String, subLevels: Int = 0) {
startNewTrace(subLevels: subLevels)
let regexp = NSRegularExpression(regexp: pattern)
forAllClasses {
Expand All @@ -297,7 +286,7 @@ open class SwiftTrace: NSObject {
Underlying implementation of tracing an individual classs.
- parameter aClass: the class, the methods of which to trace
*/
open class func trace(aClass: AnyClass) {
@objc open class func trace(aClass: AnyClass) {
let className = NSStringFromClass(aClass)
if className.hasPrefix("Swift.") || className.hasPrefix("__") {
return
Expand Down Expand Up @@ -335,7 +324,7 @@ open class SwiftTrace: NSObject {
- parameter aClass: the class, the methods of which to trace
- parameter subLevels: levels of unqualified traces to show
*/
open class func traceInstances(ofClass aClass: AnyClass, subLevels: Int = 0) {
@objc open class func traceInstances(ofClass aClass: AnyClass, subLevels: Int = 0) {
startNewTrace(subLevels: subLevels).classFilter = aClass
var tClass: AnyClass? = aClass
while tClass != NSObject.self && tClass != nil {
Expand All @@ -349,7 +338,7 @@ open class SwiftTrace: NSObject {
- parameter anInstance: the class, the methods of which to trace
- parameter subLevels: levels of unqualified traces to show
*/
open class func trace(anInstance: AnyObject, subLevels: Int = 0) {
@objc open class func trace(anInstance: AnyObject, subLevels: Int = 0) {
traceInstances(ofClass: object_getClass(anInstance)!, subLevels: subLevels)
lastSwiftTrace.instanceFilter = unsafeBitCast(anInstance, to: intptr_t.self)
lastSwiftTrace.classFilter = nil
Expand Down Expand Up @@ -416,14 +405,15 @@ open class SwiftTrace: NSObject {
- parameter subLevels: subLevels to log of previous traces to trace
*/
#if swift(>=5.0)
open class func traceProtocolsInBundle(containing aClass: AnyClass? = nil, matchingPattern: String? = nil, subLevels: Int = 0) {
@objc open class func traceProtocolsInBundle(containing aClass: AnyClass? = nil, matchingPattern: String? = nil, subLevels: Int = 0) {
startNewTrace(subLevels: subLevels)
let regex = matchingPattern != nil ?
NSRegularExpression(regexp: matchingPattern!) : nil
findSwiftSymbols(aClass == nil ? callerBundle() :
aClass == NSObject.self ? nil : class_getImageName(aClass), "WP") {
(address: UnsafeMutableRawPointer, _, typeref, typeend) in
let witnessTable = address.assumingMemoryBound(to: SIMP.self)
(address: UnsafeRawPointer, _, typeref, typeend) in
let witnessTable = UnsafeMutableRawPointer(mutating: address)
.assumingMemoryBound(to: SIMP.self)
var info = Dl_info()
// The start of a witness table is always the protocol descriptor
// then the associated types (always in section `__swift5_typeref`)
Expand All @@ -449,7 +439,7 @@ open class SwiftTrace: NSObject {
regex == nil || regex!.matches(demangled) {
if let factory = methodFilter(demangled),
let swizzle = factory.init(name: demangled,
vtableSlot: &witnessTable[slot]) {
vtableSlot: &witnessTable[slot]) {
witnessTable[slot] = swizzle.forwardingImplementation()
}
continue
Expand Down

0 comments on commit b68e55a

Please sign in to comment.