Trace Swift and Objective-C method invocations

Related tags

Logging SwiftTrace
Overview

SwiftTrace

Trace Swift and Objective-C method invocations of non-final classes in an app bundle or framework. Think Xtrace but for Swift and Objective-C. You can also add "aspects" to member functions of non-final Swift classes to have a closure called before or after a function implementation executes which in turn can modify incoming arguments or the return value! Apart from the logging functionality, with binary distribution of Swift frameworks on the horizon perhaps this will be of use in the same way "Swizzling" was in days of yore.

SwiftTrace Example

Note: none of these features will work on a class or method that is final or internal in a module compiled with whole module optimisation as the dispatch of the method will be "direct" i.e. linked to a symbol at the call site rather than going through the class' vtable. As such it is possible to trace calls to methods of a struct but only if they are referenced through a protocol as they use a witness table which can be patched.

SwiftTrace can be used with the Swift Package Manager or as a CocoaPod by adding the following to your project's Podfile:

    pod 'SwiftTrace'

Once the project has rebuilt, import SwiftTrace into the application's AppDelegate and add something like the following to the beginning of it's didFinishLaunchingWithOptions method:

    SwiftTrace.traceBundle(containing: type(of: self))

This traces all classes defined in the main application bundle. To trace, for example, all classes in the RxSwift Pod add the following

    SwiftTrace.traceBundle(containing: RxSwift.DisposeBase.self)

This gives output in the Xcode debug console such as that above.

To trace a system framework such as UIKit you can trace classes using a pattern:

    SwiftTrace.traceClasses(matchingPattern:"^UI")

Individual classes can be traced using the underlying api:

    SwiftTrace.trace(aClass: MyClass.self)

Or to trace all methods of instances of a particular class including those of their superclasses use the following:

    SwiftTrace.traceInstances(ofClass: aClass)

Or to trace only a particular instance use the following:

    SwiftTrace.trace(anInstance: anObject)

If you have specified "-Xlinker -interposable" in your project's "Other Linker Flags" it's possible to trace all methods in the application's main bundle at once which can be useful for profiling SwiftUI using the following call:

    SwiftTrace.traceMainBundleMethods()

It is possible to trace methods of a structs or other types if they are messaged through protools as this would then be indirect via what is called a witness table. Tracing protocols is available at the bundle level where the bundle being traced is specified using a class instance. They can be further filtered by an optional regular expression. For example, the following:

SwiftTrace.traceProtocolsInBundle(containing: AClassInTheBundle.self, matchingPattern: "regexp")

For example, to trace internal calls made in the SwiftUI framework you can use the following:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    SwiftTrace.traceProtocolsInBundle(containing: UIHostingController<HomeView>.self)
    return true
}

Which traces are applied can be filtered using method name inclusion and exclusion regexps.

    SwiftTrace.methodInclusionPattern = "TestClass"
    SwiftTrace.methodExclusionPattern = "init|"+SwiftTrace.defaultMethodExclusions

These methods must be called before you start the trace as they are applied during the "Swizzle" phase. There is a default set of exclusions setup as a result of testing by tracing UIKit.

open class var defaultMethodExclusions: String {
    return """
        \\.getter| (?:retain|_tryRetain|release|_isDeallocating|.cxx_destruct|dealloc|description| debugDescription)]|initWithCoder|\
        ^\\+\\[(?:Reader_Base64|UI(?:NibStringIDTable|NibDecoder|CollectionViewData|WebTouchEventsGestureRecognizer)) |\
        ^.\\[(?:UIView|RemoteCapture) |UIDeviceWhiteColor initWithWhite:alpha:|UIButton _defaultBackgroundImageForType:andState:|\
        UIImage _initWithCompositedSymbolImageLayers:name:alignUsingBaselines:|\
        _UIWindowSceneDeviceOrientationSettingsDiffAction _updateDeviceOrientationWithSettingObserverContext:windowScene:transitionContext:|\
        UIColorEffect colorEffectSaturate:|UIWindow _windowWithContextId:|RxSwift.ScheduledDisposable.dispose| ns(?:li|is)_
        """
}

If you want to further process output you can define your own custom tracing sub class:

    class MyTracer: SwiftTrace.Decorated {

        override func onEntry(stack: inout SwiftTrace.EntryStack) {
            print( ">> "+stack )
        }
    }
    
    SwiftTrace.swizzleFactory = MyTracer.self

