├── SwiftTraceD
├── .gitignore
├── SwiftTraceGutsD
├── fishhook.c
├── ObjCBridge.mm
├── SwiftTrace.mm
├── Trampolines.mm
├── fast_dladdr.mm
├── SwiftTrace-Swift.h
├── include
│ ├── fishhook.h
│ └── SwiftTrace.h
├── xt_forwarding_trampoline_arm64.s
├── xt_forwarding_trampoline_arm7.s
├── xt_forwarding_trampoline_x64.s
└── xt_forwarding_trampoline_x86.s
├── SwiftTrace
├── SwiftTrace.h
├── Info.plist
├── SwiftStats.swift
├── EasyPointer.swift
├── SwiftAspects.swift
├── SwiftInvoke.swift
├── SwiftStack.swift
├── SwiftLifetime.swift
├── StringIndex.swift
├── SwiftInterpose.swift
└── SwiftRefs.swift
├── SwiftTrace.gif
├── .github
└── FUNDING.yml
├── SwiftTraceOSX
├── SwiftTwaceOSX-Bridging-Header.h
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
└── AppDelegate.swift
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── SwiftTraceApp.xcodeproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── SwiftTrace.podspec
├── SwiftTraceTests
├── Info.plist
└── SwiftTraceTests.swift
├── SwiftTraceXTests
├── Info.plist
└── SwiftTraceXTests.swift
├── SwiftTraceApp
├── DetailViewController.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
└── MasterViewController.swift
├── Package.swift
├── LICENSE
├── SwiftTraceGuts
├── xt_forwarding_trampoline_x86.s
├── xt_forwarding_trampoline_arm7.s
├── include
│ ├── fishhook.h
│ └── SwiftTrace.h
├── xt_forwarding_trampoline_arm64.s
├── xt_forwarding_trampoline_x64.s
├── ObjCBridge.mm
├── Trampolines.mm
├── SwiftTrace.mm
└── fishhook.c
└── README.md
/SwiftTraceD:
--------------------------------------------------------------------------------
1 | SwiftTrace
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
2 | *Library/*
3 | *xcuserdata*
4 |
--------------------------------------------------------------------------------
/SwiftTraceGutsD/fishhook.c:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/fishhook.c
--------------------------------------------------------------------------------
/SwiftTrace/SwiftTrace.h:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/include/SwiftTrace.h
--------------------------------------------------------------------------------
/SwiftTraceGutsD/ObjCBridge.mm:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/ObjCBridge.mm
--------------------------------------------------------------------------------
/SwiftTraceGutsD/SwiftTrace.mm:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/SwiftTrace.mm
--------------------------------------------------------------------------------
/SwiftTraceGutsD/Trampolines.mm:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/Trampolines.mm
--------------------------------------------------------------------------------
/SwiftTraceGutsD/fast_dladdr.mm:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/fast_dladdr.mm
--------------------------------------------------------------------------------
/SwiftTraceGutsD/SwiftTrace-Swift.h:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/SwiftTrace-Swift.h
--------------------------------------------------------------------------------
/SwiftTraceGutsD/include/fishhook.h:
--------------------------------------------------------------------------------
1 | ../../SwiftTraceGuts/include/fishhook.h
--------------------------------------------------------------------------------
/SwiftTraceGutsD/include/SwiftTrace.h:
--------------------------------------------------------------------------------
1 | ../../SwiftTraceGuts/include/SwiftTrace.h
--------------------------------------------------------------------------------
/SwiftTrace.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/SwiftTrace/HEAD/SwiftTrace.gif
--------------------------------------------------------------------------------
/SwiftTraceGutsD/xt_forwarding_trampoline_arm64.s:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/xt_forwarding_trampoline_arm64.s
--------------------------------------------------------------------------------
/SwiftTraceGutsD/xt_forwarding_trampoline_arm7.s:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/xt_forwarding_trampoline_arm7.s
--------------------------------------------------------------------------------
/SwiftTraceGutsD/xt_forwarding_trampoline_x64.s:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/xt_forwarding_trampoline_x64.s
--------------------------------------------------------------------------------
/SwiftTraceGutsD/xt_forwarding_trampoline_x86.s:
--------------------------------------------------------------------------------
1 | ../SwiftTraceGuts/xt_forwarding_trampoline_x86.s
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: johnno1962 # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 |
--------------------------------------------------------------------------------
/SwiftTraceOSX/SwiftTwaceOSX-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "SwiftTrace.h"
6 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftTraceApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftTraceApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftTrace.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "SwiftTrace"
3 | s.version = "7.0.0"
4 | s.summary = "Log Swift or Objective-C method invocations"
5 | s.homepage = "https://github.com/johnno1962/SwiftTrace"
6 | s.social_media_url = "https://twitter.com/Injection4Xcode"
7 | s.documentation_url = "https://github.com/johnno1962/SwiftTrace/blob/master/README.md"
8 | s.license = { :type => "MIT" }
9 | s.authors = { "johnno1962" => "swifttrace@johnholdsworth.com" }
10 |
11 | s.osx.deployment_target = "10.10"
12 | s.ios.deployment_target = "10.0"
13 | s.source = { :git => "https://github.com/johnno1962/SwiftTrace.git", :tag => s.version }
14 | s.source_files = "{SwiftTrace,SwiftTraceGuts,SwiftTraceGuts/include}/*.{swift,h,mm,s}"
15 | end
16 |
--------------------------------------------------------------------------------
/SwiftTraceTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftTraceXTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftTrace/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SwiftTraceOSX/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/SwiftTraceOSX/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2016 John Holdsworth. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/SwiftTraceApp/DetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailViewController.swift
3 | // SwiftTraceApp
4 | //
5 | // Created by John Holdsworth on 10/06/2016.
6 | // Copyright © 2016 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftTrace
11 |
12 | class DetailViewController: UIViewController {
13 |
14 | @IBOutlet weak var detailDescriptionLabel: UILabel!
15 |
16 |
17 | var detailItem: AnyObject? {
18 | didSet {
19 | // Update the view.
20 | self.configureView()
21 | }
22 | }
23 |
24 | func configureView() {
25 | // Update the user interface for the detail item.
26 | if let detail = self.detailItem {
27 | if let label = self.detailDescriptionLabel {
28 | label.text = detail.description
29 | }
30 | }
31 | print(SwiftTrace.sortedElapsedTimes(onlyFirst: 10))
32 | print(SwiftTrace.sortedInvocationCounts(onlyFirst: 10))
33 | }
34 |
35 | override func viewDidLoad() {
36 | super.viewDidLoad()
37 | // Do any additional setup after loading the view, typically from a nib.
38 | self.configureView()
39 | }
40 |
41 | override func didReceiveMemoryWarning() {
42 | super.didReceiveMemoryWarning()
43 | // Dispose of any resources that can be recreated.
44 | }
45 |
46 |
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/SwiftTraceApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 | //
4 | // Repo: https://github.com/johnno1962/SwiftTrace
5 | // $Id: //depot/SwiftTrace/Package.swift#14 $
6 | //
7 |
8 | import PackageDescription
9 |
10 | let package = Package(
11 | name: "SwiftTrace",
12 | platforms: [.macOS("10.12"), .iOS("10.0")],
13 | products: [
14 | // SwiftTrace needs to be .dynamic for
15 | // the trampolines to work on Intel.
16 | .library(name: "SwiftTrace", type: .dynamic, targets: ["SwiftTrace"]),
17 | .library(name: "SwiftTraceGuts", type: .dynamic, targets: ["SwiftTraceGuts"]),
18 | .library(name: "SwiftTraceD", type: .dynamic, targets: ["SwiftTraceD"]),
19 | .library(name: "SwiftTraceGutsD", type: .dynamic, targets: ["SwiftTraceGutsD"]),
20 | ],
21 | dependencies: [],
22 | targets: [
23 | .target(name: "SwiftTrace", dependencies: ["SwiftTraceGuts"], path: "SwiftTrace/"),
24 | .target(name: "SwiftTraceGuts", dependencies: [], path: "SwiftTraceGuts/"),
25 | .target(name: "SwiftTraceD", dependencies: ["SwiftTraceGutsD"],
26 | path: "SwiftTraceD/", swiftSettings: [.define("DEBUG_ONLY")]),
27 | .target(name: "SwiftTraceGutsD", dependencies: [],
28 | path: "SwiftTraceGutsD/", cSettings: [.define("DEBUG_ONLY")]),
29 | ],
30 | cxxLanguageStandard: .cxx11
31 | )
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 John Holdsworth
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
21 | This software contains code written by Oliver Letterer obtained from the
22 | following github project which is licensed under the terms of that project:
23 |
24 | https://github.com/OliverLetterer/imp_implementationForwardingToSelector
25 |
26 | Now uses the very handy https://github.com/facebook/fishhook.
27 | See the source and header files for licensing details.
28 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/xt_forwarding_trampoline_x86.s:
--------------------------------------------------------------------------------
1 |
2 | // $Id: //depot/SwiftTrace/SwiftTraceGuts/xt_forwarding_trampoline_x86.s#4 $
3 |
4 | // *** This architecture is no longer supported *** //
5 |
6 | #if DEBUG || !DEBUG_ONLY
7 | #if defined(__i386__)
8 | .text
9 | .align 12
10 | .globl _xt_forwarding_trampoline_page
11 | .globl _xt_forwarding_trampolines_start
12 | .globl _xt_forwarding_trampolines_next
13 | .globl _xt_forwarding_trampolines_end
14 |
15 | _xt_forwarding_trampoline_page:
16 | _xt_forwarding_trampoline:
17 | popl %eax // pop saved pc (address of first of the three nops)
18 | pushl %ebp // save frame pointer
19 | movl %esp, %ebp // set up new frame
20 | subl $4096+5, %eax // offset address by one page and the length of the call instrux
21 | pushl %eax // save pointer to trampoline data (func+data)
22 | movl 4(%eax), %eax
23 | pushl %eax // save pointer to user data
24 | movl 4(%esp), %eax // fetch pointer to C aspect handler
25 | call *(%eax) // call trace handler
26 | popl %ebp
27 | popl %ebp
28 | popl %ebp // restore frame pointer
29 | jmpl *%eax // pass on to original implementation
30 | nop
31 | nop
32 | nop
33 | nop
34 | nop
35 | nop
36 | nop
37 |
38 | // 508 trampoline entry points
39 | _xt_forwarding_trampolines_start:
40 | call _xt_forwarding_trampoline
41 | nop
42 | nop
43 | nop
44 |
45 | _xt_forwarding_trampolines_next:
46 | .rept 507
47 | call _xt_forwarding_trampoline
48 | nop
49 | nop
50 | nop
51 | .endr
52 |
53 | _xt_forwarding_trampolines_end:
54 | nop
55 | #endif
56 | #endif
57 |
--------------------------------------------------------------------------------
/SwiftTraceApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/xt_forwarding_trampoline_arm7.s:
--------------------------------------------------------------------------------
1 |
2 | // $Id: //depot/SwiftTrace/SwiftTraceGuts/xt_forwarding_trampoline_arm7.s#3 $
3 |
4 | // *** This architecture is no longer supported *** //
5 |
6 | #if DEBUG || !DEBUG_ONLY
7 | #ifdef __arm__
8 |
9 | #include
10 | #if defined(_ARM_ARCH_7)
11 |
12 | // for abi see: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf
13 | // instrux ref: http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001m/QRC0001_UAL.pdf
14 |
15 | # Write out the trampoline table, aligned to the page boundary
16 | .text
17 | .align 14
18 | .globl _xt_forwarding_trampoline_page
19 | .globl _xt_forwarding_trampolines_start
20 | .globl _xt_forwarding_trampolines_next
21 | .globl _xt_forwarding_trampolines_end
22 |
23 | _xt_forwarding_trampoline_page:
24 | _xt_forwarding_trampoline:
25 | push {r7, lr} // save frame pointer and return addresss
26 | mov r7, sp // set up new frame
27 | push {r0, r1, r2, r3, r9} // save first four args on stack
28 | sub r12, #0x4000 // r12 = r12 - pagesize, that is where the data for this trampoline is stored
29 | ldr r0, [r12, #-4] // first arg is user data ptr
30 | ldr r12, [r12] // get pointer to tracer func
31 | blx r12 // call it
32 | mov r12, r0 // return value is original implementation
33 | pop {r0, r1, r2, r3, r9}
34 | mov sp, r7 // unwind stack
35 | pop {r7, lr}
36 | mov pc, r12 // pass control to original imp.
37 |
38 | _xt_forwarding_trampolines_start:
39 | # Save pc+8 into r12, then jump to the actual trampoline implementation
40 | mov r12, pc
41 | b _xt_forwarding_trampoline;
42 |
43 | _xt_forwarding_trampolines_next:
44 | .rept 2041
45 | # Next trampoline entry point
46 | mov r12, pc
47 | b _xt_forwarding_trampoline;
48 | .endr
49 |
50 | _xt_forwarding_trampolines_end:
51 | #endif
52 | #endif
53 | #endif
54 |
--------------------------------------------------------------------------------
/SwiftTraceApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarTintParameters
34 |
35 | UINavigationBar
36 |
37 | Style
38 | UIBarStyleDefault
39 | Translucent
40 |
41 |
42 |
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | UISupportedInterfaceOrientations~ipad
50 |
51 | UIInterfaceOrientationPortrait
52 | UIInterfaceOrientationPortraitUpsideDown
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/SwiftTrace/SwiftStats.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftStats.swift
3 | // SwiftTrace
4 | //
5 | // Created by John Holdsworth on 23/09/2020.
6 | // Copyright © 2020 John Holdsworth. All rights reserved.
7 | //
8 | // Obtaining invocation statistics
9 | // ===============================
10 | //
11 | // $Id: //depot/SwiftTrace/SwiftTrace/SwiftStats.swift#7 $
12 | //
13 |
14 | #if DEBUG || !DEBUG_ONLY
15 | import Foundation
16 |
17 | extension SwiftTrace {
18 |
19 | func populate(elapsedTimes: inout [String: Double]) {
20 | previousSwiftTrace?.populate(elapsedTimes: &elapsedTimes)
21 | for (_, swizzle) in activeSwizzles {
22 | elapsedTimes[swizzle.signature] = swizzle.totalElapsed
23 | }
24 | }
25 |
26 | /**
27 | Accumulated amount of time spent in each swizzled method.
28 | */
29 | public static func elapsedTimes() -> [String: Double] {
30 | var elapsedTimes = [String: Double]()
31 | lastSwiftTrace.populate(elapsedTimes: &elapsedTimes)
32 | return elapsedTimes
33 | }
34 |
35 | /**
36 | Sorted descending accumulated amount of time spent in each swizzled method.
37 | */
38 | public static func sortedElapsedTimes(onlyFirst: Int? = nil) -> [(key: String, value: TimeInterval)] {
39 | let sorted = elapsedTimes().sorted { $1.value < $0.value }
40 | return onlyFirst != nil ? Array(sorted.prefix(onlyFirst!)) : sorted
41 | }
42 |
43 | func populate(invocationCounts: inout [String: Int]) {
44 | previousSwiftTrace?.populate(invocationCounts: &invocationCounts)
45 | for (_, swizzle) in activeSwizzles {
46 | invocationCounts[swizzle.signature] = swizzle.invocationCount
47 | }
48 | }
49 |
50 | /**
51 | Numbers of times each swizzled method has been invoked.
52 | */
53 | public static func invocationCounts() -> [String: Int] {
54 | var invocationCounts = [String: Int]()
55 | lastSwiftTrace.populate(invocationCounts: &invocationCounts)
56 | return invocationCounts
57 | }
58 |
59 | /**
60 | Sorted descending numbers of times each swizzled method has been invoked.
61 | */
62 | public static func sortedInvocationCounts(onlyFirst: Int? = nil) -> [(key: String, value: Int)] {
63 | let sorted = invocationCounts().sorted { $1.value < $0.value }
64 | return onlyFirst != nil ? Array(sorted.prefix(onlyFirst!)) : sorted
65 | }
66 |
67 | public static func callOrder() -> [Swizzle] {
68 | var calls = [Swizzle]()
69 | var call = firstCalled
70 | while call != nil {
71 | calls.append(call!)
72 | call = call!.nextCalled
73 | }
74 | return calls
75 | }
76 | }
77 | #endif
78 |
--------------------------------------------------------------------------------
/SwiftTraceXTests/SwiftTraceXTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftTraceXTests.swift
3 | // SwiftTraceXTests
4 | //
5 | // Created by John Holdsworth on 13/06/2016.
6 | // Copyright © 2016 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | struct TestStruct: Equatable {
12 |
13 | let a = 1.0, b = 2.0, c = 3.0
14 | let i = 111, j = 222, k = 333
15 |
16 | }
17 |
18 | func ==(lhs: TestStruct, rhs: TestStruct) -> Bool {
19 |
20 | return lhs.a == lhs.a && lhs.b == lhs.b && lhs.c == lhs.c && lhs.i == lhs.i && lhs.j == lhs.j && lhs.k == lhs.k
21 | }
22 |
23 | protocol P {
24 |
25 | func x()
26 | func y() -> Float
27 | func z( d: Int, f: Double, g: Float, h: Double, f1: Double, g1: Float, h1: Double, f2: Double, g2: Float, h2: Double, e: Int )
28 | func s( a: TestStruct ) -> TestStruct
29 |
30 | }
31 |
32 | var got = ""
33 |
34 | class SwiftTraceTests: XCTestCase {
35 |
36 | class TestClass: P {
37 |
38 | let i = 111
39 |
40 | func x() {
41 | got = "\(i)"
42 | }
43 |
44 | func y() -> Float {
45 | got = "\(i)"
46 | return -222.0
47 | }
48 |
49 | func z( d: Int, f: Double, g: Float, h: Double, f1: Double, g1: Float, h1: Double, f2: Double, g2: Float, h2: Double, e: Int ) {
50 | got = "\(i) \(d) \(e) \(f) \(g) \(h) \(f1) \(g1) \(h1) \(f2) \(g2) \(h2)"
51 | }
52 |
53 | func s( a: TestStruct ) -> TestStruct {
54 | return a
55 | }
56 |
57 | }
58 |
59 | let p: P = TestClass()
60 |
61 | override func setUp() {
62 | super.setUp()
63 | // Put setup code here. This method is called before the invocation of each test method in the class.
64 | SwiftTrace.trace( aClass: TestClass.self )
65 | }
66 |
67 | override func tearDown() {
68 | // Put teardown code here. This method is called after the invocation of each test method in the class.
69 | super.tearDown()
70 | }
71 |
72 | func testExample() {
73 | // This is an example of a functional test case.
74 | // Use XCTAssert and related functions to verify your tests produce the correct results.
75 |
76 | p.x()
77 | XCTAssertEqual( got, "111" )
78 |
79 | XCTAssertEqual( p.y(), -222.0 )
80 | XCTAssertEqual( got, "111" )
81 |
82 | p.z( d: 88, f: 66, g: 55, h: 44, f1: 66, g1: 55, h1: 44, f2: 66, g2: 55, h2: 44, e: 77 )
83 | XCTAssertEqual( got, "111 88 77 66.0 55.0 44.0 66.0 55.0 44.0 66.0 55.0 44.0" )
84 |
85 | XCTAssertEqual( p.s( a: TestStruct() ), TestStruct() )
86 | }
87 |
88 | func testPerformanceExample() {
89 | // This is an example of a performance test case.
90 | self.measure {
91 | // Put the code you want to measure the time of here.
92 | }
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/SwiftTraceTests/SwiftTraceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftTraceTests.swift
3 | // SwiftTraceTests
4 | //
5 | // Created by John Holdsworth on 13/06/2016.
6 | // Copyright © 2016 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import SwiftTrace
11 |
12 | struct TestStruct: Equatable {
13 |
14 | let a = 1.0, b = 2.0, c = 3.0
15 | let i = 111, j = 222, k = 333
16 |
17 | }
18 |
19 | func ==(lhs: TestStruct, rhs: TestStruct) -> Bool {
20 |
21 | return lhs.a == lhs.a && lhs.b == lhs.b && lhs.c == lhs.c && lhs.i == lhs.i && lhs.j == lhs.j && lhs.k == lhs.k
22 | }
23 |
24 | protocol P {
25 |
26 | func x()
27 | func y() -> Float
28 | func z( d: Int, f: Double, g: Float, h: Double, f1: Double, g1: Float, h1: Double, f2: Double, g2: Float, h2: Double, e: Int )
29 | func s( a: TestStruct ) -> TestStruct
30 |
31 | }
32 |
33 | var got = ""
34 | var args = ""
35 |
36 | class SwiftTwaceTests: XCTestCase {
37 |
38 | class TestClass: P {
39 |
40 | let i = 111
41 |
42 | func x() {
43 | got = "\(i)"
44 | }
45 |
46 | func y() -> Float {
47 | got = "\(i)"
48 | return -222.0
49 | }
50 |
51 | func z(d: Int, f: Double, g: Float, h: Double, f1: Double, g1: Float, h1: Double, f2: Double, g2: Float, h2: Double, e: Int) {
52 | got = "\(i) \(d) \(e) \(f) \(g) \(h) \(f1) \(g1) \(h1) \(f2) \(g2) \(h2)"
53 | }
54 |
55 | func s(a: TestStruct) -> TestStruct {
56 | return a
57 | }
58 |
59 | class TestSwizzle: SwiftTrace.Swizzle {
60 |
61 | override func onEntry(stack: inout SwiftTrace.EntryStack) {
62 | args = "\(stack.intArg1) \(getSelf(as: TestClass.self).i) \(stack.floatArg1)"
63 | }
64 | }
65 | }
66 |
67 | let p: P = TestClass()
68 |
69 | override func setUp() {
70 | super.setUp()
71 | // Put setup code here. This method is called before the invocation of each test method in the class.
72 | SwiftTrace.swizzleFactory = TestClass.TestSwizzle.self
73 | SwiftTrace.trace(aClass: TestClass.self)
74 | }
75 |
76 | override func tearDown() {
77 | // Put teardown code here. This method is called after the invocation of each test method in the class.
78 | super.tearDown()
79 | }
80 |
81 | func testExample() {
82 | // This is an example of a functional test case.
83 | // Use XCTAssert and related functions to verify your tests produce the correct results.
84 |
85 | p.x()
86 | XCTAssertEqual(got, "111")
87 |
88 | XCTAssertEqual(p.y(), -222.0)
89 | XCTAssertEqual(got, "111")
90 |
91 | p.z( d: 88, f: 66, g: 55, h: 44, f1: 66, g1: 55, h1: 44, f2: 66, g2: 55, h2: 44, e: 77 )
92 | XCTAssertEqual(got, "111 88 77 66.0 55.0 44.0 66.0 55.0 44.0 66.0 55.0 44.0" )
93 | XCTAssertEqual(args, "88 111 66.0")
94 |
95 | XCTAssertEqual(p.s( a: TestStruct() ), TestStruct())
96 | }
97 |
98 | func testPerformanceExample() {
99 | // This is an example of a performance test case.
100 | self.measure {
101 | // Put the code you want to measure the time of here.
102 | }
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/include/fishhook.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, Facebook, Inc.
2 | // All rights reserved.
3 | // Redistribution and use in source and binary forms, with or without
4 | // modification, are permitted provided that the following conditions are met:
5 | // * Redistributions of source code must retain the above copyright notice,
6 | // this list of conditions and the following disclaimer.
7 | // * Redistributions in binary form must reproduce the above copyright notice,
8 | // this list of conditions and the following disclaimer in the documentation
9 | // and/or other materials provided with the distribution.
10 | // * Neither the name Facebook nor the names of its contributors may be used to
11 | // endorse or promote products derived from this software without specific
12 | // prior written permission.
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
24 | #ifndef fishhook_h
25 | #define fishhook_h
26 |
27 | #include
28 | #include
29 |
30 | #if defined(FISHHOOK_HIDDEN)
31 | #define FISHHOOK_VISIBILITY __attribute__((visibility("hidden")))
32 | #else
33 | #define FISHHOOK_VISIBILITY __attribute__((visibility("default")))
34 | #endif
35 |
36 | #ifdef __cplusplus
37 | extern "C" {
38 | #endif //__cplusplus
39 |
40 | /*
41 | * A structure representing a particular intended rebinding from a symbol
42 | * name to its replacement
43 | */
44 | struct rebinding {
45 | const char * _Nonnull name;
46 | void * _Nonnull replacement;
47 | void * _Nonnull * _Nullable replaced;
48 | };
49 |
50 | /*
51 | * For each rebinding in rebindings, rebinds references to external, indirect
52 | * symbols with the specified name to instead point at replacement for each
53 | * image in the calling process as well as for all future images that are loaded
54 | * by the process. If rebind_functions is called more than once, the symbols to
55 | * rebind are added to the existing list of rebindings, and if a given symbol
56 | * is rebound more than once, the later rebinding will take precedence.
57 | */
58 | FISHHOOK_VISIBILITY
59 | int rebind_symbols(struct rebinding rebindings[_Nonnull], size_t rebindings_nel);
60 |
61 | /*
62 | * Rebinds as above, but only in the specified image. The header should point
63 | * to the mach-o header, the slide should be the slide offset. Others as above.
64 | */
65 | FISHHOOK_VISIBILITY
66 | int rebind_symbols_image(void * _Nonnull header,
67 | intptr_t slide,
68 | struct rebinding rebindings[_Nonnull],
69 | size_t rebindings_nel);
70 |
71 | // SwiftTrace additions here
72 | typedef void * _Nullable(* _Nullable STTracer)(void * _Nonnull existing,
73 | const char * _Nonnull symname);
74 | int rebind_symbols_trace(void * _Nonnull header,
75 | intptr_t slide,
76 | STTracer interposer);
77 | // SwiftTrace additions end
78 | #ifdef __cplusplus
79 | }
80 | #endif //__cplusplus
81 |
82 | #endif //fishhook_h
83 |
84 |
--------------------------------------------------------------------------------
/SwiftTraceApp/MasterViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MasterViewController.swift
3 | // z50
4 | //
5 | // Created by John Holdsworth on 14/09/2016.
6 | // Copyright © 2016 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MasterViewController: UITableViewController {
12 |
13 | var detailViewController: DetailViewController? = nil
14 | var objects = [Any]()
15 |
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 | // Do any additional setup after loading the view, typically from a nib.
20 | self.navigationItem.leftBarButtonItem = self.editButtonItem
21 |
22 | let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
23 | self.navigationItem.rightBarButtonItem = addButton
24 | if let split = self.splitViewController {
25 | let controllers = split.viewControllers
26 | self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
27 | }
28 | }
29 |
30 | override func viewWillAppear(_ animated: Bool) {
31 | self.clearsSelectionOnViewWillAppear = self.splitViewController!.isCollapsed
32 | super.viewWillAppear(animated)
33 | }
34 |
35 | override func didReceiveMemoryWarning() {
36 | super.didReceiveMemoryWarning()
37 | // Dispose of any resources that can be recreated.
38 | }
39 |
40 | @objc
41 | func insertNewObject(_ sender: Any) {
42 | objects.insert(NSDate(), at: 0)
43 | let indexPath = IndexPath(row: 0, section: 0)
44 | self.tableView.insertRows(at: [indexPath], with: .automatic)
45 | }
46 |
47 | // MARK: - Segues
48 |
49 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
50 | if segue.identifier == "showDetail" {
51 | if let indexPath = self.tableView.indexPathForSelectedRow {
52 | let object = objects[indexPath.row] as! NSDate
53 | let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
54 | controller.detailItem = object
55 | controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem
56 | controller.navigationItem.leftItemsSupplementBackButton = true
57 | }
58 | }
59 | }
60 |
61 | // MARK: - Table View
62 |
63 | override func numberOfSections(in tableView: UITableView) -> Int {
64 | return 1
65 | }
66 |
67 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
68 | return objects.count
69 | }
70 |
71 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
72 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
73 |
74 | let object = objects[indexPath.row] as! NSDate
75 | cell.textLabel!.text = object.description
76 | return cell
77 | }
78 |
79 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
80 | // Return false if you do not want the specified item to be editable.
81 | return true
82 | }
83 |
84 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
85 | if editingStyle == .delete {
86 | objects.remove(at: indexPath.row)
87 | tableView.deleteRows(at: [indexPath], with: .fade)
88 | } else if editingStyle == .insert {
89 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
90 | }
91 | }
92 |
93 |
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/xt_forwarding_trampoline_arm64.s:
--------------------------------------------------------------------------------
1 |
2 | // $Id: //depot/SwiftTrace/SwiftTraceGuts/xt_forwarding_trampoline_arm64.s#9 $
3 |
4 | // for ARM64 abi see http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
5 | // Layout shadowed in SwiftStack.swift
6 |
7 | #if DEBUG || !DEBUG_ONLY
8 | #if defined(__arm64__)
9 | .text
10 | .align 14
11 | onEntry:
12 | .quad 0 // pointer to function to trace call extry
13 | onExit:
14 | .quad 0 // pointer to function to trace call exit
15 | // SwiftTrace.Swizzle instance pointer at trampoline offset
16 |
17 | .align 14
18 | .globl _xt_forwarding_trampoline_page
19 | .globl _xt_forwarding_trampolines_start
20 | .globl _xt_forwarding_trampolines_next
21 | .globl _xt_forwarding_trampolines_end
22 |
23 | _xt_forwarding_trampoline_page:
24 | _xt_forwarding_trampoline:
25 | sub x16, lr, #0x8 // x16 = lr - 8, that is the address of the corresponding `mov x17, lr` instruction of the current trampoline
26 | sub x16, x16, #0x4000 // x16 = x16 - 16384, that is where the data for this trampoline is stored
27 | mov lr, x17 // restore the link register to that to be used when calling the original implementation
28 | stp fp, lr, [sp, #-16]! // set up frame pointers
29 | mov fp, sp
30 | stp x20, x21, [sp, #-16]! // save error return and context reg (self)
31 | stp x8, fp, [sp, #-16]! // x20 "context" (self), r8 for return of structs
32 | stp x6, x7, [sp, #-16]! // save all regs used in parameter passing
33 | stp x4, x5, [sp, #-16]!
34 | stp x2, x3, [sp, #-16]!
35 | stp x0, x1, [sp, #-16]!
36 | stp d6, d7, [sp, #-16]!
37 | stp d4, d5, [sp, #-16]!
38 | stp d2, d3, [sp, #-16]!
39 | stp d0, d1, [sp, #-16]!
40 | ldr x0, [x16] // first argument is pointer to Swizzle instance
41 | mov x1, lr // second argument is return address
42 | mov x2, sp // third argument is pointer to stack
43 | ldr x16, onEntry
44 | blr x16 // call tracing entry routine (saves return address)
45 | mov x16, x0 // original implementation to call is returned
46 | ldp d0, d1, [sp], #16
47 | ldp d2, d3, [sp], #16
48 | ldp d4, d5, [sp], #16
49 | ldp d6, d7, [sp], #16
50 | ldp x0, x1, [sp], #16
51 | ldp x2, x3, [sp], #16
52 | ldp x4, x5, [sp], #16
53 | ldp x6, x7, [sp], #16
54 | ldp x8, fp, [sp], #16
55 | ldp x20, x21, [sp], #16
56 | ldp fp, lr, [sp], #16
57 | bl getpc
58 | getpc:
59 | add lr, lr, #8
60 | br x16 // continue onto original implemntation
61 |
62 | returning:
63 | stp fp, lr, [sp, #-16]! // set up frame pointers
64 | mov fp, sp
65 | stp x20, x21, [sp, #-16]!
66 | stp x8, fp, [sp, #-16]!// save frame pointer and struct return
67 | stp x6, x7, [sp, #-16]! // save all regs used in parameter passing
68 | stp x4, x5, [sp, #-16]!
69 | stp x2, x3, [sp, #-16]!
70 | stp x0, x1, [sp, #-16]!
71 | stp d6, d7, [sp, #-16]!
72 | stp d4, d5, [sp, #-16]!
73 | stp d2, d3, [sp, #-16]!
74 | stp d0, d1, [sp, #-16]!
75 | ldr x16, onExit
76 | blr x16 // call tracing exit routine
77 | ldp d0, d1, [sp], #16
78 | ldp d2, d3, [sp], #16
79 | ldp d4, d5, [sp], #16
80 | ldp d6, d7, [sp], #16
81 | ldp x0, x1, [sp], #16
82 | ldp x2, x3, [sp], #16
83 | ldp x4, x5, [sp], #16
84 | ldp x6, x7, [sp], #16
85 | ldp x8, fp, [sp], #16
86 | ldp x20, x21, [sp], #16
87 | ldp fp, lr, [sp], #16
88 | ret // return to caller - reset by "onExit()"
89 | nop
90 |
91 | _xt_forwarding_trampolines_start:
92 | # Save lr, which contains the address to where we need to branch back after function returns, then jump to the actual trampoline implementation
93 | mov x17, lr
94 | bl _xt_forwarding_trampoline;
95 |
96 | _xt_forwarding_trampolines_next:
97 | .rept 2016
98 | # Next trampoline entry point
99 | mov x17, lr
100 | bl _xt_forwarding_trampoline;
101 | .endr
102 |
103 | _xt_forwarding_trampolines_end:
104 | nop
105 |
106 | #endif
107 | #endif
108 |
--------------------------------------------------------------------------------
/SwiftTrace/EasyPointer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyPointer.swift
3 | // EasyPointer
4 | //
5 | // Created by John Holdsworth on 29/10/2020.
6 | // Copyright © 2020 John Holdsworth. All rights reserved.
7 | //
8 | // Pragmatic extensions to unify conversions between various pointer types.
9 | //
10 | // You must watch this video for context on why this may be a very bad idea:
11 | //
12 | // https://developer.apple.com/videos/play/wwdc2020/10167/
13 | //
14 | // Repo: https://github.com/johnno1962/EasyPointer.git
15 | //
16 | // $Id: //depot/EasyPointer/Sources/EasyPointer/EasyPointer.swift#7 $
17 | //
18 |
19 | #if DEBUG || !DEBUG_ONLY
20 | public func autoBitCast(_ x: IN) -> OUT {
21 | if SwiftMeta.sizeof(anyType: IN.self) !=
22 | SwiftMeta.sizeof(anyType: OUT.self) {
23 | print("⚠️ size mismatch, autoBitCast(\(IN.self)[\(SwiftMeta.sizeof(anyType: IN.self))]) -> \(OUT.self)[\(SwiftMeta.sizeof(anyType: OUT.self))]")
24 | }
25 | return unsafeBitCast(x, to: OUT.self)
26 | }
27 |
28 | extension UnsafePointer {
29 | public init(cast: UnsafePointer) {
30 | self = cast.withMemoryRebound(to: Pointee.self, capacity: 1) { $0 }
31 | }
32 | public init(cast: UnsafeMutablePointer) {
33 | self = autoBitCast(cast)
34 | }
35 | public init(cast: UnsafeMutableRawPointer) {
36 | self = autoBitCast(cast)
37 | }
38 | public init(cast: UnsafeRawPointer) {
39 | self = cast.bindMemory(to: Pointee.self, capacity: 1)
40 | }
41 | public init(cast: OpaquePointer) {
42 | self = autoBitCast(cast)
43 | }
44 |
45 | // It's handy to be able to compare Mutable and non-Mutable pointers
46 | public static func == (lhs: UnsafePointer,
47 | rhs: UnsafeMutablePointer) -> Bool {
48 | return lhs == UnsafePointer(cast: rhs)
49 | }
50 | public static func == (lhs: UnsafeMutablePointer,
51 | rhs: UnsafePointer) -> Bool {
52 | return UnsafePointer(cast: lhs) == lhs
53 | }
54 | public static func != (lhs: UnsafePointer,
55 | rhs: UnsafeMutablePointer) -> Bool {
56 | return lhs != UnsafePointer(cast: rhs)
57 | }
58 | public static func != (lhs: UnsafeMutablePointer,
59 | rhs: UnsafePointer) -> Bool {
60 | return UnsafePointer(cast: lhs) != lhs
61 | }
62 | public static func < (lhs: UnsafePointer,
63 | rhs: UnsafeMutablePointer) -> Bool {
64 | return lhs == UnsafePointer(cast: rhs)
65 | }
66 | public static func < (lhs: UnsafeMutablePointer,
67 | rhs: UnsafePointer) -> Bool {
68 | return UnsafePointer(cast: lhs) < lhs
69 | }
70 | }
71 |
72 | extension UnsafeMutablePointer {
73 | public init(cast: UnsafeMutablePointer) {
74 | self = cast.withMemoryRebound(to: Pointee.self, capacity: 1) { $0 }
75 | }
76 | public init(mutating cast: UnsafeRawPointer) {
77 | self = autoBitCast(cast)
78 | }
79 | public init(cast: UnsafeMutableRawPointer) {
80 | self = cast.bindMemory(to: Pointee.self, capacity: 1)
81 | }
82 | public init(cast: OpaquePointer) {
83 | self = autoBitCast(cast)
84 | }
85 | }
86 |
87 | // Mutable-unmutable comparisons
88 | extension UnsafeRawPointer {
89 | public static func == (lhs: UnsafeRawPointer,
90 | rhs: UnsafeMutableRawPointer) -> Bool {
91 | return lhs == UnsafeRawPointer(rhs)
92 | }
93 | public static func == (lhs: UnsafeMutableRawPointer,
94 | rhs: UnsafeRawPointer) -> Bool {
95 | return UnsafeRawPointer(lhs) == rhs
96 | }
97 | public static func != (lhs: UnsafeRawPointer,
98 | rhs: UnsafeMutableRawPointer) -> Bool {
99 | return lhs != UnsafeRawPointer(rhs)
100 | }
101 | public static func != (lhs: UnsafeMutableRawPointer,
102 | rhs: UnsafeRawPointer) -> Bool {
103 | return UnsafeRawPointer(lhs) != rhs
104 | }
105 | public static func < (lhs: UnsafeRawPointer,
106 | rhs: UnsafeMutableRawPointer) -> Bool {
107 | return lhs < UnsafeRawPointer(rhs)
108 | }
109 | public static func < (lhs: UnsafeMutableRawPointer,
110 | rhs: UnsafeRawPointer) -> Bool {
111 | return UnsafeRawPointer(lhs) < rhs
112 | }
113 | }
114 | #endif
115 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/xt_forwarding_trampoline_x64.s:
--------------------------------------------------------------------------------
1 |
2 | // $Id: //depot/SwiftTrace/SwiftTraceGuts/xt_forwarding_trampoline_x64.s#12 $
3 |
4 | // https://en.wikipedia.org/wiki/X86_calling_conventions
5 | // Layout shadowed in SwiftStack.swift
6 |
7 | #if DEBUG || !DEBUG_ONLY
8 | #if defined(__LP64__) && !defined(__arm64__)
9 | .text
10 | .align 12
11 | onEntry:
12 | .quad 0 // pointer to function to trace call extry
13 | onExit:
14 | .quad 0 // pointer to function to trace call exit
15 | // SwiftTrace.Swizzle instance pointer at trampoline offset
16 |
17 | .align 12
18 | .globl _xt_forwarding_trampoline_page
19 | .globl _xt_forwarding_trampolines_start
20 | .globl _xt_forwarding_trampolines_next
21 | .globl _xt_forwarding_trampolines_end
22 |
23 | _xt_forwarding_trampoline_page:
24 | _xt_forwarding_trampoline:
25 | popq %r11 // recover trampoline return address
26 | pushq %rbp // save frame pointer
27 | movq %rsp, %rbp
28 | pushq %rbp // align stack to 16 bytes
29 | pushq %rbx
30 | pushq %rax // pointer for return of struct
31 | pushq %r10
32 | pushq %r9 // push the 6 registers for int parameters
33 | pushq %r8
34 | pushq %rcx
35 | pushq %rdx
36 | pushq %rsi
37 | pushq %rdi
38 | pushq %r15
39 | pushq %r14
40 | pushq %r13 // Swift "call context" register for self
41 | pushq %r12
42 | subq $64, %rsp // make space for floating point registers and save
43 | movsd %xmm0, (%rsp)
44 | movsd %xmm1, 8(%rsp)
45 | movsd %xmm2, 16(%rsp)
46 | movsd %xmm3, 24(%rsp)
47 | movsd %xmm4, 32(%rsp)
48 | movsd %xmm5, 40(%rsp)
49 | movsd %xmm6, 48(%rsp)
50 | movsd %xmm7, 56(%rsp)
51 | subq $4096+5, %r11 // find trampoline info relative to return address
52 | movq (%r11), %rdi // first argument is pointer to Swizzle instance
53 | movq 184(%rsp), %rsi // second argument is original return address
54 | movq %rsp, %rdx // third argument is stack pointer
55 | leaq onEntry(%rip), %r11
56 | callq *(%r11) // call tracing entry routine (saves return address)
57 | leaq returning(%rip), %r11
58 | movq %r11, 184(%rsp) // patch return address to "returning" code
59 | movq %rax, %r11 // pointer to original implementation returned
60 | movsd (%rsp), %xmm0 // restore all registers
61 | movsd 8(%rsp), %xmm1
62 | movsd 16(%rsp), %xmm2
63 | movsd 24(%rsp), %xmm3
64 | movsd 32(%rsp), %xmm4
65 | movsd 40(%rsp), %xmm5
66 | movsd 48(%rsp), %xmm6
67 | movsd 56(%rsp), %xmm7
68 | addq $64, %rsp
69 | popq %r12
70 | popq %r13
71 | popq %r14
72 | popq %r15
73 | popq %rdi
74 | popq %rsi
75 | popq %rdx
76 | popq %rcx
77 | popq %r8
78 | popq %r9
79 | popq %r10
80 | popq %rax
81 | popq %rbx
82 | popq %rbp
83 | popq %rbp // restore frame pointer
84 | jmpq *%r11 // forward onto original implementation
85 |
86 | returning:
87 | pushq %rbp // make space for real return address
88 | pushq %rbp // bump frame
89 | movq %rsp, %rbp
90 | pushq %rbp // align stack to 16 bytes
91 | pushq %rbx
92 | pushq %r10
93 | pushq %r9
94 | pushq %r8 // push the 4 regs used for int returns
95 | pushq %rcx
96 | pushq %rdx
97 | pushq %rax
98 | pushq %rsi
99 | pushq %rdi
100 | pushq %r15
101 | pushq %r14
102 | pushq %r13 // Swift "call context" register for self
103 | pushq %r12
104 | subq $64, %rsp // make space for floating point regeisters and save
105 | movsd %xmm0, (%rsp)
106 | movsd %xmm1, 8(%rsp)
107 | movsd %xmm2, 16(%rsp)
108 | movsd %xmm3, 24(%rsp)
109 | movsd %xmm4, 32(%rsp)
110 | movsd %xmm5, 40(%rsp)
111 | movsd %xmm6, 48(%rsp)
112 | movsd %xmm7, 56(%rsp)
113 | leaq onExit(%rip), %r11
114 | callq *(%r11) // call tracing exit routine
115 | movsd (%rsp), %xmm0 // restore all registers
116 | movsd 8(%rsp), %xmm1
117 | movsd 16(%rsp), %xmm2
118 | movsd 24(%rsp), %xmm3
119 | movsd 32(%rsp), %xmm4
120 | movsd 40(%rsp), %xmm5
121 | movsd 48(%rsp), %xmm6
122 | movsd 56(%rsp), %xmm7
123 | addq $64, %rsp
124 | popq %r12
125 | popq %r13
126 | popq %r14
127 | popq %r15
128 | popq %rdi
129 | popq %rsi
130 | popq %rax
131 | popq %rdx
132 | popq %rcx
133 | popq %r8
134 | popq %r9
135 | popq %r10
136 | popq %rbx
137 | popq %rbp
138 | popq %rbp
139 | ret // return to original caller - reset by "onExit()"
140 | nop
141 | nop
142 | nop
143 | nop
144 |
145 | _xt_forwarding_trampolines_start:
146 | callq _xt_forwarding_trampoline
147 | nop
148 | nop
149 | nop
150 |
151 | // another 465 trampoline entry points
152 | _xt_forwarding_trampolines_next:
153 | .rept 465
154 | callq _xt_forwarding_trampoline
155 | nop
156 | nop
157 | nop
158 | .endr
159 |
160 | _xt_forwarding_trampolines_end:
161 | nop
162 |
163 | #endif
164 | #endif
165 |
--------------------------------------------------------------------------------
/SwiftTrace/SwiftAspects.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftAspects.swift
3 | // SwiftTrace
4 | //
5 | // Created by John Holdsworth on 20/04/2020.
6 | // Copyright © 2020 John Holdsworth. All rights reserved.
7 | //
8 | // Repo: https://github.com/johnno1962/SwiftTrace
9 | // $Id: //depot/SwiftTrace/SwiftTrace/SwiftAspects.swift#17 $
10 | //
11 | // Add aspects to Swift methods
12 | // ============================
13 | //
14 |
15 | #if DEBUG || !DEBUG_ONLY
16 | import Foundation
17 |
18 | extension SwiftTrace {
19 |
20 | public typealias EntryAspect = (_ swizzle: Swizzle, _ stack: inout EntryStack) -> Void
21 | public typealias ExitAspect = (_ swizzle: Swizzle, _ stack: inout ExitStack) -> Void
22 |
23 | /**
24 | Add a closure aspect to be called before or after a "Swizzle" is called
25 | - parameter methodName: - unmangled name of Method for aspect
26 | - parameter onEntry: - closure to be called before "Swizzle" is called
27 | - parameter onExit: - closure to be called after "Swizzle" returns
28 | */
29 | public class func addAspect(methodName: String,
30 | patchClass: Aspect.Type = Aspect.self,
31 | onEntry: EntryAspect? = nil,
32 | onExit: ExitAspect? = nil,
33 | replaceWith: nullImplementationType? = nil) -> Bool {
34 | return forAllClasses {
35 | (aClass, stop) in
36 | stop = addAspect(aClass: aClass, methodName: methodName,
37 | onEntry: onEntry, onExit: onExit, replaceWith: replaceWith)
38 | }
39 | }
40 |
41 | /**
42 | Add a closure aspect to be called before or after a "Swizzle" is called
43 | - parameter toClass: - specifying the class to add aspect is more efficient
44 | - parameter methodName: - unmangled name of Method for aspect
45 | - parameter onEntry: - closure to be called before "Swizzle" is called
46 | - parameter onExit: - closure to be called after "Swizzle" returns
47 | */
48 | public class func addAspect(aClass: AnyClass, methodName: String,
49 | patchClass: Aspect.Type = Aspect.self,
50 | onEntry: EntryAspect? = nil,
51 | onExit: ExitAspect? = nil,
52 | replaceWith: nullImplementationType? = nil) -> Bool {
53 | return iterateMethods(ofClass: aClass) {
54 | (name, slotIndex, vtableSlot, stop) in
55 | if name == methodName, let method = patchClass.init(name: name,
56 | vtableSlot: vtableSlot, onEntry: onEntry,
57 | onExit: onExit, replaceWith: replaceWith) {
58 | vtableSlot.pointee = method.forwardingImplementation
59 | stop = true
60 | }
61 | }
62 | }
63 |
64 | /**
65 | Add a closure aspect to be called before or after a "Swizzle" is called
66 | - parameter methodName: - unmangled name of Method for aspect
67 | */
68 | @discardableResult
69 | public class func removeAspect(methodName: String) -> Bool {
70 | return forAllClasses {
71 | (aClass, stop) in
72 | stop = removeAspect(aClass: aClass, methodName: methodName)
73 | }
74 | }
75 |
76 | /**
77 | Add a closure aspect to be called before or after a "Swizzle" is called
78 | - parameter aClass: - specifying the class to remove aspect is more efficient
79 | - parameter methodName: - unmangled name of Method for aspect
80 | */
81 | @discardableResult
82 | public class func removeAspect(aClass: AnyClass, methodName: String) -> Bool {
83 | return iterateMethods(ofClass: aClass) {
84 | (name, slotIndex, vtableSlot, stop) in
85 | if name == methodName,
86 | let swizzle = SwiftTrace.lastSwiftTrace.activeSwizzles[unsafeBitCast(vtableSlot.pointee, to: IMP.self)] {
87 | swizzle.remove()
88 | stop = true
89 | }
90 | }
91 | }
92 |
93 | /**
94 | Internal class used in the implementation of aspects
95 | */
96 | open class Aspect: Decorated {
97 |
98 | let entryAspect: EntryAspect?
99 | let exitAspect: ExitAspect?
100 |
101 | public required init?(name: String, vtableSlot: UnsafeMutablePointer? = nil, original: OpaquePointer? = nil,
102 | onEntry: EntryAspect? = nil, onExit: ExitAspect? = nil,
103 | replaceWith: nullImplementationType? = nil) {
104 | self.entryAspect = onEntry
105 | self.exitAspect = onExit
106 | super.init(name: name, vtableSlot: vtableSlot,
107 | original: original, replaceWith: replaceWith)
108 | }
109 |
110 | public required init?(name: String, vtableSlot: UnsafeMutablePointer? = nil, objcMethod: Method? = nil, objcClass: AnyClass?, original: OpaquePointer? = nil, replaceWith: nullImplementationType? = nil) {
111 | fatalError("Aspect.init(name:vtableSlot:objcMethod:objcClass:replaceWith:) should not be used")
112 | }
113 |
114 | open override func onEntry(stack: inout EntryStack, invocation: Invocation) {
115 | if entryAspect != nil || exitAspect != nil {
116 | entryAspect?(self, &stack)
117 | } else {
118 | super.onEntry(stack: &stack, invocation: invocation)
119 | }
120 | }
121 |
122 | open override func onExit(stack: inout ExitStack, invocation: Invocation) {
123 | if entryAspect != nil || exitAspect != nil {
124 | exitAspect?(self, &stack)
125 | } else {
126 | super.onExit(stack: &stack, invocation: invocation)
127 | }
128 | }
129 | }
130 | }
131 | #endif
132 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/ObjCBridge.mm:
--------------------------------------------------------------------------------
1 | //
2 | // ObjCBridge.mm
3 | // SwiftTrace
4 | //
5 | // Created by John Holdsworth on 21/01/2022.
6 | // Repo: https://github.com/johnno1962/SwiftTrace
7 | // $Id: //depot/SwiftTrace/SwiftTraceGuts/ObjCBridge.mm#3 $
8 | //
9 |
10 | #if DEBUG || !DEBUG_ONLY
11 | #import "include/SwiftTrace.h"
12 |
13 | #ifndef SWIFTUISUPPORT
14 | // Bridge via NSObject for when SwiftTrace is dynamically loaded
15 | #import "SwiftTrace-Swift.h"
16 |
17 | @implementation NSObject(SwiftTrace)
18 | + (NSString *)swiftTraceDefaultMethodExclusions {
19 | return [SwiftTrace defaultMethodExclusions];
20 | }
21 | + (NSString *)swiftTraceMethodExclusionPattern {
22 | return [SwiftTrace methodExclusionPattern];
23 | }
24 | + (void)setSwiftTraceMethodExclusionPattern:(NSString *)pattern {
25 | [SwiftTrace setMethodExclusionPattern:pattern];
26 | }
27 | + (NSString *)swiftTraceMethodInclusionPattern {
28 | return [SwiftTrace methodInclusionPattern];
29 | }
30 | + (void)setSwiftTraceMethodInclusionPattern:(NSString *)pattern {
31 | [SwiftTrace setMethodInclusionPattern:pattern];
32 | }
33 | + (NSArray * _Nonnull)swiftTraceFunctionSuffixes {
34 | return [SwiftTrace swiftFunctionSuffixes];
35 | }
36 | + (void)setSwiftTraceFunctionSuffixes:(NSArray * _Nonnull)value {
37 | [SwiftTrace setSwiftFunctionSuffixes:value];
38 | }
39 | + (BOOL)swiftTracing {
40 | return [SwiftTrace isTracing];
41 | }
42 | + (void *)swiftTraceInterposed {
43 | return [SwiftTrace interposedPointer];
44 | }
45 | + (BOOL)swiftTraceTypeLookup {
46 | return [SwiftTrace typeLookup];
47 | }
48 | + (void)setSwiftTraceTypeLookup:(BOOL)enabled {
49 | [SwiftTrace setTypeLookup:enabled];
50 | [SwiftTrace setDecorateAny:enabled];
51 | }
52 | + (void)swiftTrace {
53 | [SwiftTrace traceWithAClass:self];
54 | }
55 | + (void)swiftTraceBundle {
56 | [self swiftTraceBundleWithSubLevels:0];
57 | }
58 | + (void)swiftTraceBundleWithSubLevels:(int)subLevels {
59 | [SwiftTrace traceBundleWithContaining:self subLevels:subLevels];
60 | }
61 | + (void)swiftTraceMainBundle {
62 | [self swiftTraceMainBundleWithSubLevels:0];
63 | }
64 | + (void)swiftTraceMainBundleWithSubLevels:(int)subLevels {
65 | [SwiftTrace traceMainBundleWithSubLevels:subLevels];
66 | }
67 | + (void)swiftTraceClassesMatchingPattern:(NSString *)pattern {
68 | [self swiftTraceClassesMatchingPattern:pattern subLevels:0];
69 | }
70 | + (void)swiftTraceClassesMatchingPattern:(NSString *)pattern subLevels:(intptr_t)subLevels {
71 | [SwiftTrace traceClassesMatchingPattern:pattern subLevels:subLevels];
72 | }
73 | + (NSArray *)swiftTraceMethodNames {
74 | return [SwiftTrace methodNamesOfClass:self];
75 | }
76 | + (NSArray *)switTraceMethodsNamesOfClass:(Class)aClass {
77 | return [SwiftTrace methodNamesOfClass:aClass];
78 | }
79 | + (BOOL)swiftTraceUndoLastTrace {
80 | return [SwiftTrace undoLastTrace];
81 | }
82 | + (void)swiftTraceRemoveAllTraces {
83 | [SwiftTrace removeAllTraces];
84 | }
85 | + (void)swiftTraceRevertAllInterposes {
86 | [SwiftTrace revertInterposes];
87 | }
88 | + (void)swiftTraceInstances {
89 | [self swiftTraceInstancesWithSubLevels:0];
90 | }
91 | + (void)swiftTraceInstancesWithSubLevels:(int)subLevels {
92 | [SwiftTrace traceInstancesOfClass:self subLevels:subLevels];
93 | }
94 | - (void)swiftTraceInstance {
95 | [self swiftTraceInstanceWithSubLevels:0];
96 | }
97 | - (void)swiftTraceInstanceWithSubLevels:(int)subLevels {
98 | [SwiftTrace traceWithAnInstance:self subLevels:subLevels];
99 | }
100 | + (void)swiftTraceProtocolsInBundle {
101 | [self swiftTraceProtocolsInBundleWithMatchingPattern:nil subLevels:0];
102 | }
103 | + (void)swiftTraceProtocolsInBundleWithMatchingPattern:(NSString * _Nullable)pattern {
104 | [self swiftTraceProtocolsInBundleWithMatchingPattern:pattern subLevels:0];
105 | }
106 | + (void)swiftTraceProtocolsInBundleWithSubLevels:(int)subLevels {
107 | [self swiftTraceProtocolsInBundleWithMatchingPattern:nil subLevels:subLevels];
108 | }
109 | + (void)swiftTraceProtocolsInBundleWithMatchingPattern:(NSString *)pattern subLevels:(int)subLevels {
110 | [SwiftTrace traceProtocolsInBundleWithContaining:self matchingPattern:pattern subLevels:subLevels];
111 | }
112 | + (NSInteger)swiftTraceMethodsInFrameworkContaining:(Class _Nonnull)aClass {
113 | return [SwiftTrace traceMethodsInFrameworkContaining:aClass];
114 | }
115 | + (NSInteger)swiftTraceMainBundleMethods {
116 | return [SwiftTrace traceMainBundleMethods];
117 | }
118 | + (NSInteger)swiftTraceFrameworkMethods {
119 | return [SwiftTrace traceFrameworkMethods];
120 | }
121 | + (NSInteger)swiftTraceMethodsInBundle:(const char * _Nonnull)bundlePath
122 | packageName:(NSString * _Nullable)packageName {
123 | return [SwiftTrace interposeMethodsInBundlePath:(const int8_t *)bundlePath
124 | packageName:packageName subLevels:0];
125 | }
126 | + (void)swiftTraceBundlePath:(const char * _Nonnull)bundlePath {
127 | [SwiftTrace traceWithBundlePath:(const int8_t *)bundlePath subLevels:0];
128 | }
129 | + (NSString * _Nullable)swiftTraceFilterInclude {
130 | return [SwiftTrace traceFilterInclude];
131 | }
132 | + (void)setSwiftTraceFilterInclude:(NSString * _Nullable)include {
133 | [SwiftTrace setTraceFilterInclude:include];
134 | }
135 | + (NSString * _Nullable)swiftTraceFilterExclude {
136 | return [SwiftTrace traceFilterExclude];
137 | }
138 | + (void)setSwiftTraceFilterExclude:(NSString * _Nullable)exclude {
139 | [SwiftTrace setTraceFilterExclude:exclude];
140 | }
141 | + (STSymbolFilter _Nonnull)swiftTraceSymbolFilter {
142 | return [SwiftTrace injectableSymbol];
143 | }
144 | + (void)setSwiftTraceSymbolFilter:(STSymbolFilter _Nonnull)filter {
145 | [SwiftTrace setInjectableSymbol:filter];
146 | }
147 | + (NSDictionary * _Nonnull)swiftTraceElapsedTimes {
148 | return [SwiftTrace elapsedTimes];
149 | }
150 | + (NSDictionary * _Nonnull)swiftTraceInvocationCounts {
151 | return [SwiftTrace invocationCounts];
152 | }
153 | + (NSString * _Nullable)swiftTraceDemangle:(char const * _Nonnull)symbol {
154 | return [SwiftMeta demangleWithSymbol:(const int8_t *)symbol];
155 | }
156 | @end
157 | #endif
158 |
159 | #ifdef OBJC_TRACE_TESTER
160 | @implementation ObjcTraceTester: NSObject
161 |
162 | - (OSRect)a:(float)a i:(int)i b:(double)b c:(NSString *)c o:o s:(SEL)s {
163 | return OSMakeRect(1, 2, 3, 4);
164 | }
165 | @end
166 | #endif
167 | #endif
168 |
--------------------------------------------------------------------------------
/SwiftTrace/SwiftInvoke.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftInvoke.swift
3 | // SwiftTrace
4 | //
5 | // Created by John Holdsworth on 20/04/2020.
6 | // Copyright © 2020 John Holdsworth. All rights reserved.
7 | //
8 | // Repo: https://github.com/johnno1962/SwiftTrace
9 | // $Id: //depot/SwiftTrace/SwiftTrace/SwiftInvoke.swift#31 $
10 | //
11 | // Invocation interface for Swift
12 | // ==============================
13 | //
14 |
15 | #if DEBUG || !DEBUG_ONLY
16 | extension SwiftTraceArg {
17 | public func add(toCall call: SwiftTrace.Call) {
18 | call.add(arg: self)
19 | }
20 | }
21 |
22 | extension Array: SwiftTraceArg {
23 | }
24 | extension Optional: SwiftTraceArg {
25 | }
26 | extension Dictionary: SwiftTraceArg {
27 | }
28 |
29 | extension SwiftTrace {
30 |
31 | /**
32 | Implementation of invocation api
33 | */
34 | public class Call: Swizzle {
35 |
36 | public var input = EntryStack()
37 | public var output = ExitStack()
38 | var backup = EntryStack()
39 | var target: AnyObject
40 | var caller: SIMP? = nil
41 |
42 | public init?(target: AnyObject, methodName: String) {
43 | self.target = target
44 | var sigh: SIMP = { }
45 | super.init(name: methodName, vtableSlot: &sigh)
46 |
47 | guard iterateMethods(ofClass: type(of: target), callback: {
48 | (name, slotIndex, vtableSlot, stop) in
49 | if name == methodName {
50 | self.vtableSlot = vtableSlot
51 | implementation = rebind(vtableSlot).pointee
52 | stop = true
53 | }
54 | }) else {
55 | return nil
56 | }
57 |
58 | input.swiftSelf = autoBitCast(target)
59 |
60 | caller = autoBitCast(imp_implementationForwardingToTracer(autoBitCast(self),
61 | autoBitCast(Swizzle.onEntry), autoBitCast(Swizzle.onExit)))
62 | }
63 |
64 | public required init?(name signature: String, vtableSlot: UnsafeMutablePointer? = nil, objcMethod: Method? = nil, objcClass: AnyClass? = nil, original: OpaquePointer? = nil, replaceWith: nullImplementationType? = nil) {
65 | fatalError("SwiftTrace.Call.init(name:vtableSlot:objcMethod:objcClass:replaceWith:) must not be used")
66 | }
67 |
68 | public func reset(target: AnyObject) {
69 | self.target = target
70 | }
71 |
72 | public var intArgNumber = 0
73 | public var floatArgNumber = 0
74 |
75 | public func resetArgs() {
76 | intArgNumber = 0
77 | floatArgNumber = 0
78 | }
79 |
80 | public func add(arg: T) {
81 | if arg is SwiftTraceFloatArg {
82 | let registers = (MemoryLayout.size +
83 | MemoryLayout.size - 1) / MemoryLayout.size
84 | if floatArgNumber + registers > EntryStack.maxFloatSlots {
85 | fatalError("Too many float args for SwiftTrace.Call")
86 | }
87 | withUnsafeMutablePointer(to: &input.floatArg1) {
88 | rebind($0.advanced(by: floatArgNumber), to: T.self)
89 | .pointee = arg
90 | }
91 | floatArgNumber += registers
92 | return
93 | }
94 | else {
95 | let registers = (MemoryLayout.size +
96 | MemoryLayout.size - 1) / MemoryLayout.size
97 | if intArgNumber + registers > EntryStack.maxIntSlots {
98 | fatalError("Too many int args for SwiftTrace.Call")
99 | }
100 | withUnsafeMutablePointer(to: &input.intArg1) {
101 | rebind($0.advanced(by: intArgNumber), to: T.self)
102 | .pointee = arg
103 | }
104 | intArgNumber += registers
105 | }
106 | }
107 |
108 | public func invoke() {
109 | caller!()
110 | resetArgs()
111 | }
112 |
113 | public override func onEntry(stack: inout EntryStack, invocation: Invocation) {
114 | input.frame = stack.frame
115 | backup = stack
116 | stack = input
117 | }
118 |
119 | public override func onExit(stack: inout ExitStack, invocation: Invocation) {
120 | output = stack
121 | rebind(&stack.floatReturn1).pointee = backup
122 | }
123 |
124 | public func getReturn() -> T {
125 | return output.genericReturn(swizzle: self).pointee
126 | }
127 |
128 | public func invokeStret(args: Any...) -> T {
129 | for arg in args {
130 | if let arg = arg as? SwiftTraceArg {
131 | arg.add(toCall: self)
132 | }
133 | else if type(of: arg) is AnyObject.Type {
134 | self.add(arg: unsafeBitCast(arg, to: Int.self))
135 | }
136 | else {
137 | fatalError("Unsupported argument type \(type(of: arg))")
138 | }
139 | }
140 | let ptr = UnsafeMutablePointer.allocate(capacity: 1)
141 | input.structReturn = autoBitCast(ptr)
142 | caller!()
143 | resetArgs()
144 | let out = ptr.pointee
145 | ptr.deinitialize(count: 1)
146 | ptr.deallocate()
147 | return out
148 | }
149 | }
150 |
151 | /**
152 | Basic Swift method invocation api
153 | - parameter self: instance to message
154 | - parameter methodName: de-mangled method name to invoke
155 | - parameter args: list of values to use as arguments
156 | */
157 | public class func invoke(target: AnyObject, methodName: String, args: Any...) -> T {
158 | guard let call = Call(target: target, methodName: methodName) else {
159 | fatalError("Unknown method \(methodName) on class \(target)")
160 | }
161 |
162 | for arg in args {
163 | if let arg = arg as? SwiftTraceArg {
164 | arg.add(toCall: call)
165 | }
166 | else if type(of: arg) is AnyObject.Type {
167 | call.add(arg: unsafeBitCast(arg, to: Int.self))
168 | }
169 | else {
170 | fatalError("Unsupported argument type \(type(of: arg))")
171 | }
172 | }
173 |
174 | call.invoke()
175 |
176 | return call.getReturn()
177 | }
178 | }
179 | #endif
180 |
--------------------------------------------------------------------------------
/SwiftTrace/SwiftStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftStack.swift
3 | // SwiftTrace
4 | //
5 | // Created by John Holdsworth on 20/04/2020.
6 | // Copyright © 2020 John Holdsworth. All rights reserved.
7 | //
8 | // $Id: //depot/SwiftTrace/SwiftTrace/SwiftStack.swift#24 $
9 | //
10 | // Stack layout used by assembly trampolines
11 | // =========================================
12 | //
13 | // See: https://github.com/apple/swift/blob/main/docs/ABI/RegisterUsage.md
14 | // https://github.com/apple/swift/blob/main/docs/ABI/CallingConvention.rst
15 | //
16 |
17 | #if DEBUG || !DEBUG_ONLY
18 | import Foundation
19 |
20 | extension SwiftTrace {
21 |
22 | public struct StackFrame {
23 | public var fp: UnsafePointer? = nil
24 | public var lr: UnsafeRawPointer? = nil
25 | }
26 |
27 | #if arch(arm64)
28 | /**
29 | Stack layout on entry from xt_forwarding_trampoline_arm64.s
30 | */
31 | public struct EntryStack {
32 | static let maxFloatSlots = 8
33 | static let maxIntSlots = 8 // Was 6 for Intel
34 |
35 | public var floatArg1: Double = 0.0
36 | public var floatArg2: Double = 0.0
37 | public var floatArg3: Double = 0.0
38 | public var floatArg4: Double = 0.0
39 | public var floatArg5: Double = 0.0
40 | public var floatArg6: Double = 0.0
41 | public var floatArg7: Double = 0.0
42 | public var floatArg8: Double = 0.0
43 | public var intArg1: intptr_t = 0
44 | public var intArg2: intptr_t = 0
45 | public var intArg3: intptr_t = 0
46 | public var intArg4: intptr_t = 0
47 | public var intArg5: intptr_t = 0
48 | public var intArg6: intptr_t = 0
49 | public var intArg7: intptr_t = 0
50 | public var intArg8: intptr_t = 0
51 | public var structReturn: intptr_t = 0 // x8
52 | public var framePointer: intptr_t = 0
53 | public var swiftSelf: intptr_t = 0 // x20
54 | public var thrownError: intptr_t = 0 // x21
55 | public var frame = StackFrame()
56 | }
57 |
58 | /**
59 | Stack layout on exit from xt_forwarding_trampoline_arm64.s
60 | */
61 | public struct ExitStack {
62 | static let returnRegs = 4
63 |
64 | public var floatReturn1: Double = 0.0
65 | public var floatReturn2: Double = 0.0
66 | public var floatReturn3: Double = 0.0
67 | public var floatReturn4: Double = 0.0
68 | public var d4: Double = 0.0
69 | public var d5: Double = 0.0
70 | public var d6: Double = 0.0
71 | public var d7: Double = 0.0
72 | public var intReturn1: intptr_t = 0
73 | public var intReturn2: intptr_t = 0
74 | public var intReturn3: intptr_t = 0
75 | public var intReturn4: intptr_t = 0
76 | public var x4: intptr_t = 0
77 | public var x5: intptr_t = 0
78 | public var x6: intptr_t = 0
79 | public var x7: intptr_t = 0
80 | public var structReturn: intptr_t = 0 // x8
81 | public var framePointer: intptr_t = 0
82 | public var swiftSelf: intptr_t = 0 // x20
83 | public var thrownError: intptr_t = 0 // x21
84 | public var frame = StackFrame()
85 |
86 | mutating func resyncStructReturn() {
87 | structReturn = autoBitCast(invocation.structReturn)
88 | }
89 | }
90 |
91 | #else // x86_64
92 | /**
93 | Stack layout on entry from xt_forwarding_trampoline_x64.s
94 | */
95 | public struct EntryStack {
96 | static let maxFloatSlots = 8
97 | static let maxIntSlots = 6
98 |
99 | public var floatArg1: Double = 0.0
100 | public var floatArg2: Double = 0.0
101 | public var floatArg3: Double = 0.0
102 | public var floatArg4: Double = 0.0
103 | public var floatArg5: Double = 0.0
104 | public var floatArg6: Double = 0.0
105 | public var floatArg7: Double = 0.0
106 | public var floatArg8: Double = 0.0
107 | public var r12: intptr_t = 0
108 | public var swiftSelf: intptr_t = 0 // r13
109 | public var r14: intptr_t = 0
110 | public var r15: intptr_t = 0
111 | public var intArg1: intptr_t = 0 // rdi
112 | public var intArg2: intptr_t = 0 // rsi
113 | public var intArg3: intptr_t = 0 // rcx
114 | public var intArg4: intptr_t = 0 // rdx
115 | public var intArg5: intptr_t = 0 // r8
116 | public var intArg6: intptr_t = 0 // r9
117 | public var r10: intptr_t = 0
118 | public var structReturn: intptr_t = 0 // rax
119 | public var rbx: intptr_t = 0
120 | public var rbp: intptr_t = 0 // for alignment
121 | public var frame = StackFrame()
122 | }
123 |
124 | /**
125 | Stack layout on exit from xt_forwarding_trampoline_x64.s
126 | */
127 | public struct ExitStack {
128 | static let returnRegs = 4
129 |
130 | public var floatReturn1: Double = 0.0 // xmm0
131 | public var floatReturn2: Double = 0.0 // xmm1
132 | public var floatReturn3: Double = 0.0 // xmm2
133 | public var floatReturn4: Double = 0.0 // xmm3
134 | public var xmm4: Double = 0.0
135 | public var xmm5: Double = 0.0
136 | public var xmm6: Double = 0.0
137 | public var xmm7: Double = 0.0
138 | public var thrownError: intptr_t = 0 // r12
139 | public var swiftSelf: intptr_t = 0 // r13
140 | public var r14: intptr_t = 0
141 | public var r15: intptr_t = 0
142 | public var rdi: intptr_t = 0
143 | public var rsi: intptr_t = 0
144 | public var intReturn1: intptr_t = 0 // rax (also struct Return)
145 | public var intReturn2: intptr_t = 0 // rdx
146 | public var intReturn3: intptr_t = 0 // rcx
147 | public var intReturn4: intptr_t = 0 // r8
148 | public var r9: intptr_t = 0
149 | public var r10: intptr_t = 0
150 | public var rbx: intptr_t = 0
151 | public var rbp: intptr_t = 0
152 | public var frame = StackFrame()
153 | public var structReturn: intptr_t {
154 | return intReturn1
155 | }
156 | mutating func resyncStructReturn() {
157 | intReturn1 = autoBitCast(invocation.structReturn)
158 | }
159 | }
160 | #endif
161 | }
162 |
163 | extension SwiftTrace.EntryStack {
164 | public var invocation: SwiftTrace.Swizzle.Invocation! {
165 | return SwiftTrace.Swizzle.Invocation.current
166 | }
167 | }
168 |
169 | extension SwiftTrace.ExitStack {
170 | public var invocation: SwiftTrace.Swizzle.Invocation! {
171 | return SwiftTrace.Swizzle.Invocation.current
172 | }
173 | public mutating func genericReturn(swizzle: SwiftTrace.Swizzle? = nil,
174 | to: Any.Type = T.self) -> UnsafeMutablePointer {
175 | if MemoryLayout.size > MemoryLayout.size * SwiftTrace.ExitStack.returnRegs {
176 | resyncStructReturn()
177 | return UnsafeMutablePointer(cast: invocation.structReturn!)
178 | }
179 | else {
180 | let swizzle = swizzle ?? invocation!.swizzle
181 | if T.self is SwiftTraceFloatArg.Type {
182 | return swizzle.rebind(&floatReturn1)
183 | }
184 | else {
185 | return swizzle.rebind(&intReturn1)
186 | }
187 | }
188 | }
189 | mutating public func getReturn() -> T {
190 | return genericReturn().pointee
191 | }
192 | mutating public func setReturn(value: T) {
193 | intReturn1 = 0
194 | intReturn2 = 0
195 | intReturn3 = 0
196 | intReturn4 = 0
197 | return genericReturn().pointee = value
198 | }
199 | mutating public func stringReturn() -> String {
200 | return getReturn()
201 | }
202 | }
203 | #endif
204 |
--------------------------------------------------------------------------------
/SwiftTrace/SwiftLifetime.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftLifetime.swift
3 | // SwiftTrace
4 | //
5 | // Created by John Holdsworth on 23/09/2020.
6 | // Copyright © 2020 John Holdsworth. All rights reserved.
7 | //
8 | // $Id: //depot/SwiftTrace/SwiftTrace/SwiftLifetime.swift#26 $
9 | //
10 | // Trace instance life cycle for tracking down reference cycles.
11 | // =============================================================
12 | //
13 | // Needs "Other Linker Flags" -Xlinker -interposable and to have
14 | // called both traceMainBundleMethods() and traceMainBundle().
15 | //
16 | // "allocating_init" initialiazers of Swift classes are intercepted
17 | // and init* methods of Objective-C classe are swizzled to record new
18 | // objects in a "live" Set on a per-class basis. Swift classes with
19 | // non-trivial initialisers that inherit from NSObject have an Objective-C
20 | // method ".cxx_destruct" which is swizzled to remove the instances of the
21 | // live Set as they are dealloctaed. For classes that don't inherit from
22 | // NSObject an associated reference is added to a "Reaper" instance that
23 | // removes the instance from the live Set using a method _cxx_destruct
24 | // added to the tracked class.
25 | //
26 |
27 | #if DEBUG || !DEBUG_ONLY
28 | import Foundation
29 |
30 | extension SwiftTrace {
31 |
32 | public static var liveObjects = [UnsafeRawPointer: Set]()
33 | public static var liveObjectsLock = OS_SPINLOCK_INIT
34 | private static var reaperKey = strdup("_reaper_")!
35 |
36 | open class LifetimeTracker: Decorated {
37 |
38 | /// Tracker to detect deallocations for
39 | /// classes not inheriting from NSObject
40 | open class Reaper: NSObject {
41 | let owner: UnsafeRawPointer
42 | init(owner: UnsafeRawPointer) {
43 | self.owner = owner
44 | }
45 | @objc func _cxx_destruct() {
46 | }
47 | deinit {
48 | (autoBitCast(owner) as AnyObject)._cxx_destruct?()
49 | }
50 | }
51 |
52 | public let isAllocator: Bool
53 | public let isDeallocator: Bool
54 | override var isLifetime: Bool { return isAllocator || isDeallocator }
55 |
56 | public required init?(name signature: String,
57 | vtableSlot: UnsafeMutablePointer? = nil,
58 | objcMethod: Method? = nil, objcClass: AnyClass? = nil,
59 | original: OpaquePointer? = nil,
60 | replaceWith: nullImplementationType? = nil) {
61 | isAllocator = signature.contains(".__allocating_init(") ||
62 | !class_isMetaClass(objcClass) && signature.contains(" init")
63 | && !signature.contains(" initial")
64 |
65 | isDeallocator = signature.contains("cxx_destruct") ||
66 | signature.contains(".__deallocating_deinit") // not used
67 | super.init(name: signature, vtableSlot: vtableSlot,
68 | objcMethod: objcMethod, objcClass: objcClass,
69 | original: original, replaceWith: replaceWith)
70 | }
71 |
72 | /**
73 | Update instances for each class
74 | */
75 | open func update(instance: AnyObject) -> UnsafeRawPointer {
76 | let metaType: UnsafeRawPointer = autoBitCast(type(of: instance))
77 | OSSpinLockLock(&liveObjectsLock)
78 | if liveObjects.index(forKey: metaType) == nil {
79 | liveObjects[metaType] = Set()
80 | trackGenerics(metaType, register: metaType)
81 | }
82 | if isAllocator {
83 | liveObjects[metaType]!
84 | .insert(autoBitCast(instance))
85 | } else {
86 | liveObjects[metaType]!
87 | .remove(autoBitCast(instance))
88 | }
89 | invocation()?.numberLive = liveObjects[metaType]!.count
90 | OSSpinLockUnlock(&liveObjectsLock)
91 | return metaType
92 | }
93 |
94 | /// Make sure deallocations of generic classes are tracked.
95 | /// - Parameter metaType: pointer to type info
96 | /// - Parameter register: generic to actually register
97 | open func trackGenerics(_ metaType: UnsafeRawPointer,
98 | register: UnsafeRawPointer) {
99 | if !tracksDeallocs.contains(register),
100 | let generic = autoBitCast(metaType) as Any.Type as? AnyClass {
101 | var methodCount: UInt32 = 0
102 | if let methods = class_copyMethodList(generic, &methodCount) {
103 | for method in (0.. \(String(cString: type))",
110 | objcMethod: method, objcClass: generic) {
111 | class_replaceMethod(generic, sel,
112 | autoBitCast(swizzle.forwardingImplementation), type)
113 | tracksDeallocs.insert(register)
114 | }
115 | }
116 | free(methods)
117 | }
118 | if !tracksDeallocs.contains(register) {
119 | if let superClass = class_getSuperclass(generic) {
120 | trackGenerics(autoBitCast(superClass), register: register)
121 | } else {
122 | // fallback where class doesn't inherit from NSObject
123 | if let tracker = class_getInstanceMethod(Reaper.self,
124 | #selector(Reaper._cxx_destruct)),
125 | let type = method_getTypeEncoding(tracker),
126 | let originalClass = autoBitCast(register) as Any.Type as? AnyClass,
127 | let swizzle = swizzleFactory.init(name:
128 | "-[\(generic) _cxx_destruct] -> \(String(cString: type))",
129 | objcMethod: tracker, objcClass: originalClass) {
130 | class_replaceMethod(originalClass,
131 | #selector(Reaper._cxx_destruct),
132 | autoBitCast(swizzle.forwardingImplementation),
133 | type)
134 | }
135 | }
136 | }
137 | }
138 | }
139 |
140 | /**
141 | Increment live instances for initialisers
142 | */
143 | open override func exitDecorate(stack: inout ExitStack,
144 | invocation: Invocation) -> String? {
145 | var info = "", live = "live"
146 | if isAllocator || isDeallocator {
147 | if isAllocator {
148 | let instance = returnAsAny as AnyObject
149 | let metaType = update(instance: instance)
150 | if !tracksDeallocs.contains(metaType) {
151 | objc_setAssociatedObject(instance, reaperKey,
152 | Reaper(owner: autoBitCast(instance)),
153 | .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
154 | live = "allocated"
155 | }
156 | }
157 | info = " [\(invocation.numberLive) \(live)]"
158 | }
159 | return super.exitDecorate(stack: &stack, invocation: invocation)! + info
160 | }
161 |
162 | /**
163 | Decrement live instances on deallocations
164 | */
165 | open override func entryDecorate(stack: inout EntryStack,
166 | invocation: Invocation) -> String? {
167 | if isDeallocator {
168 | _ = update(instance: getSelf())
169 | }
170 | return super.entryDecorate(stack: &stack, invocation: invocation)
171 | }
172 | }
173 | }
174 | #endif
175 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/Trampolines.mm:
--------------------------------------------------------------------------------
1 | //
2 | // Trampolines.mm
3 | // SwiftTrace
4 | //
5 | // Created by John Holdsworth on 21/01/2022.
6 | // Repo: https://github.com/johnno1962/SwiftTrace
7 | // $Id: //depot/SwiftTrace/SwiftTraceGuts/Trampolines.mm#7 $
8 | //
9 |
10 | #if DEBUG || !DEBUG_ONLY
11 | #import "include/SwiftTrace.h"
12 |
13 | //
14 | // Trampoline code adapted from:
15 | // https://github.com/OliverLetterer/imp_implementationForwardingToSelector
16 | //
17 | // imp_implementationForwardingToSelector.m
18 | // imp_implementationForwardingToSelector
19 | //
20 | // Created by Oliver Letterer on 22.03.14.
21 | // Copyright (c) 2014 Oliver Letterer
22 | //
23 | // Permission is hereby granted, free of charge, to any person obtaining a copy
24 | // of this software and associated documentation files (the "Software"), to deal
25 | // in the Software without restriction, including without limitation the rights
26 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 | // copies of the Software, and to permit persons to whom the Software is
28 | // furnished to do so, subject to the following conditions:
29 | //
30 | // The above copyright notice and this permission notice shall be included in
31 | // all copies or substantial portions of the Software.
32 | //
33 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 | // THE SOFTWARE.
40 | //
41 |
42 | #import
43 | #import
44 |
45 | #import
46 | #import
47 | #import
48 | #import
49 | #import
50 |
51 | extern char xt_forwarding_trampoline_page, xt_forwarding_trampolines_start,
52 | xt_forwarding_trampolines_next, xt_forwarding_trampolines_end;
53 |
54 | // trampoline implementation specific stuff...
55 | typedef struct {
56 | #if !defined(__LP64__)
57 | IMP tracer;
58 | #endif
59 | void *patch; // Pointer to SwiftTrace.Patch instance retained elsewhere
60 | } SwiftTraceTrampolineDataBlock;
61 |
62 | typedef int32_t SPLForwardingTrampolineEntryPointBlock[2];
63 | #if defined(__i386__)
64 | static const int32_t SPLForwardingTrampolineInstructionCount = 8;
65 | #elif defined(_ARM_ARCH_7)
66 | static const int32_t SPLForwardingTrampolineInstructionCount = 12;
67 | #undef PAGE_SIZE
68 | #define PAGE_SIZE (1<<14)
69 | #elif defined(__arm64__)
70 | static const int32_t SPLForwardingTrampolineInstructionCount = 62;
71 | #undef PAGE_SIZE
72 | #define PAGE_SIZE (1<<14)
73 | #elif defined(__LP64__) // x86_64
74 | static const int32_t SPLForwardingTrampolineInstructionCount = 92;
75 | #else
76 | #error SwiftTrace is not supported on this platform
77 | #endif
78 |
79 | static const size_t numberOfTrampolinesPerPage = (PAGE_SIZE - SPLForwardingTrampolineInstructionCount * sizeof(int32_t)) / sizeof(SPLForwardingTrampolineEntryPointBlock);
80 |
81 | typedef struct {
82 | union {
83 | struct {
84 | #if defined(__LP64__)
85 | IMP onEntry;
86 | IMP onExit;
87 | #endif
88 | int32_t nextAvailableTrampolineIndex;
89 | };
90 | int32_t trampolineSize[SPLForwardingTrampolineInstructionCount];
91 | };
92 |
93 | SwiftTraceTrampolineDataBlock trampolineData[numberOfTrampolinesPerPage];
94 |
95 | int32_t trampolineInstructions[SPLForwardingTrampolineInstructionCount];
96 | SPLForwardingTrampolineEntryPointBlock trampolineEntryPoints[numberOfTrampolinesPerPage];
97 | } SPLForwardingTrampolinePage;
98 |
99 | static_assert(sizeof(SPLForwardingTrampolineEntryPointBlock) == sizeof(SwiftTraceTrampolineDataBlock),
100 | "Inconsistent entry point/data block sizes");
101 | static_assert(sizeof(SPLForwardingTrampolinePage) == 2 * PAGE_SIZE,
102 | "Incorrect trampoline pages size");
103 | static_assert(offsetof(SPLForwardingTrampolinePage, trampolineInstructions) == PAGE_SIZE,
104 | "Incorrect trampoline page offset");
105 |
106 | static SPLForwardingTrampolinePage *SPLForwardingTrampolinePageAlloc()
107 | {
108 | vm_address_t trampolineTemplatePage = (vm_address_t)&xt_forwarding_trampoline_page;
109 |
110 | vm_address_t newTrampolinePage = 0;
111 | kern_return_t kernReturn = KERN_SUCCESS;
112 |
113 | //printf( "%d %d %d %d %d\n", vm_page_size, &xt_forwarding_trampolines_start - &xt_forwarding_trampoline_page, SPLForwardingTrampolineInstructionCount*4, &xt_forwarding_trampolines_end - &xt_forwarding_trampoline_page, &xt_forwarding_trampolines_next - &xt_forwarding_trampolines_start );
114 |
115 | assert( &xt_forwarding_trampolines_start - &xt_forwarding_trampoline_page ==
116 | SPLForwardingTrampolineInstructionCount * sizeof(int32_t) );
117 | assert( &xt_forwarding_trampolines_end - &xt_forwarding_trampoline_page == PAGE_SIZE );
118 | assert( &xt_forwarding_trampolines_next - &xt_forwarding_trampolines_start == sizeof(SwiftTraceTrampolineDataBlock) );
119 |
120 | // allocate two consequent memory pages
121 | kernReturn = vm_allocate(mach_task_self(), &newTrampolinePage, PAGE_SIZE * 2, VM_FLAGS_ANYWHERE);
122 | NSCAssert1(kernReturn == KERN_SUCCESS, @"vm_allocate failed", kernReturn);
123 |
124 | // deallocate second page where we will store our trampoline
125 | vm_address_t trampoline_page = newTrampolinePage + PAGE_SIZE;
126 | kernReturn = vm_deallocate(mach_task_self(), trampoline_page, PAGE_SIZE);
127 | NSCAssert1(kernReturn == KERN_SUCCESS, @"vm_deallocate failed", kernReturn);
128 |
129 | // trampoline page will be remapped with implementation of spl_objc_forwarding_trampoline
130 | vm_prot_t cur_protection, max_protection;
131 | kernReturn = vm_remap(mach_task_self(), &trampoline_page, PAGE_SIZE, 0, 0, mach_task_self(), trampolineTemplatePage, FALSE, &cur_protection, &max_protection, VM_INHERIT_SHARE);
132 | NSCAssert1(kernReturn == KERN_SUCCESS, @"vm_remap failed", kernReturn);
133 |
134 | return (SPLForwardingTrampolinePage *)newTrampolinePage;
135 | }
136 |
137 | static NSMutableArray *normalTrampolinePages = nil;
138 |
139 | static SPLForwardingTrampolinePage *nextTrampolinePage()
140 | {
141 | static std::vector normalTrampolinePages;
142 |
143 | auto &thisArray = normalTrampolinePages;
144 | auto trampolinePage = thisArray.empty() ? nullptr : thisArray.back();
145 |
146 | if (!trampolinePage || (trampolinePage->nextAvailableTrampolineIndex == numberOfTrampolinesPerPage) ) {
147 | trampolinePage = SPLForwardingTrampolinePageAlloc();
148 | thisArray.push_back(trampolinePage);
149 | }
150 |
151 | return trampolinePage;
152 | }
153 |
154 | #if 00
155 | /// Fix for libMainThreadCheck when using trampolines
156 | typedef const char * (*image_path_func)(const void *ptr);
157 | static image_path_func orig_path_func;
158 |
159 | static const char *myld_image_path_containing_address(const void* addr) {
160 | return orig_path_func(addr) ?: "/trampoline";
161 | }
162 | #endif
163 |
164 | IMP imp_implementationForwardingToTracer(void *patch, IMP onEntry, IMP onExit)
165 | {
166 | #if 00
167 | static dispatch_once_t once;
168 | dispatch_once(&once, ^{
169 | struct rebinding path_rebinding = {"dyld_image_path_containing_address",
170 | (void *)myld_image_path_containing_address, (void **)&orig_path_func};
171 | rebind_symbols(&path_rebinding, 1);
172 | });
173 | #endif
174 |
175 | static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
176 | os_unfair_lock_lock(&lock);
177 |
178 | SPLForwardingTrampolinePage *dataPageLayout = nextTrampolinePage();
179 |
180 | int32_t nextAvailableTrampolineIndex = dataPageLayout->nextAvailableTrampolineIndex;
181 |
182 | #if !defined(__LP64__)
183 | dataPageLayout->trampolineData[nextAvailableTrampolineIndex].tracer = onEntry;
184 | #else
185 | dataPageLayout->onEntry = onEntry;
186 | dataPageLayout->onExit = onExit;
187 | #endif
188 | dataPageLayout->trampolineData[nextAvailableTrampolineIndex].patch = patch;
189 | dataPageLayout->nextAvailableTrampolineIndex++;
190 |
191 | IMP implementation = (IMP)&dataPageLayout->trampolineEntryPoints[nextAvailableTrampolineIndex];
192 |
193 | os_unfair_lock_unlock(&lock);
194 |
195 | return implementation;
196 | }
197 |
198 | id findSwizzleOf(void * _Nonnull trampoline) {
199 | for (NSValue *allocated in normalTrampolinePages) {
200 | SPLForwardingTrampolinePage *trampolinePage =
201 | (SPLForwardingTrampolinePage *)allocated.pointerValue;
202 | if (trampoline >= trampolinePage->trampolineInstructions && trampoline <
203 | trampolinePage->trampolineInstructions + numberOfTrampolinesPerPage)
204 | return *(id const *)(void *)((char *)trampoline - PAGE_SIZE);
205 | }
206 | return nil;
207 | }
208 | #endif
209 |
--------------------------------------------------------------------------------
/SwiftTraceApp/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/SwiftTraceGuts/SwiftTrace.mm:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftTrace.mm
3 | // SwiftTrace
4 | //
5 | // Repo: https://github.com/johnno1962/SwiftTrace
6 | // $Id: //depot/SwiftTrace/SwiftTraceGuts/SwiftTrace.mm#120 $
7 | //
8 |
9 | #if DEBUG || !DEBUG_ONLY
10 | #import "include/SwiftTrace.h"
11 | #import
12 | #import
13 | #import
14 | #import