├── LXDAppMonitor.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── linxinda.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── linxinda.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── LXDAppMonitor.xcscheme │ └── xcschememanagement.plist ├── LXDAppMonitor ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── LXDAppMonitor │ ├── Crash │ │ ├── LXDCrashLogger.h │ │ ├── LXDCrashLogger.m │ │ ├── LXDCrashMonitor.h │ │ └── LXDCrashMonitor.m │ ├── FPS │ │ ├── LXDFPSDisplayer.h │ │ ├── LXDFPSDisplayer.m │ │ ├── LXDFPSMonitor.h │ │ ├── LXDFPSMonitor.m │ │ ├── LXDWeakProxy.h │ │ └── LXDWeakProxy.m │ ├── Fluency │ │ ├── LXDAppFluencyMonitor.h │ │ └── LXDAppFluencyMonitor.m │ ├── Intercept │ │ ├── LXDDNSInterceptor.h │ │ ├── LXDDNSInterceptor.m │ │ ├── LXDHostFilterRule.h │ │ ├── LXDHostFilterRule.m │ │ ├── LXDHostMapper.h │ │ ├── LXDHostMapper.m │ │ ├── NSURLProtocol+WebKitSupport.h │ │ └── NSURLProtocol+WebKitSupport.m │ ├── Resource │ │ ├── LXDApplicationCPU.h │ │ ├── LXDApplicationCPU.m │ │ ├── LXDApplicationMemory.h │ │ ├── LXDApplicationMemory.m │ │ ├── LXDCPUDisplayer.h │ │ ├── LXDCPUDisplayer.m │ │ ├── LXDMemoryDisplayer.h │ │ ├── LXDMemoryDisplayer.m │ │ ├── LXDResourceMonitor.h │ │ ├── LXDResourceMonitor.m │ │ ├── LXDSystemCPU.h │ │ ├── LXDSystemCPU.m │ │ ├── LXDSystemMemory.h │ │ └── LXDSystemMemory.m │ └── Tool │ │ ├── Backtrace │ │ ├── LXDBacktraceLogger.h │ │ └── LXDBacktraceLogger.m │ │ ├── Dispatch │ │ ├── LXDDispatchAsync.h │ │ ├── LXDDispatchAsync.m │ │ ├── LXDDispatchOperation.h │ │ └── LXDDispatchOperation.m │ │ ├── Performance │ │ ├── LXDTransaction.h │ │ └── LXDTransaction.m │ │ ├── Timer │ │ ├── LXDGlobalTimer.h │ │ └── LXDGlobalTimer.m │ │ └── UI │ │ ├── LXDAsyncLabel.h │ │ ├── LXDAsyncLabel.m │ │ ├── LXDMonitorUI.h │ │ ├── LXDTopWindow.h │ │ ├── LXDTopWindow.m │ │ ├── UIApplication+LXDTool.h │ │ └── UIApplication+LXDTool.m ├── ViewController.h ├── ViewController.m ├── WebViewController.h ├── WebViewController.m ├── WebViewController.xib └── main.m ├── LXDAppMonitorTests ├── Info.plist └── LXDAppMonitorTests.m ├── LXDAppMonitorUITests ├── Info.plist └── LXDAppMonitorUITests.m └── ReadMe.md /LXDAppMonitor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | EF368A901EBC6FA200A629B1 /* LXDCrashLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A4C1EBC6FA200A629B1 /* LXDCrashLogger.m */; }; 11 | EF368A911EBC6FA200A629B1 /* LXDCrashMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A4E1EBC6FA200A629B1 /* LXDCrashMonitor.m */; }; 12 | EF368A921EBC6FA200A629B1 /* LXDAppFluencyMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A511EBC6FA200A629B1 /* LXDAppFluencyMonitor.m */; }; 13 | EF368A931EBC6FA200A629B1 /* LXDFPSDisplayer.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A541EBC6FA200A629B1 /* LXDFPSDisplayer.m */; }; 14 | EF368A941EBC6FA200A629B1 /* LXDFPSMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A561EBC6FA200A629B1 /* LXDFPSMonitor.m */; }; 15 | EF368A951EBC6FA200A629B1 /* LXDWeakProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A581EBC6FA200A629B1 /* LXDWeakProxy.m */; }; 16 | EF368A961EBC6FA200A629B1 /* LXDDNSInterceptor.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A5B1EBC6FA200A629B1 /* LXDDNSInterceptor.m */; }; 17 | EF368A971EBC6FA200A629B1 /* LXDHostFilterRule.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A5D1EBC6FA200A629B1 /* LXDHostFilterRule.m */; }; 18 | EF368A981EBC6FA200A629B1 /* LXDHostMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A5F1EBC6FA200A629B1 /* LXDHostMapper.m */; }; 19 | EF368A991EBC6FA200A629B1 /* NSURLProtocol+WebKitSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A611EBC6FA200A629B1 /* NSURLProtocol+WebKitSupport.m */; }; 20 | EF368A9A1EBC6FA200A629B1 /* LXDApplicationCPU.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A641EBC6FA200A629B1 /* LXDApplicationCPU.m */; }; 21 | EF368A9B1EBC6FA200A629B1 /* LXDApplicationMemory.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A661EBC6FA200A629B1 /* LXDApplicationMemory.m */; }; 22 | EF368A9C1EBC6FA200A629B1 /* LXDCPUDisplayer.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A681EBC6FA200A629B1 /* LXDCPUDisplayer.m */; }; 23 | EF368A9D1EBC6FA200A629B1 /* LXDMemoryDisplayer.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A6A1EBC6FA200A629B1 /* LXDMemoryDisplayer.m */; }; 24 | EF368A9E1EBC6FA200A629B1 /* LXDResourceMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A6C1EBC6FA200A629B1 /* LXDResourceMonitor.m */; }; 25 | EF368A9F1EBC6FA200A629B1 /* LXDSystemCPU.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A6E1EBC6FA200A629B1 /* LXDSystemCPU.m */; }; 26 | EF368AA01EBC6FA200A629B1 /* LXDSystemMemory.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A701EBC6FA200A629B1 /* LXDSystemMemory.m */; }; 27 | EF368AA11EBC6FA200A629B1 /* LXDBacktraceLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A741EBC6FA200A629B1 /* LXDBacktraceLogger.m */; }; 28 | EF368AA21EBC6FA200A629B1 /* LXDDispatchAsync.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A771EBC6FA200A629B1 /* LXDDispatchAsync.m */; }; 29 | EF368AA31EBC6FA200A629B1 /* LXDDispatchOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A791EBC6FA200A629B1 /* LXDDispatchOperation.m */; }; 30 | EF368AA91EBC6FA200A629B1 /* LXDGlobalTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A871EBC6FA200A629B1 /* LXDGlobalTimer.m */; }; 31 | EF368AAA1EBC6FA200A629B1 /* LXDAsyncLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A8A1EBC6FA200A629B1 /* LXDAsyncLabel.m */; }; 32 | EF368AAB1EBC6FA200A629B1 /* LXDTopWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A8D1EBC6FA200A629B1 /* LXDTopWindow.m */; }; 33 | EF368AAC1EBC6FA200A629B1 /* UIApplication+LXDTool.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368A8F1EBC6FA200A629B1 /* UIApplication+LXDTool.m */; }; 34 | EF368AB21EBC6FDE00A629B1 /* LXDTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = EF368AB11EBC6FDE00A629B1 /* LXDTransaction.m */; }; 35 | EF82D86E1E8B8E9600DD4776 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = EF82D86D1E8B8E9600DD4776 /* main.m */; }; 36 | EF82D8711E8B8E9600DD4776 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EF82D8701E8B8E9600DD4776 /* AppDelegate.m */; }; 37 | EF82D8741E8B8E9600DD4776 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF82D8731E8B8E9600DD4776 /* ViewController.m */; }; 38 | EF82D8771E8B8E9600DD4776 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EF82D8751E8B8E9600DD4776 /* Main.storyboard */; }; 39 | EF82D8791E8B8E9600DD4776 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EF82D8781E8B8E9600DD4776 /* Assets.xcassets */; }; 40 | EF82D87C1E8B8E9600DD4776 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EF82D87A1E8B8E9600DD4776 /* LaunchScreen.storyboard */; }; 41 | EF82D8871E8B8E9600DD4776 /* LXDAppMonitorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = EF82D8861E8B8E9600DD4776 /* LXDAppMonitorTests.m */; }; 42 | EF82D8921E8B8E9600DD4776 /* LXDAppMonitorUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = EF82D8911E8B8E9600DD4776 /* LXDAppMonitorUITests.m */; }; 43 | EF82D8C01E8B901000DD4776 /* WebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF82D8BE1E8B901000DD4776 /* WebViewController.m */; }; 44 | EF82D8C11E8B901000DD4776 /* WebViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = EF82D8BF1E8B901000DD4776 /* WebViewController.xib */; }; 45 | EFF925A01EB2E5B1005D9BEB /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EFF9259F1EB2E5B1005D9BEB /* libsqlite3.0.tbd */; }; 46 | /* End PBXBuildFile section */ 47 | 48 | /* Begin PBXContainerItemProxy section */ 49 | EF82D8831E8B8E9600DD4776 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = EF82D8611E8B8E9600DD4776 /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = EF82D8681E8B8E9600DD4776; 54 | remoteInfo = LXDAppMonitor; 55 | }; 56 | EF82D88E1E8B8E9600DD4776 /* PBXContainerItemProxy */ = { 57 | isa = PBXContainerItemProxy; 58 | containerPortal = EF82D8611E8B8E9600DD4776 /* Project object */; 59 | proxyType = 1; 60 | remoteGlobalIDString = EF82D8681E8B8E9600DD4776; 61 | remoteInfo = LXDAppMonitor; 62 | }; 63 | /* End PBXContainerItemProxy section */ 64 | 65 | /* Begin PBXFileReference section */ 66 | EF368A4B1EBC6FA200A629B1 /* LXDCrashLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDCrashLogger.h; sourceTree = ""; }; 67 | EF368A4C1EBC6FA200A629B1 /* LXDCrashLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDCrashLogger.m; sourceTree = ""; }; 68 | EF368A4D1EBC6FA200A629B1 /* LXDCrashMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDCrashMonitor.h; sourceTree = ""; }; 69 | EF368A4E1EBC6FA200A629B1 /* LXDCrashMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDCrashMonitor.m; sourceTree = ""; }; 70 | EF368A501EBC6FA200A629B1 /* LXDAppFluencyMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDAppFluencyMonitor.h; sourceTree = ""; }; 71 | EF368A511EBC6FA200A629B1 /* LXDAppFluencyMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDAppFluencyMonitor.m; sourceTree = ""; }; 72 | EF368A531EBC6FA200A629B1 /* LXDFPSDisplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDFPSDisplayer.h; sourceTree = ""; }; 73 | EF368A541EBC6FA200A629B1 /* LXDFPSDisplayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDFPSDisplayer.m; sourceTree = ""; }; 74 | EF368A551EBC6FA200A629B1 /* LXDFPSMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDFPSMonitor.h; sourceTree = ""; }; 75 | EF368A561EBC6FA200A629B1 /* LXDFPSMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDFPSMonitor.m; sourceTree = ""; }; 76 | EF368A571EBC6FA200A629B1 /* LXDWeakProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDWeakProxy.h; sourceTree = ""; }; 77 | EF368A581EBC6FA200A629B1 /* LXDWeakProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDWeakProxy.m; sourceTree = ""; }; 78 | EF368A5A1EBC6FA200A629B1 /* LXDDNSInterceptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDDNSInterceptor.h; sourceTree = ""; }; 79 | EF368A5B1EBC6FA200A629B1 /* LXDDNSInterceptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDDNSInterceptor.m; sourceTree = ""; }; 80 | EF368A5C1EBC6FA200A629B1 /* LXDHostFilterRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDHostFilterRule.h; sourceTree = ""; }; 81 | EF368A5D1EBC6FA200A629B1 /* LXDHostFilterRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDHostFilterRule.m; sourceTree = ""; }; 82 | EF368A5E1EBC6FA200A629B1 /* LXDHostMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDHostMapper.h; sourceTree = ""; }; 83 | EF368A5F1EBC6FA200A629B1 /* LXDHostMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDHostMapper.m; sourceTree = ""; }; 84 | EF368A601EBC6FA200A629B1 /* NSURLProtocol+WebKitSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLProtocol+WebKitSupport.h"; sourceTree = ""; }; 85 | EF368A611EBC6FA200A629B1 /* NSURLProtocol+WebKitSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLProtocol+WebKitSupport.m"; sourceTree = ""; }; 86 | EF368A631EBC6FA200A629B1 /* LXDApplicationCPU.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDApplicationCPU.h; sourceTree = ""; }; 87 | EF368A641EBC6FA200A629B1 /* LXDApplicationCPU.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDApplicationCPU.m; sourceTree = ""; }; 88 | EF368A651EBC6FA200A629B1 /* LXDApplicationMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDApplicationMemory.h; sourceTree = ""; }; 89 | EF368A661EBC6FA200A629B1 /* LXDApplicationMemory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDApplicationMemory.m; sourceTree = ""; }; 90 | EF368A671EBC6FA200A629B1 /* LXDCPUDisplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDCPUDisplayer.h; sourceTree = ""; }; 91 | EF368A681EBC6FA200A629B1 /* LXDCPUDisplayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDCPUDisplayer.m; sourceTree = ""; }; 92 | EF368A691EBC6FA200A629B1 /* LXDMemoryDisplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDMemoryDisplayer.h; sourceTree = ""; }; 93 | EF368A6A1EBC6FA200A629B1 /* LXDMemoryDisplayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDMemoryDisplayer.m; sourceTree = ""; }; 94 | EF368A6B1EBC6FA200A629B1 /* LXDResourceMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDResourceMonitor.h; sourceTree = ""; }; 95 | EF368A6C1EBC6FA200A629B1 /* LXDResourceMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDResourceMonitor.m; sourceTree = ""; }; 96 | EF368A6D1EBC6FA200A629B1 /* LXDSystemCPU.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDSystemCPU.h; sourceTree = ""; }; 97 | EF368A6E1EBC6FA200A629B1 /* LXDSystemCPU.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDSystemCPU.m; sourceTree = ""; }; 98 | EF368A6F1EBC6FA200A629B1 /* LXDSystemMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDSystemMemory.h; sourceTree = ""; }; 99 | EF368A701EBC6FA200A629B1 /* LXDSystemMemory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDSystemMemory.m; sourceTree = ""; }; 100 | EF368A731EBC6FA200A629B1 /* LXDBacktraceLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDBacktraceLogger.h; sourceTree = ""; }; 101 | EF368A741EBC6FA200A629B1 /* LXDBacktraceLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDBacktraceLogger.m; sourceTree = ""; }; 102 | EF368A761EBC6FA200A629B1 /* LXDDispatchAsync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDDispatchAsync.h; sourceTree = ""; }; 103 | EF368A771EBC6FA200A629B1 /* LXDDispatchAsync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDDispatchAsync.m; sourceTree = ""; }; 104 | EF368A781EBC6FA200A629B1 /* LXDDispatchOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDDispatchOperation.h; sourceTree = ""; }; 105 | EF368A791EBC6FA200A629B1 /* LXDDispatchOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDDispatchOperation.m; sourceTree = ""; }; 106 | EF368A861EBC6FA200A629B1 /* LXDGlobalTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDGlobalTimer.h; sourceTree = ""; }; 107 | EF368A871EBC6FA200A629B1 /* LXDGlobalTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDGlobalTimer.m; sourceTree = ""; }; 108 | EF368A891EBC6FA200A629B1 /* LXDAsyncLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDAsyncLabel.h; sourceTree = ""; }; 109 | EF368A8A1EBC6FA200A629B1 /* LXDAsyncLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDAsyncLabel.m; sourceTree = ""; }; 110 | EF368A8B1EBC6FA200A629B1 /* LXDMonitorUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDMonitorUI.h; sourceTree = ""; }; 111 | EF368A8C1EBC6FA200A629B1 /* LXDTopWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDTopWindow.h; sourceTree = ""; }; 112 | EF368A8D1EBC6FA200A629B1 /* LXDTopWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDTopWindow.m; sourceTree = ""; }; 113 | EF368A8E1EBC6FA200A629B1 /* UIApplication+LXDTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIApplication+LXDTool.h"; sourceTree = ""; }; 114 | EF368A8F1EBC6FA200A629B1 /* UIApplication+LXDTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIApplication+LXDTool.m"; sourceTree = ""; }; 115 | EF368AB01EBC6FDE00A629B1 /* LXDTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXDTransaction.h; sourceTree = ""; }; 116 | EF368AB11EBC6FDE00A629B1 /* LXDTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXDTransaction.m; sourceTree = ""; }; 117 | EF82D8691E8B8E9600DD4776 /* LXDAppMonitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LXDAppMonitor.app; sourceTree = BUILT_PRODUCTS_DIR; }; 118 | EF82D86D1E8B8E9600DD4776 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 119 | EF82D86F1E8B8E9600DD4776 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 120 | EF82D8701E8B8E9600DD4776 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 121 | EF82D8721E8B8E9600DD4776 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 122 | EF82D8731E8B8E9600DD4776 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 123 | EF82D8761E8B8E9600DD4776 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 124 | EF82D8781E8B8E9600DD4776 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 125 | EF82D87B1E8B8E9600DD4776 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 126 | EF82D87D1E8B8E9600DD4776 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 127 | EF82D8821E8B8E9600DD4776 /* LXDAppMonitorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LXDAppMonitorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 128 | EF82D8861E8B8E9600DD4776 /* LXDAppMonitorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LXDAppMonitorTests.m; sourceTree = ""; }; 129 | EF82D8881E8B8E9600DD4776 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 130 | EF82D88D1E8B8E9600DD4776 /* LXDAppMonitorUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LXDAppMonitorUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 131 | EF82D8911E8B8E9600DD4776 /* LXDAppMonitorUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LXDAppMonitorUITests.m; sourceTree = ""; }; 132 | EF82D8931E8B8E9600DD4776 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 133 | EF82D8BD1E8B901000DD4776 /* WebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewController.h; sourceTree = ""; }; 134 | EF82D8BE1E8B901000DD4776 /* WebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewController.m; sourceTree = ""; }; 135 | EF82D8BF1E8B901000DD4776 /* WebViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WebViewController.xib; sourceTree = ""; }; 136 | EFF9259F1EB2E5B1005D9BEB /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; 137 | /* End PBXFileReference section */ 138 | 139 | /* Begin PBXFrameworksBuildPhase section */ 140 | EF82D8661E8B8E9600DD4776 /* Frameworks */ = { 141 | isa = PBXFrameworksBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | EFF925A01EB2E5B1005D9BEB /* libsqlite3.0.tbd in Frameworks */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | EF82D87F1E8B8E9600DD4776 /* Frameworks */ = { 149 | isa = PBXFrameworksBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | EF82D88A1E8B8E9600DD4776 /* Frameworks */ = { 156 | isa = PBXFrameworksBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXFrameworksBuildPhase section */ 163 | 164 | /* Begin PBXGroup section */ 165 | EF368A491EBC6FA200A629B1 /* LXDAppMonitor */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | EF368A4A1EBC6FA200A629B1 /* Crash */, 169 | EF368A4F1EBC6FA200A629B1 /* Fluency */, 170 | EF368A521EBC6FA200A629B1 /* FPS */, 171 | EF368A591EBC6FA200A629B1 /* Intercept */, 172 | EF368A621EBC6FA200A629B1 /* Resource */, 173 | EF368A711EBC6FA200A629B1 /* Tool */, 174 | ); 175 | path = LXDAppMonitor; 176 | sourceTree = ""; 177 | }; 178 | EF368A4A1EBC6FA200A629B1 /* Crash */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | EF368A4B1EBC6FA200A629B1 /* LXDCrashLogger.h */, 182 | EF368A4C1EBC6FA200A629B1 /* LXDCrashLogger.m */, 183 | EF368A4D1EBC6FA200A629B1 /* LXDCrashMonitor.h */, 184 | EF368A4E1EBC6FA200A629B1 /* LXDCrashMonitor.m */, 185 | ); 186 | path = Crash; 187 | sourceTree = ""; 188 | }; 189 | EF368A4F1EBC6FA200A629B1 /* Fluency */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | EF368A501EBC6FA200A629B1 /* LXDAppFluencyMonitor.h */, 193 | EF368A511EBC6FA200A629B1 /* LXDAppFluencyMonitor.m */, 194 | ); 195 | path = Fluency; 196 | sourceTree = ""; 197 | }; 198 | EF368A521EBC6FA200A629B1 /* FPS */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | EF368A531EBC6FA200A629B1 /* LXDFPSDisplayer.h */, 202 | EF368A541EBC6FA200A629B1 /* LXDFPSDisplayer.m */, 203 | EF368A551EBC6FA200A629B1 /* LXDFPSMonitor.h */, 204 | EF368A561EBC6FA200A629B1 /* LXDFPSMonitor.m */, 205 | EF368A571EBC6FA200A629B1 /* LXDWeakProxy.h */, 206 | EF368A581EBC6FA200A629B1 /* LXDWeakProxy.m */, 207 | ); 208 | path = FPS; 209 | sourceTree = ""; 210 | }; 211 | EF368A591EBC6FA200A629B1 /* Intercept */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | EF368A5A1EBC6FA200A629B1 /* LXDDNSInterceptor.h */, 215 | EF368A5B1EBC6FA200A629B1 /* LXDDNSInterceptor.m */, 216 | EF368A5C1EBC6FA200A629B1 /* LXDHostFilterRule.h */, 217 | EF368A5D1EBC6FA200A629B1 /* LXDHostFilterRule.m */, 218 | EF368A5E1EBC6FA200A629B1 /* LXDHostMapper.h */, 219 | EF368A5F1EBC6FA200A629B1 /* LXDHostMapper.m */, 220 | EF368A601EBC6FA200A629B1 /* NSURLProtocol+WebKitSupport.h */, 221 | EF368A611EBC6FA200A629B1 /* NSURLProtocol+WebKitSupport.m */, 222 | ); 223 | path = Intercept; 224 | sourceTree = ""; 225 | }; 226 | EF368A621EBC6FA200A629B1 /* Resource */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | EF368A631EBC6FA200A629B1 /* LXDApplicationCPU.h */, 230 | EF368A641EBC6FA200A629B1 /* LXDApplicationCPU.m */, 231 | EF368A651EBC6FA200A629B1 /* LXDApplicationMemory.h */, 232 | EF368A661EBC6FA200A629B1 /* LXDApplicationMemory.m */, 233 | EF368A671EBC6FA200A629B1 /* LXDCPUDisplayer.h */, 234 | EF368A681EBC6FA200A629B1 /* LXDCPUDisplayer.m */, 235 | EF368A691EBC6FA200A629B1 /* LXDMemoryDisplayer.h */, 236 | EF368A6A1EBC6FA200A629B1 /* LXDMemoryDisplayer.m */, 237 | EF368A6B1EBC6FA200A629B1 /* LXDResourceMonitor.h */, 238 | EF368A6C1EBC6FA200A629B1 /* LXDResourceMonitor.m */, 239 | EF368A6D1EBC6FA200A629B1 /* LXDSystemCPU.h */, 240 | EF368A6E1EBC6FA200A629B1 /* LXDSystemCPU.m */, 241 | EF368A6F1EBC6FA200A629B1 /* LXDSystemMemory.h */, 242 | EF368A701EBC6FA200A629B1 /* LXDSystemMemory.m */, 243 | ); 244 | path = Resource; 245 | sourceTree = ""; 246 | }; 247 | EF368A711EBC6FA200A629B1 /* Tool */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | EF368A721EBC6FA200A629B1 /* Backtrace */, 251 | EF368A751EBC6FA200A629B1 /* Dispatch */, 252 | EF368A7A1EBC6FA200A629B1 /* Performance */, 253 | EF368A851EBC6FA200A629B1 /* Timer */, 254 | EF368A881EBC6FA200A629B1 /* UI */, 255 | ); 256 | path = Tool; 257 | sourceTree = ""; 258 | }; 259 | EF368A721EBC6FA200A629B1 /* Backtrace */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | EF368A731EBC6FA200A629B1 /* LXDBacktraceLogger.h */, 263 | EF368A741EBC6FA200A629B1 /* LXDBacktraceLogger.m */, 264 | ); 265 | path = Backtrace; 266 | sourceTree = ""; 267 | }; 268 | EF368A751EBC6FA200A629B1 /* Dispatch */ = { 269 | isa = PBXGroup; 270 | children = ( 271 | EF368A761EBC6FA200A629B1 /* LXDDispatchAsync.h */, 272 | EF368A771EBC6FA200A629B1 /* LXDDispatchAsync.m */, 273 | EF368A781EBC6FA200A629B1 /* LXDDispatchOperation.h */, 274 | EF368A791EBC6FA200A629B1 /* LXDDispatchOperation.m */, 275 | ); 276 | path = Dispatch; 277 | sourceTree = ""; 278 | }; 279 | EF368A7A1EBC6FA200A629B1 /* Performance */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | EF368AB01EBC6FDE00A629B1 /* LXDTransaction.h */, 283 | EF368AB11EBC6FDE00A629B1 /* LXDTransaction.m */, 284 | ); 285 | path = Performance; 286 | sourceTree = ""; 287 | }; 288 | EF368A851EBC6FA200A629B1 /* Timer */ = { 289 | isa = PBXGroup; 290 | children = ( 291 | EF368A861EBC6FA200A629B1 /* LXDGlobalTimer.h */, 292 | EF368A871EBC6FA200A629B1 /* LXDGlobalTimer.m */, 293 | ); 294 | path = Timer; 295 | sourceTree = ""; 296 | }; 297 | EF368A881EBC6FA200A629B1 /* UI */ = { 298 | isa = PBXGroup; 299 | children = ( 300 | EF368A891EBC6FA200A629B1 /* LXDAsyncLabel.h */, 301 | EF368A8A1EBC6FA200A629B1 /* LXDAsyncLabel.m */, 302 | EF368A8B1EBC6FA200A629B1 /* LXDMonitorUI.h */, 303 | EF368A8C1EBC6FA200A629B1 /* LXDTopWindow.h */, 304 | EF368A8D1EBC6FA200A629B1 /* LXDTopWindow.m */, 305 | EF368A8E1EBC6FA200A629B1 /* UIApplication+LXDTool.h */, 306 | EF368A8F1EBC6FA200A629B1 /* UIApplication+LXDTool.m */, 307 | ); 308 | path = UI; 309 | sourceTree = ""; 310 | }; 311 | EF82D8601E8B8E9600DD4776 = { 312 | isa = PBXGroup; 313 | children = ( 314 | EF82D86B1E8B8E9600DD4776 /* LXDAppMonitor */, 315 | EF82D8851E8B8E9600DD4776 /* LXDAppMonitorTests */, 316 | EF82D8901E8B8E9600DD4776 /* LXDAppMonitorUITests */, 317 | EF82D86A1E8B8E9600DD4776 /* Products */, 318 | EFF9259E1EB2E5B1005D9BEB /* Frameworks */, 319 | ); 320 | sourceTree = ""; 321 | }; 322 | EF82D86A1E8B8E9600DD4776 /* Products */ = { 323 | isa = PBXGroup; 324 | children = ( 325 | EF82D8691E8B8E9600DD4776 /* LXDAppMonitor.app */, 326 | EF82D8821E8B8E9600DD4776 /* LXDAppMonitorTests.xctest */, 327 | EF82D88D1E8B8E9600DD4776 /* LXDAppMonitorUITests.xctest */, 328 | ); 329 | name = Products; 330 | sourceTree = ""; 331 | }; 332 | EF82D86B1E8B8E9600DD4776 /* LXDAppMonitor */ = { 333 | isa = PBXGroup; 334 | children = ( 335 | EF368A491EBC6FA200A629B1 /* LXDAppMonitor */, 336 | EF82D86F1E8B8E9600DD4776 /* AppDelegate.h */, 337 | EF82D8701E8B8E9600DD4776 /* AppDelegate.m */, 338 | EF82D8721E8B8E9600DD4776 /* ViewController.h */, 339 | EF82D8731E8B8E9600DD4776 /* ViewController.m */, 340 | EF82D8751E8B8E9600DD4776 /* Main.storyboard */, 341 | EF82D8781E8B8E9600DD4776 /* Assets.xcassets */, 342 | EF82D87A1E8B8E9600DD4776 /* LaunchScreen.storyboard */, 343 | EF82D87D1E8B8E9600DD4776 /* Info.plist */, 344 | EF82D86C1E8B8E9600DD4776 /* Supporting Files */, 345 | EF82D8BD1E8B901000DD4776 /* WebViewController.h */, 346 | EF82D8BE1E8B901000DD4776 /* WebViewController.m */, 347 | EF82D8BF1E8B901000DD4776 /* WebViewController.xib */, 348 | ); 349 | path = LXDAppMonitor; 350 | sourceTree = ""; 351 | }; 352 | EF82D86C1E8B8E9600DD4776 /* Supporting Files */ = { 353 | isa = PBXGroup; 354 | children = ( 355 | EF82D86D1E8B8E9600DD4776 /* main.m */, 356 | ); 357 | name = "Supporting Files"; 358 | sourceTree = ""; 359 | }; 360 | EF82D8851E8B8E9600DD4776 /* LXDAppMonitorTests */ = { 361 | isa = PBXGroup; 362 | children = ( 363 | EF82D8861E8B8E9600DD4776 /* LXDAppMonitorTests.m */, 364 | EF82D8881E8B8E9600DD4776 /* Info.plist */, 365 | ); 366 | path = LXDAppMonitorTests; 367 | sourceTree = ""; 368 | }; 369 | EF82D8901E8B8E9600DD4776 /* LXDAppMonitorUITests */ = { 370 | isa = PBXGroup; 371 | children = ( 372 | EF82D8911E8B8E9600DD4776 /* LXDAppMonitorUITests.m */, 373 | EF82D8931E8B8E9600DD4776 /* Info.plist */, 374 | ); 375 | path = LXDAppMonitorUITests; 376 | sourceTree = ""; 377 | }; 378 | EFF9259E1EB2E5B1005D9BEB /* Frameworks */ = { 379 | isa = PBXGroup; 380 | children = ( 381 | EFF9259F1EB2E5B1005D9BEB /* libsqlite3.0.tbd */, 382 | ); 383 | name = Frameworks; 384 | sourceTree = ""; 385 | }; 386 | /* End PBXGroup section */ 387 | 388 | /* Begin PBXNativeTarget section */ 389 | EF82D8681E8B8E9600DD4776 /* LXDAppMonitor */ = { 390 | isa = PBXNativeTarget; 391 | buildConfigurationList = EF82D8961E8B8E9600DD4776 /* Build configuration list for PBXNativeTarget "LXDAppMonitor" */; 392 | buildPhases = ( 393 | EF82D8651E8B8E9600DD4776 /* Sources */, 394 | EF82D8661E8B8E9600DD4776 /* Frameworks */, 395 | EF82D8671E8B8E9600DD4776 /* Resources */, 396 | ); 397 | buildRules = ( 398 | ); 399 | dependencies = ( 400 | ); 401 | name = LXDAppMonitor; 402 | productName = LXDAppMonitor; 403 | productReference = EF82D8691E8B8E9600DD4776 /* LXDAppMonitor.app */; 404 | productType = "com.apple.product-type.application"; 405 | }; 406 | EF82D8811E8B8E9600DD4776 /* LXDAppMonitorTests */ = { 407 | isa = PBXNativeTarget; 408 | buildConfigurationList = EF82D8991E8B8E9600DD4776 /* Build configuration list for PBXNativeTarget "LXDAppMonitorTests" */; 409 | buildPhases = ( 410 | EF82D87E1E8B8E9600DD4776 /* Sources */, 411 | EF82D87F1E8B8E9600DD4776 /* Frameworks */, 412 | EF82D8801E8B8E9600DD4776 /* Resources */, 413 | ); 414 | buildRules = ( 415 | ); 416 | dependencies = ( 417 | EF82D8841E8B8E9600DD4776 /* PBXTargetDependency */, 418 | ); 419 | name = LXDAppMonitorTests; 420 | productName = LXDAppMonitorTests; 421 | productReference = EF82D8821E8B8E9600DD4776 /* LXDAppMonitorTests.xctest */; 422 | productType = "com.apple.product-type.bundle.unit-test"; 423 | }; 424 | EF82D88C1E8B8E9600DD4776 /* LXDAppMonitorUITests */ = { 425 | isa = PBXNativeTarget; 426 | buildConfigurationList = EF82D89C1E8B8E9600DD4776 /* Build configuration list for PBXNativeTarget "LXDAppMonitorUITests" */; 427 | buildPhases = ( 428 | EF82D8891E8B8E9600DD4776 /* Sources */, 429 | EF82D88A1E8B8E9600DD4776 /* Frameworks */, 430 | EF82D88B1E8B8E9600DD4776 /* Resources */, 431 | ); 432 | buildRules = ( 433 | ); 434 | dependencies = ( 435 | EF82D88F1E8B8E9600DD4776 /* PBXTargetDependency */, 436 | ); 437 | name = LXDAppMonitorUITests; 438 | productName = LXDAppMonitorUITests; 439 | productReference = EF82D88D1E8B8E9600DD4776 /* LXDAppMonitorUITests.xctest */; 440 | productType = "com.apple.product-type.bundle.ui-testing"; 441 | }; 442 | /* End PBXNativeTarget section */ 443 | 444 | /* Begin PBXProject section */ 445 | EF82D8611E8B8E9600DD4776 /* Project object */ = { 446 | isa = PBXProject; 447 | attributes = { 448 | LastUpgradeCheck = 0830; 449 | ORGANIZATIONNAME = Jolimark; 450 | TargetAttributes = { 451 | EF82D8681E8B8E9600DD4776 = { 452 | CreatedOnToolsVersion = 8.3; 453 | DevelopmentTeam = 7J57ACJ383; 454 | ProvisioningStyle = Automatic; 455 | }; 456 | EF82D8811E8B8E9600DD4776 = { 457 | CreatedOnToolsVersion = 8.3; 458 | DevelopmentTeam = 7J57ACJ383; 459 | ProvisioningStyle = Automatic; 460 | TestTargetID = EF82D8681E8B8E9600DD4776; 461 | }; 462 | EF82D88C1E8B8E9600DD4776 = { 463 | CreatedOnToolsVersion = 8.3; 464 | DevelopmentTeam = 7J57ACJ383; 465 | ProvisioningStyle = Automatic; 466 | TestTargetID = EF82D8681E8B8E9600DD4776; 467 | }; 468 | }; 469 | }; 470 | buildConfigurationList = EF82D8641E8B8E9600DD4776 /* Build configuration list for PBXProject "LXDAppMonitor" */; 471 | compatibilityVersion = "Xcode 3.2"; 472 | developmentRegion = English; 473 | hasScannedForEncodings = 0; 474 | knownRegions = ( 475 | en, 476 | Base, 477 | ); 478 | mainGroup = EF82D8601E8B8E9600DD4776; 479 | productRefGroup = EF82D86A1E8B8E9600DD4776 /* Products */; 480 | projectDirPath = ""; 481 | projectRoot = ""; 482 | targets = ( 483 | EF82D8681E8B8E9600DD4776 /* LXDAppMonitor */, 484 | EF82D8811E8B8E9600DD4776 /* LXDAppMonitorTests */, 485 | EF82D88C1E8B8E9600DD4776 /* LXDAppMonitorUITests */, 486 | ); 487 | }; 488 | /* End PBXProject section */ 489 | 490 | /* Begin PBXResourcesBuildPhase section */ 491 | EF82D8671E8B8E9600DD4776 /* Resources */ = { 492 | isa = PBXResourcesBuildPhase; 493 | buildActionMask = 2147483647; 494 | files = ( 495 | EF82D87C1E8B8E9600DD4776 /* LaunchScreen.storyboard in Resources */, 496 | EF82D8791E8B8E9600DD4776 /* Assets.xcassets in Resources */, 497 | EF82D8771E8B8E9600DD4776 /* Main.storyboard in Resources */, 498 | EF82D8C11E8B901000DD4776 /* WebViewController.xib in Resources */, 499 | ); 500 | runOnlyForDeploymentPostprocessing = 0; 501 | }; 502 | EF82D8801E8B8E9600DD4776 /* Resources */ = { 503 | isa = PBXResourcesBuildPhase; 504 | buildActionMask = 2147483647; 505 | files = ( 506 | ); 507 | runOnlyForDeploymentPostprocessing = 0; 508 | }; 509 | EF82D88B1E8B8E9600DD4776 /* Resources */ = { 510 | isa = PBXResourcesBuildPhase; 511 | buildActionMask = 2147483647; 512 | files = ( 513 | ); 514 | runOnlyForDeploymentPostprocessing = 0; 515 | }; 516 | /* End PBXResourcesBuildPhase section */ 517 | 518 | /* Begin PBXSourcesBuildPhase section */ 519 | EF82D8651E8B8E9600DD4776 /* Sources */ = { 520 | isa = PBXSourcesBuildPhase; 521 | buildActionMask = 2147483647; 522 | files = ( 523 | EF82D8741E8B8E9600DD4776 /* ViewController.m in Sources */, 524 | EF368A911EBC6FA200A629B1 /* LXDCrashMonitor.m in Sources */, 525 | EF368A971EBC6FA200A629B1 /* LXDHostFilterRule.m in Sources */, 526 | EF368A931EBC6FA200A629B1 /* LXDFPSDisplayer.m in Sources */, 527 | EF82D8711E8B8E9600DD4776 /* AppDelegate.m in Sources */, 528 | EF368AA11EBC6FA200A629B1 /* LXDBacktraceLogger.m in Sources */, 529 | EF368AB21EBC6FDE00A629B1 /* LXDTransaction.m in Sources */, 530 | EF82D8C01E8B901000DD4776 /* WebViewController.m in Sources */, 531 | EF368AAA1EBC6FA200A629B1 /* LXDAsyncLabel.m in Sources */, 532 | EF368AA91EBC6FA200A629B1 /* LXDGlobalTimer.m in Sources */, 533 | EF368A9B1EBC6FA200A629B1 /* LXDApplicationMemory.m in Sources */, 534 | EF368A9D1EBC6FA200A629B1 /* LXDMemoryDisplayer.m in Sources */, 535 | EF368A951EBC6FA200A629B1 /* LXDWeakProxy.m in Sources */, 536 | EF368A941EBC6FA200A629B1 /* LXDFPSMonitor.m in Sources */, 537 | EF368A9F1EBC6FA200A629B1 /* LXDSystemCPU.m in Sources */, 538 | EF368AA01EBC6FA200A629B1 /* LXDSystemMemory.m in Sources */, 539 | EF368A961EBC6FA200A629B1 /* LXDDNSInterceptor.m in Sources */, 540 | EF368A901EBC6FA200A629B1 /* LXDCrashLogger.m in Sources */, 541 | EF368A9A1EBC6FA200A629B1 /* LXDApplicationCPU.m in Sources */, 542 | EF368A991EBC6FA200A629B1 /* NSURLProtocol+WebKitSupport.m in Sources */, 543 | EF368AAC1EBC6FA200A629B1 /* UIApplication+LXDTool.m in Sources */, 544 | EF368A921EBC6FA200A629B1 /* LXDAppFluencyMonitor.m in Sources */, 545 | EF368AA31EBC6FA200A629B1 /* LXDDispatchOperation.m in Sources */, 546 | EF82D86E1E8B8E9600DD4776 /* main.m in Sources */, 547 | EF368AA21EBC6FA200A629B1 /* LXDDispatchAsync.m in Sources */, 548 | EF368AAB1EBC6FA200A629B1 /* LXDTopWindow.m in Sources */, 549 | EF368A9E1EBC6FA200A629B1 /* LXDResourceMonitor.m in Sources */, 550 | EF368A9C1EBC6FA200A629B1 /* LXDCPUDisplayer.m in Sources */, 551 | EF368A981EBC6FA200A629B1 /* LXDHostMapper.m in Sources */, 552 | ); 553 | runOnlyForDeploymentPostprocessing = 0; 554 | }; 555 | EF82D87E1E8B8E9600DD4776 /* Sources */ = { 556 | isa = PBXSourcesBuildPhase; 557 | buildActionMask = 2147483647; 558 | files = ( 559 | EF82D8871E8B8E9600DD4776 /* LXDAppMonitorTests.m in Sources */, 560 | ); 561 | runOnlyForDeploymentPostprocessing = 0; 562 | }; 563 | EF82D8891E8B8E9600DD4776 /* Sources */ = { 564 | isa = PBXSourcesBuildPhase; 565 | buildActionMask = 2147483647; 566 | files = ( 567 | EF82D8921E8B8E9600DD4776 /* LXDAppMonitorUITests.m in Sources */, 568 | ); 569 | runOnlyForDeploymentPostprocessing = 0; 570 | }; 571 | /* End PBXSourcesBuildPhase section */ 572 | 573 | /* Begin PBXTargetDependency section */ 574 | EF82D8841E8B8E9600DD4776 /* PBXTargetDependency */ = { 575 | isa = PBXTargetDependency; 576 | target = EF82D8681E8B8E9600DD4776 /* LXDAppMonitor */; 577 | targetProxy = EF82D8831E8B8E9600DD4776 /* PBXContainerItemProxy */; 578 | }; 579 | EF82D88F1E8B8E9600DD4776 /* PBXTargetDependency */ = { 580 | isa = PBXTargetDependency; 581 | target = EF82D8681E8B8E9600DD4776 /* LXDAppMonitor */; 582 | targetProxy = EF82D88E1E8B8E9600DD4776 /* PBXContainerItemProxy */; 583 | }; 584 | /* End PBXTargetDependency section */ 585 | 586 | /* Begin PBXVariantGroup section */ 587 | EF82D8751E8B8E9600DD4776 /* Main.storyboard */ = { 588 | isa = PBXVariantGroup; 589 | children = ( 590 | EF82D8761E8B8E9600DD4776 /* Base */, 591 | ); 592 | name = Main.storyboard; 593 | sourceTree = ""; 594 | }; 595 | EF82D87A1E8B8E9600DD4776 /* LaunchScreen.storyboard */ = { 596 | isa = PBXVariantGroup; 597 | children = ( 598 | EF82D87B1E8B8E9600DD4776 /* Base */, 599 | ); 600 | name = LaunchScreen.storyboard; 601 | sourceTree = ""; 602 | }; 603 | /* End PBXVariantGroup section */ 604 | 605 | /* Begin XCBuildConfiguration section */ 606 | EF82D8941E8B8E9600DD4776 /* Debug */ = { 607 | isa = XCBuildConfiguration; 608 | buildSettings = { 609 | ALWAYS_SEARCH_USER_PATHS = NO; 610 | CLANG_ANALYZER_NONNULL = YES; 611 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 612 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 613 | CLANG_CXX_LIBRARY = "libc++"; 614 | CLANG_ENABLE_MODULES = YES; 615 | CLANG_ENABLE_OBJC_ARC = YES; 616 | CLANG_WARN_BOOL_CONVERSION = YES; 617 | CLANG_WARN_CONSTANT_CONVERSION = YES; 618 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 619 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 620 | CLANG_WARN_EMPTY_BODY = YES; 621 | CLANG_WARN_ENUM_CONVERSION = YES; 622 | CLANG_WARN_INFINITE_RECURSION = YES; 623 | CLANG_WARN_INT_CONVERSION = YES; 624 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 625 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 626 | CLANG_WARN_UNREACHABLE_CODE = YES; 627 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 628 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 629 | COPY_PHASE_STRIP = NO; 630 | DEBUG_INFORMATION_FORMAT = dwarf; 631 | ENABLE_STRICT_OBJC_MSGSEND = YES; 632 | ENABLE_TESTABILITY = YES; 633 | GCC_C_LANGUAGE_STANDARD = gnu99; 634 | GCC_DYNAMIC_NO_PIC = NO; 635 | GCC_NO_COMMON_BLOCKS = YES; 636 | GCC_OPTIMIZATION_LEVEL = 0; 637 | GCC_PREPROCESSOR_DEFINITIONS = ( 638 | "DEBUG=1", 639 | "$(inherited)", 640 | ); 641 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 642 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 643 | GCC_WARN_UNDECLARED_SELECTOR = YES; 644 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 645 | GCC_WARN_UNUSED_FUNCTION = YES; 646 | GCC_WARN_UNUSED_VARIABLE = YES; 647 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 648 | MTL_ENABLE_DEBUG_INFO = YES; 649 | ONLY_ACTIVE_ARCH = YES; 650 | SDKROOT = iphoneos; 651 | }; 652 | name = Debug; 653 | }; 654 | EF82D8951E8B8E9600DD4776 /* Release */ = { 655 | isa = XCBuildConfiguration; 656 | buildSettings = { 657 | ALWAYS_SEARCH_USER_PATHS = NO; 658 | CLANG_ANALYZER_NONNULL = YES; 659 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 660 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 661 | CLANG_CXX_LIBRARY = "libc++"; 662 | CLANG_ENABLE_MODULES = YES; 663 | CLANG_ENABLE_OBJC_ARC = YES; 664 | CLANG_WARN_BOOL_CONVERSION = YES; 665 | CLANG_WARN_CONSTANT_CONVERSION = YES; 666 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 667 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 668 | CLANG_WARN_EMPTY_BODY = YES; 669 | CLANG_WARN_ENUM_CONVERSION = YES; 670 | CLANG_WARN_INFINITE_RECURSION = YES; 671 | CLANG_WARN_INT_CONVERSION = YES; 672 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 673 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 674 | CLANG_WARN_UNREACHABLE_CODE = YES; 675 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 676 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 677 | COPY_PHASE_STRIP = NO; 678 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 679 | ENABLE_NS_ASSERTIONS = NO; 680 | ENABLE_STRICT_OBJC_MSGSEND = YES; 681 | GCC_C_LANGUAGE_STANDARD = gnu99; 682 | GCC_NO_COMMON_BLOCKS = YES; 683 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 684 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 685 | GCC_WARN_UNDECLARED_SELECTOR = YES; 686 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 687 | GCC_WARN_UNUSED_FUNCTION = YES; 688 | GCC_WARN_UNUSED_VARIABLE = YES; 689 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 690 | MTL_ENABLE_DEBUG_INFO = NO; 691 | SDKROOT = iphoneos; 692 | VALIDATE_PRODUCT = YES; 693 | }; 694 | name = Release; 695 | }; 696 | EF82D8971E8B8E9600DD4776 /* Debug */ = { 697 | isa = XCBuildConfiguration; 698 | buildSettings = { 699 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 700 | DEVELOPMENT_TEAM = 7J57ACJ383; 701 | INFOPLIST_FILE = LXDAppMonitor/Info.plist; 702 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 703 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 704 | OTHER_LDFLAGS = ""; 705 | PRODUCT_BUNDLE_IDENTIFIER = jolimark.com.LXDAppMonitor; 706 | PRODUCT_NAME = "$(TARGET_NAME)"; 707 | }; 708 | name = Debug; 709 | }; 710 | EF82D8981E8B8E9600DD4776 /* Release */ = { 711 | isa = XCBuildConfiguration; 712 | buildSettings = { 713 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 714 | DEVELOPMENT_TEAM = 7J57ACJ383; 715 | INFOPLIST_FILE = LXDAppMonitor/Info.plist; 716 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 717 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 718 | OTHER_LDFLAGS = ""; 719 | PRODUCT_BUNDLE_IDENTIFIER = jolimark.com.LXDAppMonitor; 720 | PRODUCT_NAME = "$(TARGET_NAME)"; 721 | }; 722 | name = Release; 723 | }; 724 | EF82D89A1E8B8E9600DD4776 /* Debug */ = { 725 | isa = XCBuildConfiguration; 726 | buildSettings = { 727 | BUNDLE_LOADER = "$(TEST_HOST)"; 728 | DEVELOPMENT_TEAM = 7J57ACJ383; 729 | INFOPLIST_FILE = LXDAppMonitorTests/Info.plist; 730 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 731 | PRODUCT_BUNDLE_IDENTIFIER = jolimark.com.LXDAppMonitorTests; 732 | PRODUCT_NAME = "$(TARGET_NAME)"; 733 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LXDAppMonitor.app/LXDAppMonitor"; 734 | }; 735 | name = Debug; 736 | }; 737 | EF82D89B1E8B8E9600DD4776 /* Release */ = { 738 | isa = XCBuildConfiguration; 739 | buildSettings = { 740 | BUNDLE_LOADER = "$(TEST_HOST)"; 741 | DEVELOPMENT_TEAM = 7J57ACJ383; 742 | INFOPLIST_FILE = LXDAppMonitorTests/Info.plist; 743 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 744 | PRODUCT_BUNDLE_IDENTIFIER = jolimark.com.LXDAppMonitorTests; 745 | PRODUCT_NAME = "$(TARGET_NAME)"; 746 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LXDAppMonitor.app/LXDAppMonitor"; 747 | }; 748 | name = Release; 749 | }; 750 | EF82D89D1E8B8E9600DD4776 /* Debug */ = { 751 | isa = XCBuildConfiguration; 752 | buildSettings = { 753 | DEVELOPMENT_TEAM = 7J57ACJ383; 754 | INFOPLIST_FILE = LXDAppMonitorUITests/Info.plist; 755 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 756 | PRODUCT_BUNDLE_IDENTIFIER = jolimark.com.LXDAppMonitorUITests; 757 | PRODUCT_NAME = "$(TARGET_NAME)"; 758 | TEST_TARGET_NAME = LXDAppMonitor; 759 | }; 760 | name = Debug; 761 | }; 762 | EF82D89E1E8B8E9600DD4776 /* Release */ = { 763 | isa = XCBuildConfiguration; 764 | buildSettings = { 765 | DEVELOPMENT_TEAM = 7J57ACJ383; 766 | INFOPLIST_FILE = LXDAppMonitorUITests/Info.plist; 767 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 768 | PRODUCT_BUNDLE_IDENTIFIER = jolimark.com.LXDAppMonitorUITests; 769 | PRODUCT_NAME = "$(TARGET_NAME)"; 770 | TEST_TARGET_NAME = LXDAppMonitor; 771 | }; 772 | name = Release; 773 | }; 774 | /* End XCBuildConfiguration section */ 775 | 776 | /* Begin XCConfigurationList section */ 777 | EF82D8641E8B8E9600DD4776 /* Build configuration list for PBXProject "LXDAppMonitor" */ = { 778 | isa = XCConfigurationList; 779 | buildConfigurations = ( 780 | EF82D8941E8B8E9600DD4776 /* Debug */, 781 | EF82D8951E8B8E9600DD4776 /* Release */, 782 | ); 783 | defaultConfigurationIsVisible = 0; 784 | defaultConfigurationName = Release; 785 | }; 786 | EF82D8961E8B8E9600DD4776 /* Build configuration list for PBXNativeTarget "LXDAppMonitor" */ = { 787 | isa = XCConfigurationList; 788 | buildConfigurations = ( 789 | EF82D8971E8B8E9600DD4776 /* Debug */, 790 | EF82D8981E8B8E9600DD4776 /* Release */, 791 | ); 792 | defaultConfigurationIsVisible = 0; 793 | defaultConfigurationName = Release; 794 | }; 795 | EF82D8991E8B8E9600DD4776 /* Build configuration list for PBXNativeTarget "LXDAppMonitorTests" */ = { 796 | isa = XCConfigurationList; 797 | buildConfigurations = ( 798 | EF82D89A1E8B8E9600DD4776 /* Debug */, 799 | EF82D89B1E8B8E9600DD4776 /* Release */, 800 | ); 801 | defaultConfigurationIsVisible = 0; 802 | defaultConfigurationName = Release; 803 | }; 804 | EF82D89C1E8B8E9600DD4776 /* Build configuration list for PBXNativeTarget "LXDAppMonitorUITests" */ = { 805 | isa = XCConfigurationList; 806 | buildConfigurations = ( 807 | EF82D89D1E8B8E9600DD4776 /* Debug */, 808 | EF82D89E1E8B8E9600DD4776 /* Release */, 809 | ); 810 | defaultConfigurationIsVisible = 0; 811 | defaultConfigurationName = Release; 812 | }; 813 | /* End XCConfigurationList section */ 814 | }; 815 | rootObject = EF82D8611E8B8E9600DD4776 /* Project object */; 816 | } 817 | -------------------------------------------------------------------------------- /LXDAppMonitor.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LXDAppMonitor.xcodeproj/project.xcworkspace/xcuserdata/linxinda.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindrilin/LXDAppMonitor/e2ab702e53f5e511ec44b42efe63f4eec30daa4f/LXDAppMonitor.xcodeproj/project.xcworkspace/xcuserdata/linxinda.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /LXDAppMonitor.xcodeproj/xcuserdata/linxinda.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LXDAppMonitor.xcodeproj/xcuserdata/linxinda.xcuserdatad/xcschemes/LXDAppMonitor.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 66 | 67 | 68 | 69 | 79 | 81 | 87 | 88 | 89 | 90 | 91 | 92 | 98 | 100 | 106 | 107 | 108 | 109 | 111 | 112 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /LXDAppMonitor.xcodeproj/xcuserdata/linxinda.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LXDAppMonitor.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | EF82D8681E8B8E9600DD4776 16 | 17 | primary 18 | 19 | 20 | EF82D8811E8B8E9600DD4776 21 | 22 | primary 23 | 24 | 25 | EF82D88C1E8B8E9600DD4776 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /LXDAppMonitor/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /LXDAppMonitor/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /LXDAppMonitor/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /LXDAppMonitor/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 | -------------------------------------------------------------------------------- /LXDAppMonitor/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 | 35 | 36 | 37 | 38 | 39 | 40 | 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 | -------------------------------------------------------------------------------- /LXDAppMonitor/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | CFBundleDevelopmentRegion 11 | en 12 | CFBundleExecutable 13 | $(EXECUTABLE_NAME) 14 | CFBundleIdentifier 15 | $(PRODUCT_BUNDLE_IDENTIFIER) 16 | CFBundleInfoDictionaryVersion 17 | 6.0 18 | CFBundleName 19 | $(PRODUCT_NAME) 20 | CFBundlePackageType 21 | APPL 22 | CFBundleShortVersionString 23 | 1.0 24 | CFBundleVersion 25 | 1 26 | LSRequiresIPhoneOS 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Crash/LXDCrashLogger.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDCrashLogger.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 崩溃日志 13 | */ 14 | @interface LXDCrashLogger : NSObject 15 | 16 | @property (nonatomic, readonly) NSString * name; 17 | @property (nonatomic, readonly) NSString * reason; 18 | @property (nonatomic, readonly) NSString * stackInfo; 19 | @property (nonatomic, readonly) NSString * crashTime; 20 | @property (nonatomic, readonly) NSString * topViewController; 21 | @property (nonatomic, readonly) NSString * applicationVersion; 22 | 23 | + (instancetype)crashLoggerWithName: (NSString *)name 24 | reason: (NSString *)reason 25 | stackInfo: (NSString *)stackInfo 26 | crashTime: (NSDate *)crashTime 27 | topViewController: (NSString *)topViewController 28 | applicationVersion: (NSString *)applicationVersion; 29 | 30 | - (NSString *)loggerDescription; 31 | 32 | @end 33 | 34 | 35 | /*! 36 | * @brief 日志服务管理 37 | */ 38 | @interface LXDCrashLoggerServer : NSObject 39 | 40 | + (instancetype)sharedServer; 41 | - (void)insertLogger: (LXDCrashLogger *)logger; 42 | - (void)fetchLastLogger: (void(^)(LXDCrashLogger * logger))fetchHandle; 43 | - (void)fetchLoggers: (void(^)(NSArray * loggers))fetchHandle; 44 | 45 | @end 46 | 47 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Crash/LXDCrashLogger.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDCrashLogger.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDCrashLogger.h" 10 | #import "LXDDispatchAsync.h" 11 | 12 | 13 | #if __has_include() 14 | #import 15 | #else 16 | #import "sqlite3.h" 17 | #endif 18 | 19 | 20 | static const NSUInteger kMaxLoggerNumber = 20; 21 | static NSString * const kLoggerDatabaseFileName = @"crash_logger.sqlite"; 22 | 23 | 24 | @interface LXDCrashLogger () 25 | 26 | @property (nonatomic, copy) NSString * name; 27 | @property (nonatomic, copy) NSString * reason; 28 | @property (nonatomic, copy) NSString * stackInfo; 29 | @property (nonatomic, copy) NSString * crashTime; 30 | @property (nonatomic, copy) NSString * topViewController; 31 | @property (nonatomic, copy) NSString * applicationVersion; 32 | 33 | @end 34 | 35 | 36 | @implementation LXDCrashLogger 37 | 38 | 39 | NSString * __lxd_convert_time(NSDate * date) { 40 | static NSDateFormatter * lxd_date_formatter; 41 | static dispatch_once_t onceToken; 42 | dispatch_once(&onceToken, ^{ 43 | lxd_date_formatter = [NSDateFormatter new]; 44 | lxd_date_formatter.dateFormat = @"yyyy-HH-dd HH:mm:ss"; 45 | }); 46 | return [lxd_date_formatter stringFromDate: date]; 47 | } 48 | 49 | + (instancetype)crashLoggerWithName: (NSString *)name 50 | reason: (NSString *)reason 51 | stackInfo: (NSString *)stackInfo 52 | crashTime: (NSDate *)crashTime 53 | topViewController: (NSString *)topViewController 54 | applicationVersion: (NSString *)applicationVersion { 55 | LXDCrashLogger * crashLogger = [LXDCrashLogger new]; 56 | crashLogger.name = name ?: @""; 57 | crashLogger.reason = reason ?: @""; 58 | crashLogger.stackInfo = stackInfo ?: @""; 59 | crashLogger.topViewController = topViewController ?: @""; 60 | crashLogger.applicationVersion = applicationVersion ?: @""; 61 | crashLogger.crashTime = __lxd_convert_time(crashTime); 62 | return crashLogger; 63 | } 64 | 65 | - (NSString *)loggerDescription { 66 | return [NSString stringWithFormat: @"Error: %@\nReson: %@\n%@\nTop viewcontroller: %@\nCrash time: %@\n\nCall Stack: \n%@", _name, _reason, _applicationVersion, _topViewController, _crashTime, _stackInfo]; 67 | } 68 | 69 | 70 | @end 71 | 72 | 73 | 74 | 75 | @interface LXDCrashLoggerServer () 76 | 77 | @property (nonatomic, unsafe_unretained) sqlite3 * database; 78 | @property (nonatomic, unsafe_unretained) CFMutableDictionaryRef stmtCache; 79 | 80 | @end 81 | 82 | 83 | @implementation LXDCrashLoggerServer 84 | 85 | 86 | #pragma mark - Singleton 87 | + (instancetype)sharedServer { 88 | static LXDCrashLoggerServer * sharedServer; 89 | static dispatch_once_t once; 90 | dispatch_once(&once, ^{ 91 | sharedServer = [[super allocWithZone: NSDefaultMallocZone()] init]; 92 | }); 93 | return sharedServer; 94 | } 95 | 96 | + (instancetype)allocWithZone: (struct _NSZone *)zone { 97 | return [self sharedServer]; 98 | } 99 | 100 | - (instancetype)init { 101 | if (self = [super init]) { 102 | if ([self _dbOpen]) { 103 | [self _dbInitialize]; 104 | CFDictionaryKeyCallBacks keyCallbacks = kCFCopyStringDictionaryKeyCallBacks; 105 | CFDictionaryValueCallBacks valueCallbacks = { 0 }; 106 | self.stmtCache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallbacks, &valueCallbacks); 107 | } 108 | } 109 | return self; 110 | } 111 | 112 | - (id)copy { 113 | return [[self class] sharedServer]; 114 | } 115 | 116 | - (void)dealloc { 117 | if (!_database) { 118 | sqlite3_close(_database); 119 | CFRelease(_stmtCache); 120 | _stmtCache = NULL; 121 | _database = NULL; 122 | } 123 | } 124 | 125 | 126 | #pragma mark - Public 127 | - (void)insertLogger: (LXDCrashLogger *)logger { 128 | [self _insertCrashLogger: logger]; 129 | } 130 | 131 | - (void)fetchLastLogger: (void(^)(LXDCrashLogger * logger))fetchHandle { 132 | [self _syncExecute: ^{ 133 | [self fetchLastLogger: fetchHandle]; 134 | }]; 135 | } 136 | 137 | - (void)fetchLoggers: (void(^)(NSArray *))fetchHandle { 138 | [self _syncExecute: ^{ 139 | [self _fetchCrashLoggers: fetchHandle]; 140 | }]; 141 | } 142 | 143 | 144 | #pragma mark - Private 145 | - (NSString *)_crashLoggerFilePath { 146 | return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent: kLoggerDatabaseFileName]; 147 | } 148 | 149 | - (void)_syncExecute: (dispatch_block_t)block { 150 | assert(block != nil); 151 | if ([NSThread isMainThread]) { 152 | LXDDispatchQueueAsyncBlockInUtility(block); 153 | } else { 154 | block(); 155 | } 156 | } 157 | 158 | - (void)_insertCrashLogger: (LXDCrashLogger *)crashLogger { 159 | NSString * sql = @"insert or replace into crash_logger (name, reason, stack_info, crash_time, top_view_controller, application_version) values (?1, ?2, ?3, ?4, ?5, ?6);"; 160 | sqlite3_stmt * stmt = [self _dbPrepareStmt: sql]; 161 | if (!stmt) { return; } 162 | 163 | sqlite3_bind_text(stmt, 1, crashLogger.name.UTF8String, -1, NULL); 164 | sqlite3_bind_text(stmt, 2, crashLogger.reason.UTF8String, -1, NULL); 165 | sqlite3_bind_text(stmt, 3, crashLogger.stackInfo.UTF8String, -1, NULL); 166 | sqlite3_bind_text(stmt, 4, crashLogger.crashTime.UTF8String, -1, NULL); 167 | sqlite3_bind_text(stmt, 5, crashLogger.topViewController.UTF8String, -1, NULL); 168 | sqlite3_bind_text(stmt, 6, crashLogger.applicationVersion.UTF8String, -1, NULL); 169 | sqlite3_step(stmt); 170 | } 171 | 172 | - (void)_cleanExtraLoggers { 173 | NSString * sql = @"delete from crash_logger order by crash_time desc limit (select count(crash_time) from crash_logger) offset 20"; 174 | sqlite3_stmt * stmt = [self _dbPrepareStmt: sql]; 175 | if (!stmt) { return; } 176 | sqlite3_step(stmt); 177 | } 178 | 179 | - (void)_fetchLastLogger: (void(^)(LXDCrashLogger *))fetchHandle { 180 | assert(fetchHandle != nil); 181 | sqlite3_stmt * stmt = [self _fetchLoggerWithCount: 1]; 182 | if (!stmt) { return; } 183 | 184 | if (sqlite3_step(stmt) == SQLITE_ROW) { 185 | dispatch_async(dispatch_get_main_queue(), ^{ 186 | fetchHandle([self _dbGetLoggerFromStmt: stmt]); 187 | }); 188 | } 189 | } 190 | 191 | - (void)_fetchCrashLoggers: (void(^)(NSArray *))fetchHandle { 192 | assert(fetchHandle != nil); 193 | sqlite3_stmt * stmt = [self _fetchLoggerWithCount: 20]; 194 | if (!stmt) { return; } 195 | 196 | NSMutableArray * loggers = [NSMutableArray array]; 197 | while (sqlite3_step(stmt) == SQLITE_ROW) { 198 | [loggers addObject: [self _dbGetLoggerFromStmt: stmt]]; 199 | } 200 | dispatch_async(dispatch_get_main_queue(), ^{ 201 | fetchHandle(loggers); 202 | }); 203 | if (loggers.count == 20) { 204 | [self _cleanExtraLoggers]; 205 | } 206 | } 207 | 208 | - (sqlite3_stmt *)_fetchLoggerWithCount: (NSUInteger)count { 209 | NSString * sql = @"select * from crash_logger order by crash_time desc limit 0,?1;"; 210 | sqlite3_stmt * stmt = [self _dbPrepareStmt: sql]; 211 | if (!stmt) { return NULL; } 212 | sqlite3_bind_int64(stmt, 1, count); 213 | return stmt; 214 | } 215 | 216 | - (LXDCrashLogger *)_dbGetLoggerFromStmt: (sqlite3_stmt *)stmt { 217 | int idx = 0; 218 | char * name = (char *)sqlite3_column_text(stmt, idx++); 219 | char * reason = (char *)sqlite3_column_text(stmt, idx++); 220 | char * stack_info = (char *)sqlite3_column_text(stmt, idx++); 221 | char * crash_time = (char *)sqlite3_column_text(stmt, idx++); 222 | char * top_view_controller = (char *)sqlite3_column_text(stmt, idx++); 223 | char * application_version = (char *)sqlite3_column_text(stmt, idx++); 224 | 225 | LXDCrashLogger * logger = [LXDCrashLogger new]; 226 | logger.name = [NSString stringWithUTF8String: name]; 227 | logger.reason = [NSString stringWithUTF8String: reason]; 228 | logger.stackInfo = [NSString stringWithUTF8String: stack_info]; 229 | logger.crashTime = [NSString stringWithUTF8String: crash_time]; 230 | logger.applicationVersion = [NSString stringWithUTF8String: application_version]; 231 | logger.topViewController = [NSString stringWithUTF8String: top_view_controller]; 232 | return logger; 233 | } 234 | 235 | 236 | #pragma mark - Sqlite 237 | - (BOOL)_dbOpen { 238 | if (_database) { return YES; } 239 | int result = sqlite3_open([self _crashLoggerFilePath].UTF8String, &_database); 240 | if (result == SQLITE_OK) { 241 | return YES; 242 | } else { 243 | _database = NULL; 244 | return NO; 245 | } 246 | } 247 | 248 | - (BOOL)_dbInitialize { 249 | NSString * sql = @"pragma journal_mode = wal; pragma synchronous = normal; create table if not exists crash_logger (name text, reason text, stack_info text, crash_time text primary key, top_view_controller text, application_version text);"; 250 | return [self _dbExecute: sql]; 251 | } 252 | 253 | - (BOOL)_dbExecute: (NSString *)sql { 254 | if (sql.length == 0) { return NO; } 255 | if (![self _dbCheck]) { return NO; } 256 | 257 | char * error = NULL; 258 | int result = sqlite3_exec(_database, sql.UTF8String, NULL, NULL, &error); 259 | if (error) { 260 | sqlite3_free(error); 261 | } 262 | return (result == SQLITE_OK); 263 | } 264 | 265 | - (BOOL)_dbCheck { 266 | if (!_database) { 267 | return ([self _dbOpen] && [self _dbInitialize]); 268 | } 269 | return YES; 270 | } 271 | 272 | - (sqlite3_stmt *)_dbPrepareStmt: (NSString *)sql { 273 | if (![self _dbCheck] || sql.length == 0 || !_stmtCache) { return NULL; } 274 | sqlite3_stmt * stmt = (sqlite3_stmt *)CFDictionaryGetValue(_stmtCache, (__bridge const void *)sql); 275 | if (!stmt) { 276 | int result = sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL); 277 | if (result != SQLITE_OK) { 278 | return NULL; 279 | } 280 | CFDictionarySetValue(_stmtCache, (__bridge const void *)sql, stmt); 281 | } else { 282 | sqlite3_reset(stmt); 283 | } 284 | return stmt; 285 | } 286 | 287 | 288 | @end 289 | 290 | 291 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Crash/LXDCrashMonitor.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDCrashMonitor.h 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/26. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 崩溃监控 13 | */ 14 | @interface LXDCrashMonitor : UIView 15 | 16 | + (void)startMonitoring; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Crash/LXDCrashMonitor.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDCrashMonitor.m 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/26. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDCrashMonitor.h" 10 | #import "LXDCrashLogger.h" 11 | #import "LXDBacktraceLogger.h" 12 | #import "UIApplication+LXDTool.h" 13 | 14 | 15 | void (*other_exception_caught_handler)(NSException * exception) = NULL; 16 | 17 | 18 | @implementation LXDCrashMonitor 19 | 20 | 21 | static void __lxd_exception_caught(NSException * exception) { 22 | NSDictionary * infoDict = [[NSBundle mainBundle] infoDictionary]; 23 | NSString * appInfo = [NSString stringWithFormat: @"Device: %@\nOS Version: %@\nOS System: %@", [UIDevice currentDevice].model, infoDict[@"CFBundleShortVersionString"], [[UIDevice currentDevice].systemName stringByAppendingString: [UIDevice currentDevice].systemVersion]]; 24 | 25 | LXDCrashLogger * crashLogger = [LXDCrashLogger crashLoggerWithName: exception.name 26 | reason: exception.reason 27 | stackInfo: [LXDBacktraceLogger lxd_backtraceOfCurrentThread] crashTime: [NSDate date] 28 | topViewController: NSStringFromClass([[[UIApplication sharedApplication] findTopViewController] class]) 29 | applicationVersion: appInfo]; 30 | [[LXDCrashLoggerServer sharedServer] insertLogger: crashLogger]; 31 | if (other_exception_caught_handler != NULL) { 32 | (*other_exception_caught_handler)(exception); 33 | } 34 | } 35 | 36 | CF_INLINE NSString * __signal_name(int signal) { 37 | switch (signal) { 38 | /// 非法指令 39 | case SIGILL: 40 | return @"SIGILL"; 41 | /// 计算错误 42 | case SIGFPE: 43 | return @"SIGFPE"; 44 | /// 总线错误 45 | case SIGBUS: 46 | return @"SIGBUS"; 47 | /// 无进程接手数据 48 | case SIGPIPE: 49 | return @"SIGPIPE"; 50 | /// 无效地址 51 | case SIGSEGV: 52 | return @"SIGSEGV"; 53 | /// abort信号 54 | case SIGABRT: 55 | return @"SIGABRT"; 56 | default: 57 | return @"Unknown"; 58 | } 59 | } 60 | 61 | CF_INLINE NSString * __signal_reason(int signal) { 62 | switch (signal) { 63 | /// 非法指令 64 | case SIGILL: 65 | return @"Invalid Command"; 66 | /// 计算错误 67 | case SIGFPE: 68 | return @"Math Type Error"; 69 | /// 总线错误 70 | case SIGBUS: 71 | return @"Bus Error"; 72 | /// 无进程接手数据 73 | case SIGPIPE: 74 | return @"No Data Receiver"; 75 | /// 无效地址 76 | case SIGSEGV: 77 | return @"Invalid Address"; 78 | /// abort信号 79 | case SIGABRT: 80 | return @"Abort Signal"; 81 | default: 82 | return @"Unknown"; 83 | } 84 | } 85 | 86 | static void __lxd_signal_handler(int signal) { 87 | __lxd_exception_caught([NSException exceptionWithName: __signal_name(signal) reason: __signal_reason(signal) userInfo: nil]); 88 | [LXDCrashMonitor _killApp]; 89 | } 90 | 91 | 92 | #pragma mark - Public 93 | + (void)startMonitoring { 94 | other_exception_caught_handler = NSGetUncaughtExceptionHandler(); 95 | NSSetUncaughtExceptionHandler(__lxd_exception_caught); 96 | signal(SIGILL, __lxd_signal_handler); 97 | signal(SIGFPE, __lxd_signal_handler); 98 | signal(SIGBUS, __lxd_signal_handler); 99 | signal(SIGPIPE, __lxd_signal_handler); 100 | signal(SIGSEGV, __lxd_signal_handler); 101 | signal(SIGABRT, __lxd_signal_handler); 102 | } 103 | 104 | 105 | #pragma mark - Private 106 | + (void)_killApp { 107 | NSSetUncaughtExceptionHandler(NULL); 108 | signal(SIGILL, SIG_DFL); 109 | signal(SIGFPE, SIG_DFL); 110 | signal(SIGBUS, SIG_DFL); 111 | signal(SIGPIPE, SIG_DFL); 112 | signal(SIGSEGV, SIG_DFL); 113 | signal(SIGABRT, SIG_DFL); 114 | kill(getpid(), SIGKILL); 115 | } 116 | 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/FPS/LXDFPSDisplayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDFPSDisplayer.h 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/25. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief FPS显示器 13 | */ 14 | @interface LXDFPSDisplayer : UIView 15 | 16 | - (void)updateFPS: (int)fps; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/FPS/LXDFPSDisplayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDFPSDisplayer.m 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/25. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDFPSDisplayer.h" 10 | #import "LXDMonitorUI.h" 11 | #import "LXDDispatchAsync.h" 12 | 13 | 14 | #define LXD_FPS_DISPLAYER_SIZE CGSizeMake(54, 20) 15 | 16 | 17 | @interface LXDFPSDisplayer () 18 | 19 | @property (nonatomic, strong) LXDAsyncLabel * fpsDisplayer; 20 | 21 | @end 22 | 23 | 24 | @implementation LXDFPSDisplayer 25 | 26 | 27 | - (instancetype)init { 28 | if (self = [super initWithFrame: CGRectMake((CGRectGetWidth([UIScreen mainScreen].bounds) - LXD_FPS_DISPLAYER_SIZE.width) / 2, 30, LXD_FPS_DISPLAYER_SIZE.width, LXD_FPS_DISPLAYER_SIZE.height)]) { 29 | CAShapeLayer * bgLayer = [CAShapeLayer layer]; 30 | bgLayer.fillColor = [UIColor colorWithWhite: 0 alpha: 0.7].CGColor; 31 | bgLayer.path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 0, LXD_FPS_DISPLAYER_SIZE.width, LXD_FPS_DISPLAYER_SIZE.height) cornerRadius: 5].CGPath; 32 | [self.layer addSublayer: bgLayer]; 33 | 34 | self.fpsDisplayer = [[LXDAsyncLabel alloc] initWithFrame: self.bounds]; 35 | self.fpsDisplayer.textColor = [UIColor whiteColor]; 36 | self.fpsDisplayer.textAlignment = NSTextAlignmentCenter; 37 | self.fpsDisplayer.font = [UIFont fontWithName: @"Menlo" size: 14]; 38 | [self updateFPS: 60]; 39 | [self addSubview: self.fpsDisplayer]; 40 | } 41 | return self; 42 | } 43 | 44 | - (void)updateFPS: (int)fps { 45 | LXDDispatchQueueAsyncBlockInDefault(^{ 46 | NSMutableAttributedString * attributed = [[NSMutableAttributedString alloc] initWithString: [NSString stringWithFormat: @"%d", fps] attributes: @{ NSForegroundColorAttributeName: [UIColor colorWithHue: 0.27 * (fps / 60.0 - 0.2) saturation: 1 brightness: 0.9 alpha: 1], NSFontAttributeName: _fpsDisplayer.font }]; 47 | [attributed appendAttributedString: [[NSAttributedString alloc] initWithString: @"FPS" attributes: @{ NSFontAttributeName: _fpsDisplayer.font, NSForegroundColorAttributeName: [UIColor whiteColor] }]]; 48 | self.fpsDisplayer.attributedText = attributed; 49 | }); 50 | } 51 | 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/FPS/LXDFPSMonitor.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDFPSMonitor.h 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/24. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | #define FPS_MONITOR [LXDFPSMonitor sharedMonitor] 13 | /*! 14 | * @brief 监听FPS 15 | */ 16 | @interface LXDFPSMonitor : NSObject 17 | 18 | + (instancetype)sharedMonitor; 19 | 20 | - (void)startMonitoring; 21 | - (void)stopMonitoring; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/FPS/LXDFPSMonitor.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDFPSMonitor.m 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/24. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDFPSMonitor.h" 10 | #import "LXDMonitorUI.h" 11 | #import "LXDWeakProxy.h" 12 | #import "LXDFPSDisplayer.h" 13 | #import "LXDBacktraceLogger.h" 14 | 15 | 16 | @interface LXDFPSMonitor () 17 | 18 | @property (nonatomic, assign) NSUInteger count; 19 | @property (nonatomic, assign) BOOL isMonitoring; 20 | @property (nonatomic, assign) NSTimeInterval lastTime; 21 | @property (nonatomic, assign) LXDFPSDisplayer * displayer; 22 | @property (nonatomic, strong) CADisplayLink * displayLink; 23 | 24 | @end 25 | 26 | 27 | 28 | @implementation LXDFPSMonitor 29 | 30 | 31 | #pragma mark - Singleton override 32 | + (instancetype)sharedMonitor { 33 | static LXDFPSMonitor * sharedMonitor; 34 | static dispatch_once_t once; 35 | dispatch_once(&once, ^{ 36 | sharedMonitor = [[super allocWithZone: NSDefaultMallocZone()] init]; 37 | }); 38 | return sharedMonitor; 39 | } 40 | 41 | + (instancetype)allocWithZone: (struct _NSZone *)zone { 42 | return [self sharedMonitor]; 43 | } 44 | 45 | - (void)dealloc { 46 | [self stopMonitoring]; 47 | } 48 | 49 | 50 | #pragma mark - Public 51 | - (void)startMonitoring { 52 | if (_isMonitoring) { return; } 53 | _isMonitoring = YES; 54 | [self.displayer removeFromSuperview]; 55 | LXDFPSDisplayer * displayer = [[LXDFPSDisplayer alloc] init]; 56 | self.displayer = displayer; 57 | [[LXDTopWindow topWindow] addSubview: self.displayer]; 58 | 59 | self.displayLink = [CADisplayLink displayLinkWithTarget: [LXDWeakProxy proxyWithConsignor: self] selector: @selector(monitor:)]; 60 | [self.displayLink addToRunLoop: [NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes]; 61 | self.lastTime = self.displayLink.timestamp; 62 | if ([self.displayLink respondsToSelector: @selector(setPreferredFramesPerSecond:)]) { 63 | self.displayLink.preferredFramesPerSecond = 60; 64 | } else { 65 | self.displayLink.frameInterval = 1; 66 | } 67 | } 68 | 69 | - (void)stopMonitoring { 70 | if (!_isMonitoring) { return; } 71 | _isMonitoring = NO; 72 | [self.displayer removeFromSuperview]; 73 | [self.displayLink invalidate]; 74 | self.displayLink = nil; 75 | self.displayer = nil; 76 | } 77 | 78 | 79 | #pragma mark - DisplayLink 80 | - (void)monitor: (CADisplayLink *)link { 81 | _count++; 82 | NSTimeInterval delta = link.timestamp - _lastTime; 83 | if (delta < 1) { return; } 84 | _lastTime = link.timestamp; 85 | 86 | double fps = _count / delta; 87 | _count = 0; 88 | [self.displayer updateFPS: (int)round(fps)]; 89 | } 90 | 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/FPS/LXDWeakProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDWeakProxy.h 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/24. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 弱引用代理对象 13 | */ 14 | @interface LXDWeakProxy : NSObject 15 | 16 | + (instancetype)proxyWithConsignor: (id)consignor; 17 | - (instancetype)initWithConsignor: (id)consignor; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/FPS/LXDWeakProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDWeakProxy.m 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/24. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDWeakProxy.h" 10 | 11 | 12 | @interface LXDWeakProxy () 13 | 14 | @property (nonatomic, weak) id consignor; 15 | 16 | @end 17 | 18 | 19 | @implementation LXDWeakProxy 20 | 21 | 22 | #pragma mark - Public 23 | + (instancetype)proxyWithConsignor: (id)consignor { 24 | return [[self alloc] initWithConsignor: consignor]; 25 | } 26 | 27 | - (instancetype)initWithConsignor: (id)consignor { 28 | if (self = [super init]) { 29 | self.consignor = consignor; 30 | } 31 | return self; 32 | } 33 | 34 | 35 | #pragma mark - method transmit 36 | - (id)forwardingTargetForSelector: (SEL)aSelector { 37 | return _consignor; 38 | } 39 | 40 | - (void)forwardInvocation: (NSInvocation *)anInvocation { 41 | void * null = NULL; 42 | [anInvocation setReturnValue: &null]; 43 | } 44 | 45 | - (NSMethodSignature *)methodSignatureForSelector: (SEL)aSelector { 46 | return [_consignor methodSignatureForSelector: aSelector]; 47 | } 48 | 49 | 50 | #pragma mark - judge 51 | - (BOOL)isProxy { 52 | return YES; 53 | } 54 | 55 | - (Class)class { 56 | return [_consignor class]; 57 | } 58 | 59 | - (Class)superclass { 60 | return [_consignor superclass]; 61 | } 62 | 63 | - (NSUInteger)hash { 64 | return [_consignor hash]; 65 | } 66 | 67 | - (NSString *)description { 68 | return [_consignor description]; 69 | } 70 | 71 | - (NSString *)debugDescription { 72 | return [_consignor debugDescription]; 73 | } 74 | 75 | - (BOOL)isEqual: (id)object { 76 | return [_consignor isEqual: object]; 77 | } 78 | 79 | - (BOOL)isKindOfClass: (Class)aClass { 80 | return [_consignor isKindOfClass: aClass]; 81 | } 82 | 83 | - (BOOL)isMemberOfClass: (Class)aClass { 84 | return [_consignor isMemberOfClass: aClass]; 85 | } 86 | 87 | - (BOOL)respondsToSelector: (SEL)aSelector { 88 | return [_consignor respondsToSelector: aSelector]; 89 | } 90 | 91 | - (BOOL)conformsToProtocol: (Protocol *)aProtocol { 92 | return [_consignor conformsToProtocol: aProtocol]; 93 | } 94 | 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Fluency/LXDAppFluencyMonitor.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDAppFluencyMonitor.h 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/22. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | #define FLUENCYMONITOR [LXDAppFluencyMonitor monitor] 13 | 14 | 15 | /*! 16 | * @brief 监听UI线程卡顿 17 | */ 18 | @interface LXDAppFluencyMonitor : NSObject 19 | 20 | + (instancetype)monitor; 21 | 22 | - (void)startMonitoring; 23 | - (void)stopMonitoring; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Fluency/LXDAppFluencyMonitor.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDAppFluencyMonitor.m 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/22. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDAppFluencyMonitor.h" 10 | #import "LXDBacktraceLogger.h" 11 | #import 12 | 13 | 14 | #define LXD_SEMAPHORE_SUCCESS 0 15 | static BOOL lxd_is_monitoring = NO; 16 | static dispatch_semaphore_t lxd_semaphore; 17 | static NSTimeInterval lxd_time_out_interval = 0.5; 18 | 19 | 20 | @implementation LXDAppFluencyMonitor 21 | 22 | static inline dispatch_queue_t __lxd_fluecy_monitor_queue() { 23 | static dispatch_queue_t lxd_fluecy_monitor_queue; 24 | static dispatch_once_t once; 25 | dispatch_once(&once, ^{ 26 | lxd_fluecy_monitor_queue = dispatch_queue_create("com.sindrilin.lxd_monitor_queue", NULL); 27 | }); 28 | return lxd_fluecy_monitor_queue; 29 | } 30 | 31 | static inline void __lxd_monitor_init() { 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | lxd_semaphore = dispatch_semaphore_create(0); 35 | }); 36 | } 37 | 38 | #pragma mark - Public 39 | + (instancetype)monitor { 40 | return [LXDAppFluencyMonitor new]; 41 | } 42 | 43 | - (void)startMonitoring { 44 | if (lxd_is_monitoring) { return; } 45 | lxd_is_monitoring = YES; 46 | __lxd_monitor_init(); 47 | dispatch_async(__lxd_fluecy_monitor_queue(), ^{ 48 | while (lxd_is_monitoring) { 49 | __block BOOL timeOut = YES; 50 | dispatch_async(dispatch_get_main_queue(), ^{ 51 | timeOut = NO; 52 | dispatch_semaphore_signal(lxd_semaphore); 53 | }); 54 | [NSThread sleepForTimeInterval: lxd_time_out_interval]; 55 | if (timeOut) { 56 | [LXDBacktraceLogger lxd_logMain]; 57 | } 58 | dispatch_wait(lxd_semaphore, DISPATCH_TIME_FOREVER); 59 | } 60 | }); 61 | } 62 | 63 | - (void)stopMonitoring { 64 | if (!lxd_is_monitoring) { return; } 65 | lxd_is_monitoring = NO; 66 | } 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/LXDDNSInterceptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDDNSInterceptor.h 3 | // LXDAppFluecyMonitor 4 | // 5 | // Created by linxinda on 2017/3/28. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^LXDInvalidIpHandle)(NSURL * originUrl); 12 | 13 | #define REGISTER_INTERCEPTOR [LXDDNSInterceptor registerInterceptor]; 14 | #define UNREGISTER_INTERCEPTOR [LXDDNSInterceptor unregisterInterceptor]; 15 | /*! 16 | * @brief DNS拦截器 17 | */ 18 | @interface LXDDNSInterceptor : NSURLProtocol 19 | 20 | + (void)registerInterceptor; 21 | + (void)unregisterInterceptor; 22 | + (void)registerInvalidIpHandle: (LXDInvalidIpHandle)invalidIpHandle; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/LXDDNSInterceptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDDNSInterceptor.m 3 | // LXDAppFluecyMonitor 4 | // 5 | // Created by linxinda on 2017/3/28. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDDNSInterceptor.h" 10 | #import "LXDHostMapper.h" 11 | #import "LXDHostFilterRule.h" 12 | #import "NSURLProtocol+WebKitSupport.h" 13 | #import 14 | 15 | 16 | #define INVALID_STATUS_CODE 404 17 | 18 | 19 | static LXDInvalidIpHandle lxd_invalid_ip_handle; 20 | static void(^lxd_url_session_configure)(NSURLSessionConfiguration * configuration); 21 | static NSString * const LXDURLHasHandledKey = @"LXDURLHasHandledKey"; 22 | 23 | 24 | 25 | @interface NSURLSession (LXDIntercept) 26 | 27 | - (instancetype)initWithConfiguration: (NSURLSessionConfiguration *)configuration delegate: (id)delegate delegateQueue: (NSOperationQueue *)queue; 28 | 29 | @end 30 | 31 | 32 | 33 | @implementation NSURLSession (LXDIntercept) 34 | 35 | + (void)load { 36 | Method origin = class_getClassMethod([NSURLSession class], @selector(initWithConfiguration:delegate:delegateQueue:)); 37 | Method custom = class_getClassMethod([NSURLSession class], @selector(lxd_initWithConfiguration:delegate:delegateQueue:)); 38 | method_exchangeImplementations(origin, custom); 39 | } 40 | 41 | - (NSURLSession *)lxd_initWithConfiguration: (NSURLSessionConfiguration *)configuration delegate: (id)delegate delegateQueue: (NSOperationQueue *)queue { 42 | if (lxd_url_session_configure) { 43 | lxd_url_session_configure(configuration); 44 | } 45 | return [self lxd_initWithConfiguration: configuration delegate: delegate delegateQueue: queue]; 46 | } 47 | 48 | @end 49 | 50 | 51 | 52 | @interface LXDDNSInterceptor () 53 | 54 | @property (nonatomic, copy) NSURL * originUrl; 55 | @property (nonatomic, copy) LXDInvalidIpHandle invalidIpHandle; 56 | 57 | @property (nonatomic, strong) NSURLConnection * connection; 58 | 59 | @end 60 | 61 | 62 | 63 | @implementation LXDDNSInterceptor 64 | 65 | 66 | #pragma mark - Public 67 | + (void)foreachURLSchemesWithHandle: (void(^)(NSString * scheme))handle { 68 | NSParameterAssert(handle); 69 | for (NSString * scheme in @[@"http", @"https"]) { 70 | handle(scheme); 71 | } 72 | } 73 | 74 | + (void)registerInterceptor { 75 | [NSURLProtocol registerClass: [LXDDNSInterceptor class]]; 76 | [self foreachURLSchemesWithHandle: ^(NSString *scheme) { 77 | [NSURLProtocol lxd_registerScheme: scheme]; 78 | }]; 79 | lxd_url_session_configure = ^(NSURLSessionConfiguration * configuration){ 80 | if (![configuration.protocolClasses containsObject: [LXDDNSInterceptor class]]) { 81 | NSMutableArray * protocolClasses = [NSMutableArray arrayWithArray: configuration.protocolClasses]; 82 | [protocolClasses addObject: [LXDDNSInterceptor class]]; 83 | configuration.protocolClasses = protocolClasses; 84 | } 85 | }; 86 | } 87 | 88 | + (void)unregisterInterceptor { 89 | lxd_invalid_ip_handle = nil; 90 | [NSURLProtocol unregisterClass: [LXDDNSInterceptor class]]; 91 | [self foreachURLSchemesWithHandle: ^(NSString *scheme) { 92 | [NSURLProtocol lxd_unregisterScheme: scheme]; 93 | }]; 94 | lxd_url_session_configure = nil; 95 | } 96 | 97 | + (void)registerInvalidIpHandle: (LXDInvalidIpHandle)invalidIpHandle { 98 | lxd_invalid_ip_handle = invalidIpHandle; 99 | } 100 | 101 | 102 | #pragma mark - Override 103 | + (BOOL)canInitWithTask: (NSURLSessionTask *)task { 104 | return ([NSURLProtocol propertyForKey: LXDURLHasHandledKey inRequest: task.currentRequest] == nil); 105 | } 106 | 107 | + (BOOL)canInitWithRequest: (NSURLRequest *)request { 108 | return ([NSURLProtocol propertyForKey: LXDURLHasHandledKey inRequest: request] == nil); 109 | } 110 | 111 | + (NSURLRequest *)canonicalRequestForRequest: (NSURLRequest *)request { 112 | NSString * host = request.URL.host; 113 | NSString * ip = [LXDHostMapper parseHost: host]; 114 | if (ip == nil) { return request; } 115 | if ([LXDHostFilterRule isIpInvalid: ip]) { return request; } 116 | 117 | NSString * absoluteURLString = request.URL.absoluteString; 118 | NSRange range = [absoluteURLString rangeOfString: host]; 119 | if (range.location == NSNotFound) { return request; } 120 | 121 | absoluteURLString = [absoluteURLString stringByReplacingCharactersInRange: range withString: ip]; 122 | NSMutableURLRequest * canonicalRequest = request.mutableCopy; 123 | canonicalRequest.URL = [NSURL URLWithString: absoluteURLString]; 124 | return canonicalRequest; 125 | } 126 | 127 | - (void)startLoading { 128 | NSMutableURLRequest * request = self.request.mutableCopy; 129 | [NSURLProtocol setProperty: @YES forKey: LXDURLHasHandledKey inRequest: request]; 130 | self.connection = [NSURLConnection connectionWithRequest: request delegate: self]; 131 | } 132 | 133 | - (void)stopLoading { 134 | [self.connection cancel]; 135 | [NSURLProtocol removePropertyForKey: LXDURLHasHandledKey inRequest: self.connection.currentRequest.mutableCopy]; 136 | } 137 | 138 | 139 | #pragma mark - NSURLConnectionDelegate 140 | - (void)connection: (NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response { 141 | if ([response isKindOfClass: [NSHTTPURLResponse class]]) { 142 | NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response; 143 | if (httpResponse.statusCode == INVALID_STATUS_CODE && lxd_invalid_ip_handle) { 144 | NSString * host = response.URL.host; 145 | if ([LXDHostMapper validIp: host]) { 146 | [connection cancel]; 147 | [LXDHostFilterRule registerInvailIp: host]; 148 | 149 | NSString * absoluteURLString = response.URL.absoluteString; 150 | NSRange range = [absoluteURLString rangeOfString: host]; 151 | if (range.location != NSNotFound) { 152 | absoluteURLString = [absoluteURLString stringByReplacingCharactersInRange: range withString: [LXDHostFilterRule getHostFromIpAddress: host]]; 153 | lxd_invalid_ip_handle([NSURL URLWithString: absoluteURLString]); 154 | } 155 | } 156 | } 157 | } 158 | [self.client URLProtocol: self didReceiveResponse: response cacheStoragePolicy: NSURLCacheStorageAllowedInMemoryOnly]; 159 | } 160 | 161 | - (void)connection: (NSURLConnection *)connection didReceiveData: (NSData *)data { 162 | [self.client URLProtocol: self didLoadData: data]; 163 | } 164 | 165 | - (void)connectionDidFinishLoading: (NSURLConnection *)connection { 166 | [self.client URLProtocolDidFinishLoading: self]; 167 | } 168 | 169 | - (void)connection: (NSURLConnection *)connection didFailWithError: (NSError *)error { 170 | [self.client URLProtocol: self didFailWithError: error]; 171 | } 172 | 173 | 174 | @end 175 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/LXDHostFilterRule.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDFilterRule.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 域名过滤规则 13 | */ 14 | @interface LXDHostFilterRule : NSObject 15 | 16 | + (void)registerInvailIp: (NSString *)ip; 17 | + (void)mapHost: (NSString *)host toIp: (NSString *)ip; 18 | 19 | + (BOOL)isIpInvalid: (NSString *)ip; 20 | + (NSString *)getIpAddressFromHost: (NSString *)host; 21 | + (NSString *)getHostFromIpAddress: (NSString *)ipAddress; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/LXDHostFilterRule.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDFilterRule.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDHostFilterRule.h" 10 | 11 | static inline NSMutableSet * lxd_invalid_ips() { 12 | static NSMutableSet * lxd_invalid_ips; 13 | static dispatch_once_t once; 14 | dispatch_once(&once, ^{ 15 | lxd_invalid_ips = [NSMutableSet set]; 16 | }); 17 | return lxd_invalid_ips; 18 | } 19 | 20 | static inline NSMutableDictionary * lxd_ip_mapper() { 21 | static NSMutableDictionary * lxd_ip_mapper; 22 | static dispatch_once_t once; 23 | dispatch_once(&once, ^{ 24 | lxd_ip_mapper = @{}.mutableCopy; 25 | }); 26 | return lxd_ip_mapper; 27 | } 28 | 29 | static inline NSMutableDictionary * lxd_host_mapper() { 30 | static NSMutableDictionary * lxd_host_mapper; 31 | static dispatch_once_t once; 32 | dispatch_once(&once, ^{ 33 | lxd_host_mapper = @{}.mutableCopy; 34 | }); 35 | return lxd_host_mapper; 36 | } 37 | 38 | 39 | @implementation LXDHostFilterRule 40 | 41 | 42 | + (void)registerInvailIp: (NSString *)ip { 43 | [lxd_invalid_ips() addObject: ip]; 44 | } 45 | 46 | + (void)mapHost: (NSString *)host toIp: (NSString *)ip { 47 | lxd_ip_mapper()[ip] = host; 48 | lxd_host_mapper()[host] = ip; 49 | } 50 | 51 | + (BOOL)isIpInvalid: (NSString *)ip { 52 | return [lxd_invalid_ips() containsObject: ip]; 53 | } 54 | 55 | + (NSString *)getIpAddressFromHost: (NSString *)host { 56 | return lxd_host_mapper()[host]; 57 | } 58 | 59 | + (NSString *)getHostFromIpAddress: (NSString *)ipAddress { 60 | return lxd_ip_mapper()[ipAddress]; 61 | } 62 | 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/LXDHostMapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDHostMapper.h 3 | // LXDAppFluecyMonitor 4 | // 5 | // Created by linxinda on 2017/3/28. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | typedef NSString *(^LXDHostMap)(NSString * host); 13 | 14 | 15 | /*! 16 | * @brief 域名解析 17 | */ 18 | @interface LXDHostMapper : NSObject 19 | 20 | + (BOOL)validIp: (NSString *)ip; 21 | + (void)setHostMap: (LXDHostMap)hostMap; 22 | + (void)parseHost: (NSString *)host complete: (void(^)(NSString * ip))complete; 23 | 24 | + (NSString *)parseHost: (NSString *)host; 25 | + (NSString *)getIpv4AddressFromHost: (NSString *)host; 26 | + (NSString *)getIpv6AddressFromHost: (NSString *)host; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/LXDHostMapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDHostMapper.m 3 | // LXDAppFluecyMonitor 4 | // 5 | // Created by linxinda on 2017/3/28. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDHostMapper.h" 10 | #import "LXDHostFilterRule.h" 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | 17 | static LXDHostMap lxd_host_map; 18 | 19 | 20 | @implementation LXDHostMapper 21 | 22 | 23 | + (BOOL)validIp: (NSString *)ip { 24 | return [self validIpv4: ip]; 25 | } 26 | 27 | + (BOOL)validIpv4: (NSString *)ip { 28 | NSString * ipRegExp = @"^(([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(0\\.0\\.0\\.0)$"; 29 | NSPredicate * predicate = [NSPredicate predicateWithFormat: @"SELF matches %@", ipRegExp]; 30 | return [predicate evaluateWithObject: ip]; 31 | } 32 | 33 | + (BOOL)validIpv6: (NSString *)ip { 34 | NSString * ipRegExp = @"^(^((\\p{XDigit}{1,4}):){7}(\\p{XDigit}{1,4})$)|(^(::((\\p{XDigit}//{1,4}):){0,5}(\\p{XDigit}{1,4}))$)|(^((\\p{XDigit}{1,4})(:|::)){0,6}(\\p//{XDigit}{1,4})$)$"; 35 | NSPredicate * predicate = [NSPredicate predicateWithFormat: @"SELF matches %@", ipRegExp]; 36 | return [predicate evaluateWithObject: ip]; 37 | } 38 | 39 | + (BOOL)validHost: (NSString *)host { 40 | NSString * hostRegExp = @"((http[s]?|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)"; 41 | NSPredicate * predicate = [NSPredicate predicateWithFormat: @"SELF matches %@", hostRegExp]; 42 | return [predicate evaluateWithObject: host]; 43 | } 44 | 45 | + (void)setHostMap: (LXDHostMap)hostMap { 46 | lxd_host_map = [hostMap copy]; 47 | } 48 | 49 | + (void)parseHost: (NSString *)host complete: (void(^)(NSString * ip))complete { 50 | NSParameterAssert(complete); 51 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 52 | complete([self parseHost: host]); 53 | }); 54 | } 55 | 56 | + (NSString *)parseHost: (NSString *)host { 57 | if ([self validIp: host]) { return host; } 58 | if (![self validHost: host]) { return nil; } 59 | NSString * ipAddress = [LXDHostFilterRule getIpAddressFromHost: host]; 60 | if (ipAddress != nil) { return ipAddress; } 61 | 62 | if (lxd_host_map != nil) { 63 | NSString * ipAddress = lxd_host_map(host); 64 | if (ipAddress == nil) { 65 | ipAddress = [self getIpAddressFromHostName: host]; 66 | [LXDHostFilterRule mapHost: host toIp: ipAddress]; 67 | return ipAddress; 68 | } 69 | } else { 70 | ipAddress = [self getIpAddressFromHostName: host]; 71 | } 72 | [LXDHostFilterRule mapHost: host toIp: ipAddress]; 73 | return ipAddress; 74 | } 75 | 76 | + (NSString *)getIpAddressFromHostName: (NSString *)host { 77 | NSString * ipAddress = [self getIpv6AddressFromHost: host]; 78 | if (ipAddress == nil) { 79 | ipAddress = [self getIpv4AddressFromHost: host]; 80 | } 81 | return ipAddress; 82 | } 83 | 84 | + (NSString *)getIpv4AddressFromHost: (NSString *)host { 85 | const char * hostName = host.UTF8String; 86 | __block struct hostent * phost = [self getHostByName: hostName execute: ^{ 87 | phost = gethostbyname(hostName); 88 | }]; 89 | if ( phost == NULL ) { return nil; } 90 | 91 | struct in_addr ip_addr; 92 | memcpy(&ip_addr, phost->h_addr_list[0], 4); 93 | 94 | char ip[20] = { 0 }; 95 | inet_ntop(AF_INET, &ip_addr, ip, sizeof(ip)); 96 | return [NSString stringWithUTF8String: ip]; 97 | } 98 | 99 | + (NSString *)getIpv6AddressFromHost: (NSString *)host { 100 | const char * hostName = host.UTF8String; 101 | __block struct hostent * phost = [self getHostByName: hostName execute: ^{ 102 | phost = gethostbyname2(hostName, AF_INET6); 103 | }]; 104 | if ( phost == NULL ) { return nil; } 105 | 106 | char ip[32] = { 0 }; 107 | char ** aliases; 108 | switch (phost->h_addrtype) { 109 | case AF_INET: 110 | case AF_INET6: { 111 | for (aliases = phost->h_addr_list; *aliases != NULL; aliases++) { 112 | NSString * ipAddress = [NSString stringWithUTF8String: inet_ntop(phost->h_addrtype, *aliases, ip, sizeof(ip))]; 113 | if (ipAddress) { return ipAddress; } 114 | } 115 | } break; 116 | 117 | default: 118 | break; 119 | } 120 | return nil; 121 | } 122 | 123 | 124 | + (struct hostent *)getHostByName: (const char *)hostName execute: (dispatch_block_t)execute { 125 | if (execute == nil) { return NULL; } 126 | __block struct hostent * phost = NULL; 127 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 128 | NSOperationQueue * queue = [NSOperationQueue new]; 129 | queue.maxConcurrentOperationCount = 1; 130 | [queue addOperationWithBlock: ^{ 131 | execute(); 132 | dispatch_semaphore_signal(semaphore); 133 | }]; 134 | dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC)); 135 | [queue cancelAllOperations]; 136 | return phost; 137 | } 138 | 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/NSURLProtocol+WebKitSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLProtocol+WebKitSupport.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/30. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief WK支持 13 | */ 14 | @interface NSURLProtocol (WebKitSupport) 15 | 16 | + (void)lxd_registerScheme: (NSString *)scheme; 17 | + (void)lxd_unregisterScheme: (NSString *)scheme; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Intercept/NSURLProtocol+WebKitSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLProtocol+WebKitSupport.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/30. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "NSURLProtocol+WebKitSupport.h" 10 | #import 11 | 12 | static inline NSString * lxd_scheme_selector_suffix() { 13 | return @"SchemeForCustomProtocol:"; 14 | } 15 | 16 | static inline id lxd_context_controller() { 17 | static Class cls; 18 | if (!cls) { 19 | cls = [[[NSClassFromString(@"WKWebView") new] valueForKey:@"browsingContextController"] class]; 20 | } 21 | return cls; 22 | } 23 | 24 | static inline SEL lxd_register_scheme_selector() { 25 | const NSString * const registerPrefix = @"register"; 26 | return NSSelectorFromString([registerPrefix stringByAppendingString: lxd_scheme_selector_suffix()]); 27 | } 28 | 29 | static inline SEL lxd_unregister_scheme_selector() { 30 | const NSString * const unregisterPrefix = @"unregister"; 31 | return NSSelectorFromString([unregisterPrefix stringByAppendingString: lxd_scheme_selector_suffix()]); 32 | } 33 | 34 | 35 | @implementation NSURLProtocol (WebKitSupport) 36 | 37 | 38 | #pragma clang diagnostic push 39 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 40 | + (void)lxd_registerScheme: (NSString *)scheme { 41 | if ([lxd_context_controller() respondsToSelector: lxd_register_scheme_selector()]) { 42 | [lxd_context_controller() performSelector: lxd_register_scheme_selector() withObject: scheme]; 43 | } 44 | } 45 | 46 | + (void)lxd_unregisterScheme: (NSString *)scheme { 47 | if ([lxd_context_controller() respondsToSelector: lxd_unregister_scheme_selector()]) { 48 | [lxd_context_controller() performSelector: lxd_unregister_scheme_selector() withObject: scheme]; 49 | } 50 | } 51 | #pragma clang diagnostic pop 52 | 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDApplicationCPU.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDApplicationCPU.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/25. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 应用CPU占用 13 | */ 14 | @interface LXDApplicationCPU : NSObject 15 | 16 | - (double)currentUsage; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDApplicationCPU.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDApplicationCPU.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/25. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDApplicationCPU.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | 17 | @implementation LXDApplicationCPU 18 | 19 | 20 | - (double)currentUsage { 21 | double usageRatio = 0; 22 | thread_info_data_t thinfo; 23 | thread_act_array_t threads; 24 | thread_basic_info_t basic_info_t; 25 | mach_msg_type_number_t count = 0; 26 | mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX; 27 | 28 | if (task_threads(mach_task_self(), &threads, &count) == KERN_SUCCESS) { 29 | for (int idx = 0; idx < count; idx++) { 30 | if (thread_info(threads[idx], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count) == KERN_SUCCESS) { 31 | basic_info_t = (thread_basic_info_t)thinfo; 32 | if (!(basic_info_t->flags & TH_FLAGS_IDLE)) { 33 | usageRatio += basic_info_t->cpu_usage / (double)TH_USAGE_SCALE; 34 | } 35 | } 36 | } 37 | assert(vm_deallocate(mach_task_self(), (vm_address_t)threads, count * sizeof(thread_t)) == KERN_SUCCESS); 38 | } 39 | return usageRatio * 100.; 40 | } 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDApplicationMemory.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDApplicationMemory.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/26. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef struct LXDApplicationMemoryUsage 12 | { 13 | double usage; ///< 已用内存(MB) 14 | double total; ///< 总内存(MB) 15 | double ratio; ///< 占用比率 16 | } LXDApplicationMemoryUsage; 17 | 18 | /*! 19 | * @brief 应用内存占用 20 | */ 21 | @interface LXDApplicationMemory : NSObject 22 | 23 | - (LXDApplicationMemoryUsage)currentUsage; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDApplicationMemory.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDApplicationMemory.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/26. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDApplicationMemory.h" 10 | #import 11 | #import 12 | 13 | 14 | #ifndef NBYTE_PER_MB 15 | #define NBYTE_PER_MB (1024 * 1024) 16 | #endif 17 | 18 | 19 | @implementation LXDApplicationMemory 20 | 21 | 22 | - (LXDApplicationMemoryUsage)currentUsage { 23 | struct mach_task_basic_info info; 24 | mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); 25 | if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS) { 26 | return (LXDApplicationMemoryUsage){ 27 | .usage = info.resident_size / NBYTE_PER_MB, 28 | .total = [NSProcessInfo processInfo].physicalMemory / NBYTE_PER_MB, 29 | .ratio = info.virtual_size / [NSProcessInfo processInfo].physicalMemory, 30 | }; 31 | } 32 | return (LXDApplicationMemoryUsage){ 0 }; 33 | } 34 | 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDCPUDisplayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDCPUDisplayer.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief CPU占用展示器 13 | */ 14 | @interface LXDCPUDisplayer : UIView 15 | 16 | - (void)displayCPUUsage: (double)usage; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDCPUDisplayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDCPUDisplayer.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDCPUDisplayer.h" 10 | #import "LXDMonitorUI.h" 11 | #import "LXDDispatchAsync.h" 12 | 13 | 14 | @interface LXDCPUDisplayer () 15 | 16 | @property (nonatomic, strong) LXDAsyncLabel * displayerLabel; 17 | 18 | @end 19 | 20 | 21 | @implementation LXDCPUDisplayer 22 | 23 | 24 | - (instancetype)initWithFrame: (CGRect)frame { 25 | if (self = [super initWithFrame: frame]) { 26 | CAShapeLayer * bgLayer = [CAShapeLayer layer]; 27 | bgLayer.fillColor = [UIColor colorWithWhite: 0 alpha: 0.7].CGColor; 28 | bgLayer.path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame)) cornerRadius: 5].CGPath; 29 | [self.layer addSublayer: bgLayer]; 30 | 31 | self.displayerLabel = [[LXDAsyncLabel alloc] initWithFrame: self.bounds]; 32 | self.displayerLabel.textColor = [UIColor whiteColor]; 33 | self.displayerLabel.textAlignment = NSTextAlignmentCenter; 34 | self.displayerLabel.font = [UIFont fontWithName: @"Menlo" size: 14]; 35 | [self addSubview: self.displayerLabel]; 36 | } 37 | return self; 38 | } 39 | 40 | - (void)displayCPUUsage: (double)usage { 41 | int use = usage; 42 | LXDDispatchQueueAsyncBlockInDefault(^{ 43 | NSMutableAttributedString * attributed = [[NSMutableAttributedString alloc] initWithString: @"CPU" attributes: @{ NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: _displayerLabel.font }]; 44 | [attributed appendAttributedString: [[NSAttributedString alloc] initWithString: [NSString stringWithFormat: @"%d%%", use] attributes: @{ NSFontAttributeName: _displayerLabel.font, NSForegroundColorAttributeName: [UIColor colorWithHue: 0.27 * (0.8 - (use / 100.)) saturation: 1 brightness: 0.9 alpha: 1] }]]; 45 | self.displayerLabel.attributedText = attributed; 46 | }); 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDMemoryDisplayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDMemoryDisplayer.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 内存使用展示器 13 | */ 14 | @interface LXDMemoryDisplayer : UIView 15 | 16 | - (void)displayUsage: (double)usage; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDMemoryDisplayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDMemoryDisplayer.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDMemoryDisplayer.h" 10 | #import "LXDMonitorUI.h" 11 | #import "LXDDispatchAsync.h" 12 | 13 | 14 | #define LXD_HIGH_MEMORY_USAGE (([NSProcessInfo processInfo].physicalMemory / 1024 / 1024) / 2) 15 | 16 | 17 | @interface LXDMemoryDisplayer () 18 | 19 | @property (nonatomic, strong) LXDAsyncLabel * displayerLabel; 20 | 21 | @end 22 | 23 | 24 | @implementation LXDMemoryDisplayer 25 | 26 | 27 | - (instancetype)initWithFrame: (CGRect)frame { 28 | if (self = [super initWithFrame: frame]) { 29 | CAShapeLayer * bgLayer = [CAShapeLayer layer]; 30 | bgLayer.fillColor = [UIColor colorWithWhite: 0 alpha: 0.7].CGColor; 31 | bgLayer.path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame)) cornerRadius: 5].CGPath; 32 | [self.layer addSublayer: bgLayer]; 33 | 34 | self.displayerLabel = [[LXDAsyncLabel alloc] initWithFrame: self.bounds]; 35 | self.displayerLabel.textColor = [UIColor whiteColor]; 36 | self.displayerLabel.textAlignment = NSTextAlignmentCenter; 37 | self.displayerLabel.font = [UIFont fontWithName: @"Menlo" size: 14]; 38 | [self addSubview: self.displayerLabel]; 39 | } 40 | return self; 41 | } 42 | 43 | - (void)displayUsage: (double)usage { 44 | LXDDispatchQueueAsyncBlockInBackground(^{ 45 | NSMutableAttributedString * attributed = [[NSMutableAttributedString alloc] initWithString: [NSString stringWithFormat: @"%.1f", usage] attributes: @{ NSFontAttributeName: _displayerLabel.font, NSForegroundColorAttributeName: [UIColor colorWithHue: 0.27 * (0.8 - usage / LXD_HIGH_MEMORY_USAGE) saturation: 1 brightness: 0.9 alpha: 1] }]; 46 | [attributed appendAttributedString: [[NSAttributedString alloc] initWithString: @"MB" attributes: @{ NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: _displayerLabel.font }]]; 47 | self.displayerLabel.attributedText = attributed; 48 | }); 49 | } 50 | 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDResourceMonitor.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDResourceMonitor.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | #define RESOURCE_MONITOR [LXDResourceMonitor new] 13 | 14 | 15 | typedef NS_ENUM(NSInteger, LXDResourceMonitorType) 16 | { 17 | LXDResourceMonitorTypeDefault = (1 << 2) | (1 << 3), 18 | LXDResourceMonitorTypeSystemCpu = 1 << 0, ///< 监控系统CPU使用率,优先级低 19 | LXDResourceMonitorTypeSystemMemory = 1 << 1, ///< 监控系统内存使用率,优先级低 20 | LXDResourceMonitorTypeApplicationCpu = 1 << 2, ///< 监控应用CPU使用率,优先级高 21 | LXDResourceMonitorTypeApplicationMemoty = 1 << 3, ///< 监控应用内存使用率,优先级高 22 | }; 23 | 24 | 25 | /*! 26 | * @brief 硬件资源监控 27 | */ 28 | @interface LXDResourceMonitor : NSObject 29 | 30 | + (instancetype)monitorWithMonitorType: (LXDResourceMonitorType)monitorType; 31 | - (void)startMonitoring; 32 | - (void)stopMonitoring; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDResourceMonitor.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDResourceMonitor.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDResourceMonitor.h" 10 | 11 | #import "LXDSystemCPU.h" 12 | #import "LXDApplicationCPU.h" 13 | #import "LXDSystemMemory.h" 14 | #import "LXDApplicationMemory.h" 15 | 16 | #import "LXDMonitorUI.h" 17 | #import "LXDGlobalTimer.h" 18 | #import "LXDCPUDisplayer.h" 19 | #import "LXDMemoryDisplayer.h" 20 | 21 | 22 | @interface LXDResourceMonitor () 23 | 24 | @property (nonatomic, strong) LXDSystemCPU * sysCpu; 25 | @property (nonatomic, strong) LXDApplicationCPU * appCpu; 26 | @property (nonatomic, strong) LXDSystemMemory * sysMemory; 27 | @property (nonatomic, strong) LXDApplicationMemory * appMemory; 28 | 29 | @property (nonatomic, strong) LXDCPUDisplayer * cpuDisplayer; 30 | @property (nonatomic, strong) LXDMemoryDisplayer * memoryDisplayer; 31 | 32 | @end 33 | 34 | 35 | @implementation LXDResourceMonitor 36 | 37 | 38 | + (instancetype)monitorWithMonitorType: (LXDResourceMonitorType)monitorType { 39 | return [[self alloc] initWithMonitorType: monitorType]; 40 | } 41 | 42 | - (instancetype)init { 43 | return [self initWithMonitorType: LXDResourceMonitorTypeDefault]; 44 | } 45 | 46 | - (instancetype)initWithMonitorType: (LXDResourceMonitorType)monitorType { 47 | if (self = [super init]) { 48 | BOOL cpuMonitorEnabled = YES, memoryMonitorEnabled = YES; 49 | if (monitorType & LXDResourceMonitorTypeApplicationCpu) { 50 | self.appCpu = [LXDApplicationCPU new]; 51 | } else if (monitorType & LXDResourceMonitorTypeSystemCpu) { 52 | self.sysCpu = [LXDSystemCPU new]; 53 | } else { 54 | cpuMonitorEnabled = NO; 55 | } 56 | if (monitorType & LXDResourceMonitorTypeApplicationMemoty) { 57 | self.appMemory = [LXDApplicationMemory new]; 58 | } else if (monitorType & LXDResourceMonitorTypeSystemMemory) { 59 | self.sysMemory = [LXDSystemMemory new]; 60 | } else { 61 | memoryMonitorEnabled = NO; 62 | } 63 | if (!(cpuMonitorEnabled | memoryMonitorEnabled)) { 64 | @throw [NSException exceptionWithName: NSInvalidArgumentException reason: [NSString stringWithFormat: @"[%@ initWithMonitorType]: cannot create %@ instance without monitor type", [self class], [self class]] userInfo: nil]; 65 | } 66 | 67 | if (cpuMonitorEnabled) { 68 | self.cpuDisplayer = [[LXDCPUDisplayer alloc] initWithFrame: CGRectMake(0, 30, 60, 20)]; 69 | CGFloat centerX = round(CGRectGetWidth([UIScreen mainScreen].bounds) / 4); 70 | self.cpuDisplayer.center = CGPointMake(centerX, self.cpuDisplayer.center.y); 71 | } 72 | if (memoryMonitorEnabled) { 73 | self.memoryDisplayer = [[LXDMemoryDisplayer alloc] initWithFrame: CGRectMake(CGRectGetWidth([UIScreen mainScreen].bounds) - 140, 30, 60, 20)]; 74 | CGFloat centerX = round(CGRectGetWidth([UIScreen mainScreen].bounds) / 4 * 3); 75 | self.memoryDisplayer.center = CGPointMake(centerX, self.memoryDisplayer.center.y); 76 | } 77 | } 78 | return self; 79 | } 80 | 81 | static NSString * lxd_resource_monitor_callback_key; 82 | 83 | - (void)startMonitoring { 84 | if (lxd_resource_monitor_callback_key != nil) { return; } 85 | lxd_resource_monitor_callback_key = [[LXDGlobalTimer registerTimerCallback: ^{ 86 | double cpuUsage, memoryUsage; 87 | if (_appCpu) { 88 | cpuUsage = [_appCpu currentUsage]; 89 | } else { 90 | LXDSystemCPUUsage usage = [_sysCpu currentUsage]; 91 | cpuUsage = usage.user + usage.system + usage.nice; 92 | } 93 | if (_appMemory) { 94 | LXDApplicationMemoryUsage usage = [_appMemory currentUsage]; 95 | memoryUsage = usage.usage; 96 | } else { 97 | LXDSystemMemoryUsage usage = [_sysMemory currentUsage]; 98 | memoryUsage = (usage.wired + usage.active); 99 | } 100 | [self.cpuDisplayer displayCPUUsage: cpuUsage]; 101 | [self.memoryDisplayer displayUsage: memoryUsage]; 102 | }] copy]; 103 | [[LXDTopWindow topWindow] addSubview: self.cpuDisplayer]; 104 | [[LXDTopWindow topWindow] addSubview: self.memoryDisplayer]; 105 | } 106 | 107 | - (void)stopMonitoring { 108 | if (lxd_resource_monitor_callback_key == nil) { return; } 109 | [LXDGlobalTimer resignTimerCallbackWithKey: lxd_resource_monitor_callback_key]; 110 | [self.cpuDisplayer removeFromSuperview]; 111 | [self.memoryDisplayer removeFromSuperview]; 112 | } 113 | 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDSystemCPU.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDSystemCPU.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/1. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef struct LXDSystemCPUUsage 12 | { 13 | double system; ///< 系统占用率 14 | double user; ///< user占用率 15 | double nice; ///< 加权user占用率 16 | double idle; ///< 空闲率 17 | } LXDSystemCPUUsage; 18 | 19 | /*! 20 | * @brief 系统CPU占用 21 | */ 22 | @interface LXDSystemCPU : NSObject 23 | 24 | @property (nonatomic, readonly) double systemRatio; 25 | @property (nonatomic, readonly) double userRatio; 26 | @property (nonatomic, readonly) double niceRatio; 27 | @property (nonatomic, readonly) double idleRatio; 28 | 29 | - (LXDSystemCPUUsage)currentUsage; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDSystemCPU.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDSystemCPU.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/1. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDSystemCPU.h" 10 | #import 11 | #import 12 | #import 13 | 14 | 15 | static NSArray * previousCPUInfo; 16 | 17 | 18 | /// processor_info_array_t结构数据偏移位 19 | typedef NS_ENUM(NSInteger, LXDCPUInfoOffsetState) 20 | { 21 | LXDCPUInfoOffsetStateSystem = 0, 22 | LXDCPUInfoOffsetStateUser = 1, 23 | LXDCPUInfoOffsetStateNice = 2, 24 | LXDCPUInfoOffsetStateIdle = 3, 25 | LXDCPUInfoOffsetStateMask = 4, 26 | }; 27 | 28 | /// cpu信息结构体 29 | static NSUInteger LXDSystemCPUInfoCount = 4; 30 | typedef struct LXDSystemCPUInfo { 31 | NSUInteger system; ///< 系统态占用。 32 | NSUInteger user; ///< 用户态占用。 33 | NSUInteger nice; ///< nice加权的用户态占用。 34 | NSUInteger idle; ///< 空闲占用 35 | } LXDSystemCPUInfo; 36 | 37 | /// 结构体构造转换 38 | static inline LXDSystemCPUInfo __LXDSystemCPUInfoMake(NSUInteger system, NSUInteger user, NSUInteger nice, NSUInteger idle) { 39 | return (LXDSystemCPUInfo){ system, user, nice, idle }; 40 | } 41 | 42 | static inline NSString * LXDStringFromSystemCPUInfo(LXDSystemCPUInfo systemCPUInfo) { 43 | return [NSString stringWithFormat: @"%lu-%lu-%lu-%lu", systemCPUInfo.system, systemCPUInfo.user, systemCPUInfo.nice, systemCPUInfo.idle]; 44 | } 45 | 46 | static inline LXDSystemCPUInfo LXDSystemCPUInfoFromString(NSString * string) { 47 | NSArray * infos = [string componentsSeparatedByString: @"-"]; 48 | if (infos.count == LXDSystemCPUInfoCount) { 49 | return __LXDSystemCPUInfoMake( 50 | [infos[LXDCPUInfoOffsetStateSystem] integerValue], 51 | [infos[LXDCPUInfoOffsetStateUser] integerValue], 52 | [infos[LXDCPUInfoOffsetStateNice] integerValue], 53 | [infos[LXDCPUInfoOffsetStateIdle] integerValue]); 54 | } 55 | return (LXDSystemCPUInfo){ 0 }; 56 | } 57 | 58 | 59 | @interface LXDSystemCPU () 60 | 61 | @property (nonatomic, assign) double systemRatio; 62 | @property (nonatomic, assign) double userRatio; 63 | @property (nonatomic, assign) double niceRatio; 64 | @property (nonatomic, assign) double idleRatio; 65 | 66 | @property (nonatomic, copy) NSArray * cpuInfos; 67 | 68 | @end 69 | 70 | 71 | @implementation LXDSystemCPU 72 | 73 | 74 | - (LXDSystemCPUUsage)currentUsage { 75 | return [self generateSystemCpuUsageWithCpuInfos: [self generateCpuInfos]]; 76 | } 77 | 78 | - (NSArray *)generateCpuInfos { 79 | natural_t cpu_processor_count = 0; 80 | natural_t cpu_processor_info_count = 0; 81 | processor_info_array_t cpu_processor_infos = NULL; 82 | 83 | kern_return_t result = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_processor_count, &cpu_processor_infos, &cpu_processor_info_count); 84 | if ( result == KERN_SUCCESS && cpu_processor_infos != NULL ) { 85 | NSMutableArray * infos = [NSMutableArray arrayWithCapacity: cpu_processor_count]; 86 | for (int idx = 0; idx < cpu_processor_count; idx++) { 87 | NSInteger offset = LXDCPUInfoOffsetStateMask * idx; 88 | 89 | double system, user, nice, idle; 90 | if (previousCPUInfo.count > idx) { 91 | LXDSystemCPUInfo previousInfo = LXDSystemCPUInfoFromString(previousCPUInfo[idx]); 92 | system = cpu_processor_infos[offset + LXDCPUInfoOffsetStateSystem] - previousInfo.system; 93 | user = cpu_processor_infos[offset + LXDCPUInfoOffsetStateUser] - previousInfo.user; 94 | nice = cpu_processor_infos[offset + LXDCPUInfoOffsetStateNice] - previousInfo.nice; 95 | idle = cpu_processor_infos[offset + LXDCPUInfoOffsetStateIdle] - previousInfo.idle; 96 | } else { 97 | system = cpu_processor_infos[offset + LXDCPUInfoOffsetStateSystem]; 98 | user = cpu_processor_infos[offset + LXDCPUInfoOffsetStateUser]; 99 | nice = cpu_processor_infos[offset + LXDCPUInfoOffsetStateNice]; 100 | idle = cpu_processor_infos[offset + LXDCPUInfoOffsetStateIdle]; 101 | } 102 | LXDSystemCPUInfo info = __LXDSystemCPUInfoMake( system, user, nice, idle ); 103 | [infos addObject: LXDStringFromSystemCPUInfo(info)]; 104 | } 105 | 106 | vm_size_t cpuInfoSize = sizeof(int32_t) * cpu_processor_count; 107 | vm_deallocate(mach_task_self_, (vm_address_t)cpu_processor_infos, cpuInfoSize); 108 | return infos; 109 | } 110 | return nil; 111 | } 112 | 113 | - (LXDSystemCPUUsage)generateSystemCpuUsageWithCpuInfos: (NSArray *)cpuInfos { 114 | if (cpuInfos.count == 0) { return (LXDSystemCPUUsage){ 0 }; } 115 | double system = 0, user = 0, nice = 0, idle = 0; 116 | for (NSString * cpuInfoString in cpuInfos) { 117 | LXDSystemCPUInfo cpuInfo = LXDSystemCPUInfoFromString(cpuInfoString); 118 | system += cpuInfo.system; 119 | user += cpuInfo.user; 120 | nice += cpuInfo.nice; 121 | idle += cpuInfo.idle; 122 | } 123 | system /= cpuInfos.count; 124 | user /= cpuInfos.count; 125 | nice /= cpuInfos.count; 126 | idle /= cpuInfos.count; 127 | 128 | double total = system + user + nice + idle; 129 | return (LXDSystemCPUUsage){ 130 | .system = system / total, 131 | .user = user / total, 132 | .nice = nice / total, 133 | .idle = idle / total, 134 | }; 135 | } 136 | 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDSystemMemory.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDMemoryUsage.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/26. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef struct LXDSystemMemoryUsage 12 | { 13 | double free; ///< 自由内存(MB) 14 | double wired; ///< 固定内存(MB) 15 | double active; ///< 正在使用的内存(MB) 16 | double inactive; ///< 缓存、后台内存(MB) 17 | double compressed; ///< 压缩内存(MB) 18 | double total; ///< 总内存(MB) 19 | } LXDSystemMemoryUsage; 20 | 21 | /*! 22 | * @brief 系统内存使用 23 | */ 24 | @interface LXDSystemMemory : NSObject 25 | 26 | - (LXDSystemMemoryUsage)currentUsage; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Resource/LXDSystemMemory.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDMemoryUsage.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/26. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDSystemMemory.h" 10 | #import 11 | #import 12 | 13 | 14 | #ifndef NBYTE_PER_MB 15 | #define NBYTE_PER_MB (1024 * 1024) 16 | #endif 17 | 18 | 19 | @implementation LXDSystemMemory 20 | 21 | 22 | - (LXDSystemMemoryUsage)currentUsage { 23 | vm_statistics64_data_t vmstat; 24 | natural_t size = HOST_VM_INFO64_COUNT; 25 | if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vmstat, &size) == KERN_SUCCESS) { 26 | return (LXDSystemMemoryUsage){ 27 | .free = vmstat.free_count * PAGE_SIZE / NBYTE_PER_MB, 28 | .wired = vmstat.wire_count * PAGE_SIZE / NBYTE_PER_MB, 29 | .active = vmstat.active_count * PAGE_SIZE / NBYTE_PER_MB, 30 | .inactive = vmstat.inactive_count * PAGE_SIZE / NBYTE_PER_MB, 31 | .compressed = vmstat.compressor_page_count * PAGE_SIZE / NBYTE_PER_MB, 32 | .total = [NSProcessInfo processInfo].physicalMemory / NBYTE_PER_MB, 33 | }; 34 | } 35 | return (LXDSystemMemoryUsage){ 0 }; 36 | } 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Backtrace/LXDBacktraceLogger.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDBacktraceLogger.h 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/23. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 线程堆栈上下文输出 13 | */ 14 | @interface LXDBacktraceLogger : NSObject 15 | 16 | + (NSString *)lxd_backtraceOfAllThread; 17 | + (NSString *)lxd_backtraceOfMainThread; 18 | + (NSString *)lxd_backtraceOfCurrentThread; 19 | + (NSString *)lxd_backtraceOfNSThread:(NSThread *)thread; 20 | 21 | + (void)lxd_logMain; 22 | + (void)lxd_logCurrent; 23 | + (void)lxd_logAllThread; 24 | 25 | + (NSString *)backtraceLogFilePath; 26 | + (void)recordLoggerWithFileName: (NSString *)fileName; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Backtrace/LXDBacktraceLogger.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDBacktraceLogger.m 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/23. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDBacktraceLogger.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | 20 | 21 | 22 | 23 | /*! 24 | * @brief 适配不同CPU的宏定义 25 | * 26 | * @thx 代码出自https://github.com/bestswifter/BSBacktraceLogger 加工修改 27 | */ 28 | #if defined(__arm64__) 29 | #define DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(3UL)) 30 | #define LXD_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT 31 | #define LXD_THREAD_STATE ARM_THREAD_STATE64 32 | #define LXD_FRAME_POINTER __fp 33 | #define LXD_STACK_POINTER __sp 34 | #define LXD_INSTRUCTION_ADDRESS __pc 35 | 36 | #elif defined(__arm__) 37 | #define DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(1UL)) 38 | #define LXD_THREAD_STATE_COUNT ARM_THREAD_STATE_COUNT 39 | #define LXD_THREAD_STATE ARM_THREAD_STATE 40 | #define LXD_FRAME_POINTER __r[7] 41 | #define LXD_STACK_POINTER __sp 42 | #define LXD_INSTRUCTION_ADDRESS __pc 43 | 44 | #elif defined(__x86_64__) 45 | #define DETAG_INSTRUCTION_ADDRESS(A) (A) 46 | #define LXD_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT 47 | #define LXD_THREAD_STATE x86_THREAD_STATE64 48 | #define LXD_FRAME_POINTER __rbp 49 | #define LXD_STACK_POINTER __rsp 50 | #define LXD_INSTRUCTION_ADDRESS __rip 51 | 52 | #elif defined(__i386__) 53 | #define DETAG_INSTRUCTION_ADDRESS(A) (A) 54 | #define LXD_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT 55 | #define LXD_THREAD_STATE x86_THREAD_STATE32 56 | #define LXD_FRAME_POINTER __ebp 57 | #define LXD_STACK_POINTER __esp 58 | #define LXD_INSTRUCTION_ADDRESS __eip 59 | 60 | #endif 61 | 62 | #if defined(__LP64__) 63 | #define TRACE_FMT "%-4d%-31s 0x%016lx %s + %lu" 64 | #define POINTER_FMT "0x%016lx" 65 | #define POINTER_SHORT_FMT "0x%lx" 66 | #define LXD_NLIST struct nlist_64 67 | #else 68 | #define TRACE_FMT "%-4d%-31s 0x%08lx %s + %lu" 69 | #define POINTER_FMT "0x%08lx" 70 | #define POINTER_SHORT_FMT "0x%lx" 71 | #define LXD_NLIST struct nlist 72 | 73 | #endif 74 | 75 | #define MAX_FRAME_NUMBER 30 76 | #define LOG_SEPERATE printf("\n"); 77 | #define FAILED_UINT_PTR_ADDRESS 0 78 | #define CALL_INSTRUCTION_FROM_RETURN_ADDRESS(A) (DETAG_INSTRUCTION_ADDRESS((A)) - 1) 79 | 80 | 81 | typedef struct LXDStackFrameEntry{ 82 | const struct LXDStackFrameEntry * const previous; 83 | const uintptr_t return_address; 84 | } LXDStackFrameEntry; 85 | 86 | static mach_port_t main_thread_id; 87 | 88 | static inline dispatch_queue_t lxd_log_IO_queue() { 89 | static dispatch_queue_t lxd_log_IO_queue; 90 | static dispatch_once_t once; 91 | dispatch_once(&once, ^{ 92 | lxd_log_IO_queue = dispatch_queue_create("com.sindrilin.lxd_log_IO_queue", NULL); 93 | }); 94 | return lxd_log_IO_queue; 95 | } 96 | 97 | 98 | 99 | @implementation LXDBacktraceLogger 100 | 101 | + (void)load { 102 | main_thread_id = mach_thread_self(); 103 | } 104 | 105 | 106 | #pragma mark - Public 107 | + (NSString *)lxd_backtraceOfAllThread { 108 | thread_act_array_t threads; 109 | mach_msg_type_number_t thread_count = 0; 110 | 111 | kern_return_t kr = task_threads(mach_task_self(), &threads, &thread_count); 112 | if (kr != KERN_SUCCESS) { 113 | return @"Failed to get information of all threads"; 114 | } 115 | NSMutableString * result = @"".mutableCopy; 116 | for (int idx = 0; idx < thread_count; idx++) { 117 | [result appendString: _lxd_backtraceOfThread(threads[idx])]; 118 | } 119 | return result.copy; 120 | } 121 | 122 | + (NSString *)lxd_backtraceOfMainThread { 123 | return [self lxd_backtraceOfNSThread: [NSThread mainThread]]; 124 | } 125 | 126 | + (NSString *)lxd_backtraceOfCurrentThread { 127 | return [self lxd_backtraceOfNSThread: [NSThread currentThread]]; 128 | } 129 | 130 | + (NSString *)lxd_backtraceOfNSThread:(NSThread *)thread { 131 | return _lxd_backtraceOfThread(lxd_machThreadFromNSThread(thread)); 132 | } 133 | 134 | + (void)lxd_logMain { 135 | LOG_SEPERATE 136 | NSLog(@"%@", [self lxd_backtraceOfMainThread]); 137 | LOG_SEPERATE 138 | } 139 | 140 | + (void)lxd_logCurrent { 141 | LOG_SEPERATE 142 | NSLog(@"%@", [self lxd_backtraceOfCurrentThread]); 143 | LOG_SEPERATE 144 | } 145 | 146 | + (void)lxd_logAllThread { 147 | LOG_SEPERATE 148 | NSLog(@"%@", [self lxd_backtraceOfAllThread]); 149 | LOG_SEPERATE 150 | } 151 | 152 | 153 | #pragma mark - Generate 154 | thread_t lxd_machThreadFromNSThread(NSThread * nsthread) { 155 | char name[256]; 156 | thread_act_array_t list; 157 | mach_msg_type_number_t count; 158 | task_threads(mach_task_self(), &list, &count); 159 | 160 | NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970]; 161 | NSString * originName = nsthread.name; 162 | [nsthread setName: [NSString stringWithFormat: @"%f", timeStamp]]; 163 | 164 | if ([nsthread isMainThread]) { return (thread_t)main_thread_id; } 165 | 166 | for (int idx = 0; idx < count; idx++) { 167 | pthread_t pt = pthread_from_mach_thread_np(list[idx]); 168 | if ([nsthread isMainThread] && list[idx] == main_thread_id) { return list[idx]; } 169 | if (pt) { 170 | name[0] = '\0'; 171 | pthread_getname_np(pt, name, sizeof(name)); 172 | if (!strcmp(name, [nsthread name].UTF8String)) { 173 | [nsthread setName: originName]; 174 | return list[idx]; 175 | } 176 | } 177 | } 178 | [nsthread setName: originName]; 179 | return mach_thread_self(); 180 | } 181 | 182 | NSString * _lxd_backtraceOfThread(thread_t thread) { 183 | uintptr_t backtraceBuffer[MAX_FRAME_NUMBER]; 184 | int idx = 0; 185 | NSMutableString * result = [NSString stringWithFormat: @"Backtrace of Thread %u:\n======================================================================================\n", thread].mutableCopy; 186 | 187 | _STRUCT_MCONTEXT machineContext; 188 | if (!lxd_fillThreadStateIntoMachineContext(thread, &machineContext)) { 189 | return [NSString stringWithFormat: @"Failed to get information abount thread: %u", thread]; 190 | } 191 | const uintptr_t instructionAddress = lxd_mach_instructionAddress(&machineContext); 192 | backtraceBuffer[idx++] = instructionAddress; 193 | 194 | uintptr_t linkRegister = lxd_mach_linkRegister(&machineContext); 195 | if (linkRegister) { 196 | backtraceBuffer[idx++] = linkRegister; 197 | } 198 | if (instructionAddress == FAILED_UINT_PTR_ADDRESS) { return @"Failed to get instruction address"; } 199 | 200 | LXDStackFrameEntry frame = { 0 }; 201 | const uintptr_t framePtr = lxd_mach_framePointer(&machineContext); 202 | if (framePtr == FAILED_UINT_PTR_ADDRESS || 203 | lxd_mach_copyMem((void *)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) { 204 | return @"failed to get frame pointer"; 205 | } 206 | 207 | for (; idx < MAX_FRAME_NUMBER; idx++) { 208 | backtraceBuffer[idx] = frame.return_address; 209 | if (backtraceBuffer[idx] == FAILED_UINT_PTR_ADDRESS || 210 | frame.previous == NULL || 211 | lxd_mach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { 212 | break; 213 | } 214 | } 215 | 216 | int backtraceLength = idx; 217 | Dl_info symbolicated[backtraceLength]; 218 | lxd_symbolicate(backtraceBuffer, symbolicated, backtraceLength, 0); 219 | for (int idx = 0; idx < backtraceLength; idx++) { 220 | [result appendFormat: @"%@", lxd_logBacktraceEntry(idx, backtraceBuffer[idx], &symbolicated[idx])]; 221 | } 222 | [result appendString: @"\n"]; 223 | [result appendString: @"======================================================================================"]; 224 | return result.copy; 225 | } 226 | 227 | 228 | #pragma mark - operate machine context 229 | bool lxd_fillThreadStateIntoMachineContext(thread_t thread, _STRUCT_MCONTEXT * machineContext) { 230 | mach_msg_type_number_t state_count = LXD_THREAD_STATE_COUNT; 231 | kern_return_t kr = thread_get_state(thread, LXD_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count); 232 | return (kr == KERN_SUCCESS); 233 | } 234 | 235 | uintptr_t lxd_mach_linkRegister(_STRUCT_MCONTEXT * const machineContext){ 236 | #if defined(__i386__) || defined(__x86_64__) 237 | return FAILED_UINT_PTR_ADDRESS; 238 | #else 239 | return machineContext->__ss.__lr; 240 | #endif 241 | } 242 | 243 | uintptr_t lxd_mach_framePointer(_STRUCT_MCONTEXT * const machineContext) { 244 | return machineContext->__ss.LXD_FRAME_POINTER; 245 | } 246 | 247 | uintptr_t lxd_mach_instructionAddress(_STRUCT_MCONTEXT * const machineContext) { 248 | return machineContext->__ss.LXD_INSTRUCTION_ADDRESS; 249 | } 250 | 251 | kern_return_t lxd_mach_copyMem(const void * src, const void * dst, const size_t numBytes) { 252 | vm_size_t bytesCopied = 0; 253 | return vm_read_overwrite(mach_task_self(), (vm_address_t)src, (vm_size_t)numBytes, (vm_address_t)dst, &bytesCopied); 254 | } 255 | 256 | 257 | #pragma mark - handle symbolicate 258 | void lxd_symbolicate(const uintptr_t * const backtraceBuffer, Dl_info * const symbolsBuffer, const int numEntries, const int skippedEntries) { 259 | int idx = 0; 260 | if (!skippedEntries && idx < numEntries) { 261 | lxd_dladdr(backtraceBuffer[idx], &symbolsBuffer[idx]); 262 | idx++; 263 | } 264 | 265 | for (; idx < numEntries; idx++) { 266 | lxd_dladdr(CALL_INSTRUCTION_FROM_RETURN_ADDRESS(backtraceBuffer[idx]), &symbolsBuffer[idx]); 267 | } 268 | } 269 | 270 | bool lxd_dladdr(const uintptr_t address, Dl_info * const info) { 271 | info->dli_fname = NULL; 272 | info->dli_fbase = NULL; 273 | info->dli_sname = NULL; 274 | info->dli_saddr = NULL; 275 | 276 | const uint32_t idx = lxd_imageIndexContainingAddress(address); 277 | if (idx == UINT_MAX) { return false; } 278 | 279 | const struct mach_header * header = _dyld_get_image_header(idx); 280 | const uintptr_t imageVMAddressSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx); 281 | const uintptr_t addressWithSlide = address - imageVMAddressSlide; 282 | const uintptr_t segmentBase = lxd_segmentBaseOfImageIndex(idx) + imageVMAddressSlide; 283 | if (segmentBase == FAILED_UINT_PTR_ADDRESS) { return false; } 284 | 285 | info->dli_fbase = (void *)header; 286 | info->dli_fname = _dyld_get_image_name(idx); 287 | 288 | const LXD_NLIST * bestMatch = NULL; 289 | uintptr_t bestDistance = ULONG_MAX; 290 | uintptr_t cmdPtr = lxd_firstCmdAfterHeader(header); 291 | if (cmdPtr == FAILED_UINT_PTR_ADDRESS) { return false; } 292 | 293 | for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { 294 | const struct load_command * loadCmd = (struct load_command *)cmdPtr; 295 | if (loadCmd->cmd == LC_SYMTAB) { 296 | const struct symtab_command * symtabCmd = (struct symtab_command *)cmdPtr; 297 | const LXD_NLIST * symbolTable = (LXD_NLIST *)(segmentBase + symtabCmd->symoff); 298 | const uintptr_t stringTable = segmentBase + symtabCmd->stroff; 299 | 300 | for (uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++) { 301 | if (symbolTable[iSym].n_value == FAILED_UINT_PTR_ADDRESS) { continue; } 302 | uintptr_t symbolBase = symbolTable[iSym].n_value; 303 | uintptr_t currentDistance = addressWithSlide - symbolBase; 304 | if ( (addressWithSlide >= symbolBase && currentDistance <= bestDistance) ) { 305 | bestMatch = symbolTable + iSym; 306 | bestDistance = currentDistance; 307 | } 308 | } 309 | if (bestMatch != NULL) { 310 | info->dli_saddr = (void *)(bestMatch->n_value + imageVMAddressSlide); 311 | info->dli_sname = (char *)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx); 312 | if (*info->dli_sname == '_') { 313 | info->dli_sname++; 314 | } 315 | if (info->dli_saddr == info->dli_fbase && bestMatch->n_type == 3) { 316 | info->dli_sname = NULL; 317 | } 318 | break; 319 | } 320 | } 321 | cmdPtr += loadCmd->cmdsize; 322 | } 323 | return true; 324 | } 325 | 326 | uintptr_t lxd_firstCmdAfterHeader(const struct mach_header * const header) { 327 | switch (header->magic) { 328 | case MH_MAGIC: 329 | case MH_CIGAM: 330 | return (uintptr_t)(header + 1); 331 | case MH_MAGIC_64: 332 | case MH_CIGAM_64: 333 | return (uintptr_t)(((struct mach_header_64*)header) + 1); 334 | default: 335 | return 0; 336 | } 337 | } 338 | 339 | uintptr_t lxd_segmentBaseOfImageIndex(const uint32_t idx) { 340 | const struct mach_header * header = _dyld_get_image_header(idx); 341 | 342 | uintptr_t cmdPtr = lxd_firstCmdAfterHeader(header); 343 | if (cmdPtr == FAILED_UINT_PTR_ADDRESS) { return FAILED_UINT_PTR_ADDRESS; } 344 | for (uint32_t idx = 0; idx < header->ncmds; idx++) { 345 | const struct load_command * loadCmd = (struct load_command *)cmdPtr; 346 | if (loadCmd->cmd == LC_SEGMENT) { 347 | const struct segment_command * segCmd = (struct segment_command *)cmdPtr; 348 | if (strcmp(segCmd->segname, SEG_LINKEDIT) == 0) { 349 | return segCmd->vmaddr - segCmd->fileoff; 350 | } 351 | } else if (loadCmd->cmd == LC_SEGMENT_64) { 352 | const struct segment_command_64 * segCmd = (struct segment_command_64 *)cmdPtr; 353 | if (strcmp(segCmd->segname, SEG_LINKEDIT) == 0) { 354 | return (uintptr_t)(segCmd->vmaddr - segCmd->fileoff); 355 | } 356 | } 357 | cmdPtr += loadCmd->cmdsize; 358 | } 359 | return FAILED_UINT_PTR_ADDRESS; 360 | } 361 | 362 | uint32_t lxd_imageIndexContainingAddress(const uintptr_t address) { 363 | const uint32_t imageCount = _dyld_image_count(); 364 | const struct mach_header * header = FAILED_UINT_PTR_ADDRESS; 365 | 366 | for (uint32_t iImg = 0; iImg < imageCount; iImg++) { 367 | header = _dyld_get_image_header(iImg); 368 | if (header != NULL) { 369 | uintptr_t addressWSlide = address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg); 370 | uintptr_t cmdPtr = lxd_firstCmdAfterHeader(header); 371 | if (cmdPtr == FAILED_UINT_PTR_ADDRESS) { continue; } 372 | 373 | for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { 374 | const struct load_command * loadCmd = (struct load_command *)cmdPtr; 375 | if (loadCmd->cmd == LC_SEGMENT) { 376 | const struct segment_command * segCmd = (struct segment_command *)cmdPtr; 377 | if (addressWSlide >= segCmd->vmaddr && 378 | addressWSlide < segCmd->vmaddr + segCmd->vmsize) { 379 | return iImg; 380 | } 381 | } else if (loadCmd->cmd == LC_SEGMENT_64) { 382 | const struct segment_command_64 * segCmd = (struct segment_command_64 *)cmdPtr; 383 | if (addressWSlide >= segCmd->vmaddr && 384 | addressWSlide < segCmd->vmaddr + segCmd->vmsize) { 385 | return iImg; 386 | } 387 | } 388 | cmdPtr += loadCmd->cmdsize; 389 | } 390 | } 391 | } 392 | return UINT_MAX; 393 | } 394 | 395 | 396 | #pragma mark - generate backtrace entry 397 | const char * lxd_lastPathEntry(const char * const path) { 398 | if (path == NULL) { return NULL; } 399 | char * lastFile = strrchr(path, '/'); 400 | return lastFile == NULL ? path: lastFile + 1; 401 | } 402 | 403 | NSString * lxd_logBacktraceEntry(const int entryNum, const uintptr_t address, const Dl_info * const dlInfo) { 404 | char faddrBuffer[20]; 405 | char saddrBuffer[20]; 406 | 407 | const char * fname = lxd_lastPathEntry(dlInfo->dli_fname); 408 | if (fname == NULL) { 409 | sprintf(faddrBuffer, POINTER_FMT, (uintptr_t)dlInfo->dli_fbase); 410 | fname = faddrBuffer; 411 | } 412 | 413 | uintptr_t offset = address - (uintptr_t)dlInfo->dli_saddr; 414 | const char * sname = dlInfo->dli_sname; 415 | if (sname == NULL) { 416 | sprintf(saddrBuffer, POINTER_SHORT_FMT, (uintptr_t)dlInfo->dli_fbase); 417 | sname = saddrBuffer; 418 | offset = address - (uintptr_t)dlInfo->dli_fbase; 419 | } 420 | return [NSString stringWithFormat: @"%-30s 0x%08" PRIxPTR " %s + %lu\n", fname, (uintptr_t)address, sname, offset]; 421 | } 422 | 423 | + (NSString *)backtraceLogFilePath { 424 | static NSString * const fileDirectoryName = @"lxd_backtrace"; 425 | NSString * filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent: fileDirectoryName]; 426 | NSFileManager * manager = [NSFileManager defaultManager]; 427 | if (![manager fileExistsAtPath: filePath]) { 428 | [manager createDirectoryAtPath: filePath withIntermediateDirectories: YES attributes: nil error: nil]; 429 | } 430 | return filePath; 431 | } 432 | 433 | + (void)recordLoggerWithFileName: (NSString *)fileName { 434 | NSParameterAssert(fileName); 435 | dispatch_async(lxd_log_IO_queue(), ^{ 436 | NSDateFormatter * formatter = [NSDateFormatter new]; 437 | formatter.dateFormat = @"mmssS"; 438 | NSString * filePath = [[self backtraceLogFilePath] stringByAppendingString: [formatter stringFromDate: [NSDate date]]]; 439 | NSString * backtraceStackInfo = [self lxd_backtraceOfMainThread]; 440 | [backtraceStackInfo writeToFile: filePath atomically: YES encoding: NSUTF8StringEncoding error: nil]; 441 | }); 442 | } 443 | 444 | 445 | @end 446 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Dispatch/LXDDispatchAsync.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDDispatchQueuePool.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/2. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | typedef NS_ENUM(NSInteger, LXDQualityOfService) { 13 | LXDQualityOfServiceUserInteractive = NSQualityOfServiceUserInteractive, 14 | LXDQualityOfServiceUserInitiated = NSQualityOfServiceUserInitiated, 15 | LXDQualityOfServiceUtility = NSQualityOfServiceUtility, 16 | LXDQualityOfServiceBackground = NSQualityOfServiceBackground, 17 | LXDQualityOfServiceDefault = NSQualityOfServiceDefault, 18 | }; 19 | 20 | 21 | dispatch_queue_t LXDDispatchQueueAsyncBlockInQOS(LXDQualityOfService qos, dispatch_block_t block); 22 | dispatch_queue_t LXDDispatchQueueAsyncBlockInUserInteractive(dispatch_block_t block); 23 | dispatch_queue_t LXDDispatchQueueAsyncBlockInUserInitiated(dispatch_block_t block); 24 | dispatch_queue_t LXDDispatchQueueAsyncBlockInBackground(dispatch_block_t block); 25 | dispatch_queue_t LXDDispatchQueueAsyncBlockInDefault(dispatch_block_t block); 26 | dispatch_queue_t LXDDispatchQueueAsyncBlockInUtility(dispatch_block_t block); 27 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Dispatch/LXDDispatchAsync.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDDispatchQueuePool.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/2. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDDispatchAsync.h" 10 | #import 11 | 12 | 13 | #ifndef LXDDispatchAsync_m 14 | #define LXDDispatchAsync_m 15 | #endif 16 | 17 | #define LXD_INLINE static inline 18 | #define LXD_QUEUE_MAX_COUNT 32 19 | 20 | 21 | typedef struct __LXDDispatchContext { 22 | const char * name; 23 | void ** queues; 24 | uint32_t queueCount; 25 | int32_t offset; 26 | } *DispatchContext, LXDDispatchContext; 27 | 28 | 29 | LXD_INLINE dispatch_queue_priority_t __LXDQualityOfServiceToDispatchPriority(LXDQualityOfService qos) { 30 | switch (qos) { 31 | case LXDQualityOfServiceUserInteractive: return DISPATCH_QUEUE_PRIORITY_HIGH; 32 | case LXDQualityOfServiceUserInitiated: return DISPATCH_QUEUE_PRIORITY_HIGH; 33 | case LXDQualityOfServiceUtility: return DISPATCH_QUEUE_PRIORITY_LOW; 34 | case LXDQualityOfServiceBackground: return DISPATCH_QUEUE_PRIORITY_BACKGROUND; 35 | case LXDQualityOfServiceDefault: return DISPATCH_QUEUE_PRIORITY_DEFAULT; 36 | default: return DISPATCH_QUEUE_PRIORITY_DEFAULT; 37 | } 38 | } 39 | 40 | LXD_INLINE qos_class_t __LXDQualityOfServiceToQOSClass(LXDQualityOfService qos) { 41 | switch (qos) { 42 | case LXDQualityOfServiceUserInteractive: return QOS_CLASS_USER_INTERACTIVE; 43 | case LXDQualityOfServiceUserInitiated: return QOS_CLASS_USER_INITIATED; 44 | case LXDQualityOfServiceUtility: return QOS_CLASS_UTILITY; 45 | case LXDQualityOfServiceBackground: return QOS_CLASS_BACKGROUND; 46 | case LXDQualityOfServiceDefault: return QOS_CLASS_DEFAULT; 47 | default: return QOS_CLASS_UNSPECIFIED; 48 | } 49 | } 50 | 51 | LXD_INLINE dispatch_queue_attr_t __LXDQoSToQueueAttributes(LXDQualityOfService qos) { 52 | dispatch_qos_class_t qosClass = __LXDQualityOfServiceToQOSClass(qos); 53 | return dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, qosClass, 0); 54 | }; 55 | 56 | LXD_INLINE dispatch_queue_t __LXDQualityOfServiceToDispatchQueue(LXDQualityOfService qos, const char * queueName) { 57 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { 58 | dispatch_queue_attr_t attr = __LXDQoSToQueueAttributes(qos); 59 | return dispatch_queue_create(queueName, attr); 60 | } else { 61 | dispatch_queue_t queue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL); 62 | dispatch_set_target_queue(queue, dispatch_get_global_queue(__LXDQualityOfServiceToDispatchPriority(qos), 0)); 63 | return queue; 64 | } 65 | } 66 | 67 | LXD_INLINE DispatchContext __LXDDispatchContextCreate(const char * name, 68 | uint32_t queueCount, 69 | LXDQualityOfService qos) { 70 | DispatchContext context = calloc(1, sizeof(LXDDispatchContext)); 71 | if (context == NULL) { return NULL; } 72 | 73 | context->queues = calloc(queueCount, sizeof(void *)); 74 | if (context->queues == NULL) { 75 | free(context); 76 | return NULL; 77 | } 78 | for (int idx = 0; idx < queueCount; idx++) { 79 | context->queues[idx] = (__bridge_retained void *)__LXDQualityOfServiceToDispatchQueue(qos, name); 80 | } 81 | context->queueCount = queueCount; 82 | if (name) { 83 | context->name = strdup(name); 84 | } 85 | context->offset = 0; 86 | return context; 87 | } 88 | 89 | LXD_INLINE void __LXDDispatchContextRelease(DispatchContext context) { 90 | if (context == NULL) { return; } 91 | if (context->queues != NULL) { free(context->queues); } 92 | if (context->name != NULL) { free((void *)context->name); } 93 | context->queues = NULL; 94 | if (context) { free(context); } 95 | } 96 | 97 | LXD_INLINE dispatch_semaphore_t __LXDSemaphore() { 98 | static dispatch_semaphore_t semaphore; 99 | static dispatch_once_t once; 100 | dispatch_once(&once, ^{ 101 | semaphore = dispatch_semaphore_create(0); 102 | }); 103 | return semaphore; 104 | } 105 | 106 | LXD_INLINE dispatch_queue_t __LXDDispatchContextGetQueue(DispatchContext context) { 107 | dispatch_semaphore_wait(__LXDSemaphore(), dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER)); 108 | uint32_t offset = (uint32_t)OSAtomicIncrement32(&context->offset); 109 | dispatch_queue_t queue = (__bridge dispatch_queue_t)context->queues[offset % context->queueCount]; 110 | dispatch_semaphore_signal(__LXDSemaphore()); 111 | if (queue) { return queue; } 112 | return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 113 | } 114 | 115 | LXD_INLINE DispatchContext __LXDDispatchContextGetForQos(LXDQualityOfService qos) { 116 | static DispatchContext contexts[5]; 117 | int count = (int)[NSProcessInfo processInfo].activeProcessorCount; 118 | count = MIN(1, MAX(count, LXD_QUEUE_MAX_COUNT)); 119 | switch (qos) { 120 | case LXDQualityOfServiceUserInteractive: { 121 | static dispatch_once_t once; 122 | dispatch_once(&once, ^{ 123 | contexts[0] = __LXDDispatchContextCreate("com.sindrilin.user_interactive", count, qos); 124 | }); 125 | return contexts[0]; 126 | } 127 | 128 | case LXDQualityOfServiceUserInitiated: { 129 | static dispatch_once_t once; 130 | dispatch_once(&once, ^{ 131 | contexts[1] = __LXDDispatchContextCreate("com.sindrilin.user_initated", count, qos); 132 | }); 133 | return contexts[1]; 134 | } 135 | 136 | case LXDQualityOfServiceUtility: { 137 | static dispatch_once_t once; 138 | dispatch_once(&once, ^{ 139 | contexts[2] = __LXDDispatchContextCreate("com.sindrilin.utility", count, qos); 140 | }); 141 | return contexts[2]; 142 | } 143 | 144 | case LXDQualityOfServiceBackground: { 145 | static dispatch_once_t once; 146 | dispatch_once(&once, ^{ 147 | contexts[3] = __LXDDispatchContextCreate("com.sindrilin.background", count, qos); 148 | }); 149 | return contexts[3]; 150 | } 151 | 152 | case LXDQualityOfServiceDefault: 153 | default: { 154 | static dispatch_once_t once; 155 | dispatch_once(&once, ^{ 156 | contexts[4] = __LXDDispatchContextCreate("com.sindrilin.default", count, qos); 157 | }); 158 | return contexts[4]; 159 | } 160 | } 161 | } 162 | 163 | dispatch_queue_t LXDDispatchQueueAsyncBlockInQOS(LXDQualityOfService qos, dispatch_block_t block) { 164 | if (block == nil) { return NULL; } 165 | DispatchContext context = __LXDDispatchContextGetForQos(qos); 166 | dispatch_queue_t queue = __LXDDispatchContextGetQueue(context); 167 | dispatch_async(queue, block); 168 | return queue; 169 | } 170 | 171 | dispatch_queue_t LXDDispatchQueueAsyncBlockInUserInteractive(dispatch_block_t block) { 172 | return LXDDispatchQueueAsyncBlockInQOS(LXDQualityOfServiceUserInteractive, block); 173 | } 174 | 175 | dispatch_queue_t LXDDispatchQueueAsyncBlockInUserInitiated(dispatch_block_t block) { 176 | return LXDDispatchQueueAsyncBlockInQOS(LXDQualityOfServiceUserInitiated, block); 177 | } 178 | 179 | dispatch_queue_t LXDDispatchQueueAsyncBlockInUtility(dispatch_block_t block) { 180 | return LXDDispatchQueueAsyncBlockInQOS(LXDQualityOfServiceUtility, block); 181 | } 182 | 183 | dispatch_queue_t LXDDispatchQueueAsyncBlockInBackground(dispatch_block_t block) { 184 | return LXDDispatchQueueAsyncBlockInQOS(LXDQualityOfServiceBackground, block); 185 | } 186 | 187 | dispatch_queue_t LXDDispatchQueueAsyncBlockInDefault(dispatch_block_t block) { 188 | return LXDDispatchQueueAsyncBlockInQOS(LXDQualityOfServiceDefault, block); 189 | } 190 | 191 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Dispatch/LXDDispatchOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDDispatchOperation.h 3 | // LXDDispatchOperation 4 | // 5 | // Created by linxinda on 2017/4/6. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class LXDDispatchOperation; 12 | typedef void(^LXDCancelableBlock)(LXDDispatchOperation * operation); 13 | 14 | 15 | /*! 16 | * @brief 派发任务封装 17 | */ 18 | @interface LXDDispatchOperation : NSObject 19 | 20 | @property (nonatomic, readonly) BOOL isCanceled; 21 | @property (nonatomic, readonly) dispatch_queue_t queue; 22 | 23 | + (instancetype)dispatchOperationWithBlock: (dispatch_block_t)block; 24 | + (instancetype)dispatchOperationWithBlock: (dispatch_block_t)block inQoS: (NSQualityOfService)qos; 25 | 26 | + (instancetype)dispatchOperationWithCancelableBlock:(LXDCancelableBlock)block; 27 | + (instancetype)dispatchOperationWithCancelableBlock:(LXDCancelableBlock)block inQos: (NSQualityOfService)qos; 28 | 29 | - (void)start; 30 | - (void)cancel; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Dispatch/LXDDispatchOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDDispatchOperation.m 3 | // LXDDispatchOperation 4 | // 5 | // Created by linxinda on 2017/4/6. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDDispatchOperation.h" 10 | #import "LXDDispatchAsync.h" 11 | 12 | 13 | #ifndef LXDDispatchAsync_m 14 | #define LXD_INLINE static inline 15 | #endif 16 | 17 | #define LXD_FUNCTION_OVERLOAD __attribute__((overloadable)) 18 | 19 | 20 | LXD_INLINE LXD_FUNCTION_OVERLOAD void __LXDLockExecute(dispatch_block_t block, dispatch_time_t threshold); 21 | 22 | LXD_INLINE LXD_FUNCTION_OVERLOAD void __LXDLockExecute(dispatch_block_t block) { 23 | __LXDLockExecute(block, dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER)); 24 | } 25 | 26 | LXD_INLINE LXD_FUNCTION_OVERLOAD void __LXDLockExecute(dispatch_block_t block, dispatch_time_t threshold) { 27 | if (block == nil) { return ; } 28 | static dispatch_semaphore_t lxd_queue_semaphore; 29 | static dispatch_once_t once; 30 | dispatch_once(&once, ^{ 31 | lxd_queue_semaphore = dispatch_semaphore_create(0); 32 | }); 33 | dispatch_semaphore_wait(lxd_queue_semaphore, threshold); 34 | block(); 35 | dispatch_semaphore_signal(lxd_queue_semaphore); 36 | } 37 | 38 | 39 | @interface LXDDispatchOperation () 40 | 41 | @property (nonatomic, assign) BOOL isCanceled; 42 | @property (nonatomic, assign) BOOL isExcuting; 43 | @property (nonatomic, assign) dispatch_queue_t queue; 44 | @property (nonatomic, assign) dispatch_queue_t (*asyn)(dispatch_block_t); 45 | @property (nonatomic, copy) LXDCancelableBlock cancelableBlock; 46 | 47 | @end 48 | 49 | 50 | @implementation LXDDispatchOperation 51 | 52 | 53 | + (instancetype)dispatchOperationWithBlock: (dispatch_block_t)block { 54 | return [self dispatchOperationWithCancelableBlock: ^(LXDDispatchOperation *operation) { 55 | if (!operation.isCanceled) { 56 | block(); 57 | } 58 | } inQos: NSQualityOfServiceDefault]; 59 | } 60 | 61 | + (instancetype)dispatchOperationWithBlock: (dispatch_block_t)block inQoS: (NSQualityOfService)qos { 62 | return [self dispatchOperationWithCancelableBlock: ^(LXDDispatchOperation *operation) { 63 | if (!operation.isCanceled) { 64 | block(); 65 | } 66 | } inQos: qos]; 67 | } 68 | 69 | + (instancetype)dispatchOperationWithCancelableBlock:(LXDCancelableBlock)block { 70 | return [self dispatchOperationWithCancelableBlock: block inQos: NSQualityOfServiceDefault]; 71 | } 72 | 73 | + (instancetype)dispatchOperationWithCancelableBlock:(LXDCancelableBlock)block inQos: (NSQualityOfService)qos { 74 | return [[self alloc] initWithBlock: block inQos: qos]; 75 | } 76 | 77 | - (instancetype)initWithBlock: (LXDCancelableBlock)block inQos: (NSQualityOfService)qos { 78 | if (block == nil) { return nil; } 79 | if (self = [super init]) { 80 | switch (qos) { 81 | case NSQualityOfServiceUserInteractive: 82 | self.asyn = LXDDispatchQueueAsyncBlockInUserInteractive; 83 | break; 84 | case NSQualityOfServiceUserInitiated: 85 | self.asyn = LXDDispatchQueueAsyncBlockInUserInitiated; 86 | break; 87 | case NSQualityOfServiceDefault: 88 | self.asyn = LXDDispatchQueueAsyncBlockInDefault; 89 | break; 90 | case NSQualityOfServiceUtility: 91 | self.asyn = LXDDispatchQueueAsyncBlockInUtility; 92 | break; 93 | case NSQualityOfServiceBackground: 94 | self.asyn = LXDDispatchQueueAsyncBlockInBackground; 95 | break; 96 | default: 97 | self.asyn = LXDDispatchQueueAsyncBlockInDefault; 98 | break; 99 | } 100 | self.cancelableBlock = block; 101 | } 102 | return self; 103 | } 104 | 105 | - (void)dealloc { 106 | [self cancel]; 107 | } 108 | 109 | - (void)start { 110 | __LXDLockExecute(^{ 111 | self.queue = self.asyn(^{ 112 | self.cancelableBlock(self); 113 | self.cancelableBlock = nil; 114 | }); 115 | self.isExcuting = YES; 116 | }); 117 | } 118 | 119 | - (void)cancel { 120 | __LXDLockExecute(^{ 121 | self.isCanceled = YES; 122 | if (!self.isExcuting) { 123 | self.asyn = NULL; 124 | self.cancelableBlock = nil; 125 | } 126 | }); 127 | } 128 | 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Performance/LXDTransaction.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDTransaction.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/5/2. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 任务封装 13 | */ 14 | @interface LXDTransaction : NSObject 15 | 16 | + (void)begin; 17 | + (void)commit; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Performance/LXDTransaction.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDTransaction.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/5/2. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDTransaction.h" 10 | #import 11 | 12 | 13 | 14 | #define TRANSACTION_LOCK(__lock) dispatch_wait(__lock, DISPATCH_TIME_FOREVER) 15 | #define TRANSACTION_UNLOCK(__lock) dispatch_semaphore_signal(__lock); 16 | 17 | 18 | 19 | #pragma mark - Task Queue 20 | @interface LXDExecuteTaskQueue : NSObject 21 | @end 22 | 23 | 24 | @implementation LXDExecuteTaskQueue 25 | 26 | 27 | static inline CFMutableArrayRef _lxd_execute_task_queue() { 28 | static CFMutableArrayRef lxd_execute_task_queue; 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | lxd_execute_task_queue = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 32 | }); 33 | return lxd_execute_task_queue; 34 | } 35 | 36 | static inline dispatch_semaphore_t _lxd_excute_task_queue_lock() { 37 | static dispatch_semaphore_t lxd_transaction_queue_lock; 38 | static dispatch_once_t onceToken; 39 | dispatch_once(&onceToken, ^{ 40 | lxd_transaction_queue_lock = dispatch_semaphore_create(1); 41 | }); 42 | return lxd_transaction_queue_lock; 43 | } 44 | 45 | + (dispatch_block_t)fetchExecuteTask { 46 | if (CFArrayGetCount(_lxd_execute_task_queue()) == 0) { return nil; } 47 | TRANSACTION_LOCK(_lxd_excute_task_queue_lock()); 48 | dispatch_block_t executeTask = CFArrayGetValueAtIndex(_lxd_execute_task_queue(), 0); 49 | CFArrayRemoveValueAtIndex(_lxd_execute_task_queue(), 0); 50 | TRANSACTION_UNLOCK(_lxd_excute_task_queue_lock()); 51 | return executeTask; 52 | } 53 | 54 | + (void)insertExecuteTask: (dispatch_block_t)block { 55 | assert(block != nil); 56 | TRANSACTION_LOCK(_lxd_excute_task_queue_lock()); 57 | CFArrayAppendValue(_lxd_execute_task_queue(), (__bridge void *)[block copy]); 58 | TRANSACTION_UNLOCK(_lxd_excute_task_queue_lock()); 59 | } 60 | 61 | 62 | @end 63 | 64 | 65 | 66 | 67 | #pragma mark - RunLoop Observer 68 | static struct timeval lxd_free_loop_entry_time; 69 | static inline bool _lxd_calculate_time_interval_valid(struct timeval start, struct timeval end) { 70 | static long lxd_max_loop_time = NSEC_PER_SEC / 60 * 0.8; 71 | long time_interval = (end.tv_sec - start.tv_sec) * NSEC_PER_SEC + (end.tv_usec - start.tv_usec); 72 | return time_interval < lxd_max_loop_time; 73 | } 74 | 75 | static void _lxd_run_loop_free_time_observer(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void * info) { 76 | gettimeofday(&lxd_free_loop_entry_time, NULL); 77 | } 78 | 79 | static void _lxd_transaction_run_loop_observer(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void * info) { 80 | struct timeval current_time; 81 | dispatch_block_t executeTask; 82 | 83 | do { 84 | executeTask = [LXDExecuteTaskQueue fetchExecuteTask]; 85 | if (executeTask != nil) { executeTask(); } 86 | else { break; } 87 | gettimeofday(¤t_time, NULL); 88 | } while( _lxd_calculate_time_interval_valid(lxd_free_loop_entry_time, current_time) ); 89 | } 90 | 91 | 92 | 93 | #pragma mark - Transaction 94 | @implementation LXDTransaction 95 | 96 | 97 | static bool lxd_transaction_flag = false; 98 | static inline dispatch_semaphore_t _lxd_transaction_lock() { 99 | static dispatch_semaphore_t lxd_transaction_lock; 100 | static dispatch_once_t onceToken; 101 | dispatch_once(&onceToken, ^{ 102 | lxd_transaction_lock = dispatch_semaphore_create(1); 103 | }); 104 | return lxd_transaction_lock; 105 | } 106 | 107 | + (void)load { 108 | static dispatch_once_t onceToken; 109 | dispatch_once(&onceToken, ^{ 110 | CFRunLoopRef runloop = CFRunLoopGetMain(); 111 | CFRunLoopObserverRef observer; 112 | 113 | observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), 114 | kCFRunLoopBeforeWaiting | kCFRunLoopExit, 115 | true, 0x0, 116 | _lxd_run_loop_free_time_observer, NULL); 117 | CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); 118 | CFRelease(observer); 119 | 120 | observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), 121 | kCFRunLoopBeforeWaiting | kCFRunLoopExit, 122 | true, 123 | 0xFFFFFF, 124 | _lxd_transaction_run_loop_observer, NULL); 125 | CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); 126 | CFRelease(observer); 127 | }); 128 | } 129 | 130 | + (void)begin { 131 | if (lxd_transaction_flag) { return; } 132 | TRANSACTION_LOCK(_lxd_transaction_lock()); 133 | lxd_transaction_flag = true; 134 | TRANSACTION_UNLOCK(_lxd_transaction_lock()); 135 | } 136 | 137 | + (void)commit { 138 | if (!lxd_transaction_flag) { return; } 139 | TRANSACTION_LOCK(_lxd_transaction_lock()); 140 | lxd_transaction_flag = false; 141 | TRANSACTION_UNLOCK(_lxd_transaction_lock()); 142 | } 143 | 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Timer/LXDGlobalTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDGlobalTimer.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 全局倒计时 13 | */ 14 | @interface LXDGlobalTimer : NSObject 15 | 16 | /*! 17 | * @brief 注册定时器回调处理,返回时间戳作为key 18 | */ 19 | + (NSString *)registerTimerCallback: (dispatch_block_t)callback; 20 | 21 | /*! 22 | * @brief 注册定时器回调处理 23 | */ 24 | + (void)registerTimerCallback: (dispatch_block_t)callback key: (NSString *)key; 25 | 26 | /*! 27 | * @brief 取消定时器注册 28 | */ 29 | + (void)resignTimerCallbackWithKey: (NSString *)key; 30 | 31 | /*! 32 | * @brief 设置定时器间隔,默认为2 33 | */ 34 | + (void)setCallbackInterval: (NSUInteger)interval; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/Timer/LXDGlobalTimer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDGlobalTimer.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDGlobalTimer.h" 10 | #import "LXDDispatchAsync.h" 11 | 12 | 13 | static NSUInteger lxd_timer_time_interval = 2; 14 | static dispatch_source_t lxd_global_timer = NULL; 15 | static CFMutableDictionaryRef lxd_global_callbacks = NULL; 16 | 17 | 18 | @implementation LXDGlobalTimer 19 | 20 | 21 | CF_INLINE void __LXDSyncExecute(dispatch_block_t block) { 22 | LXDDispatchQueueAsyncBlockInBackground(^{ 23 | assert(block != nil); 24 | block(); 25 | }); 26 | } 27 | 28 | CF_INLINE void __LXDInitGlobalCallbacks() { 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | lxd_global_callbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 32 | }); 33 | } 34 | 35 | CF_INLINE void __LXDResetTimer() { 36 | if (lxd_global_timer != NULL) { 37 | dispatch_source_cancel(lxd_global_timer); 38 | } 39 | lxd_global_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, LXDDispatchQueueAsyncBlockInDefault(^{})); 40 | dispatch_source_set_timer(lxd_global_timer, DISPATCH_TIME_NOW, lxd_timer_time_interval * NSEC_PER_SEC, 0); 41 | dispatch_source_set_event_handler(lxd_global_timer, ^{ 42 | NSUInteger count = CFDictionaryGetCount(lxd_global_callbacks); 43 | void * callbacks[count]; 44 | CFDictionaryGetKeysAndValues(lxd_global_callbacks, NULL, (const void **)callbacks); 45 | for (uint idx = 0; idx < count; idx++) { 46 | dispatch_block_t callback = (__bridge dispatch_block_t)callbacks[idx]; 47 | callback(); 48 | } 49 | }); 50 | } 51 | 52 | CF_INLINE void __LXDAutoSwitchTimer() { 53 | if (CFDictionaryGetCount(lxd_global_callbacks) > 0) { 54 | if (lxd_global_timer == NULL) { 55 | __LXDResetTimer(); 56 | dispatch_resume(lxd_global_timer); 57 | } 58 | } else { 59 | if (lxd_global_timer != NULL) { 60 | dispatch_source_cancel(lxd_global_timer); 61 | lxd_global_timer = NULL; 62 | } 63 | } 64 | } 65 | 66 | + (NSString *)registerTimerCallback: (dispatch_block_t)callback { 67 | NSString * key = [NSString stringWithFormat: @"%.2f", [[NSDate date] timeIntervalSince1970]]; 68 | [self registerTimerCallback: callback key: key]; 69 | return key; 70 | } 71 | 72 | + (void)registerTimerCallback: (dispatch_block_t)callback key: (NSString *)key { 73 | if (!callback) { return; } 74 | __LXDInitGlobalCallbacks(); 75 | __LXDSyncExecute(^{ 76 | CFDictionarySetValue(lxd_global_callbacks, (__bridge void *)key, (__bridge void *)[callback copy]); 77 | __LXDAutoSwitchTimer(); 78 | }); 79 | } 80 | 81 | + (void)resignTimerCallbackWithKey: (NSString *)key { 82 | if (key == nil) { return; } 83 | __LXDInitGlobalCallbacks(); 84 | __LXDSyncExecute(^{ 85 | CFDictionaryRemoveValue(lxd_global_callbacks, (__bridge void *)key); 86 | __LXDAutoSwitchTimer(); 87 | }); 88 | } 89 | 90 | + (void)setCallbackInterval: (NSUInteger)interval { 91 | if (interval <= 0) { interval = 1; } 92 | lxd_timer_time_interval = interval; 93 | } 94 | 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/UI/LXDAsyncLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDAsyncLabel.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /*! 13 | * @brief 异步绘制标签栏 14 | */ 15 | @interface LXDAsyncLabel : UILabel 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/UI/LXDAsyncLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDAsyncLabel.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDAsyncLabel.h" 10 | #import "LXDDispatchAsync.h" 11 | #import 12 | 13 | 14 | @implementation LXDAsyncLabel 15 | 16 | 17 | - (void)setText: (NSString *)text { 18 | if ([NSThread isMainThread]) { 19 | LXDDispatchQueueAsyncBlockInBackground(^{ 20 | [self displayAttributedText: [[NSAttributedString alloc] initWithString: text attributes: @{ NSFontAttributeName: self.font, NSForegroundColorAttributeName: self.textColor }]]; 21 | }); 22 | } else { 23 | [self displayAttributedText: [[NSAttributedString alloc] initWithString: text attributes: @{ NSFontAttributeName: self.font, NSForegroundColorAttributeName: self.textColor }]]; 24 | } 25 | } 26 | 27 | - (void)setAttributedText: (NSAttributedString *)attributedText { 28 | if ([NSThread isMainThread]) { 29 | LXDDispatchQueueAsyncBlockInBackground(^{ 30 | [self displayAttributedText: attributedText]; 31 | }); 32 | } else { 33 | [self displayAttributedText: attributedText]; 34 | } 35 | } 36 | 37 | - (void)displayAttributedText: (NSAttributedString *)attributedText { 38 | if (attributedText == nil) { 39 | attributedText = [NSMutableAttributedString new]; 40 | } else if ([attributedText isMemberOfClass: [NSAttributedString class]]) { 41 | attributedText = attributedText.mutableCopy; 42 | } 43 | 44 | NSMutableParagraphStyle * style = [NSMutableParagraphStyle new]; 45 | style.alignment = self.textAlignment; 46 | [((NSMutableAttributedString *)attributedText) addAttributes: @{ NSParagraphStyleAttributeName: style } range: NSMakeRange(0, attributedText.length)]; 47 | 48 | CGSize size = self.frame.size; 49 | size.height += 10; 50 | UIGraphicsBeginImageContextWithOptions(size, NO, 0); 51 | CGContextRef context = UIGraphicsGetCurrentContext(); 52 | if (context != NULL) { 53 | CGContextSetTextMatrix(context, CGAffineTransformIdentity); 54 | CGContextTranslateCTM(context, 0, size.height); 55 | CGContextScaleCTM(context, 1, -1); 56 | 57 | CGSize textSize = [attributedText.string boundingRectWithSize: size options: NSStringDrawingUsesLineFragmentOrigin attributes: @{ NSFontAttributeName: self.font } context: nil].size; 58 | textSize.width = ceil(textSize.width); 59 | textSize.height = ceil(textSize.height); 60 | 61 | CGMutablePathRef path = CGPathCreateMutable(); 62 | CGPathAddRect(path, NULL, CGRectMake((size.width - textSize.width) / 2, 5, textSize.width, textSize.height)); 63 | CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText); 64 | CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attributedText.length), path, NULL); 65 | CTFrameDraw(frame, context); 66 | 67 | UIImage * contents = UIGraphicsGetImageFromCurrentImageContext(); 68 | UIGraphicsEndImageContext(); 69 | CFRelease(frameSetter); 70 | CFRelease(frame); 71 | CFRelease(path); 72 | dispatch_async(dispatch_get_main_queue(), ^{ 73 | self.layer.contents = (id)contents.CGImage; 74 | }); 75 | } 76 | } 77 | 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/UI/LXDMonitorUI.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDMonitorUI.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #ifndef LXDMonitorUI_h 10 | #define LXDMonitorUI_h 11 | 12 | #import "LXDAsyncLabel.h" 13 | #import "LXDTopWindow.h" 14 | 15 | #endif /* LXDMonitorUI_h */ 16 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/UI/LXDTopWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXDTopWindow.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 非交互的顶部窗口 13 | */ 14 | @interface LXDTopWindow : UIWindow 15 | 16 | + (instancetype)topWindow; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/UI/LXDTopWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDTopWindow.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/27. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "LXDTopWindow.h" 10 | 11 | 12 | static LXDTopWindow * lxd_top_window; 13 | 14 | 15 | 16 | @implementation LXDTopWindow 17 | 18 | 19 | + (instancetype)topWindow { 20 | #if DEBUG 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | lxd_top_window = [[super allocWithZone: NSDefaultMallocZone()] initWithFrame: [UIScreen mainScreen].bounds]; 24 | }); 25 | #endif 26 | return lxd_top_window; 27 | } 28 | 29 | + (instancetype)allocWithZone: (struct _NSZone *)zone { 30 | return [self topWindow]; 31 | } 32 | 33 | - (instancetype)copy { 34 | return [[self class] topWindow]; 35 | } 36 | 37 | - (instancetype)initWithFrame: (CGRect)frame { 38 | if (self = [super initWithFrame: frame]) { 39 | [super setUserInteractionEnabled: NO]; 40 | [super setWindowLevel: CGFLOAT_MAX]; 41 | 42 | self.rootViewController = [UIViewController new]; 43 | [self makeKeyAndVisible]; 44 | } 45 | return self; 46 | } 47 | 48 | - (void)setWindowLevel: (UIWindowLevel)windowLevel { } 49 | - (void)setBackgroundColor: (UIColor *)backgroundColor { } 50 | - (void)setUserInteractionEnabled: (BOOL)userInteractionEnabled { } 51 | 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/UI/UIApplication+LXDTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+LXDTool.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/28. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 工具类 13 | */ 14 | @interface UIApplication (LXDTool) 15 | 16 | - (UIViewController *)findTopViewController; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/LXDAppMonitor/Tool/UI/UIApplication+LXDTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+LXDTool.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/4/28. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "UIApplication+LXDTool.h" 10 | 11 | 12 | @implementation UIApplication (LXDTool) 13 | 14 | 15 | UIViewController * _findViewController(UIViewController * viewController) { 16 | if (viewController.presentedViewController != nil) { 17 | return _findViewController(viewController.presentedViewController); 18 | } else if ([viewController isKindOfClass: [UINavigationController class]]) { 19 | return _findViewController(((UINavigationController *)viewController).topViewController); 20 | } else if ([viewController isKindOfClass: [UITabBarController class]]) { 21 | UITabBarController * tabBarController = (UITabBarController *)viewController; 22 | return _findViewController(tabBarController.viewControllers[tabBarController.selectedIndex]); 23 | } 24 | return viewController; 25 | } 26 | 27 | - (UIViewController *)findTopViewController { 28 | UIWindow * window = self.keyWindow; 29 | if (self.keyWindow.windowLevel != UIWindowLevelNormal) { 30 | for (UIWindow * win in [self windows]) { 31 | if (win.windowLevel == UIWindowLevelNormal) { 32 | window = win; 33 | break; 34 | } 35 | } 36 | } 37 | UIView * frontView = [[window subviews] lastObject]; 38 | UIViewController * viewController; 39 | if ([frontView.nextResponder isKindOfClass: [UIViewController class]]) { 40 | viewController = (UIViewController *)frontView.nextResponder; 41 | } else { 42 | viewController = window.rootViewController; 43 | } 44 | return _findViewController(viewController); 45 | } 46 | 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /LXDAppMonitor/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /LXDAppMonitor/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LXDAppFluencyMonitor 4 | // 5 | // Created by linxinda on 2017/3/22. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "LXDFPSMonitor.h" 11 | #import "LXDResourceMonitor.h" 12 | 13 | 14 | @interface ViewController () 15 | 16 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 17 | 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | [self.tableView registerClass: [UITableViewCell class] forCellReuseIdentifier: @"cell"]; 26 | 27 | [FPS_MONITOR startMonitoring]; 28 | [RESOURCE_MONITOR startMonitoring]; 29 | } 30 | 31 | - (void)viewDidAppear: (BOOL)animated { 32 | [super viewDidAppear: animated]; 33 | } 34 | 35 | 36 | #pragma mark - UITableViewDataSource 37 | - (NSInteger)tableView: (UITableView *)tableView numberOfRowsInSection: (NSInteger)section { 38 | return 1000; 39 | } 40 | 41 | - (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath { 42 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: @"cell"]; 43 | cell.textLabel.text = [NSString stringWithFormat: @"%lu", indexPath.row]; 44 | if (indexPath.row > 0 && indexPath.row % 30 == 0) { 45 | usleep(2000000); 46 | } 47 | return cell; 48 | } 49 | 50 | 51 | #pragma mark - UITableViewDelegate 52 | - (void)tableView: (UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath { 53 | for (int idx = 0; idx < 100; idx++) { 54 | usleep(10000); 55 | } 56 | } 57 | 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /LXDAppMonitor/WebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewController.h 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | * @brief 网页 13 | */ 14 | @interface WebViewController : UIViewController 15 | 16 | - (instancetype)initWithUrl: (NSString *)url; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LXDAppMonitor/WebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewController.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import "WebViewController.h" 10 | #import 11 | 12 | 13 | @interface WebViewController () 14 | 15 | @property (nonatomic, copy) NSString * url; 16 | @property (weak, nonatomic) IBOutlet UIWebView *webView; 17 | 18 | @end 19 | 20 | @implementation WebViewController 21 | 22 | - (instancetype)initWithUrl: (NSString *)url { 23 | self = [super initWithNibName: NSStringFromClass([self class]) bundle: nil]; 24 | self.url = url; 25 | return self; 26 | } 27 | 28 | #define WEBKITSUPPORT 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | } 33 | 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /LXDAppMonitor/WebViewController.xib: -------------------------------------------------------------------------------- 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 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LXDAppMonitor/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LXDAppMonitor 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LXDAppMonitorTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LXDAppMonitorTests/LXDAppMonitorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDAppMonitorTests.m 3 | // LXDAppMonitorTests 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LXDAppMonitorTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LXDAppMonitorTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LXDAppMonitorUITests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LXDAppMonitorUITests/LXDAppMonitorUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXDAppMonitorUITests.m 3 | // LXDAppMonitorUITests 4 | // 5 | // Created by linxinda on 2017/3/29. 6 | // Copyright © 2017年 Jolimark. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LXDAppMonitorUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LXDAppMonitorUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | **LXDAppMonitor** 2 | ==== 3 | `LXDAppMonitor`是一套轻量级的应用性能管理组件。 4 | 5 | 为了实现高效率,项目中对`LXDDispatchQueue`多线程组件有比较强的依赖关系。`LXDAppMonitor`目前能够完成以下的监控任务: 6 | 7 | - `LXDAppFluencyMonitor` 应用卡顿检测 8 | 9 | [FLUENCYMONITOR startMonitoring]; 10 | - `LXDFPSMonitor` FPS监控器 11 | 12 | [FPS_MONITOR startMonitoring]; 13 | - `LXDDNSInterceptor` DNS解析器 14 | 重新实现`canonicalRequestForRequest`完成请求的重定向 15 | - `LXDResourceMonitor` 系统资源监控器 16 | 17 | [RESOURCE_MONITOR startMonitoring]; 18 | 19 | - `LXDCrashMonitor` 异常监控 20 | 21 | [LXDCrashMonitor startMonitoring]; 22 | 23 | **演示效果** 24 | ==== 25 | ![](http://upload-images.jianshu.io/upload_images/783864-3adef6f9d8cabc88.gif?imageMogr2/auto-orient/strip) 26 | 27 | **性能** 28 | ==== 29 | 采用`YYDispatchQueuePool`的相同多线程封装技术,对关键数据采集异步处理。所有监控数据展示器采用异步文本渲染方式,对性能几乎无影响。数据展示控件只在`DEBUG`模式下生效。 30 | 31 | **警告** 32 | ==== 33 | 当前`demo`由于笔者公司不实现服务器数据收集业务,部分功能仅能在开发阶段使用。提交商店审核前需删除`DNS`劫持部分代码或者移除此功能 34 | 35 | --------------------------------------------------------------------------------