As the amount of of data logged can quickly get out of hand you can control what is logged by combing traces with the optional subLevels parameter to the above functions. For example, the following puts a trace on all of UIKit but will only log calls to methods of the target instance and up to three levels of calls those method make:

    SwiftTrace.traceBundle(containing: UIView.self)
    SwiftTrace.trace(anInstance: anObject, subLevels: 3)

Or, the following will log methods of the application and calls to RxSwift they make:

    SwiftTrace.traceBundle(containing: RxSwift.DisposeBase.self)
    SwiftTrace.traceMainBundle(subLevels: 3)

If this seems arbitrary the rules are reasonably simple. When you add a trace with a non-zero subLevels parameter all previous traces are inhibited unless they are being made up to subLevels inside a method in the most recent trace or if they where filtered anyway by a class or instance (traceInstances(ofClass:) and trace(anInstance:)).

If you would like to extend SwiftTrace to be able to log one of your app's types there are two steps. First, you may need to extend the type to conform to SwiftTraceFloatArg if it contains only float only float types for example SwiftUI.EdgeInsets.

extension SwiftUI.EdgeInsets: SwiftTraceFloatArg {}

Then, add a handler for the type using the following api:

    SwiftTrace.addFormattedType(SwiftUI.EdgeInsets.self, prefix: "SwiftUI")

Many of these API's are also available as a extension of NSObject which is useful when SwiftTrace is made available by dynamically loading bundle as in (InjectionIII)[https://github.com/johnno1962/InjectionIII].

    SwiftTrace.traceBundle(containing: UIView.class)
    // becomes
    UIView.traceBundle()
    
    SwiftTrace.trace(inInstance: anObject)
    // becomes
    anObject.swiftTraceInstance()

This is useful when SwiftTrace is made available by dynamically loading a bundle such as when using (InjectionIII)[https://github.com/johnno1962/InjectionIII]. Rather than having to include a CocoaPod, all you need to do is add SwiftTrace.h in the InjectionIII application's bundle to your bridging header and dynamically load the bundle.

   Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle")?.load()

Benchmarking

To benchmark an app or framework, trace it's methods then you can use one of the following:

   SwiftTrace.sortedElapsedTimes(onlyFirst: 10))
   SwiftTrace.sortedInvocationCounts(onlyFirst: 10))

Object lifetime tracking

You can track the allocations an deallocations of Swift and Objective-C classes using the SwiftTrace.LifetimeTracker class:

SwiftTrace.swizzleFactory = SwiftTrace.LifetimeTracker.self
SwiftTrace.traceMainBundleMethods() == 0 {
    print("⚠️ Tracing Swift methods can only work if you have -Xlinker -interposable to your project's \"Other Linker Flags\"")
}
SwiftTrace.traceMainBundle()

Each time an object is allocated you will see a .__allocating_init message followed by the result and the resulting count of live objects allocated since tracing was started. Each time an object is deallocated you will see a cxx_destruct message followed by the number of objects oustanding for that class.

If you would like to track the lifecycle of Swift structs, create a marker class and add a property to the struct initialised to an instance of it.

class Marker<What> {}

struct MyView: SwiftUI.View {
    var marker = Marker<MyView>()
}

This idea is based on the LifetimeTracker project by Krzysztof Zabłocki.

Aspects

You can add an aspect to a particular method using the method's de-mangled name:

    print(SwiftTrace.addAspect(aClass: TestClass.self,
                      methodName: "SwiftTwaceApp.TestClass.x() -> ()",
                      onEntry: { (_, _) in print("ONE") },
                      onExit: { (_, _) in print("TWO") }))

