From d7d3d9b735c335073cf92f439375d5b55a605e1a Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Wed, 14 Oct 2020 16:25:19 +0200 Subject: [PATCH] Objc injection goto. --- InjectionBundle/InjectionClient.h | 2 + InjectionBundle/InjectionClient.mm | 24 ++++++++++-- InjectionBundle/SwiftInjection.swift | 11 +++--- InjectionIII.xcodeproj/project.pbxproj | 5 +-- InjectionIII/AppDelegate.swift | 51 ++++++++++++++++---------- InjectionIII/Info.plist | 2 +- InjectionIII/InjectionServer.swift | 5 ++- SwiftTrace | 2 +- 8 files changed, 66 insertions(+), 36 deletions(-) diff --git a/InjectionBundle/InjectionClient.h b/InjectionBundle/InjectionClient.h index 916d701f..205d04c5 100644 --- a/InjectionBundle/InjectionClient.h +++ b/InjectionBundle/InjectionClient.h @@ -11,6 +11,7 @@ #define INJECTION_ADDRESS @":8898" #define INJECTION_KEY @"bvijkijyhbtrbrebzjbbzcfbbvvq" +#define FRAMEWORK_DELIMITER @"," @interface InjectionClient : SimpleSocket @@ -50,6 +51,7 @@ typedef NS_ENUM(int, InjectionResponse) { InjectionPause, InjectionSign, InjectionError, + InjectionFrameworks, InjectionExit = ~0 }; diff --git a/InjectionBundle/InjectionClient.mm b/InjectionBundle/InjectionClient.mm index 3a027e85..1e0f71ea 100644 --- a/InjectionBundle/InjectionClient.mm +++ b/InjectionBundle/InjectionClient.mm @@ -5,7 +5,7 @@ // Created by John Holdsworth on 06/11/2017. // Copyright © 2017 John Holdsworth. All rights reserved. // -// $Id: //depot/ResidentEval/InjectionBundle/InjectionClient.mm#115 $ +// $Id: //depot/ResidentEval/InjectionBundle/InjectionClient.mm#118 $ // #import "InjectionClient.h" @@ -163,13 +163,15 @@ + (void)load { - (void)runInBackground { SwiftEval *builder = [SwiftEval sharedInstance]; NSString *tmpDir = [self readString]; + BOOL notPlugin = ![@"/tmp" isEqualToString:tmpDir]; builder.tmpDir = tmpDir; - if (![@"/tmp" isEqualToString:tmpDir]) + if (notPlugin) [self writeInt:INJECTION_SALT]; [self writeString:INJECTION_KEY]; - [self writeString:[NSBundle - mainBundle].privateFrameworksPath]; + + NSString *frameworksPath = [NSBundle mainBundle].privateFrameworksPath; + [self writeString:frameworksPath]; [self writeString:builder.arch]; [self writeString:[NSBundle mainBundle].executablePath]; @@ -185,6 +187,20 @@ - (void)runInBackground { return [reader readString].boolValue; }; + if (notPlugin) { + NSMutableArray *frameworks = [NSMutableArray new]; + for (NSString *file in [[NSFileManager defaultManager] + contentsOfDirectoryAtPath:frameworksPath error:NULL]) { + NSString *frameworkName = [file + stringByReplacingOccurrencesOfString:@".framework" withString:@""]; + if (![frameworkName isEqualToString:file]) + [frameworks addObject:frameworkName]; + } + + [self writeCommand:InjectionFrameworks withString: + [frameworks componentsJoinedByString:FRAMEWORK_DELIMITER]]; + } + // As tmp file names come in, inject them InjectionCommand command; while ((command = (InjectionCommand)[self readInt]) != InjectionEOF) { diff --git a/InjectionBundle/SwiftInjection.swift b/InjectionBundle/SwiftInjection.swift index 7d5bc7d4..ece76a6e 100644 --- a/InjectionBundle/SwiftInjection.swift +++ b/InjectionBundle/SwiftInjection.swift @@ -5,7 +5,7 @@ // Created by John Holdsworth on 05/11/2017. // Copyright © 2017 John Holdsworth. All rights reserved. // -// $Id: //depot/ResidentEval/InjectionBundle/SwiftInjection.swift#84 $ +// $Id: //depot/ResidentEval/InjectionBundle/SwiftInjection.swift#86 $ // // Cut-down version of code injection in Swift. Uses code // from SwiftEval.swift to recompile and reload class. @@ -341,14 +341,15 @@ public class SwiftInjection: NSObject { } @objc class func dumpStats() { + let top = 200 print(""" - Sorted elapsed time/invocations by method - ========================================= + Sorted top \(top) elapsed time/invocations by method + ================================================= """) let invocationCounts = SwiftTrace.invocationCounts() - for (method, elapsed) in SwiftTrace.sortedElapsedTimes(onlyFirst: 1000) { - print("\(String(format: "%.1f", elapsed*1000.0))ms/\(invocationCounts[method] ?? -1)\t\(method)") + for (method, elapsed) in SwiftTrace.sortedElapsedTimes(onlyFirst: top) { + print("\(String(format: "%.1f", elapsed*1000.0))ms/\(invocationCounts[method] ?? 0)\t\(method)") } } } diff --git a/InjectionIII.xcodeproj/project.pbxproj b/InjectionIII.xcodeproj/project.pbxproj index e46ae226..e0746afe 100644 --- a/InjectionIII.xcodeproj/project.pbxproj +++ b/InjectionIII.xcodeproj/project.pbxproj @@ -1215,7 +1215,6 @@ IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/$(SWIFT_PLATFORM_TARGET_PREFIX) $PLATFORM_DIR/Developer/Library/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=150"; PRODUCT_NAME = macOSInjection; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -1343,7 +1342,7 @@ INFOPLIST_FILE = InjectionIII/Info.plist; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.12; - MARKETING_VERSION = 2.3.3; + MARKETING_VERSION = 2.3.4; PRODUCT_BUNDLE_IDENTIFIER = com.johnholdsworth.InjectionIII; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1366,7 +1365,7 @@ INFOPLIST_FILE = InjectionIII/Info.plist; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.12; - MARKETING_VERSION = 2.3.3; + MARKETING_VERSION = 2.3.4; PRODUCT_BUNDLE_IDENTIFIER = com.johnholdsworth.InjectionIII; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/InjectionIII/AppDelegate.swift b/InjectionIII/AppDelegate.swift index 4ed7c031..eaec1fcf 100644 --- a/InjectionIII/AppDelegate.swift +++ b/InjectionIII/AppDelegate.swift @@ -5,7 +5,7 @@ // Created by John Holdsworth on 06/11/2017. // Copyright © 2017 John Holdsworth. All rights reserved. // -// $Id: //depot/ResidentEval/InjectionIII/AppDelegate.swift#71 $ +// $Id: //depot/ResidentEval/InjectionIII/AppDelegate.swift#80 $ // import Cocoa @@ -235,22 +235,17 @@ class AppDelegate : NSObject, NSApplicationDelegate { } } - func setFrameworks(_ frameworkPath: String) { + func setFrameworks(_ frameworks: String) { DispatchQueue.main.async { guard let frameworksMenu = self.traceItem.submenu? .item(withTitle: "Trace Framework")?.submenu else { return } frameworksMenu.removeAllItems() - do { - for framework in try FileManager.default - .contentsOfDirectory(atPath: frameworkPath).sorted() - where framework.hasSuffix(".framework") { - let parts = framework.components(separatedBy: ".") - frameworksMenu.addItem(withTitle: parts[0], action: - #selector(self.traceFramework(_:)), keyEquivalent: "") - .target = self - } - } catch { - NSLog("Could not list framework driectory \(frameworkPath): \(error)") + for framework in frameworks + .components(separatedBy: FRAMEWORK_DELIMITER).sorted() { + let parts = framework.components(separatedBy: ".") + frameworksMenu.addItem(withTitle: parts[0], action: + #selector(self.traceFramework(_:)), keyEquivalent: "") + .target = self } } } @@ -405,27 +400,41 @@ class AppDelegate : NSObject, NSApplicationDelegate { builder.projectFile = selectedProject guard parts.count > 0, let (_, logsDir) = - try? builder.determineEnvironment(classNameOrFile: "") else { return } + try? builder.determineEnvironment(classNameOrFile: "") else { + lastConnection?.sendCommand(.log, with: + "💉 Injection Goto service not availble.\n") + return + } var className: String!, sourceFile: String? + let tmpDir = NSTemporaryDirectory() for part in parts { - className = part + let subParts = part.components(separatedBy: " ") + className = subParts[0] if let (_, foundSourceFile) = try? builder.findCompileCommand(logsDir: logsDir, - classNameOrFile: className, tmpfile: "/tmp/eval101") { + classNameOrFile: className, tmpfile: tmpDir+"/eval101") { sourceFile = foundSourceFile - className = parts.last + className = subParts.count > 1 ? subParts.last : parts.last break } } + className = className.replacingOccurrences(of: #"\((\S+).*"#, + with: "$1", + options: .regularExpression) + guard sourceFile != nil, let sourceText = try? NSString(contentsOfFile: sourceFile!, encoding: String.Encoding.utf8.rawValue), let finder = try? NSRegularExpression(pattern: - #"\b(?:var|func|struct|class|enum)\s+(\#(className!))\b"#, - options: [.anchorsMatchLines]) else { return } + #"(?:\b(?:var|func|struct|class|enum)\s+|^[+-]\s*(?:\([^)]*\)\s*)?)(\#(className!))\b"#, + options: [.anchorsMatchLines]) else { + lastConnection?.sendCommand(.log, with: + "💉 Unable to find source file for type '\(className!)'.\n") + return + } let match = finder.firstMatch(in: sourceText as String, options: [], range: NSMakeRange(0, sourceText.length)) @@ -450,13 +459,15 @@ class AppDelegate : NSObject, NSApplicationDelegate { } } + guard numberOfLine != 0 else { return } + var xed = "/usr/bin/xed" if let xcodeURL = self.runningXcodeDevURL { xed = xcodeURL .appendingPathComponent("usr/bin/xed").path } - let script = "/tmp/injection_goto.sh" + let script = tmpDir+"/injection_goto.sh" do { try "\"\(xed)\" --line \(numberOfLine) \"\(sourceFile!)\"" .write(toFile: script, atomically: false, encoding: .utf8) diff --git a/InjectionIII/Info.plist b/InjectionIII/Info.plist index c4cbb61c..377ffc02 100644 --- a/InjectionIII/Info.plist +++ b/InjectionIII/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4401 + 4451 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/InjectionIII/InjectionServer.swift b/InjectionIII/InjectionServer.swift index db71b873..df2b3f51 100644 --- a/InjectionIII/InjectionServer.swift +++ b/InjectionIII/InjectionServer.swift @@ -5,7 +5,7 @@ // Created by John Holdsworth on 06/11/2017. // Copyright © 2017 John Holdsworth. All rights reserved. // -// $Id: //depot/ResidentEval/InjectionIII/InjectionServer.swift#52 $ +// $Id: //depot/ResidentEval/InjectionIII/InjectionServer.swift#53 $ // let commandQueue = DispatchQueue(label: "InjectionCommand") @@ -70,7 +70,6 @@ public class InjectionServer: SimpleSocket { // client spcific data for building if let frameworks = readString() { builder.frameworks = frameworks - appDelegate.setFrameworks(frameworks) } else { return } if let arch = readString() { @@ -182,6 +181,8 @@ public class InjectionServer: SimpleSocket { break } switch command { + case .frameworks: + appDelegate.setFrameworks(readString() ?? "") case .complete: appDelegate.setMenuIcon("InjectionOK") if appDelegate.frontItem.state == .on { diff --git a/SwiftTrace b/SwiftTrace index 5fbebefe..d3de2551 160000 --- a/SwiftTrace +++ b/SwiftTrace @@ -1 +1 @@ -Subproject commit 5fbebefe1058efe4db6a6c954a647a626c67b274 +Subproject commit d3de2551a4dd447d67d933506fdaac0146dfb59c