This will print "ONE" when method "x" of TextClass is called and "TWO when it has exited. The two arguments are the Swizzle which is an object representing the "Swizzle" and the entry or exit stack. The full signature for the entry closure is:

       onEntry: { (swizzle: SwiftTrace.Swizzle, stack: inout SwiftTrace.EntryStack) in

If you understand how registers are allocated to arguments it is possible to poke into the stack to modify the incoming arguments and, for the exit aspect closure you can replace the return value and on a good day log (and prevent) an error being thrown.

Replacing an input argument in the closure is relatively simple:

    stack.intArg1 = 99
    stack.floatArg3 = 77.3

Other types of argument a little more involved. They must be cast and String takes up two integer registers.

    swizzle.rebind(&stack.intArg2).pointee = "Grief"
    swizzle.rebind(&stack.intArg4).pointee = TestClass()

In an exit aspect closure, setting the return type is easier as it is generic:

    stack.setReturn(value: "Phew")

When a function throws you can access NSError objects.

    print(swizzle.rebind(&stack.thrownError, to: NSError.self).pointee)

It is possible to set stack.thrownError to zero to cancel the throw but you will need to set the return value.

If this seems complicated there is a property swizzle.arguments which can be used onEntry which contains the arguments as an Array containing elements of type Any which can be cast to the expected type. Element 0 is self.

Invocation interface

Now we have a trampoline infrastructure, it is possible to implement an invocation api for Swift:

    print("Result: "+SwiftTrace.invoke(target: b,
        methodName: "SwiftTwaceApp.TestClass.zzz(_: Swift.Int, f: Swift.Double, g: Swift.Float, h: Swift.String, f1: Swift.Double, g1: Swift.Float, h1: Swift.Double, f2: Swift.Double, g2: Swift.Float, h2: Swift.Double, e: Swift.Int, ff: Swift.Int, o: SwiftTwaceApp.TestClass) throws -> Swift.String",
        args: 777, 101.0, Float(102.0), "2-2", 103.0, Float(104.0), 105.0, 106.0, Float(107.0), 108.0, 888, 999, TestClass()))

In order to determine the mangled name of a method you can get the full list for a class using this function:

    print(SwiftTrace.methodNames(ofClass: TestClass.self))

There are limitations to this abbreviated interface in that it only supports Double, Float, String, Int, Object, CGRect, CGSize and CGPoint arguments. For other struct types that do not contain floating point values you can conform them to protocol SwiftTraceArg to be able to pass them on the argument list or SwiftTraceFloatArg if they contain only floats. These values and return values must fit into 32 bytes and not contain floats.

How it works

A Swift AnyClass instance has a layout similar to an Objective-C class with some additional data documented in the ClassMetadataSwift in SwiftMeta.swift. After this data there is the vtable of pointers to the class and instance member functions of the class up to the size of the class instance. SwiftTrace replaces these function pointers with a pointer to a unique assembly language "trampoline" entry point which has destination function and data pointers associated with it. Registers are saved and this function is called passing the data pointer to log the method name. The method name is determined by de-mangling the symbol name associated the function address of the implementing method. The registers are then restored and control is passed to the original function implementing the method.

Please file an issue if you encounter a project that doesn't work while tracing. It should be 100% reliable as it uses assembly language trampolines rather than Swizzling like Xtrace. Otherwise, the author can be contacted on Twitter @Injection4Xcode.

Thanks to Oliver Letterer for the imp_implementationForwardingToSelector project adapted to set up the trampolines.

Now uses the very handy https://github.com/facebook/fishhook. See the source and header files for licensing details.

Thanks also to @twostraws' Unwrap and @artsy's eidolon used extensively during testing.

Enjoy!

Comments
  • Crash at macOS Runtime, possible recursive error?

    Crash at macOS Runtime, possible recursive error?

    Hey there - awesome library, looking forward to integrating this and giving it a try in my applications!

    On first run, I'm getting a long what seems to be recursive stack trace when attempting to track instances of a single class (I'm not using the bundle methods right now). I'm linking a .gist with the full trace, it's rather long, but the info is below.

    Trace Gist: https://gist.github.com/tikimcfee/8cdfaefd84e62bc61c69d1c1ed99ee53

    /*
    Error line:
           let threadLocal = ThreadLocal.current() // (!) Thread 1: EXC_BAD_ACCESS (code=2, address=0x16d097fd0)
    */
    
    static var onEntry: @convention(c) (_ swizzle: Swizzle, _ returnAddress: UnsafeRawPointer,
       _ stackPointer: UnsafeMutablePointer<UInt64>) -> IMP? = {
           (swizzle, returnAddress, stackPointer) -> IMP? in
           let threadLocal = ThreadLocal.current() // (!) Thread 1: EXC_BAD_ACCESS (code=2, address=0x16d097fd0)
           let invocation = swizzle.invocationFactory
               .init(stackDepth: threadLocal.invocationStack.count, swizzle: swizzle,
                     returnAddress: returnAddress, stackPointer: stackPointer)
           invocation.saveLevelsTracing = threadLocal.levelsTracing
           threadLocal.invocationStack.append(invocation)
           swizzle.onEntry(stack: &invocation.entryStack.pointee)
           if invocation.shouldDecorate {
                threadLocal.levelsTracing -= 1
           }
           return swizzle.nullImplmentation != nil ?
               autoBitCast(swizzle.nullImplmentation) : swizzle.implementation
    }
    

    I'm running this:

    XCode Version 12.5 (12E262)`
    MacBook Air (M1, 2020)
    
    https://github.com/johnno1962/SwiftTrace/  @ 7.4.2
    

    Much appreciate your time and help, and again, thanks for the great tool!

    EDIT: Version is 7.4.2, not 0.7.4

    opened by tikimcfee 10
  • __ARCLite__ issue

    __ARCLite__ issue

    This may be a project-dependent issue, but I got a following __ARCLite__ error:

    2016-06-10 19:51:14.632 MyApp[60920:738362] *** NSForwarding: warning: object 0x10cf23718 of class '__ARCLite__' does not implement methodSignatureForSelector: -- trouble ahead
    2016-06-10 19:51:14.632 MyApp[60920:738362] *** NSForwarding: warning: object 0x10cf23718 of class '__ARCLite__' does not implement doesNotRecognizeSelector: -- abort
    

    It seems skipping traces of __ARCLite__ class is necessary. Or, is there any good alternative on this?

    opened by inamiy 5
  • - Add external field to skip signature decoration

    - Add external field to skip signature decoration

    Hey! I've been using this as a simple but effective workaround for functions with particularly long or troublesome values. It's fairly blunt, as it directly returns an entry or exit signature if the flag is off, but so far, this has let me get to to high level function invocations, and cleared out string spew.

    Happy to include any other suggested changes, offer a hand elsewhere in this vein, or simply to close it down if you feel there are better options. For example, I could see there being a much more robust registration of specific function signatures to skip.

    Cheers!

    opened by tikimcfee 4
  • build error with undefined symbols

    build error with undefined symbols

    I tried to run the demo with Xcode 13.2.1. but I get an error said that:

    Undefined symbols for architecture armv7:
      "_xt_forwarding_trampolines_start", referenced from:
          SPLForwardingTrampolinePageAlloc() in SwiftTrace-752cea91c401faba410ba8d219a7aa33.o
      "_xt_forwarding_trampolines_next", referenced from:
          SPLForwardingTrampolinePageAlloc() in SwiftTrace-752cea91c401faba410ba8d219a7aa33.o
      "_xt_forwarding_trampolines_end", referenced from:
          SPLForwardingTrampolinePageAlloc() in SwiftTrace-752cea91c401faba410ba8d219a7aa33.o
      "_xt_forwarding_trampoline_page", referenced from:
          SPLForwardingTrampolinePageAlloc() in SwiftTrace-752cea91c401faba410ba8d219a7aa33.o
    ld: symbol(s) not found for architecture armv7
    

    How can I fix it?

    opened by starFelix 3
  • Crash on traceClassesMatching(pattern:) xCode 11.4, obj c

    Crash on traceClassesMatching(pattern:) xCode 11.4, obj c

    I'm having a crash When trying to start tracer calling something like: SwiftTrace.traceClassesMatching(pattern: "Panel") Worked perfectly until xCode 11.4. Base code is in objective-c, with swift code inclusions.

    Crashes inside forAllClasses method with message: *** NSForwarding: warning: object 0x7fff89115000 of class 'PFEmbeddedMulticasterImplementation' does not implement methodSignatureForSelector: -- trouble ahead *** NSForwarding: warning: object 0x7fff89115000 of class 'PFEmbeddedMulticasterImplementation' does not implement doesNotRecognizeSelector: -- abort

    I've found that probably return type of objc_copyClassList has changed and 've found solution for myself here: https://stackoverflow.com/questions/60853427/objc-copyclasslist-crash-exc-bad-instruction-after-update-to-ios-13-4-xcode-1

    Therefore I've overridden 'forAllClasses' method with following implementation:

        override open class func forAllClasses( callback: (_ aClass: AnyClass,
                        _ stop: inout Bool) -> Void ) -> Bool {
            var stopped = false
            var nc: UInt32 = 0
    
            if let classesPointer = objc_copyClassList(&nc) {
                let classes = UnsafeBufferPointer(start: classesPointer, count: Int(nc))
                for aClass in (0..<Int(nc)).map({ classes[$0] }) {
                    callback(aClass, &stopped)
                    if stopped {
                        break
                    }
                }
                free(UnsafeMutableRawPointer(classesPointer))
            }
    
            return stopped
        }
    

    Please review, maybe there is something you should fix in the tracer's code. Thanks a lot for all the job you've done so far!

    opened by svilon 3
  • Should not the context register be saved as well?

    Should not the context register be saved as well?

    I was wondering whether r13 (the call context register) be saved as well (inside the assembly code)? See: https://reverseengineering.stackexchange.com/questions/15725/calling-swift-4-methods-from-c And: https://github.com/apple/swift/blob/master/docs/ABIStabilityManifesto.md#calling-convention

    opened by flockoffiles 3
  • Crash using ADTECH Mobile SDK framework

    Crash using ADTECH Mobile SDK framework

    Enabling tracing of the current bundle leads to this crash at launch for my project.

    2017-03-15 16:12:33.374393 NZZ[1927:1350267] *** Terminating app due to uncaught exception 'Invalid Build Detected', reason: 'Unable to find ADTECHMobileAnalyticsSDK.bundle. Make sure it is added to your resources!'
    *** First throw call stack:
    (0x1818151b8 0x18024c55c 0x181815100 0x1005a6050 0x1005a61ec 0x1005a6688 0x1005a6120 0x1005a6960 0x18024d440 0x18024d6ac 0x180254c54 0x18025f298 0x1005a981c 0x18024d440 0x18024d6ac 0x180254c54 0x18025f298 0x101122820 0x101121e0c 0x10112096c 0x101120990 0x10048c1b0 0x10048b98c 0x10048ba10 0x10007d8ec 0x18773c6a4 0x18794ca98 0x187952808 0x187967104 0x18794f7ec 0x1833eb92c 0x1833eb798 0x1833ebb40 0x1817c2b5c 0x1817c24a4 0x1817c00a4 0x1816ee2b8 0x1877357b0 0x187730534 0x10007add0 0x1806d15b8)
    libc++abi.dylib: terminating with uncaught exception of type NSException
    

    Without SwiftTrace it works and the bundle is added to the resources. When I remove the bundle I get another error. The problem seems to be that due to SwiftTrace somehow classes are instantiated which throw an NSException in ObjC.

    I debugged this and found the following:

    • The crash happens for a specific objc class from the framework in open class func trace( _ aClass: AnyClass ) after the call traceObjcClass(object_getClass( aClass ), which: "+")
    • Excluding the class using the exclusion pattern had no effect, although I checked in the debugger that the exclusion works.
    • The exception is not thrown in the method traceObjcClass(object_getClass( aClass ), which: "+"), but immediately after returning and advancing one step.
    • I can work around this when I add an early return in open class func trace( _ aClass: AnyClass ) for this class.
    • As can be seen in the following screenshot an exception is thrown after some initializers are called. This happens after returning from the method traceObjcClass(object_getClass( aClass ), which: "+") and advancing one step
    screen shot 2017-03-15 at 16 23 39

    Do you need any further information or can I try something to fix this?

    opened by ghost 3
  • Crashing Out on First Usage - Basic configuration demo?

    Crashing Out on First Usage - Basic configuration demo?

    Hey again! Saw a ton of work on this and wanted to try it out again. For whatever reason, I can't get back to the original working implementation. Now, all I do is something like SwiftTrace.traceInstances(ofClass: CodeGrid.self), but as soon as SwiftTrace gets the call, it ends up spitting out thousands of Exclude... print statements for... what seems like pretty much everything in all SDKs everywhere, heh. There's a snippet below where it's pulling in the SiriOntology, et al! Just looking to see what I can / should do to get every I dotted and T crossed to use this fantastic tool.

    EDIT: It looks like I'm seeing something different when using SwiftTrace.trace(aClass: CodeGrid.self) instead of SwiftTrace.traceInstances(ofClass: CodeGrid.self). I'm not getting the world traced which is good, but it looks like not everything is tracing out - functions in extensions in this case. I'd expect there to be lots of function calls between the exclusions and the logging of the two nodes being updated. Seems like I'm just not configuring stuff right, and have a tricky object hierarchy to boot.

    EDIT 2: Might be better if I explain what I'm expecting to happen, or trying to do. I've got this class, CodeGrid, with a few extensions and lots of little formatting / IO stuff. I'd like to trace all of the ins and outs of this class being interacted with - even if just an instance - because I'd like to see a really basic 'running trace' of what the order of certain calls is, how often they're called, etc. I'd like to further get that into a list of some kind in-memory so I can iterate over the output and do some analysis on it. However, as mentioned, I'm just hitting little blocks here and there in understanding how this works and how to properly use it. I'd love to expand further if it helps, or work to produce some samples that you can reproduce with!

    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.id.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.rootContainerNodeName.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.glyphNodeName.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.flattedGlyphNodeName.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.backgroundNodeName.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.backgroundNodeGeometry.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.cloneId.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.fileName.getter : Swift.String
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.(showingRawGlyphs in _264F3C980A92A6BD3C2F4B4FF84BF4B4).getter : Swift.Bool
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.codeGridSemanticInfo.getter : LookAtThat_AppKit.CodeGridSemanticMap
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.walkSemantics.getter : Swift.Bool
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.displayMode.getter : LookAtThat_AppKit.CodeGrid.DisplayMode
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.renderer.getter : LookAtThat_AppKit.CodeGrid.Renderer
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.measures.getter : LookAtThat_AppKit.CodeGrid.Measures
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.rootNode.getter : __C.SCNNode
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.rootContainerNode.getter : __C.SCNNode
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.rawGlyphsNode.getter : __C.SCNNode
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.flattenedGlyphsNode.getter : Swift.Optional<__C.SCNNode>
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.backgroundGeometryNode.getter : __C.SCNNode
    Excluding SwiftTrace of LookAtThat_AppKit.CodeGrid.backgroundGeometry.getter : __C.SCNBox
    LookAtThat_AppKit.CodeGrid.id.setter : "CodeGrid-A9BF7752-132A-4025-8202-95A02BBBECE4"  828.1ms
    LookAtThat_AppKit.CodeGrid.flattenedGlyphsNode.setter : <SCNNode: 0x60000077cb00 'CodeGrid-A9BF7752-132A-4025-8202-95A02BBBECE4-glyphs-flattened' | geometry=<SCNGeometry: 0x600000ac33e0> | no child>  0.2ms
    Test Case '-[LookAtThat_AppKitTests.LookAtThat_AppKitCodeGridTests testTracing]' passed (0.906 seconds).
    

    Lots of exclusions using `SwiftTrace.traceInstances(ofClass: CodeGrid.self)

    opened by tikimcfee 2
  • I'm using CocoaDebug

    I'm using CocoaDebug

    https://github.com/CocoaDebug/CocoaDebug/tree/master/Example_Swift

    it has a line to override print stuff https://github.com/CocoaDebug/CocoaDebug/blob/master/Example_Swift/AppDelegate.swift#L40

    how can I reroute swift to this?

    I'm thinking we need a delegate to pass back to app - or is there a better way you can think of?

    this didn't work

    extension SwiftTrace{
        override open func onExit(stack: inout ExitStack) {
            if let invocation = Invocation.current {
                print("\(String(repeating: "  ", count: invocation.stackDepth))\(name) \(String(format: "%.1fms", elapsed ))")
            }
        }
    }
    
    
    opened by 8secz-johndpope 2
  • parameter 'subLevels' doesn't work

    parameter 'subLevels' doesn't work

    SwiftTrace.traceMainBundle(subLevels: 3)

    when i set parameter 'subLevels' to 3, all methods are traced, not top 3 level methods.

    public func f1() { f2() }

    public func f2() { f3() }

    public func f3() { f4() }

    public func f4() {

    }

    I expect f1、f2、f3 are traced and f4 are not traced, what can I to do?

    opened by neebel 1
  • assembler directive is a better choice.

    assembler directive is a better choice.

    https://github.com/johnno1962/SwiftTrace/blob/master/SwiftTraceGuts/xt_forwarding_trampoline_arm64.s#L93

    https://github.com/jmpews/Dobby/blob/master/source/TrampolineKit/ClosureTrampolineBridge/arm64/dummy/dynamic-closure-trampoline-template-arm64.S#L20

    opened by jmpews 1
Owner
John Holdsworth
Add a bio
John Holdsworth
Logging utility for Swift and Objective C

Swell - Swift Logging A logging utility for Swift and Objective C. ##Features Turn on logging during development, turn them off when building for the

Hubert Rabago 361 Jun 29, 2022
A lightweight Swift logger, uses `print` in development and `NSLog` in production. Support colourful and formatted output.

Loggerithm A lightweight Swift logger, uses print in Debug and NSLog in Production with colourful output. Why In Swift, we usually use print to log in

HongHao Zhang 270 Oct 8, 2022
Easy to use and lightweight logger for iOS, macOS, tvOS, watchOS and Linux in Swift.

Lighty Easy to use and lightweight logger for iOS, macOS, tvOS, watchOS and Linux in Swift. Screenshots Requirements Lighty Version Minimum iOS Target

Abdullah Selek 51 Dec 21, 2022
Elegant and extensive logging facility for OS X & iOS (includes database, Telnet and HTTP servers)

Overview XLFacility, which stands for Extensive Logging Facility, is an elegant and powerful logging facility for OS X & iOS. It was written from scra

Pierre-Olivier Latour 315 Sep 7, 2022
CleanroomLogger provides an extensible Swift-based logging API that is simple, lightweight and performant

CleanroomLogger CleanroomLogger provides an extensible Swift-based logging API that is simple, lightweight and performant. The API provided by Cleanro

null 1.3k Dec 8, 2022
A simple Swift package for measuring and reporting the time taken for operations

Duration A simple Swift package for measuring and reporting the time taken for operations. It is derived from a version for Playgrounds that I blogged

Swift Studies 325 Nov 6, 2022
Simple, lightweight and flexible debug logging framework written in Swift

AELog Simple, lightweight and flexible debug logging minion written in Swift If you find yourself in upcoming statements, then you probably want to us

Marko Tadić 28 Jul 6, 2022
A custom logger implementation and Task Local helper for swift-log

LGNLog A custom logger implementation and TaskLocal helper for Swift-Log. Why and how This package provides two and a half things (and a small bonus):

17:11 Games 0 Oct 26, 2021
A pure Swift library for using ANSI codes. Basically makes command-line coloring and styling very easy!

Colors A pure Swift library for using ANSI codes. Basically makes command-line coloring and styling very easy! Note: Colors master requires Xcode 7.3

Chad Scira 27 Jun 3, 2021
Tracker - A simple location logger app written in Swift and MVVM architecture

Tracker - A simple location logger app written in Swift and MVVM architecture

Loay Ashraf 1 Mar 12, 2022
📱💬🚦 TinyConsole is a micro-console that can help you log and display information inside an iOS application, where having a connection to a development computer is not possible.

TinyConsole TinyConsole is a tiny log console to display information while using your iOS app and written in Swift. Usage Wrap your Main ViewControlle

Devran Cosmo Uenal 2k Jan 3, 2023
A fancy logger yet lightweight, and configurable. 🖨

?? ?? Important: Printer can only print console logs if you're running an app in the Simulator. If you're running in a real device it will not print a

Hemang 66 Dec 7, 2022
In-App iOS Debugging Tool With Enhanced Logging, Networking Info, Crash reporting And More.

The debugger tool for iOS developer. Display logs, network request, device informations, crash logs while using the app. Easy accessible with its bubble head button ?? . Easy to integrate in any apps, to handle development or testing apps easier. First version, there is plenty of room for improvement.

Remi ROBERT 1.8k Dec 29, 2022
Monitor and terminate/throttle CPU hogging processes in iOS

Vedette Monitor and terminate/throttle CPU hogging processes in iOS Vedette is a CPU usage monitoring tweak for processes in iOS like apps and daemons

null 13 Dec 22, 2022
Log every incoming notification to view them again later, also includes attachments and advanced settings to configure

Vē Natively integrated notification logger Installation Add this repository to your package manager

alexandra 43 Dec 25, 2022
A fast & simple, yet powerful & flexible logging framework for Mac and iOS

CocoaLumberjack CocoaLumberjack is a fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS and watchOS. How to get started Fir

null 12.9k Jan 9, 2023
Styling and coloring your XCTest logs on Xcode Console

XLTestLog Notes with Xcode 8 and XLTestLog Since Xcode 8 killed XcodeColors, the current way using XCTestLog on Xcode 8 is just plain texts with emoji

Xaree Lee 58 Feb 2, 2022
JustLog brings logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support for logz.io available.

JustLog JustLog takes logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support f

Just Eat 509 Dec 10, 2022