├── .gitignore ├── Example ├── APMInsight_iOS.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── APMInsight_iOS.xcscheme │ └── xcuserdata │ │ └── xuminghao.eric.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── APMInsight_iOS.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── xuminghao.eric.xcuserdatad │ │ ├── IDEFindNavigatorScopes.plist │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── APMInsight_iOS │ ├── ALogUpload │ │ ├── APMInsightALogViewController.h │ │ └── APMInsightALogViewController.m │ ├── APMHomeViewController.h │ ├── APMHomeViewController.m │ ├── APMInitialViewController.h │ ├── APMInitialViewController.m │ ├── APMInsightCellItem.h │ ├── APMInsightCellItem.m │ ├── APMInsightViewController.h │ ├── APMInsightViewController.m │ ├── APMInsight_iOS.entitlements │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── 120*120.png │ │ │ ├── 152*152.png │ │ │ ├── 167*167.png │ │ │ ├── 180*180.png │ │ │ ├── 76*76.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── LaunchImage.imageset │ │ │ ├── APM-LOGO-1倍图.png │ │ │ ├── APM-LOGO-2倍图.png │ │ │ ├── APM-LOGO-3倍图.png │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CPUMonitor │ │ ├── APMInsightCPUViewController.h │ │ └── APMInsightCPUViewController.m │ ├── CloudCommand │ │ ├── APMInsightCustomCloudHandler.h │ │ └── APMInsightCustomCloudHandler.m │ ├── CrashAnalysis │ │ ├── APMInsightCoredumpViewController.h │ │ ├── APMInsightCoredumpViewController.m │ │ ├── APMInsightCrashViewController.h │ │ ├── APMInsightCrashViewController.mm │ │ ├── APMInsightGWPASanViewController.h │ │ └── APMInsightGWPASanViewController.m │ ├── DiskAnalysis │ │ ├── APMInsightDiskViewController.h │ │ └── APMInsightDiskViewController.m │ ├── Doctor │ │ ├── APMInsightDoctorViewController.h │ │ └── APMInsightDoctorViewController.m │ ├── ErrorAnalysis │ │ ├── APMInsightExceptionViewController.h │ │ └── APMInsightExceptionViewController.m │ ├── EventAnalysis │ │ ├── APMInsightEventViewController.h │ │ ├── APMInsightEventViewController.m │ │ ├── EventRecordManager.h │ │ └── EventRecordManager.m │ ├── Info.plist │ ├── LaggingAnalysis │ │ ├── APMInsightLagViewController.h │ │ └── APMInsightLagViewController.m │ ├── MemoryOptimization │ │ ├── APMInsightAutoreleaseObject │ │ │ ├── APMInsightAutoreleaseObject.h │ │ │ └── APMInsightAutoreleaseObject.m │ │ ├── APMInsightCaptureUITestViewController.h │ │ ├── APMInsightCaptureUITestViewController.m │ │ ├── APMInsightCaptureUITestViewController.xib │ │ ├── APMInsightLargeObject │ │ │ ├── APMInsightLargeObject.h │ │ │ └── APMInsightLargeObject.m │ │ ├── APMInsightLeakedObject │ │ │ ├── APMInsightLeakedObject.h │ │ │ └── APMInsightLeakedObject.m │ │ ├── APMInsightMemoryViewController.h │ │ └── APMInsightMemoryViewController.m │ ├── NetworkAnalysis │ │ ├── APMInsightNetworkViewController.h │ │ └── APMInsightNetworkViewController.m │ ├── PageMonitoring │ │ ├── APMInsightHybridViewController.h │ │ ├── APMInsightHybridViewController.m │ │ ├── APMInsightWebViewController.h │ │ └── APMInsightWebViewController.m │ ├── ProtectorAnalysis │ │ ├── APMInsightProtectorObject.h │ │ ├── APMInsightProtectorObject.m │ │ ├── APMInsightProtectorViewController.h │ │ └── APMInsightProtectorViewController.m │ ├── UserExperience │ │ ├── APMInsightPerformanceViewController.h │ │ └── APMInsightPerformanceViewController.m │ └── main.m ├── APMInsight_iOS_extension │ ├── APMInsight_iOS_extension.entitlements │ ├── Base.lproj │ │ └── MainInterface.storyboard │ ├── Info.plist │ ├── ShareViewController.h │ └── ShareViewController.m ├── Gemfile ├── Gemfile.lock ├── Podfile └── Podfile.lock ├── LICENSE ├── README.md ├── RangersAPM.podspec └── 说明.md /.gitignore: -------------------------------------------------------------------------------- 1 | Example/Pods/ -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcodeproj/xcshareddata/xcschemes/APMInsight_iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 44 | 46 | 52 | 53 | 54 | 55 | 61 | 63 | 69 | 70 | 71 | 72 | 74 | 75 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcodeproj/xcuserdata/xuminghao.eric.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | APMInsight_iOS.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | APMInsight_iOS_extension.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 7 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 4D931237255D1BCE00402D0B 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcworkspace/xcuserdata/xuminghao.eric.xcuserdatad/IDEFindNavigatorScopes.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS.xcworkspace/xcuserdata/xuminghao.eric.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ALogUpload/APMInsightALogViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightALogViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/8/5. 6 | // 7 | 8 | #import 9 | #import "APMInsightViewController.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface APMInsightALogViewController : APMInsightViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ALogUpload/APMInsightALogViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightALogViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/8/5. 6 | // 7 | 8 | #import "APMInsightALogViewController.h" 9 | #if __has_include() 10 | #import "RangersAPM+ALog.h" 11 | #endif 12 | #import "APMInsightCellItem.h" 13 | 14 | @implementation APMInsightALogViewController 15 | 16 | - (instancetype)init 17 | { 18 | self = [super init]; 19 | if (self) { 20 | self.cellIdentifier = @"APMInsightALogCell"; 21 | self.vcTitle = @"日志回捞"; 22 | #if __has_include() 23 | [RangersAPM setALogEnabled]; 24 | [RangersAPM enableConsoleLog]; 25 | #endif 26 | } 27 | return self; 28 | } 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | // Do any additional setup after loading the view. 33 | } 34 | 35 | - (void)setupItems { 36 | APMInsightCellItem *item = [[APMInsightCellItem alloc] init]; 37 | item.title = @"记录一条日志"; 38 | item.selectBlock = ^{ 39 | #if __has_include() 40 | RANGERSAPM_ALOG_INFO(@"APMInsight", @"write a log"); 41 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"日志记录成功" message:@"在平台创建云控命令后,APP下一次切换至前台或重新启动后,会上报记录的日志" preferredStyle:UIAlertControllerStyleAlert]; 42 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil]; 43 | [alert addAction:ok]; 44 | dispatch_async(dispatch_get_main_queue(), ^{ 45 | [self presentViewController:alert animated:YES completion:nil]; 46 | }); 47 | #endif 48 | }; 49 | [self.items addObject:item]; 50 | } 51 | 52 | /* 53 | #pragma mark - Navigation 54 | 55 | // In a storyboard-based application, you will often want to do a little preparation before navigation 56 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 57 | // Get the new view controller using [segue destinationViewController]. 58 | // Pass the selected object to the new view controller. 59 | } 60 | */ 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMHomeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | #import "APMInsightViewController.h" 10 | 11 | @interface APMHomeViewController : APMInsightViewController 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMHomeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMHomeViewController.h" 9 | #import "APMInsightCellItem.h" 10 | #import "APMInsightCrashViewController.h" 11 | #import "APMInsightExceptionViewController.h" 12 | #import "APMInsightLagViewController.h" 13 | #import "APMInsightPerformanceViewController.h" 14 | #import "APMInsightHybridViewController.h" 15 | #import "APMInsightMemoryViewController.h" 16 | #import "APMInsightNetworkViewController.h" 17 | #import "APMInsightEventViewController.h" 18 | #import "APMInsightALogViewController.h" 19 | #import "APMInsightProtectorViewController.h" 20 | #import "APMInsightCPUViewController.h" 21 | #import "APMInsightDiskViewController.h" 22 | #import "APMInsightDoctorViewController.h" 23 | 24 | @implementation APMHomeViewController 25 | 26 | - (instancetype)init 27 | { 28 | self = [super init]; 29 | if (self) { 30 | self.cellIdentifier = @"APMInsightHomeCell"; 31 | self.vcTitle = @"APMInsight"; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)viewDidLoad { 37 | [super viewDidLoad]; 38 | // Do any additional setup after loading the view. 39 | } 40 | 41 | - (void)viewWillAppear:(BOOL)animated { 42 | [super viewWillAppear:animated]; 43 | } 44 | 45 | - (void)viewDidAppear:(BOOL)animated { 46 | [super viewDidAppear:animated]; 47 | } 48 | 49 | - (void)setupItems { 50 | [super setupItems]; 51 | 52 | [self.items addObject:[self itemWithTitle:@"崩溃分析" viewController:[[APMInsightCrashViewController alloc] init]]]; 53 | [self.items addObject:[self itemWithTitle:@"错误分析" viewController:[[APMInsightExceptionViewController alloc] init]]]; 54 | [self.items addObject:[self itemWithTitle:@"卡顿分析" viewController:[[APMInsightLagViewController alloc] init]]]; 55 | [self.items addObject:[self itemWithTitle:@"事件分析" viewController:[[APMInsightEventViewController alloc] init]]]; 56 | [self.items addObject:[self itemWithTitle:@"用户体验" viewController:[[APMInsightPerformanceViewController alloc] init]]]; 57 | [self.items addObject:[self itemWithTitle:@"页面监控" viewController:[[APMInsightHybridViewController alloc] init]]]; 58 | [self.items addObject:[self itemWithTitle:@"网络优化" viewController:[[APMInsightNetworkViewController alloc] init]]]; 59 | [self.items addObject:[self itemWithTitle:@"内存优化" viewController:[[APMInsightMemoryViewController alloc] init]]]; 60 | [self.items addObject:[self itemWithTitle:@"日志回捞" viewController:[[APMInsightALogViewController alloc] init]]]; 61 | [self.items addObject:[self itemWithTitle:@"异常防护 - 崩溃防护" viewController:[[APMInsightProtectorViewController alloc] init]]]; 62 | [self.items addObject:[self itemWithTitle:@"CPU监控" viewController:[[APMInsightCPUViewController alloc] init]]]; 63 | [self.items addObject:[self itemWithTitle:@"磁盘监控" viewController:[[APMInsightDiskViewController alloc] init]]]; 64 | 65 | [self.items addObject:[self itemWithTitle:@"接入验证" viewController:[[APMInsightDoctorViewController alloc] init]]]; 66 | } 67 | 68 | - (APMInsightCellItem *)itemWithTitle:(NSString *)title viewController:(UIViewController *)viewController { 69 | __weak typeof(self) weakSelf = self; 70 | 71 | void(^block)(void) = ^{ 72 | dispatch_async(dispatch_get_main_queue(), ^{ 73 | __strong typeof(self) strongSelf = weakSelf; 74 | if (strongSelf) { 75 | [strongSelf.navigationController pushViewController:viewController animated:YES]; 76 | } 77 | }); 78 | }; 79 | APMInsightCellItem *item = [APMInsightCellItem itemWithTitle:title block:block]; 80 | return item; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMInitialViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInitialViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2023/5/22. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface APMInitialViewController : UIViewController 14 | 15 | @property (nonatomic, strong, class) RangersAPMConfig *config; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMInitialViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInitialViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2023/5/22. 6 | // 7 | 8 | #import "APMInitialViewController.h" 9 | #import "APMHomeViewController.h" 10 | 11 | /** 12 | 可复制部分开始--- 13 | Copyable section starts --- 14 | */ 15 | #import 16 | #import 17 | #if __has_include() 18 | #import 19 | #endif 20 | #if __has_include() 21 | #import 22 | #import "APMInsightCustomCloudHandler.h" 23 | #endif 24 | #if __has_include() 25 | #import 26 | #endif 27 | /** 28 | ---可复制部分结束 29 | --- Copyable section ends 30 | */ 31 | 32 | #import 33 | 34 | @interface APMInitialViewController () 35 | 36 | @property (nonatomic, strong) UIButton *initializeAPM; 37 | 38 | @property (nonatomic, strong) UIButton *initializeAPMAndStart; 39 | 40 | @end 41 | 42 | @implementation APMInitialViewController 43 | 44 | - (instancetype)init 45 | { 46 | self = [super init]; 47 | if (self) { 48 | self.title = @"APMInsight Init"; 49 | self.view.backgroundColor = [UIColor whiteColor]; 50 | [self setup]; 51 | } 52 | return self; 53 | } 54 | 55 | - (void)viewDidLoad { 56 | [super viewDidLoad]; 57 | // Do any additional setup after loading the view. 58 | } 59 | 60 | static RangersAPMConfig *_config; 61 | 62 | + (RangersAPMConfig *)config { 63 | return _config; 64 | } 65 | 66 | + (void)setConfig:(RangersAPMConfig *)config { 67 | _config = config; 68 | } 69 | 70 | #pragma mark View 71 | 72 | - (void)setup { 73 | CGFloat height = self.view.bounds.size.height; 74 | CGFloat width = self.view.bounds.size.width; 75 | 76 | CGFloat X = 100; 77 | CGFloat buttonWidth = width - X * 2; 78 | CGFloat buttonHeight = 100; 79 | 80 | CGFloat initializeAPMY = height / 2 - buttonHeight * 1.5; 81 | self.initializeAPM = [self buttonWithFrame:CGRectMake(X, initializeAPMY, buttonWidth, buttonHeight) title:@" 仅初始化APM\n(不启动信息采集)" selector:@selector(initializeAPMOnClick:)]; 82 | 83 | CGFloat initializeAPMAndStartY = height / 2 + buttonHeight * 0.5; 84 | self.initializeAPMAndStart = [self buttonWithFrame:CGRectMake(X, initializeAPMAndStartY, buttonWidth, buttonHeight) title:@"启动APM开始采集信息" selector:@selector(initializeAPMAndStartOnClick:)]; 85 | 86 | [self.view addSubview:self.initializeAPM]; 87 | [self.view addSubview:self.initializeAPMAndStart]; 88 | } 89 | 90 | #define BUTTON_RED 0 91 | #define BUTTON_GREEN 191.0/255.0 92 | #define BUTTON_BLUE 1.0 93 | #define BUTTON_ALPHA_NORMAL 1.0 94 | #define BUTTON_ALPHA_TOUCHDOWN 0.5 95 | 96 | - (UIButton *)buttonWithFrame:(CGRect)frame title:(NSString *)title selector:(SEL)aSelector { 97 | UIButton *button = [[UIButton alloc] initWithFrame:frame]; 98 | 99 | //Color 100 | [button setBackgroundColor:[UIColor colorWithRed:BUTTON_RED green:BUTTON_GREEN blue:BUTTON_BLUE alpha:BUTTON_ALPHA_NORMAL]]; 101 | [button addTarget:self action:@selector(buttonOnTouchDown:) forControlEvents:UIControlEventTouchDown]; 102 | [button addTarget:self action:@selector(buttonOnTouchUpInside:) forControlEvents:UIControlEventTouchUpInside]; 103 | [button addTarget:self action:@selector(buttonOnTouchUpOutside:) forControlEvents:UIControlEventTouchUpOutside]; 104 | 105 | button.layer.masksToBounds = YES; 106 | button.layer.cornerRadius = 20; 107 | 108 | [button setTitle:title forState:UIControlStateNormal]; 109 | button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping; 110 | button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter; 111 | 112 | [button addTarget:self action:aSelector forControlEvents:UIControlEventTouchUpInside]; 113 | 114 | return button; 115 | } 116 | 117 | - (void)buttonOnTouchDown:(UIButton *)button { 118 | [button setBackgroundColor:[UIColor colorWithRed:BUTTON_RED green:BUTTON_GREEN blue:BUTTON_BLUE alpha:BUTTON_ALPHA_TOUCHDOWN]]; 119 | } 120 | 121 | - (void)buttonOnTouchUpInside:(UIButton *)button { 122 | [button setBackgroundColor:[UIColor colorWithRed:BUTTON_RED green:BUTTON_GREEN blue:BUTTON_BLUE alpha:BUTTON_ALPHA_NORMAL]]; 123 | } 124 | 125 | - (void)buttonOnTouchUpOutside:(UIButton *)button { 126 | [button setBackgroundColor:[UIColor colorWithRed:BUTTON_RED green:BUTTON_GREEN blue:BUTTON_BLUE alpha:BUTTON_ALPHA_NORMAL]]; 127 | } 128 | 129 | 130 | #pragma mark APM 131 | 132 | - (void)initializeAPMOnClick:(UIButton *)button { 133 | static dispatch_once_t onceToken; 134 | dispatch_once(&onceToken, ^{ 135 | /** 136 | 可以把这部分初始化代码copy到您的工程中,建议初始化时机尽量靠前,否则可能出现启动崩溃无法捕获,或者启动分析数据产生误差 137 | ⚠️注意:①在复制此部分代码前请先参考Podfile文件,引入对应的组件 138 | ②导入头文件 139 | ③请修改config对应的属性值 140 | appID:平台为APP分配的ID 141 | channel:APP的发布渠道 142 | groupID:需要和扩展程序一致,以便读取共享空间 143 | 144 | You can copy the initialization code as follows to the same part in your project. 145 | And I suggest the code be excuted as early as possible. Otherwise, the crash that occurs during App Launching may not be detected and there may be error in the data of launch analysis. 146 | ⚠️Tips: ① Before you copy the code, please read the Podfile and install necessary cocoapods components first. 147 | ② Import the header files. 148 | ③ Set the propertys of the variable named "config" with your own values. 149 | appID: the ID of your App on APMInsight 150 | channel: the channel you App will publish to 151 | groupID: should be consistent with the extension in order to read the shared space 152 | */ 153 | /** 154 | 必要部分 !!! 155 | 可复制部分开始--- 156 | Necessary !!! 157 | Copyable section starts --- 158 | */ 159 | 160 | RangersAPMConfig *config = [RangersAPMConfig configWithAppID:@"233805" appToken:@"appToken"]; 161 | config.channel = @"App Store"; 162 | config.deviceIDSource = RAPMDeviceIDSourceFromAPMService; 163 | config.groupID = @"xxx"; //如果不需要监控扩展程序,则不需要此行代码 164 | 165 | /** 166 | 首次启动由于没有获取到配置,无法确定需要开启哪些功能模块。可以配置此属性,来决定首次启动默认需要开启的功能模块,仅对首次启动生效,一旦拉取到配置,下次启动就会先读取本地缓存的配置来决定。 167 | ⚠️注意: 168 | ①建议默认开启崩溃分析(RangersAPMCrashMonitorSwitch)、启动分析(RangersAPMLaunchMonitorSwitch)、网络分析(RangersAPMNetworkMonitorSwitch),避免一些和首次启动强相关的数据丢失 169 | ②配置默认开启模块后,新设备首次启动会默认打开这些模块,可能会出现平台上关闭了这些模块,但是依然有数据上报的情况,可能会给您的事件量造成意外的消耗;请根据您的应用情况灵活配置。 170 | 171 | Since the configuration is not obtained during the first startup, it is impossible to determine which function modules need to be enabled. This property can be configured to determine the function modules that need to be enabled by default for the first startup. It only takes effect for the first startup. Once the configuration is pulled, the next startup will read the configuration of the local cache to determine. 172 | ⚠️Tips: 173 | ① It is recommended to enable crash analysis (RangersAPMCrashMonitorSwitch), startup analysis (RangersAPMLaunchMonitorSwitch), and network analysis (RangersAPMNetworkMonitorSwitch) by default to avoid data loss that is strongly related to the first startup. 174 | ② After configuring the default enabled modules, these modules will be enabled by default when the new device is started for the first time. It may happen that these modules are closed on the platform, but there are still data reports, which may cause unexpected consumption of your event volume; please refer to Flexible configuration for your application. 175 | */ 176 | config.defaultMonitors = RangersAPMCrashMonitorSwitch; 177 | 178 | #if __has_include() 179 | /** 180 | 自定义日志相关配置 181 | */ 182 | RangersAPMALogParams *alogParams = [[RangersAPMALogParams alloc] init]; 183 | alogParams.maxDiskUsage = 50 * 1024 * 1024; 184 | alogParams.validityPeriod = 7 * 24 * 60 * 60; 185 | config.alogParams = alogParams; 186 | #endif 187 | /** 188 | 输出控制台日志,需要导入头文件 RangersAPM+DebugLog.h 189 | Print console log, import RangersAPM+DebugLog.h first 190 | */ 191 | #if DEBUG 192 | [RangersAPM allowDebugLogUsingLogger:^(NSString * _Nonnull log) { 193 | NSLog(@"APMInsight_iOS Debug Log : %@", log); 194 | }]; 195 | #endif 196 | /** 197 | ---可复制部分结束 198 | --- Copyable section ends 199 | */ 200 | 201 | 202 | APMInitialViewController.config = config; 203 | 204 | [button setTitle:@"APM已初始化" forState:UIControlStateNormal]; 205 | }); 206 | } 207 | 208 | - (void)initializeAPMAndStartOnClick:(UIButton *)button { 209 | [self initializeAPMOnClick:self.initializeAPM]; 210 | static dispatch_once_t onceToken; 211 | dispatch_once(&onceToken, ^{ 212 | /** 213 | 必要部分 !!! 214 | 可复制部分开始--- 215 | Necessary !!! 216 | Copyable section starts --- 217 | */ 218 | 219 | [RangersAPM startWithConfig:APMInitialViewController.config]; 220 | 221 | #if __has_include() 222 | [RangersAPM startProtectWithBootingThreshold:10 bootingCrashHandler:^(RangersAPMBootingInfo * _Nonnull info) { 223 | /** 224 | 对连续异常的场景进行防护策略,这里的demo只输出了一些log,您可以在自己的应用中做一些本地缓存清理或其他策略。可以针对不同的异常发生次数制定不同的策略。 225 | 226 | Implement protection strategies for consecutive exceptions. The demo here only outputs some logs. You can do some local cache cleaning or other strategies in your own application. Different strategies can be formulated for different exception occurrence times. 227 | */ 228 | if (info.consecutiveExceptionTimes >= 1) { 229 | NSLog(@"⚠️Consecutive exception 1 time"); 230 | } else if (info.consecutiveExceptionTimes >= 3) { 231 | NSAssert(NO, @"⚠️Consecutive exception 3 times !!!"); 232 | } 233 | 234 | /** 235 | 除了对连续异常进行防护,您也可以针对不同的异常类型执行不同的防护策略。 236 | 237 | In addition to protecting against consecutive exceptions, you can also implement different protection strategies for different exception types. 238 | */ 239 | if (info.crashTimes >= 1) { 240 | NSLog(@"⚠️Consecutive crash 1 time"); 241 | } 242 | if (info.watchdogTimes >= 1) { 243 | NSLog(@"⚠️Consecutive watchdog 1 time"); 244 | } 245 | if (info.OOMTimes >= 1) { 246 | NSLog(@"⚠️Consecutive oom 1 time"); 247 | } 248 | }]; 249 | #endif 250 | 251 | #if __has_include() 252 | [RangersAPM addCustomCommandHandlerCls:[APMInsightCustomCloudHandler class]]; 253 | #endif 254 | 255 | [RangersAPM setUserID:@"MYUSERID194767"]; 256 | 257 | /** 258 | ---可复制部分结束 259 | --- Copyable section ends 260 | */ 261 | 262 | [button setTitle:@"APM已启动,进入测试页面" forState:UIControlStateNormal]; 263 | [RangersAPM setupWindowWithAppID:APMInitialViewController.config.appID windowScene:nil]; 264 | }); 265 | 266 | [self.navigationController pushViewController:[[APMHomeViewController alloc] init] animated:YES]; 267 | } 268 | 269 | 270 | /* 271 | #pragma mark - Navigation 272 | 273 | // In a storyboard-based application, you will often want to do a little preparation before navigation 274 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 275 | // Get the new view controller using [segue destinationViewController]. 276 | // Pass the selected object to the new view controller. 277 | } 278 | */ 279 | 280 | @end 281 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMInsightCellItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCellItem.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightCellItem : NSObject 13 | 14 | @property (nonatomic, copy) NSString *title; 15 | 16 | @property (nonatomic, copy) dispatch_block_t selectBlock; 17 | 18 | + (instancetype)itemWithTitle:(NSString *)title block:(dispatch_block_t)block; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMInsightCellItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCellItem.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightCellItem.h" 9 | 10 | @implementation APMInsightCellItem 11 | 12 | + (instancetype)itemWithTitle:(NSString *)title block:(dispatch_block_t)block 13 | { 14 | APMInsightCellItem *item = [[APMInsightCellItem alloc] init]; 15 | item.title = title; 16 | item.selectBlock = block; 17 | return item; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMInsightViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/8/5. 6 | // 7 | 8 | #import 9 | #import "APMInsightCellItem.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface APMInsightViewController : UIViewController 14 | 15 | @property (nonatomic, strong) UITableView *tableView; 16 | 17 | @property (nonatomic, strong) NSMutableArray *items; 18 | 19 | @property (nonatomic, copy) NSString *cellIdentifier; 20 | 21 | @property (nonatomic, copy) NSString *vcTitle; 22 | 23 | - (void)setupItems; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMInsightViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/8/5. 6 | // 7 | 8 | #import "APMInsightViewController.h" 9 | #import "APMInsightCellItem.h" 10 | 11 | @interface APMInsightViewController () 12 | 13 | @end 14 | 15 | @implementation APMInsightViewController 16 | 17 | - (instancetype)init 18 | { 19 | self = [super init]; 20 | if (self) { 21 | self.items = [[NSMutableArray alloc] init]; 22 | self.cellIdentifier = NSStringFromClass([self class]); 23 | } 24 | return self; 25 | } 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 31 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:self.cellIdentifier]; 32 | self.tableView.delegate = self; 33 | self.tableView.dataSource = self; 34 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 35 | [self.view addSubview:self.tableView]; 36 | 37 | self.title = self.vcTitle; 38 | 39 | [self setupItems]; 40 | // Do any additional setup after loading the view. 41 | } 42 | 43 | #pragma mark UITableViewDelegate, UITableViewDelegate 44 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 45 | { 46 | return self.items.count; 47 | } 48 | 49 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 50 | { 51 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier]; 52 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 53 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 54 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 55 | return cell; 56 | } 57 | 58 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 59 | { 60 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 61 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 62 | if (item.selectBlock) { 63 | item.selectBlock(); 64 | } 65 | } 66 | 67 | - (void)setupItems { 68 | 69 | } 70 | 71 | /* 72 | #pragma mark - Navigation 73 | 74 | // In a storyboard-based application, you will often want to do a little preparation before navigation 75 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 76 | // Get the new view controller using [segue destinationViewController]. 77 | // Pass the selected object to the new view controller. 78 | } 79 | */ 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/APMInsight_iOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (nonatomic, strong) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "APMInitialViewController.h" 10 | /** 11 | 可复制部分开始--- 12 | Copyable section starts --- 13 | */ 14 | #if __has_include() 15 | #import 16 | #endif 17 | /** 18 | ---可复制部分结束 19 | --- Copyable section ends 20 | */ 21 | 22 | @interface AppDelegate () 23 | 24 | @end 25 | 26 | @implementation AppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 29 | [RangersAPM prewarmCheckEnd]; 30 | return YES; 31 | } 32 | 33 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 34 | // Override point for customization after application launch. 35 | 36 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 37 | APMInitialViewController * controller = [[APMInitialViewController alloc] init]; 38 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:controller]; 39 | [self.window makeKeyAndVisible]; 40 | 41 | return YES; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/120*120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/120*120.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/152*152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/152*152.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/167*167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/167*167.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/180*180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/180*180.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/76*76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/76*76.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "120*120.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "60x60" 38 | }, 39 | { 40 | "filename" : "180*180.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "scale" : "1x", 48 | "size" : "20x20" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "scale" : "2x", 53 | "size" : "20x20" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "scale" : "1x", 58 | "size" : "29x29" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "scale" : "2x", 63 | "size" : "29x29" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "scale" : "1x", 68 | "size" : "40x40" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "40x40" 74 | }, 75 | { 76 | "filename" : "76*76.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "76x76" 80 | }, 81 | { 82 | "filename" : "152*152.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "76x76" 86 | }, 87 | { 88 | "filename" : "167*167.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "83.5x83.5" 92 | }, 93 | { 94 | "idiom" : "ios-marketing", 95 | "scale" : "1x", 96 | "size" : "1024x1024" 97 | } 98 | ], 99 | "info" : { 100 | "author" : "xcode", 101 | "version" : 1 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/LaunchImage.imageset/APM-LOGO-1倍图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/LaunchImage.imageset/APM-LOGO-1倍图.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/LaunchImage.imageset/APM-LOGO-2倍图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/LaunchImage.imageset/APM-LOGO-2倍图.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/LaunchImage.imageset/APM-LOGO-3倍图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volcengine/APMInsight_iOS/042448ec3df813db78dd77900fa51c255db335ce/Example/APMInsight_iOS/Assets.xcassets/LaunchImage.imageset/APM-LOGO-3倍图.png -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "APM-LOGO-1倍图.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "APM-LOGO-2倍图.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "APM-LOGO-3倍图.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/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 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CPUMonitor/APMInsightCPUViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCPUViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/12/6. 6 | // 7 | 8 | #import "APMInsightViewController.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightCPUViewController : APMInsightViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CPUMonitor/APMInsightCPUViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCPUViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/12/6. 6 | // 7 | 8 | #import "APMInsightCPUViewController.h" 9 | #import "APMInsightCellItem.h" 10 | #include 11 | 12 | unsigned int countOfCPUCores(void) { 13 | unsigned int cpuCount; 14 | size_t len = sizeof(cpuCount); 15 | if(sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0) != 0) { // 系统调用失败,返回1 16 | return 1; 17 | } 18 | 19 | return cpuCount; 20 | } 21 | 22 | 23 | @interface APMInsightCPUViewController () 24 | 25 | @end 26 | 27 | @implementation APMInsightCPUViewController 28 | 29 | - (instancetype)init 30 | { 31 | self = [super init]; 32 | if (self) { 33 | self.cellIdentifier = @"APMInsightCPUCell"; 34 | self.vcTitle = @"CPU监控"; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)viewDidLoad { 40 | [super viewDidLoad]; 41 | // Do any additional setup after loading the view. 42 | } 43 | 44 | - (void)setupItems { 45 | APMInsightCellItem *item = [[APMInsightCellItem alloc] init]; 46 | item.title = @"触发CPU异常"; 47 | item.selectBlock = ^{ 48 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"触发CPU异常" message:@"触发CPU异常后,等待1~2分钟之后退出APP,然后重新启动,即可上报CPU异常日志" preferredStyle:UIAlertControllerStyleAlert]; 49 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 50 | int queueCount = countOfCPUCores(); 51 | for (int i = 0; i < queueCount; i++) { 52 | NSString *queueName = [NSString stringWithFormat:@"com.apminsight.testcpu%d",i]; 53 | dispatch_queue_t queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL); 54 | dispatch_async(queue, ^{ 55 | int sum = 0; 56 | while (1) { 57 | sum++; 58 | } 59 | }); 60 | } 61 | }]; 62 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 63 | [alert addAction:ok]; 64 | [alert addAction:cancel]; 65 | dispatch_async(dispatch_get_main_queue(), ^{ 66 | [self presentViewController:alert animated:YES completion:nil]; 67 | }); 68 | }; 69 | [self.items addObject:item]; 70 | } 71 | 72 | /* 73 | #pragma mark - Navigation 74 | 75 | // In a storyboard-based application, you will often want to do a little preparation before navigation 76 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 77 | // Get the new view controller using [segue destinationViewController]. 78 | // Pass the selected object to the new view controller. 79 | } 80 | */ 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CloudCommand/APMInsightCustomCloudHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCustomCloudHandler.h 3 | // APMInsight_iOS 4 | // 5 | // Created by ByteDance on 2023/7/18. 6 | // 7 | 8 | #if __has_include() 9 | 10 | #import 11 | #import "RangersAPM+CloudCommand.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface APMInsightCustomCloudHandler : RangersAPMCustomCommandBase 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CloudCommand/APMInsightCustomCloudHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCustomCloudHandler.m 3 | // APMInsight_iOS 4 | // 5 | // Created by ByteDance on 2023/7/18. 6 | // 7 | 8 | #if __has_include() 9 | 10 | #import "APMInsightCustomCloudHandler.h" 11 | 12 | @implementation APMInsightCustomCloudHandler 13 | 14 | + (NSString *)cloudCommandIdentifier { 15 | return @"pull_file"; 16 | } 17 | 18 | /// 创建用于执行指令的实例变量 19 | + (instancetype)createInstance { 20 | static APMInsightCustomCloudHandler *handler = nil; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | handler = [[APMInsightCustomCloudHandler alloc] init]; 24 | }); 25 | return handler; 26 | } 27 | 28 | 29 | /// 执行自定义命令 30 | - (void)executeCustomCommandWithParams:(NSDictionary *)params completion:(RangersAPMCustomCommandCompletion)completion { 31 | 32 | RangersAPMCustomCommandResult *result = [[RangersAPMCustomCommandResult alloc] init]; 33 | result.specificParams = @{@"aKey": @"aValue", @"bKey": @"bValue"}; 34 | 35 | NSString *filePath = [params objectForKey:@"file_path"]; 36 | if (!filePath || filePath.length == 0) { 37 | NSError *error = [NSError errorWithDomain:@"pull_file" code:0 userInfo:@{NSLocalizedFailureReasonErrorKey: @"pull file params is missing file_path."}]; 38 | result.error = error; 39 | 40 | if (completion) { 41 | completion(result); 42 | } 43 | 44 | return; 45 | } 46 | 47 | NSString *toBeUplodFilePath = [NSHomeDirectory() stringByAppendingPathComponent:filePath]; 48 | 49 | if (![[NSFileManager defaultManager] fileExistsAtPath:toBeUplodFilePath]) { 50 | NSError *error = [NSError errorWithDomain:@"pull_file" code:0 userInfo:@{NSLocalizedFailureReasonErrorKey: @"file is not exist."}]; 51 | result.error = error; 52 | 53 | if (completion) { 54 | completion(result); 55 | } 56 | 57 | return; 58 | } 59 | 60 | result.data = [NSData dataWithContentsOfFile:toBeUplodFilePath]; 61 | result.fileName = @"custom_file_name"; 62 | if (completion) { 63 | completion(result); 64 | } 65 | } 66 | 67 | /// 上传结果到服务器成功 68 | - (void)uploadCustomCommandResultSucceededWithParams:(NSDictionary *)params { 69 | NSLog(@"------ %s", __func__); 70 | } 71 | 72 | /// 上传结果到服务器失败 73 | - (void)uploadCustomCommandResultFailedWithParams:(NSDictionary *)params error:(NSError *)error { 74 | NSLog(@"------ %s", __func__); 75 | } 76 | 77 | @end 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CrashAnalysis/APMInsightCoredumpViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCoredumpViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by ByteDance on 2023/2/25. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightCoredumpViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CrashAnalysis/APMInsightCoredumpViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCoredumpViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by ByteDance on 2023/2/25. 6 | // 7 | 8 | #import "APMInsightCoredumpViewController.h" 9 | #import "APMInsightCellItem.h" 10 | 11 | @interface APMInsightCoredumpViewController () 12 | { 13 | dispatch_queue_t queue; 14 | } 15 | 16 | @property (nonatomic, strong) UITableView *tableView; 17 | 18 | @property (nonatomic, copy) NSMutableArray *items; 19 | 20 | @property (nonatomic, strong) NSMutableDictionary *testMultiThreadDictionary; 21 | 22 | @end 23 | 24 | @implementation APMInsightCoredumpViewController 25 | 26 | #pragma mark - Test case 1 27 | CF_INLINE void methodA() { 28 | methodB(); 29 | } 30 | 31 | CF_INLINE void methodB() { 32 | methodC(); 33 | } 34 | 35 | CF_INLINE void methodC() { 36 | int *pointer = (int *)0x01; 37 | *pointer = 6; 38 | } 39 | 40 | - (void)inlineFunctionTrigger { 41 | // do sth. 42 | methodA(); 43 | // do sth. 44 | } 45 | 46 | #pragma mark - Test case 2 47 | - (void)dispatchAsyncTrigger { 48 | queue = dispatch_queue_create("cn.volcengine.apmplus.testQueue", NULL); 49 | dispatch_queue_t queue1 = dispatch_queue_create("cn.volcengine.apmplus.testQueue1", NULL); 50 | __weak typeof(self) weakSelf = self; 51 | for (int i = 0; i < 100000; i++) { 52 | NSLog(@"1"); 53 | dispatch_async(queue1,^{ 54 | __strong typeof(self) strongSelf = weakSelf; 55 | NSLog(@"2"); 56 | dispatch_async(strongSelf->queue, ^{ 57 | NSLog(@"3"); 58 | }); 59 | }); 60 | } 61 | 62 | for (int i = 0; i < 100000; i++) { 63 | NSLog(@"4"); 64 | queue = dispatch_queue_create("cn.volcengine.apmplus.testQueue", NULL); 65 | } 66 | } 67 | 68 | - (void)viewDidLoad { 69 | [super viewDidLoad]; 70 | 71 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 72 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightCrashCell"]; 73 | self.tableView.delegate = self; 74 | self.tableView.dataSource = self; 75 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 76 | [self.view addSubview:self.tableView]; 77 | 78 | self.title = @"Coredump相关功能测试"; 79 | } 80 | 81 | #pragma mark UITableViewDataSource, UITableViewDelegate 82 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 83 | { 84 | return self.items.count; 85 | } 86 | 87 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 88 | { 89 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightCrashCell"]; 90 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 91 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 92 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 93 | return cell; 94 | } 95 | 96 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 97 | { 98 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 99 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 100 | if (item.selectBlock) { 101 | item.selectBlock(); 102 | } 103 | } 104 | 105 | 106 | #pragma mark Lazy-load 107 | - (NSMutableArray *)items { 108 | if (!_items) { 109 | _items = [[NSMutableArray alloc] init]; 110 | 111 | __weak typeof(self) weakSelf = self; 112 | void(^inlineFunctionTestBlock)(void) = ^{ 113 | __strong typeof(self) strongSelf = weakSelf; 114 | if (strongSelf) { 115 | [strongSelf inlineFunctionTrigger]; 116 | } 117 | }; 118 | APMInsightCellItem *inlineFunctionTestItem = [APMInsightCellItem itemWithTitle:@"测试Coredump对内联函数的支持" block:inlineFunctionTestBlock]; 119 | 120 | void(^dispatchAsyncBlock)(void) = ^{ 121 | __strong typeof(self) strongSelf = weakSelf; 122 | if (strongSelf) { 123 | [strongSelf dispatchAsyncTrigger]; 124 | } 125 | }; 126 | APMInsightCellItem *dispatchAsyncItem = [APMInsightCellItem itemWithTitle:@"多线程导致dispatch_async的Crash" block:dispatchAsyncBlock]; 127 | 128 | [_items addObject:inlineFunctionTestItem]; 129 | [_items addObject:dispatchAsyncItem]; 130 | } 131 | 132 | return _items; 133 | } 134 | 135 | #pragma mark - Tools 136 | 137 | - (UIAlertController *)alertWithTitle:(NSString *)title message:(NSString *)message { 138 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; 139 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"Get it." style:UIAlertActionStyleDefault handler:nil]; 140 | [alert addAction:ok]; 141 | return alert; 142 | } 143 | 144 | - (UIAlertController *)alertWithTitle:(NSString *)title message:(NSString *)message okHandler:(void (^)(void))okHandler{ 145 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; 146 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 147 | okHandler(); 148 | }]; 149 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 150 | [alert addAction:ok]; 151 | [alert addAction:cancel]; 152 | return alert; 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CrashAnalysis/APMInsightCrashViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCrashViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | @interface APMInsightCrashViewController : UIViewController 11 | 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CrashAnalysis/APMInsightCrashViewController.mm: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCrashViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightCrashViewController.h" 9 | #import "APMInsightCellItem.h" 10 | #include 11 | #import "APMInsightGWPASanViewController.h" 12 | #import "APMInsightCoredumpViewController.h" 13 | 14 | @interface APMInsightCrashViewController () 15 | 16 | @property (nonatomic, strong) UITableView *tableView; 17 | 18 | @property (nonatomic, copy) NSMutableArray *items; 19 | 20 | @end 21 | 22 | @implementation APMInsightCrashViewController 23 | 24 | #pragma mark - Test cases 25 | 26 | - (void)NSExceptionTrigger { 27 | dispatch_async(dispatch_get_main_queue(), ^{ 28 | UIAlertController *alert = [self alertWithTitle:@"NSException" message:@"即将触发NSException类型崩溃,APP将闪退,稍后重新启动APP即可在平台上看到崩溃日志" okHandler:^{ 29 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 30 | NSException *exception = [NSException exceptionWithName:@"TEST_EXCEPTION" reason:@"APMInsight_iOS is testing NSException" userInfo:nil]; 31 | [exception raise]; 32 | }); 33 | }]; 34 | [self presentViewController:alert animated:YES completion:nil]; 35 | }); 36 | } 37 | 38 | - (void)CPPExceptionTrigger { 39 | throw 0; 40 | } 41 | 42 | static BOOL shouldRaiseException = NO; 43 | 44 | - (void)AsyncExceptionTigger { 45 | shouldRaiseException = YES; 46 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 47 | [self tryRaiseException]; 48 | }); 49 | } 50 | 51 | - (void)tryRaiseException { 52 | if (shouldRaiseException) { 53 | [[NSException exceptionWithName:@"Test_Async_Exception" reason:@"APMInsight is testing AsyncException" userInfo:nil] raise]; 54 | } 55 | } 56 | 57 | - (void)signalExceptionTrigger { 58 | dispatch_async(dispatch_get_main_queue(), ^{ 59 | UIAlertController *alert = [self alertWithTitle:@"SignalException" message:@"即将触发Signal类型崩溃,APP将闪退,稍后重新启动APP即可在平台上看到崩溃日志" okHandler:^{ 60 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 61 | raise(SIGABRT); 62 | }); 63 | }]; 64 | [self presentViewController:alert animated:YES completion:nil]; 65 | }); 66 | } 67 | 68 | - (void)machExceptionTrigger { 69 | dispatch_async(dispatch_get_main_queue(), ^{ 70 | UIAlertController *alert = [self alertWithTitle:@"MachException" message:@"即将触发Mach类型崩溃,APP将闪退,稍后重新启动APP即可在平台上看到崩溃日志" okHandler:^{ 71 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 72 | int *pointer = (int *)0x01; 73 | *pointer = 6; 74 | }); 75 | }]; 76 | [self presentViewController:alert animated:YES completion:nil]; 77 | }); 78 | } 79 | 80 | - (void)watchdogExceptionTrigger { 81 | dispatch_async(dispatch_get_main_queue(), ^{ 82 | if (TARGET_IPHONE_SIMULATOR) { 83 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"不支持的设备" message:@"WatchDog无法在模拟器触发,请使用真机进行测试" preferredStyle:UIAlertControllerStyleAlert]; 84 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; 85 | [alert addAction:action]; 86 | [self presentViewController:alert animated:YES completion:nil]; 87 | } else if (TARGET_OS_IPHONE) { 88 | UIAlertController *alert = [self alertWithTitle:@"WatchDog" message:@"即将触发WatchDog,APP将卡住一段时间后闪退,稍后重新启动APP即可在平台上看到崩溃日志" okHandler:^{ 89 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 90 | dispatch_queue_t testWatchDogQueue = dispatch_queue_create("com.apminsight.testwatchdog", 0); 91 | static pthread_mutex_t lock1; 92 | pthread_mutex_init(&lock1, NULL); 93 | static pthread_mutex_t lock2; 94 | pthread_mutex_init(&lock2, NULL); 95 | dispatch_async(testWatchDogQueue, ^{ 96 | while (1) { 97 | pthread_mutex_lock(&lock1); 98 | // do something 99 | pthread_mutex_lock(&lock2); 100 | // do something 101 | pthread_mutex_unlock(&lock1); 102 | // do something 103 | pthread_mutex_unlock(&lock2); 104 | } 105 | }); 106 | dispatch_async(dispatch_get_main_queue(), ^{ 107 | while (1) { 108 | pthread_mutex_lock(&lock2); 109 | // do something 110 | pthread_mutex_lock(&lock1); 111 | // do something 112 | pthread_mutex_unlock(&lock2); 113 | // do something 114 | pthread_mutex_unlock(&lock1); 115 | } 116 | }); 117 | }); 118 | }]; 119 | [self presentViewController:alert animated:YES completion:nil]; 120 | } 121 | }); 122 | } 123 | 124 | - (void)OOMExceptionTrigger { 125 | dispatch_async(dispatch_get_main_queue(), ^{ 126 | if (TARGET_IPHONE_SIMULATOR) { 127 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"不支持的设备" message:@"OOM无法在模拟器触发,请使用真机进行测试" preferredStyle:UIAlertControllerStyleAlert]; 128 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; 129 | [alert addAction:action]; 130 | [self presentViewController:alert animated:YES completion:nil]; 131 | } else if (TARGET_OS_IPHONE) { 132 | UIAlertController *alert = [self alertWithTitle:@"Out of Memory" message:@"即将触发OOM,APP将闪退,稍后重新启动APP即可在平台上看到崩溃日志" okHandler:^{ 133 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 134 | while (1) { 135 | CGSize size = CGSizeMake(1024 * 8, 1024 * 8 * 9.0f/16.0); 136 | const size_t bitsPerComponent = 8; 137 | const size_t bytesPerRow = size.width * 4; 138 | CGContextRef ctx = CGBitmapContextCreate(calloc(sizeof(unsigned char), bytesPerRow * size.height), size.width, size.height, 139 | bitsPerComponent, bytesPerRow, 140 | CGColorSpaceCreateDeviceRGB(), 141 | kCGImageAlphaPremultipliedLast); 142 | CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0); 143 | CGContextFillRect(ctx, CGRectMake(0, 0, size.width, size.height)); 144 | } 145 | }); 146 | }]; 147 | [self presentViewController:alert animated:YES completion:nil]; 148 | } 149 | }); 150 | } 151 | 152 | - (void)GWPASanPageTrigger { 153 | APMInsightGWPASanViewController *GWPASanVC = [[APMInsightGWPASanViewController alloc] init]; 154 | [self.navigationController pushViewController:GWPASanVC animated:YES]; 155 | } 156 | 157 | - (void)coredumpPageTrigger { 158 | APMInsightCoredumpViewController *coredumpVC = [[APMInsightCoredumpViewController alloc] init]; 159 | [self.navigationController pushViewController:coredumpVC animated:YES]; 160 | } 161 | 162 | #pragma mark - UIViewController 163 | 164 | - (void)viewDidLoad { 165 | [super viewDidLoad]; 166 | 167 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 168 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightCrashCell"]; 169 | self.tableView.delegate = self; 170 | self.tableView.dataSource = self; 171 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 172 | [self.view addSubview:self.tableView]; 173 | 174 | self.title = @"崩溃分析"; 175 | // Do any additional setup after loading the view. 176 | } 177 | 178 | #pragma mark UITableViewDataSource, UITableViewDelegate 179 | 180 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 181 | { 182 | return self.items.count; 183 | } 184 | 185 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 186 | { 187 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightCrashCell"]; 188 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 189 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 190 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 191 | return cell; 192 | } 193 | 194 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 195 | { 196 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 197 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 198 | if (item.selectBlock) { 199 | item.selectBlock(); 200 | } 201 | } 202 | 203 | #pragma mark Lazy-load 204 | - (NSMutableArray *)items { 205 | if (!_items) { 206 | _items = [[NSMutableArray alloc] init]; 207 | 208 | __weak typeof(self) weakSelf = self; 209 | void(^NSExceptionBlock)(void) = ^{ 210 | __strong typeof(self) strongSelf = weakSelf; 211 | if (strongSelf) { 212 | [strongSelf NSExceptionTrigger]; 213 | } 214 | }; 215 | APMInsightCellItem *NSExceptionItem = [APMInsightCellItem itemWithTitle:@"触发NSException类型崩溃" block:NSExceptionBlock]; 216 | 217 | void(^cppExceptionBlock)(void) = ^{ 218 | __strong typeof(self) strongSelf = weakSelf; 219 | if (strongSelf) { 220 | [strongSelf CPPExceptionTrigger]; 221 | } 222 | }; 223 | APMInsightCellItem *cppExceptionItem = [APMInsightCellItem itemWithTitle:@"Hightlights -- 触发CPP类型崩溃" block:cppExceptionBlock]; 224 | 225 | void(^machExceptionBlock)(void) = ^{ 226 | __strong typeof(self) strongSelf = weakSelf; 227 | if (strongSelf) { 228 | [strongSelf machExceptionTrigger]; 229 | } 230 | }; 231 | APMInsightCellItem *machExceptionItem = [APMInsightCellItem itemWithTitle:@"触发Mach(EXC_BAD_ACCESS)类型崩溃" block:machExceptionBlock]; 232 | 233 | void(^fatalSignalBlock)(void) = ^{ 234 | __strong typeof(self) strongSelf = weakSelf; 235 | if (strongSelf) { 236 | [strongSelf signalExceptionTrigger]; 237 | } 238 | }; 239 | APMInsightCellItem *fatalSignalItem = [APMInsightCellItem itemWithTitle:@"触发SIGNAL类型崩溃" block:fatalSignalBlock]; 240 | 241 | void(^watchDogBlock)(void) = ^{ 242 | __strong typeof(self) strongSelf = weakSelf; 243 | if (strongSelf) { 244 | [strongSelf watchdogExceptionTrigger]; 245 | } 246 | }; 247 | APMInsightCellItem *watchDogItem = [APMInsightCellItem itemWithTitle:@"Highlights -- 触发卡死" block:watchDogBlock]; 248 | 249 | void(^OOMBlock)(void) = ^{ 250 | __strong typeof(self) strongSelf = weakSelf; 251 | if (strongSelf) { 252 | [self OOMExceptionTrigger]; 253 | } 254 | }; 255 | APMInsightCellItem *OOMItem = [APMInsightCellItem itemWithTitle:@"触发OOM" block:OOMBlock]; 256 | 257 | void(^AsyncBlock)(void) = ^{ 258 | __strong typeof(self) strongSelf = weakSelf; 259 | if (strongSelf) { 260 | [self AsyncExceptionTigger]; 261 | } 262 | }; 263 | APMInsightCellItem *AsyncItem = [APMInsightCellItem itemWithTitle:@"Highlights -- 触发AsyncException" block:AsyncBlock]; 264 | 265 | void(^GWPASanPageBlock)(void) = ^{ 266 | __strong typeof(self) strongSelf = weakSelf; 267 | if (strongSelf) { 268 | [strongSelf GWPASanPageTrigger]; 269 | } 270 | }; 271 | APMInsightCellItem *GWPASanPageItem = [APMInsightCellItem itemWithTitle:@"GWPASan相关功能测试" block:GWPASanPageBlock]; 272 | 273 | void(^coredumpPageBlock)(void) = ^{ 274 | __strong typeof(self) strongSelf = weakSelf; 275 | if (strongSelf) { 276 | [strongSelf coredumpPageTrigger]; 277 | } 278 | }; 279 | APMInsightCellItem *coredumpPageItem = [APMInsightCellItem itemWithTitle:@"Coredump相关功能测试" block:coredumpPageBlock]; 280 | 281 | [_items addObject:NSExceptionItem]; 282 | [_items addObject:cppExceptionItem]; 283 | [_items addObject:machExceptionItem]; 284 | [_items addObject:fatalSignalItem]; 285 | [_items addObject:watchDogItem]; 286 | [_items addObject:OOMItem]; 287 | [_items addObject:AsyncItem]; 288 | [_items addObject:GWPASanPageItem]; 289 | [_items addObject:coredumpPageItem]; 290 | } 291 | 292 | return _items; 293 | } 294 | 295 | #pragma mark - Tools 296 | 297 | - (UIAlertController *)alertWithTitle:(NSString *)title message:(NSString *)message okHandler:(void (^)(void))okHandler{ 298 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; 299 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 300 | okHandler(); 301 | }]; 302 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 303 | [alert addAction:ok]; 304 | [alert addAction:cancel]; 305 | return alert; 306 | } 307 | 308 | @end 309 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/CrashAnalysis/APMInsightGWPASanViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightGWPASanViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by ByteDance on 2023/2/22. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightGWPASanViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/DiskAnalysis/APMInsightDiskViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightDiskViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by Jerry on 2022/10/13. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightDiskViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/DiskAnalysis/APMInsightDiskViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightDiskViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by Jerry on 2022/10/13. 6 | // 7 | 8 | #import "APMInsightDiskViewController.h" 9 | 10 | @interface APMInsightDiskViewController () 11 | 12 | @property (nonatomic, strong) UILabel *remarkLabel; 13 | 14 | @end 15 | 16 | @implementation APMInsightDiskViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | [self.view addSubview:self.remarkLabel]; 22 | [self.view setBackgroundColor:[UIColor whiteColor]]; 23 | } 24 | 25 | #pragma mark - Lazy Loading 26 | - (UILabel *)remarkLabel { 27 | if (!_remarkLabel) { 28 | _remarkLabel = [[UILabel alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 29 | [_remarkLabel setTextAlignment:NSTextAlignmentCenter]; 30 | [_remarkLabel setNumberOfLines:0]; 31 | [_remarkLabel setFont:[UIFont systemFontOfSize:12]]; 32 | 33 | [_remarkLabel setText:@"磁盘监控启动的情况下,她会在程序后台时,检索沙盒文件并上报。\n 详细配置文档:https://www.volcengine.com/docs/6431/126085#%E7%A3%81%E7%9B%98%E7%9B%91%E6%8E%A7"]; 34 | } 35 | return _remarkLabel; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Doctor/APMInsightDoctorViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightDoctorViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2024/7/8. 6 | // 7 | 8 | #import "APMInsightViewController.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightDoctorViewController : APMInsightViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Doctor/APMInsightDoctorViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightDoctorViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2024/7/8. 6 | // 7 | 8 | #import "APMInsightDoctorViewController.h" 9 | #import "APMInitialViewController.h" 10 | #import 11 | 12 | @interface APMInsightDoctorViewController () 13 | 14 | @end 15 | 16 | @implementation APMInsightDoctorViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | } 22 | 23 | - (void)setupItems { 24 | APMInsightCellItem *item = [[APMInsightCellItem alloc] init]; 25 | NSString *appID = APMInitialViewController.config.appID; 26 | item.title = appID; 27 | item.selectBlock = ^{ 28 | [self.navigationController pushViewController:[RangersAPM generateViewControllerWithAppID:appID] animated:YES]; 29 | }; 30 | [self.items addObject:item]; 31 | } 32 | 33 | /* 34 | #pragma mark - Navigation 35 | 36 | // In a storyboard-based application, you will often want to do a little preparation before navigation 37 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 38 | // Get the new view controller using [segue destinationViewController]. 39 | // Pass the selected object to the new view controller. 40 | } 41 | */ 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ErrorAnalysis/APMInsightExceptionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightExceptionViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightExceptionViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ErrorAnalysis/APMInsightExceptionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightExceptionViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightExceptionViewController.h" 9 | #import "APMInsightCellItem.h" 10 | #if __has_include() 11 | #import 12 | #endif 13 | 14 | static NSString *const kExceptionTypePlaceholder = @"ExceptionType, Default:ExceptionTypeTest"; 15 | static NSString *const kCustomKeyPlaceholder = @"CustomKey, Default:customKeyDemoTest"; 16 | static NSString *const kCustomValuePlaceholder = @"CustomValue, Default:customValueDemoTest"; 17 | static NSString *const kFilterKeyPlaceholder = @"FilterKey, Default:filterKeyDemoTest"; 18 | static NSString *const kFilterValuePlaceholder = @"FilterValue, Default:filterValueDemoTest"; 19 | static NSString *const kDefaultAppIDPlaceholder = @"AppID, Default:"; 20 | static NSString *const kDefaultAppID = @"194767"; 21 | 22 | static NSString *const kUserNetworkErrorPlaceholder = @"https://www.ababaabb.com"; 23 | 24 | static NSInteger kExceptionCounts = 0; 25 | 26 | typedef void (^manualUserExceptionAlertHandler)(NSString *exceptionType, NSString *customKey, NSString *customValue, NSString *filterKey, NSString *filterValue, NSString *appID); 27 | 28 | @interface APMInsightExceptionViewController () 29 | 30 | @property (nonatomic, strong) UITableView *tableView; 31 | 32 | @property (nonatomic, copy) NSMutableArray *items; 33 | 34 | @end 35 | 36 | @implementation APMInsightExceptionViewController 37 | 38 | #pragma mark - Test cases 39 | 40 | - (void)recordUserException:(NSString *)exceptionType customs:(NSDictionary *)customs filters:(NSDictionary *)filters appID:(NSString *)appID callback:(void (^)(NSError * _Nullable))callback { 41 | #if __has_include() 42 | [RangersAPM trackAllThreadsLogExceptionType:exceptionType skippedDepth:0 customParams:customs filters:filters callback:^(NSError * _Nullable error) { 43 | callback(error); 44 | }]; 45 | #endif 46 | } 47 | 48 | - (void)userExceptionTrigger { 49 | BOOL __block success = YES; 50 | NSInteger exceptionCountsAfterRecord = kExceptionCounts + 5; //由于短时间内无法记录相同的错误,维护一个全局变量,以错误次数作为后缀 51 | for (NSInteger i = kExceptionCounts; i < exceptionCountsAfterRecord; i++) { 52 | [self recordUserException:[NSString stringWithFormat:@"ExceptionTypeDemoTest%ld", i] customs:@{@"customKeyDemoTest":@"customValueDemoTest"} filters:@{@"filterKeyDemoTest":@"filterValueDemooTest"} appID:kDefaultAppID callback:^(NSError * _Nullable error) { 53 | if (error) { 54 | success = NO; 55 | } 56 | }]; 57 | } 58 | kExceptionCounts = exceptionCountsAfterRecord; 59 | 60 | dispatch_async(dispatch_get_main_queue(), ^{ 61 | if (success) { 62 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"错误记录上报成功" message:@"请到应用性能监控全链路版控制台查看上报的自定义错误日志" preferredStyle:UIAlertControllerStyleAlert]; 63 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; 64 | [alert addAction:action]; 65 | [self presentViewController:alert animated:YES completion:nil]; 66 | } else { 67 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"错误记录上报失败" message:@"请重新尝试或手动触发" preferredStyle:UIAlertControllerStyleAlert]; 68 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; 69 | [alert addAction:action]; 70 | [self presentViewController:alert animated:YES completion:nil]; 71 | } 72 | }); 73 | } 74 | 75 | - (void)manualUserExceptionTrigger { 76 | dispatch_async(dispatch_get_main_queue(), ^{ 77 | UIAlertController *alert = [self userExceptionAlertWithOKHandler:^(NSString *exceptionType, NSString *customKey, NSString *customValue, NSString *filterKey, NSString *filterValue, NSString *appID) { 78 | [self recordUserException:exceptionType customs:@{customKey:customValue} filters:@{filterKey:filterValue} appID:appID callback:^(NSError * _Nullable error) { 79 | dispatch_async(dispatch_get_main_queue(), ^{ 80 | if (error) { 81 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"自定义错误记录失败" message:[NSString stringWithFormat:@"%@", error] preferredStyle:UIAlertControllerStyleAlert]; 82 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; 83 | [alert addAction:action]; 84 | [self presentViewController:alert animated:YES completion:nil]; 85 | } else { 86 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"自定义错误记录成功" message:nil preferredStyle:UIAlertControllerStyleAlert]; 87 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; 88 | [alert addAction:action]; 89 | [self presentViewController:alert animated:YES completion:nil]; 90 | } 91 | }); 92 | }]; 93 | }]; 94 | [self presentViewController:alert animated:YES completion:nil]; 95 | }); 96 | } 97 | 98 | - (void)networkErrorTrigger { 99 | NSString __block *urlString = @"https://www.ababaabb.com"; 100 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"网络错误" message:@"请输入能够触发错误的URL,如果不输入将使用默认值" preferredStyle:UIAlertControllerStyleAlert]; 101 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 102 | for (UITextField *textField in alert.textFields) { 103 | if ([textField.placeholder isEqualToString:kUserNetworkErrorPlaceholder]) { 104 | urlString = textField.text ?: urlString; 105 | } 106 | } 107 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]]; 108 | [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 109 | dispatch_async(dispatch_get_main_queue(), ^{ 110 | UIAlertController *successAlert = [UIAlertController alertControllerWithTitle:@"网络错误记录成功" message:@"请把APP退到后台以触发上报,稍后即可在平台上看到网络错误日志" preferredStyle:UIAlertControllerStyleAlert]; 111 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]; 112 | [successAlert addAction:action]; 113 | [self presentViewController:successAlert animated:YES completion:nil]; 114 | }); 115 | }] resume]; 116 | }]; 117 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 118 | [alert addAction:ok]; 119 | [alert addAction:cancel]; 120 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 121 | textField.placeholder = kUserNetworkErrorPlaceholder; 122 | }]; 123 | dispatch_async(dispatch_get_main_queue(), ^{ 124 | [self presentViewController:alert animated:YES completion:nil]; 125 | }); 126 | } 127 | 128 | #pragma mark - UIViewController 129 | 130 | - (void)viewDidLoad { 131 | [super viewDidLoad]; 132 | 133 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 134 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightExceptionCell"]; 135 | self.tableView.delegate = self; 136 | self.tableView.dataSource = self; 137 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 138 | [self.view addSubview:self.tableView]; 139 | 140 | self.title = @"错误分析"; 141 | // Do any additional setup after loading the view. 142 | } 143 | 144 | #pragma mark UITableViewDataSource, UITableViewDelegate 145 | 146 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 147 | { 148 | return self.items.count; 149 | } 150 | 151 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 152 | { 153 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightExceptionCell"]; 154 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 155 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 156 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 157 | return cell; 158 | } 159 | 160 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 161 | { 162 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 163 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 164 | if (item.selectBlock) { 165 | item.selectBlock(); 166 | } 167 | } 168 | 169 | #pragma mark - Lazy-load 170 | - (NSMutableArray *)items 171 | { 172 | if (!_items) { 173 | _items = [[NSMutableArray alloc] init]; 174 | 175 | __weak typeof(self) weakSelf = self; 176 | void(^userExceptionBlock)(void) = ^{ 177 | __strong typeof(self) strongSelf = weakSelf; 178 | if (strongSelf) { 179 | [strongSelf userExceptionTrigger]; 180 | } 181 | }; 182 | APMInsightCellItem *userExceptionItem = [APMInsightCellItem itemWithTitle:@"记录五次自定义错误并上报" block:userExceptionBlock]; 183 | 184 | void(^manualUserExceptionBlock)(void) = ^{ 185 | __strong typeof(self) strongSelf = weakSelf; 186 | if (strongSelf) { 187 | [strongSelf manualUserExceptionTrigger]; 188 | } 189 | }; 190 | APMInsightCellItem *manualUserExceptionItem = [APMInsightCellItem itemWithTitle:@"手动记录自定义错误(五次记录触发一次上报)" block:manualUserExceptionBlock]; 191 | 192 | void(^networkErrorBlock)(void) = ^{ 193 | __strong typeof(self) strongSelf = weakSelf; 194 | if (strongSelf) { 195 | [strongSelf networkErrorTrigger]; 196 | } 197 | }; 198 | APMInsightCellItem *networkErrorItem = [APMInsightCellItem itemWithTitle:@"触发网络错误" block:networkErrorBlock]; 199 | 200 | [_items addObject:userExceptionItem]; 201 | [_items addObject:manualUserExceptionItem]; 202 | [_items addObject:networkErrorItem]; 203 | } 204 | return _items; 205 | } 206 | 207 | - (UIAlertController *)userExceptionAlertWithOKHandler:(manualUserExceptionAlertHandler)okHandler{ 208 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"定制自定义错误" message:@"请输入自定义信息,不输入则使用默认值" preferredStyle:UIAlertControllerStyleAlert]; 209 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 210 | NSString *exceptionType; 211 | NSString *customKey; 212 | NSString *customValue; 213 | NSString *filterKey; 214 | NSString *filterValue; 215 | NSString *aid; 216 | for (UITextField *text in alert.textFields) { 217 | if ([text.placeholder isEqualToString:kExceptionTypePlaceholder]) { 218 | exceptionType = [NSString stringWithString:(text.text.length ? text.text : @"ExceptionTypeTest")]; 219 | continue;; 220 | } 221 | if ([text.placeholder isEqualToString:kCustomKeyPlaceholder]) { 222 | customKey = [NSString stringWithString:(text.text.length ? text.text : @"customKeyTest")]; 223 | continue;; 224 | } 225 | if ([text.placeholder isEqualToString:kCustomValuePlaceholder]) { 226 | customValue = [NSString stringWithString:(text.text.length ? text.text : @"customValueTest")]; 227 | continue;; 228 | } 229 | if ([text.placeholder isEqualToString:kFilterKeyPlaceholder]) { 230 | filterKey = [NSString stringWithString:(text.text.length ? text.text : @"filterKeyTest")]; 231 | continue;; 232 | } 233 | if ([text.placeholder isEqualToString:kFilterValuePlaceholder]) { 234 | filterValue = [NSString stringWithString:(text.text.length ? text.text : @"filterValueTest")]; 235 | continue;; 236 | } 237 | if ([text.placeholder isEqualToString:[NSString stringWithFormat:@"%@%@",kDefaultAppIDPlaceholder, kDefaultAppID]]) { 238 | aid = [NSString stringWithString:(text.text.length ? text.text : kDefaultAppID)]; 239 | continue;; 240 | } 241 | } 242 | 243 | okHandler(exceptionType, customKey, customValue, filterKey, filterValue, aid); 244 | }]; 245 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 246 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 247 | textField.placeholder = kExceptionTypePlaceholder; 248 | }]; 249 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 250 | textField.placeholder = kCustomKeyPlaceholder; 251 | }]; 252 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 253 | textField.placeholder = kCustomValuePlaceholder; 254 | }]; 255 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 256 | textField.placeholder = kFilterKeyPlaceholder; 257 | }]; 258 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 259 | textField.placeholder = kFilterValuePlaceholder; 260 | }]; 261 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 262 | textField.placeholder = [NSString stringWithFormat:@"%@%@",kDefaultAppIDPlaceholder, kDefaultAppID]; 263 | }]; 264 | [alert addAction:ok]; 265 | [alert addAction:cancel]; 266 | return alert; 267 | } 268 | 269 | @end 270 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/EventAnalysis/APMInsightEventViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightEventViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/2/28. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightEventViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/EventAnalysis/APMInsightEventViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightEventViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/2/28. 6 | // 7 | 8 | #import "APMInsightEventViewController.h" 9 | #import "EventRecordManager.h" 10 | #import "APMInsightCellItem.h" 11 | 12 | static NSString *const kEventNamePlaceholder = @"EventName, Default:eventNameDemoTest"; 13 | static NSString *const kMetricKeyPlaceholder = @"MetricKey, Default:metricKeyDemoTest"; 14 | static NSString *const kMetricValuePlaceholder = @"MetricValue, Default:0"; 15 | static NSString *const kDimensionKeyPlaceholder = @"DimensionKey, Default:DimensionKeyDemoTest"; 16 | static NSString *const kDimensionValuePlaceholder = @"DimensionValue, Default:DimensionValueDemoTest"; 17 | 18 | @interface APMInsightEventViewController () 19 | 20 | @property (nonatomic, strong) UITableView *tableView; 21 | 22 | @property (nonatomic, copy) NSMutableArray *items; 23 | 24 | @end 25 | 26 | @implementation APMInsightEventViewController 27 | 28 | - (void)viewDidLoad { 29 | [super viewDidLoad]; 30 | 31 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 32 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightEventCell"]; 33 | self.tableView.delegate = self; 34 | self.tableView.dataSource = self; 35 | 36 | UIView *footerView = [[UIView alloc] init]; 37 | 38 | UILabel *footerViewLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, self.view.frame.size.width - 40, 0)]; 39 | footerViewLabel.text = @"事件分析的日志,在APP启动之后会触发一次上报,之后每两分钟或者APP退到后台时上报一次,如果需要立即上报查看数据,可以尝试把APP切换到后台来触发上报"; 40 | footerViewLabel.textColor = [UIColor grayColor]; 41 | footerViewLabel.lineBreakMode = NSLineBreakByWordWrapping; 42 | footerViewLabel.numberOfLines = 0; 43 | footerViewLabel.font = [UIFont systemFontOfSize:13]; 44 | [footerViewLabel sizeToFit]; 45 | 46 | CGFloat height = footerViewLabel.frame.size.height; 47 | [footerView setFrame:CGRectMake(0, 0, 0, height)]; 48 | [footerView addSubview:footerViewLabel]; 49 | 50 | self.tableView.tableFooterView = footerView; 51 | 52 | [self.view addSubview:self.tableView]; 53 | 54 | self.title = @"事件分析"; 55 | } 56 | 57 | #pragma mark UITableViewDelegate, UITableViewDelegate 58 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 59 | { 60 | return self.items.count; 61 | } 62 | 63 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 64 | { 65 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightEventCell"]; 66 | if (indexPath.row < self.items.count) { 67 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 68 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 69 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 70 | } 71 | return cell; 72 | } 73 | 74 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 75 | { 76 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 77 | if (indexPath.row < self.items.count) { 78 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 79 | if (item.selectBlock) { 80 | item.selectBlock(); 81 | } 82 | } 83 | } 84 | 85 | #pragma mark Lazy-load 86 | - (NSMutableArray *)items { 87 | if (!_items) { 88 | _items = [[NSMutableArray alloc] init]; 89 | 90 | __weak typeof(self) weakSelf = self; 91 | 92 | void(^customEventBlock)(void) = ^{ 93 | __strong typeof(self) strongSelf = weakSelf; 94 | if (strongSelf) { 95 | 96 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"自定义事件" message:@"⚠️只有在平台事件管理配置开启的事件才能成功记录" preferredStyle:UIAlertControllerStyleAlert]; 97 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"记录事件" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 98 | NSString *eventName = @"eventNameDemoTest"; 99 | NSString *metricKey = @"metricKeyDemoTest"; 100 | NSNumber *metricValue = @(0); 101 | NSString *dimensionKey = @"dimensionKeyDemoTest"; 102 | NSString *dimensionValue = @"dimensionValueDemoTest"; 103 | for (UITextField *textField in alert.textFields) { 104 | NSString *placeholder = textField.placeholder; 105 | NSString *text = textField.text; 106 | if ([placeholder isEqualToString:kEventNamePlaceholder]) { 107 | eventName = text.length > 0 ? text : eventName; 108 | } else if ([placeholder isEqualToString:kMetricKeyPlaceholder]) { 109 | metricKey = text.length > 0 ? text : metricKey; 110 | } else if ([placeholder isEqualToString:kMetricValuePlaceholder]) { 111 | metricValue = text.length > 0 ? @([text integerValue]) : metricValue; 112 | } else if ([placeholder isEqualToString:kDimensionKeyPlaceholder]) { 113 | dimensionKey = text.length > 0 ? text : dimensionKey; 114 | } else if ([placeholder isEqualToString:kDimensionValuePlaceholder]) { 115 | dimensionValue = text.length > 0 ? text : dimensionValue; 116 | } 117 | } 118 | [EventRecordManager recordEvent:eventName metrics:@{metricKey:metricValue} dimension:@{dimensionKey:dimensionValue} extraValue:nil]; 119 | }]; 120 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 121 | 122 | }]; 123 | [alert addAction:ok]; 124 | [alert addAction:cancel]; 125 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 126 | textField.placeholder = kEventNamePlaceholder; 127 | }]; 128 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 129 | textField.placeholder = kMetricKeyPlaceholder; 130 | }]; 131 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 132 | textField.placeholder = kMetricValuePlaceholder; 133 | }]; 134 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 135 | textField.placeholder = kDimensionKeyPlaceholder; 136 | }]; 137 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 138 | textField.placeholder = kDimensionValuePlaceholder; 139 | }]; 140 | dispatch_async(dispatch_get_main_queue(), ^{ 141 | [strongSelf presentViewController:alert animated:YES completion:nil]; 142 | }); 143 | } 144 | }; 145 | APMInsightCellItem *customEventItem = [APMInsightCellItem itemWithTitle:@"记录一条自定义事件" block:customEventBlock]; 146 | 147 | [_items addObject:customEventItem]; 148 | } 149 | return _items; 150 | } 151 | /* 152 | #pragma mark - Navigation 153 | 154 | // In a storyboard-based application, you will often want to do a little preparation before navigation 155 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 156 | // Get the new view controller using [segue destinationViewController]. 157 | // Pass the selected object to the new view controller. 158 | } 159 | */ 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/EventAnalysis/EventRecordManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // EventRecordManager.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/2/28. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface EventRecordManager : NSObject 13 | 14 | + (void)recordEvent:(NSString *)eventName 15 | metrics:(nullable NSDictionary *)metrics 16 | dimension:(nullable NSDictionary *)dimension 17 | extraValue:(nullable NSDictionary *)extraValue; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/EventAnalysis/EventRecordManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // EventRecordManager.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2021/2/28. 6 | // 7 | 8 | #import "EventRecordManager.h" 9 | #if __has_include() 10 | #import 11 | #endif 12 | 13 | @implementation EventRecordManager 14 | 15 | + (void)recordEvent:(NSString *)eventName metrics:(NSDictionary *)metrics dimension:(NSDictionary *)dimension extraValue:(NSDictionary *)extraValue { 16 | #if __has_include() 17 | [RangersAPM trackEvent:eventName metrics:metrics dimension:dimension extraValue:extraValue]; 18 | #endif 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSupportsIndirectInputEvents 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/LaggingAnalysis/APMInsightLagViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightLagViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightLagViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/LaggingAnalysis/APMInsightLagViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightLagViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightLagViewController.h" 9 | #import "APMInsightCellItem.h" 10 | 11 | static NSString *const kUserLagPlaceholder = @"卡顿时长,单位s"; 12 | 13 | @interface APMInsightLagViewController () 14 | 15 | @property (nonatomic, strong) UITableView *tableView; 16 | 17 | @property (nonatomic, copy) NSMutableArray *items; 18 | 19 | @end 20 | 21 | @implementation APMInsightLagViewController 22 | 23 | #pragma mark - Test cases 24 | 25 | - (void)lagTrigger { 26 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"自定义卡顿" message:@"输入卡顿时长,点击确定将在5s后触发卡顿,如果不输入则默认卡顿3s。注意:相同的卡顿场景只会上报一次" preferredStyle:UIAlertControllerStyleAlert]; 27 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 28 | NSInteger lagTime = 0; 29 | for (UITextField *textFiled in alert.textFields) { 30 | if ([textFiled.placeholder isEqualToString:kUserLagPlaceholder]) { 31 | lagTime = [textFiled.text integerValue] ?: 3; 32 | break; 33 | } 34 | } 35 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 36 | sleep((unsigned int)lagTime); 37 | }); 38 | }]; 39 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 40 | [alert addAction:ok]; 41 | [alert addAction:cancel]; 42 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 43 | textField.placeholder = kUserLagPlaceholder; 44 | }]; 45 | dispatch_async(dispatch_get_main_queue(), ^{ 46 | [self presentViewController:alert animated:YES completion:nil]; 47 | }); 48 | } 49 | 50 | #pragma mark - UIViewController 51 | 52 | - (void)viewDidLoad { 53 | [super viewDidLoad]; 54 | 55 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 56 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightLagCell"]; 57 | self.tableView.delegate = self; 58 | self.tableView.dataSource = self; 59 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 60 | [self.view addSubview:self.tableView]; 61 | 62 | self.title = @"卡顿分析"; 63 | // Do any additional setup after loading the view. 64 | } 65 | 66 | #pragma mark UITableViewDelegate, UITableViewDelegate 67 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 68 | { 69 | return self.items.count; 70 | } 71 | 72 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 73 | { 74 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightLagCell"]; 75 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 76 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 77 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 78 | return cell; 79 | } 80 | 81 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 82 | { 83 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 84 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 85 | if (item.selectBlock) { 86 | item.selectBlock(); 87 | } 88 | } 89 | 90 | #pragma mark Lazy-load 91 | - (NSMutableArray *)items { 92 | if (!_items) { 93 | _items = [[NSMutableArray alloc] init]; 94 | 95 | __weak typeof(self) weakSelf = self; 96 | 97 | void(^userLagBlock)(void) = ^{ 98 | __strong typeof(self) strongSelf = weakSelf; 99 | if (strongSelf) { 100 | [strongSelf lagTrigger]; 101 | } 102 | }; 103 | APMInsightCellItem *userLagItem = [APMInsightCellItem itemWithTitle:@"触发自定义时长卡顿" block:userLagBlock]; 104 | 105 | [_items addObject:userLagItem]; 106 | } 107 | return _items; 108 | } 109 | /* 110 | #pragma mark - Navigation 111 | 112 | // In a storyboard-based application, you will often want to do a little preparation before navigation 113 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 114 | // Get the new view controller using [segue destinationViewController]. 115 | // Pass the selected object to the new view controller. 116 | } 117 | */ 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightAutoreleaseObject/APMInsightAutoreleaseObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightAutoreleaseObject.h 3 | // MemoryGraphDemo 4 | // 5 | // Created by bytedance on 2021/9/18. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightAutoreleaseObject : NSObject 13 | 14 | @property (nonatomic, strong) NSObject *helpManager; 15 | @property (nonatomic, strong) NSString *name; 16 | 17 | - (instancetype)initWithName:(NSString *)name; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightAutoreleaseObject/APMInsightAutoreleaseObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightAutoreleaseObject.m 3 | // MemoryGraphDemo 4 | // 5 | // Created by bytedance on 2021/9/18. 6 | // 7 | 8 | #import "APMInsightAutoreleaseObject.h" 9 | #import "APMInsightLargeObject.h" 10 | 11 | @interface APMInsightAutoreleaseObject () 12 | 13 | @property (nonatomic, strong) APMInsightLargeObject *largeObject; 14 | 15 | @end 16 | 17 | @implementation APMInsightAutoreleaseObject 18 | 19 | - (void)dealloc { 20 | NSLog(@"%s;", __func__); 21 | } 22 | 23 | - (instancetype)initWithName:(NSString *)name { 24 | if (self) { 25 | self.name = name; 26 | self.helpManager = [NSObject new]; 27 | self.largeObject = [[APMInsightLargeObject alloc] initWithLeakedObject:NO]; 28 | } 29 | return self; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightCaptureUITestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCaptureUITestViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by Jerry on 2022/1/30. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightCaptureUITestViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightCaptureUITestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightCaptureUITestViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by Jerry on 2022/1/30. 6 | // 7 | 8 | #import "APMInsightCaptureUITestViewController.h" 9 | #import "APMInsightLeakedObject.h" 10 | 11 | @interface APMInsightCaptureUITestViewController () 12 | 13 | @end 14 | 15 | @implementation APMInsightCaptureUITestViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view from its nib. 20 | } 21 | 22 | #pragma mark - Action 23 | - (IBAction)memoryTriggerAction:(UIButton *)sender { 24 | dispatch_async(dispatch_get_main_queue(), ^{ 25 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"触发内存泄漏" message:@"点击确定开始触发Leaked类型OOM崩溃,APP将闪退,稍后重新启动APP即可在平台上看到崩溃日志" preferredStyle:UIAlertControllerStyleAlert]; 26 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 27 | for (int index = 0; index < 500; index++) { 28 | #pragma clang diagnostic push 29 | #pragma clang diagnostic ignored "-Wunused-variable" 30 | APMInsightLeakedObject *leakedObject = [[APMInsightLeakedObject alloc] initWithName:[NSString stringWithFormat:@"APMInsightLeakedObject index = %d", index]]; 31 | #pragma clang diagnostic pop 32 | 33 | if (0 != index && 0 == index % 100) { 34 | sleep(1); 35 | } 36 | } 37 | }]; 38 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 39 | [alert addAction:ok]; 40 | [alert addAction:cancel]; 41 | [self presentViewController:alert animated:YES completion:nil]; 42 | }); 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightCaptureUITestViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 35 | 48 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightLargeObject/APMInsightLargeObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightLargeObject.h 3 | // MemoryGraphDemo 4 | // 5 | // Created by bytedance on 2021/10/19. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightLargeObject : NSObject 13 | 14 | /** 15 | * 内部是否要泄露内存 16 | */ 17 | - (instancetype)initWithLeakedObject:(BOOL)isLeaked; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightLargeObject/APMInsightLargeObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightLargeObject.m 3 | // MemoryGraphDemo 4 | // 5 | // Created by bytedance on 2021/10/19. 6 | // 7 | 8 | #import "APMInsightLargeObject.h" 9 | 10 | @interface APMInsightInnerLargeObject : NSObject 11 | { 12 | long *_nums; 13 | } 14 | @end 15 | 16 | @implementation APMInsightInnerLargeObject 17 | 18 | - (void)dealloc { 19 | [super dealloc]; 20 | free(_nums); 21 | } 22 | 23 | - (instancetype)init { 24 | self = [super init]; 25 | if (self) { 26 | /** 27 | * 申请内存块 28 | */ 29 | _nums = (long *)malloc(sizeof(long) * 1024 * 512); 30 | /** 31 | * 苹果对于内存块不设置的情况,认定为clean memory并不会真正的分配内存。 32 | * 因此,这里对申请的内存块进行设置值,变更为dirty memory。 33 | */ 34 | for (int i = 0; i < 1024 * 512; i++) { 35 | _nums[i] = i; 36 | } 37 | } 38 | return self; 39 | } 40 | 41 | @end 42 | 43 | 44 | @interface APMInsightLargeObject () 45 | { 46 | BOOL _isLeaked; 47 | } 48 | 49 | @property (nonatomic, strong) APMInsightInnerLargeObject *largeObject; 50 | 51 | @end 52 | 53 | @implementation APMInsightLargeObject 54 | 55 | - (void)dealloc { 56 | if (!_isLeaked) { 57 | [self.largeObject release]; 58 | } 59 | 60 | [super dealloc]; 61 | } 62 | 63 | - (instancetype)initWithLeakedObject:(BOOL)isLeaked { 64 | self = [super init]; 65 | if (self) { 66 | _isLeaked = isLeaked; 67 | 68 | self.largeObject = [[APMInsightInnerLargeObject alloc] init]; 69 | } 70 | return self; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightLeakedObject/APMInsightLeakedObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightLeakedObject.h 3 | // MemoryGraphDemo 4 | // 5 | // Created by bytedance on 2021/10/19. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightLeakedObject : NSObject 13 | 14 | @property (nonatomic, strong) NSObject *helpManager; 15 | @property (nonatomic, strong) NSString *name; 16 | 17 | - (instancetype)initWithName:(NSString *)name; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightLeakedObject/APMInsightLeakedObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightLeakedObject.m 3 | // MemoryGraphDemo 4 | // 5 | // Created by bytedance on 2021/10/19. 6 | // 7 | 8 | #import "APMInsightLeakedObject.h" 9 | #import "APMInsightLargeObject.h" 10 | #import 11 | 12 | @interface APMInsightLeakedObject () 13 | 14 | @property (nonatomic, strong) APMInsightLargeObject *largeObject; 15 | 16 | @end 17 | 18 | @implementation APMInsightLeakedObject 19 | 20 | - (void)dealloc { 21 | NSLog(@"%s;", __func__); 22 | } 23 | 24 | - (instancetype)initWithName:(NSString *)name { 25 | if (self) { 26 | self.name = name; 27 | self.helpManager = [NSObject new]; 28 | self.largeObject = [[APMInsightLargeObject alloc] initWithLeakedObject:YES]; 29 | } 30 | return self; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightMemoryViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightMemoryViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightMemoryViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/MemoryOptimization/APMInsightMemoryViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightMemoryViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightMemoryViewController.h" 9 | #import "APMInsightCellItem.h" 10 | #import 11 | #import "APMInsightCaptureUITestViewController.h" 12 | #import "APMInsightLeakedObject.h" 13 | #import "APMInsightAutoreleaseObject.h" 14 | 15 | static float dangerousMemoryThreshold = 512.0; 16 | 17 | bool overMemoryThreshold(void) 18 | { 19 | kern_return_t kr; 20 | 21 | task_vm_info_data_t task_vm; 22 | mach_msg_type_number_t task_vm_count = TASK_VM_INFO_COUNT; 23 | kr = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &task_vm, &task_vm_count); 24 | 25 | if (kr == KERN_SUCCESS) { 26 | printf("APMInsight Debug Log : Current App Memory is :%f\n\n", task_vm.phys_footprint / (1024.0 * 1024.0)); 27 | if (task_vm.phys_footprint / (1024.0 * 1024.0) > dangerousMemoryThreshold) { 28 | return true; 29 | } else { 30 | return false; 31 | } 32 | } 33 | 34 | return false; 35 | } 36 | 37 | @interface APMInsightMemoryViewController () 38 | 39 | @property (nonatomic, strong) UITableView *tableView; 40 | 41 | @property (nonatomic, copy) NSMutableArray *items; 42 | 43 | @end 44 | 45 | @implementation APMInsightMemoryViewController 46 | 47 | - (void)viewDidLoad { 48 | [super viewDidLoad]; 49 | 50 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 51 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightMemoryCell"]; 52 | self.tableView.delegate = self; 53 | self.tableView.dataSource = self; 54 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 55 | [self.view addSubview:self.tableView]; 56 | 57 | self.title = @"内存优化"; 58 | // Do any additional setup after loading the view. 59 | } 60 | 61 | #pragma mark UITableViewDelegate, UITableViewDelegate 62 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 63 | { 64 | return self.items.count; 65 | } 66 | 67 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 68 | { 69 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightMemoryCell"]; 70 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 71 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 72 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 73 | return cell; 74 | } 75 | 76 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 77 | { 78 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 79 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 80 | if (item.selectBlock) { 81 | item.selectBlock(); 82 | } 83 | } 84 | 85 | #pragma mark - Lazy-load 86 | - (NSMutableArray *)items { 87 | if (!_items) { 88 | _items = [[NSMutableArray alloc] init]; 89 | 90 | __weak typeof(self) weakSelf = self; 91 | 92 | void(^memoryTriggerBlock)(void) = ^{ 93 | __strong typeof(weakSelf) strongSelf = weakSelf; 94 | if (strongSelf) { 95 | dispatch_async(dispatch_get_main_queue(), ^{ 96 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"触发内存泄漏" message:@"点击确定开始触发内存泄漏,当APP占用内存超过512MB时会触发内存分析,在某些情况下,可能APP内存没有达到512MB就被系统KILL,如果未收到内存分析成功提示(大概5s之后),请重新启动APP触发泄漏。" preferredStyle:UIAlertControllerStyleAlert]; 97 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 98 | while (1) { 99 | if (!overMemoryThreshold()) { 100 | CGSize size = CGSizeMake(1024 * 8, 1024 * 8 * 9.0f/16.0); 101 | const size_t bitsPerComponent = 8; 102 | const size_t bytesPerRow = size.width * 4; 103 | CGContextRef ctx = CGBitmapContextCreate(calloc(sizeof(unsigned char), bytesPerRow * size.height), size.width, size.height, bitsPerComponent, bytesPerRow, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast); 104 | CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0); 105 | CGContextFillRect(ctx, CGRectMake(0, 0, size.width, size.height)); 106 | } else { 107 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 108 | UIAlertController *successAlert = [UIAlertController alertControllerWithTitle:@"内存分析完成" message:@"请重新启动APP,然后APP会自动上报内存日志,由于存在采样,可能需要多次启动才可成功上报,具体可以查看帮助文档。" preferredStyle:UIAlertControllerStyleAlert]; 109 | UIAlertAction *okk = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]; 110 | [successAlert addAction:okk]; 111 | [strongSelf presentViewController:successAlert animated:YES completion:nil]; 112 | }); 113 | break; 114 | } 115 | } 116 | }]; 117 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 118 | [alert addAction:ok]; 119 | [alert addAction:cancel]; 120 | [strongSelf presentViewController:alert animated:YES completion:nil]; 121 | }); 122 | } 123 | }; 124 | APMInsightCellItem *memoryTriggerItem = [APMInsightCellItem itemWithTitle:@"测试内存优化(泄漏、大对象、单设备查询)" block:memoryTriggerBlock]; 125 | 126 | [_items addObject:memoryTriggerItem]; 127 | 128 | void(^captureUITestBlock)(void) = ^{ 129 | __strong typeof(weakSelf) strongSelf = weakSelf; 130 | if (strongSelf) { 131 | APMInsightCaptureUITestViewController *captureUITest = [[APMInsightCaptureUITestViewController alloc] initWithNibName:@"APMInsightCaptureUITestViewController" bundle:nil]; 132 | [strongSelf.navigationController pushViewController:captureUITest animated:YES]; 133 | } 134 | }; 135 | APMInsightCellItem *captureUITestItem = [APMInsightCellItem itemWithTitle:@"视图层级" block:captureUITestBlock]; 136 | [_items addObject:captureUITestItem]; 137 | 138 | 139 | void(^leakedTriggerBlock)(void) = ^{ 140 | __strong typeof(weakSelf) strongSelf = weakSelf; 141 | if (strongSelf) { 142 | dispatch_async(dispatch_get_main_queue(), ^{ 143 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"触发内存泄漏" message:@"点击确定开始触发Leaked类型OOM崩溃,APP将闪退,稍后重新启动APP即可在平台上看到崩溃日志" preferredStyle:UIAlertControllerStyleAlert]; 144 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 145 | for (int index = 0; index < 500; index++) { 146 | #pragma clang diagnostic push 147 | #pragma clang diagnostic ignored "-Wunused-variable" 148 | APMInsightLeakedObject *leakedObject = [[APMInsightLeakedObject alloc] initWithName:[NSString stringWithFormat:@"APMInsightLeakedObject index = %d", index]]; 149 | #pragma clang diagnostic pop 150 | 151 | if (0 != index && 0 == index % 100) { 152 | sleep(1); 153 | } 154 | } 155 | }]; 156 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 157 | [alert addAction:ok]; 158 | [alert addAction:cancel]; 159 | [strongSelf presentViewController:alert animated:YES completion:nil]; 160 | }); 161 | } 162 | }; 163 | APMInsightCellItem *leakedTriggerItem = [APMInsightCellItem itemWithTitle:@"内存泄露问题" block:leakedTriggerBlock]; 164 | 165 | [_items addObject:leakedTriggerItem]; 166 | 167 | 168 | void(^autoreleaseTriggerBlock)(void) = ^{ 169 | __strong typeof(weakSelf) strongSelf = weakSelf; 170 | if (strongSelf) { 171 | dispatch_async(dispatch_get_main_queue(), ^{ 172 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"触发内存问题" message:@"点击确定开始触发AutoreleasePool类型OOM崩溃,APP将闪退,稍后重新启动APP即可在平台上看到崩溃日志" preferredStyle:UIAlertControllerStyleAlert]; 173 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 174 | for (int index = 0; index < 500; index++) { 175 | #pragma clang diagnostic push 176 | #pragma clang diagnostic ignored "-Wunused-variable" 177 | __autoreleasing APMInsightAutoreleaseObject *autoreleaseObject = [[APMInsightAutoreleaseObject alloc] initWithName:[NSString stringWithFormat:@"APMInsightAutoreleaseObject index %d", index]]; 178 | #pragma clang diagnostic pop 179 | 180 | if (0 != index && 0 == index % 100) { 181 | sleep(1); 182 | } 183 | } 184 | }]; 185 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 186 | [alert addAction:ok]; 187 | [alert addAction:cancel]; 188 | [strongSelf presentViewController:alert animated:YES completion:nil]; 189 | }); 190 | } 191 | }; 192 | APMInsightCellItem *autoreleaseTriggerItem = [APMInsightCellItem itemWithTitle:@"自动释放池问题" block:autoreleaseTriggerBlock]; 193 | 194 | [_items addObject:autoreleaseTriggerItem]; 195 | } 196 | return _items; 197 | } 198 | 199 | /* 200 | #pragma mark - Navigation 201 | 202 | // In a storyboard-based application, you will often want to do a little preparation before navigation 203 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 204 | // Get the new view controller using [segue destinationViewController]. 205 | // Pass the selected object to the new view controller. 206 | } 207 | */ 208 | 209 | @end 210 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/NetworkAnalysis/APMInsightNetworkViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightNetworkViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightNetworkViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/NetworkAnalysis/APMInsightNetworkViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightNetworkViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightNetworkViewController.h" 9 | #import "APMInsightCellItem.h" 10 | 11 | static NSString *const kUserRequestPlaceholder = @"请输入完整的URL,默认:https://www.volcengine.cn"; 12 | 13 | @interface APMInsightNetworkViewController () 14 | 15 | @property (nonatomic, strong) UITableView *tableView; 16 | 17 | @property (nonatomic, copy) NSMutableArray *items; 18 | 19 | @end 20 | 21 | @implementation APMInsightNetworkViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 27 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightNetworkCell"]; 28 | self.tableView.delegate = self; 29 | self.tableView.dataSource = self; 30 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 31 | [self.view addSubview:self.tableView]; 32 | 33 | self.title = @"网络分析"; 34 | // Do any additional setup after loading the view. 35 | } 36 | 37 | #pragma mark UITableViewDelegate, UITableViewDelegate 38 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 39 | { 40 | return self.items.count; 41 | } 42 | 43 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 44 | { 45 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightNetworkCell"]; 46 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 47 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 48 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 49 | return cell; 50 | } 51 | 52 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 53 | { 54 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 55 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 56 | if (item.selectBlock) { 57 | item.selectBlock(); 58 | } 59 | } 60 | 61 | #pragma mark - Lazy-load 62 | - (NSMutableArray *)items { 63 | if (!_items) { 64 | _items = [[NSMutableArray alloc] init]; 65 | 66 | UIAlertController *successAlert = [UIAlertController alertControllerWithTitle:@"请求成功" message:@"网络请求成功,请把APP切换到后台以触发日志上报,稍后即可在平台上看到网络统计" preferredStyle:UIAlertControllerStyleAlert]; 67 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]; 68 | [successAlert addAction:ok]; 69 | 70 | __weak typeof(self) weakSelf = self; 71 | void(^requestBlock)(void) = ^{ 72 | __strong typeof(self) strongSelf = weakSelf; 73 | if (strongSelf) { 74 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.volcengine.cn"]]; 75 | [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 76 | if (!error) { 77 | dispatch_async(dispatch_get_main_queue(), ^{ 78 | [strongSelf presentViewController:successAlert animated:YES completion:nil]; 79 | }); 80 | } 81 | }] resume]; 82 | } 83 | }; 84 | APMInsightCellItem *requestkItem = [APMInsightCellItem itemWithTitle:@"网络请求:https://www.volcengine.cn" block:requestBlock]; 85 | 86 | void(^userRequestBlock)(void) = ^{ 87 | __strong typeof(self) strongSelf = weakSelf; 88 | if (strongSelf) { 89 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"自定义网络请求" message:@"示例 https://www.volcengine.cn" preferredStyle:UIAlertControllerStyleAlert]; 90 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 91 | NSString *url = @"https://www.volcengine.cn"; 92 | for (UITextField *textField in alert.textFields) { 93 | if ([textField.text isEqualToString:kUserRequestPlaceholder]) { 94 | url = textField.text ?: url; 95 | break; 96 | } 97 | } 98 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; 99 | [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 100 | if (!error) { 101 | dispatch_async(dispatch_get_main_queue(), ^{ 102 | [strongSelf presentViewController:successAlert animated:YES completion:nil]; 103 | }); 104 | } 105 | }] resume]; 106 | }]; 107 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 108 | [alert addAction:ok]; 109 | [alert addAction:cancel]; 110 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 111 | textField.placeholder = kUserRequestPlaceholder; 112 | }]; 113 | dispatch_async(dispatch_get_main_queue(), ^{ 114 | [strongSelf presentViewController:alert animated:YES completion:nil]; 115 | }); 116 | } 117 | }; 118 | APMInsightCellItem *userRequestItem = [APMInsightCellItem itemWithTitle:@"自定义网络请求" block:userRequestBlock]; 119 | 120 | [_items addObject:requestkItem]; 121 | [_items addObject:userRequestItem]; 122 | } 123 | return _items; 124 | } 125 | /* 126 | #pragma mark - Navigation 127 | 128 | // In a storyboard-based application, you will often want to do a little preparation before navigation 129 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 130 | // Get the new view controller using [segue destinationViewController]. 131 | // Pass the selected object to the new view controller. 132 | } 133 | */ 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/PageMonitoring/APMInsightHybridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightHybridViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightHybridViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/PageMonitoring/APMInsightHybridViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightHybridViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightHybridViewController.h" 9 | #import "APMInsightCellItem.h" 10 | #import "APMInsightWebViewController.h" 11 | 12 | static NSString *const kUserWebViewPlaceholder = @"请输入完整的URL"; 13 | 14 | @interface APMInsightHybridViewController () 15 | 16 | @property (nonatomic, strong) UITableView *tableView; 17 | 18 | @property (nonatomic, copy) NSMutableArray *items; 19 | 20 | @end 21 | 22 | @implementation APMInsightHybridViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 28 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightHybridCell"]; 29 | self.tableView.delegate = self; 30 | self.tableView.dataSource = self; 31 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 32 | [self.view addSubview:self.tableView]; 33 | 34 | self.title = @"页面监控"; 35 | // Do any additional setup after loading the view. 36 | } 37 | 38 | #pragma mark UITableViewDelegate, UITableViewDelegate 39 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 40 | { 41 | return self.items.count; 42 | } 43 | 44 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 45 | { 46 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightHybridCell"]; 47 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 48 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 49 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 50 | return cell; 51 | } 52 | 53 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 54 | { 55 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 56 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 57 | if (item.selectBlock) { 58 | item.selectBlock(); 59 | } 60 | } 61 | 62 | #pragma mark Lazy-load 63 | - (NSMutableArray *)items { 64 | if (!_items) { 65 | _items = [[NSMutableArray alloc] init]; 66 | 67 | __weak typeof(self) weakSelf = self; 68 | 69 | void(^normalWebViewBlock)(void) = ^{ 70 | __strong typeof(self) strongSelf = weakSelf; 71 | if (strongSelf) { 72 | APMInsightWebViewController *webViewController = [APMInsightWebViewController webViewControllerWithURLString:@"https://www.volcengine.cn" title:@"火山引擎"]; 73 | [strongSelf.navigationController pushViewController:webViewController animated:YES]; 74 | } 75 | }; 76 | APMInsightCellItem *normalWebViewItem = [APMInsightCellItem itemWithTitle:@"正常页面" block:normalWebViewBlock]; 77 | 78 | void(^testWebViewBlock)(void) = ^{ 79 | __strong typeof(self) strongSelf = weakSelf; 80 | if (strongSelf) { 81 | APMInsightWebViewController *webViewController = [APMInsightWebViewController webViewControllerWithURLString:@"https://datarangers.com.cn/apminsight/demo/demo/rangers-site-sdk-npm" title:@"异常测试"]; 82 | [strongSelf.navigationController pushViewController:webViewController animated:YES]; 83 | } 84 | }; 85 | APMInsightCellItem *testWebViewItem = [APMInsightCellItem itemWithTitle:@"异常测试页面" block:testWebViewBlock]; 86 | 87 | void(^userWebViewBlock)(void) = ^{ 88 | __strong typeof(self) strongSelf = weakSelf; 89 | if (strongSelf) { 90 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"自定义WebView" message:@"示例 https://www.volcengine.cn" preferredStyle:UIAlertControllerStyleAlert]; 91 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 92 | NSString *url = @"https://www.volcengine.cn"; 93 | for (UITextField *textField in alert.textFields) { 94 | if ([textField.placeholder isEqualToString:kUserWebViewPlaceholder]) { 95 | url = textField.text ?: url; 96 | break; 97 | } 98 | } 99 | APMInsightWebViewController *webViewController = [APMInsightWebViewController webViewControllerWithURLString:url title:@"自定义页面"]; 100 | [self.navigationController pushViewController:webViewController animated:YES]; 101 | }]; 102 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 103 | [alert addAction:ok]; 104 | [alert addAction:cancel]; 105 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 106 | textField.placeholder = kUserWebViewPlaceholder; 107 | }]; 108 | [strongSelf presentViewController:alert animated:YES completion:nil]; 109 | } 110 | }; 111 | APMInsightCellItem *userWebViewItem = [APMInsightCellItem itemWithTitle:@"自定义页面" block:userWebViewBlock]; 112 | 113 | [_items addObject:normalWebViewItem]; 114 | [_items addObject:testWebViewItem]; 115 | [_items addObject:userWebViewItem]; 116 | } 117 | return _items; 118 | } 119 | /* 120 | #pragma mark - Navigation 121 | 122 | // In a storyboard-based application, you will often want to do a little preparation before navigation 123 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 124 | // Get the new view controller using [segue destinationViewController]. 125 | // Pass the selected object to the new view controller. 126 | } 127 | */ 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/PageMonitoring/APMInsightWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightWebViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/18. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightWebViewController : UIViewController 13 | 14 | + (instancetype)webViewControllerWithURLString:(NSString *)urlString title:(NSString *)title; 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/PageMonitoring/APMInsightWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightWebViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/18. 6 | // 7 | 8 | #import "APMInsightWebViewController.h" 9 | #import 10 | 11 | @interface APMInsightWebViewController () 12 | 13 | @property (nonatomic, copy) NSString *urlString; 14 | 15 | @property (nonatomic, strong) WKWebView *webView; 16 | 17 | @end 18 | 19 | @implementation APMInsightWebViewController 20 | 21 | + (instancetype)webViewControllerWithURLString:(NSString *)urlString title:(NSString *)title { 22 | APMInsightWebViewController *webViewController = [[APMInsightWebViewController alloc] init]; 23 | webViewController.urlString = urlString; 24 | webViewController.title = title; 25 | return webViewController; 26 | } 27 | 28 | - (void)viewDidLoad { 29 | [super viewDidLoad]; 30 | 31 | self.view.backgroundColor = [UIColor whiteColor]; 32 | WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; 33 | self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; 34 | [self.view addSubview:self.webView]; 35 | self.webView.navigationDelegate = self; 36 | 37 | // Do any additional setup after loading the view. 38 | } 39 | 40 | - (void)viewWillAppear:(BOOL)animated { 41 | [super viewWillAppear:animated]; 42 | [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.urlString]]]; 43 | } 44 | 45 | #pragma mark - WKUIDelegate 46 | - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { 47 | 48 | } 49 | 50 | /* 51 | #pragma mark - Navigation 52 | 53 | // In a storyboard-based application, you will often want to do a little preparation before navigation 54 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 55 | // Get the new view controller using [segue destinationViewController]. 56 | // Pass the selected object to the new view controller. 57 | } 58 | */ 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ProtectorAnalysis/APMInsightProtectorObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightProtectorObject.h 3 | // APMInsight_iOS 4 | // 5 | // Created by bytedance on 2021/9/30. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface APMInsightProtectorObject : NSObject 14 | 15 | @property (nonatomic, strong) UIView *subview; 16 | 17 | @property (nonatomic, strong) NSNumber *number; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ProtectorAnalysis/APMInsightProtectorObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightProtectorObject.m 3 | // APMInsight_iOS 4 | // 5 | // Created by bytedance on 2021/9/30. 6 | // 7 | 8 | #import "APMInsightProtectorObject.h" 9 | 10 | @implementation APMInsightProtectorObject 11 | 12 | - (instancetype)init { 13 | self = [super init]; 14 | if (self) { 15 | _subview = [UIView new]; 16 | _number = @(0); 17 | [self.subview addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 18 | } 19 | 20 | return self; 21 | } 22 | 23 | - (void)dealloc { 24 | self.subview.frame = CGRectMake(0, 1, 2, 3); 25 | [self.subview removeObserver:self forKeyPath:@"frame"]; 26 | } 27 | 28 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 29 | NSLog(@"%@ get %@ keypath:(%@) context:(%p) change:%@ -> %@", self, object, keyPath, context, change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ProtectorAnalysis/APMInsightProtectorViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightProtectorViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by bytedance on 2021/9/30. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightProtectorViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/ProtectorAnalysis/APMInsightProtectorViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightProtectorViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by bytedance on 2021/9/30. 6 | // 7 | 8 | #import "APMInsightProtectorViewController.h" 9 | #import "APMInsightCellItem.h" 10 | #import "APMInsightProtectorObject.h" 11 | #include 12 | 13 | @interface APMInsightProtectorViewController () 14 | 15 | @property (nonatomic, strong) UITableView *tableView; 16 | 17 | @property (nonatomic, copy) NSMutableArray *items; 18 | 19 | @end 20 | 21 | @implementation APMInsightProtectorViewController 22 | 23 | #pragma mark - Test cases 24 | - (void)USELProblemTrigger { 25 | dispatch_async(dispatch_get_main_queue(), ^{ 26 | UIAlertController *alert = [self alertWithTitle:@"Container Problem" message:@"5秒后触发Urecognized Selector类型问题,若APP闪退,请检测安全气垫开关是否开启" okHandler:^{ 27 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 28 | // objc_msgSend 29 | SEL aSEL = sel_registerName("TEST_INVALID_SELECTOR"); 30 | ((void (*)(Class, SEL))objc_msgSend)(NSObject.class, aSEL); 31 | 32 | // performSelector 33 | UIWebView *web = [UIWebView new]; 34 | [web performSelector:@selector(cut:)]; 35 | }); 36 | }]; 37 | [self presentViewController:alert animated:YES completion:nil]; 38 | }); 39 | } 40 | 41 | - (void)ContainerProblemTrigger { 42 | dispatch_async(dispatch_get_main_queue(), ^{ 43 | UIAlertController *alert = [self alertWithTitle:@"Container Problem" message:@"5秒后触发容器类型问题,若APP闪退,请检测安全气垫开关是否开启" okHandler:^{ 44 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 45 | // Array 46 | NSMutableArray *array = [@[@"A", @"B", @"C"] mutableCopy]; 47 | [array addObject:nil]; 48 | [array removeObject:nil]; 49 | 50 | [array removeObjectAtIndex:3]; 51 | 52 | // Dictionary 53 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 54 | [dictionary setObject:nil forKey:@"NIL-KEY"]; 55 | [dictionary removeObjectForKey:nil]; 56 | 57 | // Set 58 | NSMutableSet *set = [NSMutableSet setWithObjects:@"1", @"2", nil]; 59 | [set addObject:nil]; 60 | }); 61 | }]; 62 | [self presentViewController:alert animated:YES completion:nil]; 63 | }); 64 | } 65 | 66 | - (void)KVCProblemTrigger { 67 | dispatch_async(dispatch_get_main_queue(), ^{ 68 | UIAlertController *alert = [self alertWithTitle:@"KVC Problem" message:@"5秒后触发KVC类型问题,若APP闪退,请检测安全气垫开关是否开启" okHandler:^{ 69 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 70 | APMInsightProtectorObject *object = [APMInsightProtectorObject new]; 71 | [object setValue:nil forKey:nil]; 72 | [object setValue:nil forKeyPath:nil]; 73 | [object setValue:nil forUndefinedKey:nil]; 74 | 75 | [object valueForKey:nil]; 76 | }); 77 | }]; 78 | [self presentViewController:alert animated:YES completion:nil]; 79 | }); 80 | } 81 | 82 | - (void)KVOProblemTrigger { 83 | dispatch_async(dispatch_get_main_queue(), ^{ 84 | UIAlertController *alert = [self alertWithTitle:@"KVO Problem" message:@"5秒后触发KVO类型问题,若APP闪退,请检测安全气垫开关是否开启" okHandler:^{ 85 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 86 | APMInsightProtectorObject *aObject = [APMInsightProtectorObject new]; 87 | 88 | @autoreleasepool { 89 | APMInsightProtectorObject *bObject = [APMInsightProtectorObject new]; 90 | [aObject addObserver:bObject forKeyPath:@"number" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:0]; 91 | [aObject addObserver:bObject forKeyPath:@"subview.frame" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:0]; 92 | bObject = nil; 93 | } 94 | 95 | aObject.number = @(1); 96 | aObject.subview.frame = CGRectMake(0, 0, 100, 100); 97 | }); 98 | }]; 99 | [self presentViewController:alert animated:YES completion:nil]; 100 | }); 101 | 102 | } 103 | 104 | - (void)NSUserDefaultsProblemTrigger { 105 | dispatch_async(dispatch_get_main_queue(), ^{ 106 | UIAlertController *alert = [self alertWithTitle:@"NSUserDefaults Problem" message:@"5秒后触发NSUserDefaults类型问题,若APP闪退,请检测安全气垫开关是否开启" okHandler:^{ 107 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 108 | NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 109 | [ud setBool:nil forKey:[NSObject new]]; 110 | [ud boolForKey:[NSObject new]]; 111 | }); 112 | }]; 113 | [self presentViewController:alert animated:YES completion:nil]; 114 | }); 115 | } 116 | 117 | #pragma mark - UIViewController 118 | - (void)viewDidLoad { 119 | [super viewDidLoad]; 120 | 121 | // Do any additional setup after loading the view. 122 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 123 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightProtectorCell"]; 124 | self.tableView.delegate = self; 125 | self.tableView.dataSource = self; 126 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 127 | [self.view addSubview:self.tableView]; 128 | 129 | self.title = @"异常防护 - 崩溃防护分析"; 130 | } 131 | 132 | #pragma mark UITableViewDataSource, UITableViewDelegate 133 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 134 | { 135 | return self.items.count; 136 | } 137 | 138 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 139 | { 140 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightProtectorCell"]; 141 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 142 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 143 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 144 | return cell; 145 | } 146 | 147 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 148 | { 149 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 150 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 151 | if (item.selectBlock) { 152 | item.selectBlock(); 153 | } 154 | } 155 | 156 | #pragma mark Lazy-load 157 | - (NSMutableArray *)items { 158 | if (!_items) { 159 | _items = [[NSMutableArray alloc] init]; 160 | 161 | __weak typeof(self) weakSelf = self; 162 | void(^USELProblemBlock)(void) = ^{ 163 | __strong typeof(self) strongSelf = weakSelf; 164 | if (strongSelf) { 165 | [strongSelf USELProblemTrigger]; 166 | } 167 | }; 168 | APMInsightCellItem *USELProblemItem = [APMInsightCellItem itemWithTitle:@"Urecognized Selector类型问题" block:USELProblemBlock]; 169 | 170 | void(^ContainerProblemBlock)(void) = ^{ 171 | __strong typeof(self) strongSelf = weakSelf; 172 | if (strongSelf) { 173 | [strongSelf ContainerProblemTrigger]; 174 | } 175 | }; 176 | APMInsightCellItem *ContainerProblemItem = [APMInsightCellItem itemWithTitle:@"触发容器类型问题" block:ContainerProblemBlock]; 177 | 178 | void(^KVCProblemBlock)(void) = ^{ 179 | __strong typeof(self) strongSelf = weakSelf; 180 | if (strongSelf) { 181 | [strongSelf KVCProblemTrigger]; 182 | } 183 | }; 184 | APMInsightCellItem *KVCProblemItem = [APMInsightCellItem itemWithTitle:@"触发KVC类型问题" block:KVCProblemBlock]; 185 | 186 | void(^KVOProblemBlock)(void) = ^{ 187 | __strong typeof(self) strongSelf = weakSelf; 188 | if (strongSelf) { 189 | [strongSelf KVOProblemTrigger]; 190 | } 191 | }; 192 | APMInsightCellItem *KVOProblemItem = [APMInsightCellItem itemWithTitle:@"触发KVO类型问题" block:KVOProblemBlock]; 193 | 194 | void(^NSUserDefaultsProblemBlock)(void) = ^{ 195 | __strong typeof(self) strongSelf = weakSelf; 196 | if (strongSelf) { 197 | [strongSelf NSUserDefaultsProblemTrigger]; 198 | } 199 | }; 200 | APMInsightCellItem *NSUserDefaultsProblemItem = [APMInsightCellItem itemWithTitle:@"触发NSUserDefaults类型问题" block:NSUserDefaultsProblemBlock]; 201 | 202 | [_items addObject:USELProblemItem]; 203 | [_items addObject:ContainerProblemItem]; 204 | [_items addObject:KVCProblemItem]; 205 | [_items addObject:KVOProblemItem]; 206 | [_items addObject:NSUserDefaultsProblemItem]; 207 | } 208 | return _items; 209 | } 210 | 211 | #pragma mark - Tools 212 | - (UIAlertController *)alertWithTitle:(NSString *)title message:(NSString *)message okHandler:(void (^)(void))okHandler{ 213 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; 214 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 215 | okHandler(); 216 | }]; 217 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 218 | [alert addAction:ok]; 219 | [alert addAction:cancel]; 220 | return alert; 221 | } 222 | 223 | @end 224 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/UserExperience/APMInsightPerformanceViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightPerformanceViewController.h 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface APMInsightPerformanceViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/UserExperience/APMInsightPerformanceViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // APMInsightPerformanceViewController.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import "APMInsightPerformanceViewController.h" 9 | #import "APMInsightCellItem.h" 10 | 11 | @interface APMInsightPerformanceViewController () 12 | 13 | @property (nonatomic, strong) UITableView *tableView; 14 | 15 | @property (nonatomic, copy) NSMutableArray *items; 16 | 17 | @end 18 | 19 | @implementation APMInsightPerformanceViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 25 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"APMInsightPerformanceCell"]; 26 | self.tableView.delegate = self; 27 | self.tableView.dataSource = self; 28 | 29 | UIView *footerView = [[UIView alloc] init]; 30 | 31 | UILabel *footerViewLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, self.view.frame.size.width - 40, 0)]; 32 | footerViewLabel.text = @"用户体验产生的日志,在APP启动之后会触发一次上报,之后每两分钟或者APP退到后台时上报一次,如果需要立即上报查看数据,可以尝试把APP切换到后台来触发上报"; 33 | footerViewLabel.textColor = [UIColor grayColor]; 34 | footerViewLabel.lineBreakMode = NSLineBreakByWordWrapping; 35 | footerViewLabel.numberOfLines = 0; 36 | footerViewLabel.font = [UIFont systemFontOfSize:13]; 37 | [footerViewLabel sizeToFit]; 38 | 39 | CGFloat height = footerViewLabel.frame.size.height; 40 | [footerView setFrame:CGRectMake(0, 0, 0, height)]; 41 | [footerView addSubview:footerViewLabel]; 42 | 43 | self.tableView.tableFooterView = footerView; 44 | 45 | [self.view addSubview:self.tableView]; 46 | 47 | self.title = @"用户体验"; 48 | // Do any additional setup after loading the view. 49 | } 50 | 51 | #pragma mark UITableViewDataSource, UITableViewDelegate 52 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 53 | { 54 | return self.items.count; 55 | } 56 | 57 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 58 | { 59 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APMInsightPerformanceCell"]; 60 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 61 | cell.textLabel.text = [NSString stringWithFormat:@"%@",item.title]; 62 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 63 | return cell; 64 | } 65 | 66 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 67 | { 68 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 69 | APMInsightCellItem *item = [self.items objectAtIndex:indexPath.row]; 70 | if (item.selectBlock) { 71 | item.selectBlock(); 72 | } 73 | } 74 | 75 | #pragma mark Lazy-load 76 | - (NSMutableArray *)items 77 | { 78 | if (!_items) { 79 | _items = [[NSMutableArray alloc] init]; 80 | 81 | __weak typeof(self) weakSelf = self; 82 | void(^startBlock)(void) = ^{ 83 | dispatch_async(dispatch_get_main_queue(), ^{ 84 | __strong typeof(self) strongSelf = weakSelf; 85 | if (strongSelf) { 86 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"启动分析" message:@"冷启动日志在APP启动时自动记录,如果需要测试热启动,可以把APP进行前后台切换来产生日志" preferredStyle:UIAlertControllerStyleAlert]; 87 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]; 88 | [alert addAction:ok]; 89 | [strongSelf presentViewController:alert animated:YES completion:nil]; 90 | } 91 | }); 92 | }; 93 | APMInsightCellItem *startItem = [APMInsightCellItem itemWithTitle:@"启动分析" block:startBlock]; 94 | 95 | void(^pageLoadBlock)(void) = ^{ 96 | dispatch_async(dispatch_get_main_queue(), ^{ 97 | __strong typeof(self) strongSelf = weakSelf; 98 | if (strongSelf) { 99 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"页面响应" message:@"页面响应日志在页面切换时自动记录,可以尝试在不同的ViewController之间切换来产生日志" preferredStyle:UIAlertControllerStyleAlert]; 100 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]; 101 | [alert addAction:ok]; 102 | [strongSelf presentViewController:alert animated:YES completion:nil]; 103 | } 104 | }); 105 | }; 106 | APMInsightCellItem *pageLoadItem = [APMInsightCellItem itemWithTitle:@"页面响应" block:pageLoadBlock]; 107 | 108 | void(^fpsBlock)(void) = ^{ 109 | dispatch_async(dispatch_get_main_queue(), ^{ 110 | __strong typeof(self) strongSelf = weakSelf; 111 | if (strongSelf) { 112 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"FPS" message:@"FPS日志在页面切换或滑动时自动记录,可以尝试在不同的ViewController之间切换或滑动View来产生日志" preferredStyle:UIAlertControllerStyleAlert]; 113 | UIAlertAction *ok = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]; 114 | [alert addAction:ok]; 115 | [strongSelf presentViewController:alert animated:YES completion:nil]; 116 | } 117 | }); 118 | }; 119 | APMInsightCellItem *fpsItem = [APMInsightCellItem itemWithTitle:@"流畅性和丢帧分析" block:fpsBlock]; 120 | 121 | [_items addObject:startItem]; 122 | [_items addObject:pageLoadItem]; 123 | [_items addObject:fpsItem]; 124 | } 125 | return _items; 126 | } 127 | /* 128 | #pragma mark - Navigation 129 | 130 | // In a storyboard-based application, you will often want to do a little preparation before navigation 131 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 132 | // Get the new view controller using [segue destinationViewController]. 133 | // Pass the selected object to the new view controller. 134 | } 135 | */ 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // APMInsight_iOS 4 | // 5 | // Created by xuminghao.eric on 2020/11/12. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | #if __has_include() 11 | #import 12 | #endif 13 | #import 14 | 15 | int main(int argc, char * argv[]) { 16 | [RangersAPM setDoctorEnabled:YES]; 17 | #if __has_include() 18 | [RangersAPM prewarmCheckStart]; 19 | #endif 20 | NSString * appDelegateClassName; 21 | @autoreleasepool { 22 | // Setup code that might create autoreleased objects goes here. 23 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 24 | } 25 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 26 | } 27 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS_extension/APMInsight_iOS_extension.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS_extension/Base.lproj/MainInterface.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 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS_extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionAttributes 8 | 9 | NSExtensionActivationRule 10 | TRUEPREDICATE 11 | 12 | NSExtensionMainStoryboard 13 | MainInterface 14 | NSExtensionPointIdentifier 15 | com.apple.share-services 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS_extension/ShareViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ShareViewController.h 3 | // APMInsight_iOS_extension 4 | // 5 | // Created by xuminghao.eric on 2022/3/23. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @interface ShareViewController : SLComposeServiceViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/APMInsight_iOS_extension/ShareViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ShareViewController.m 3 | // APMInsight_iOS_extension 4 | // 5 | // Created by xuminghao.eric on 2022/3/23. 6 | // 7 | 8 | #import "ShareViewController.h" 9 | #import 10 | 11 | @interface ShareViewController () 12 | 13 | @end 14 | 15 | @implementation ShareViewController 16 | 17 | - (BOOL)isContentValid { 18 | /** 19 | 可以把这部分初始化代码copy到您的工程中,建议初始化时机尽量靠前,否则可能出现启动崩溃无法捕获 20 | ⚠️注意:①在复制此部分代码前请先参考Podfile文件,引入对应的组件 21 | ②导入头文件 22 | ③请修改config对应的属性值 23 | appID:平台为APP分配的ID 24 | groupID:需要和主程序一致,以便读取共享空间 25 | 26 | You can copy the initialization code as follows to the same part in your project. 27 | And I suggest the code be excuted as early as possible. Otherwise, the crash that occurs during App Launching may not be detected. 28 | ⚠️Tips: ① Before you copy the code, please read the Podfile and install necessary cocoapods components first. 29 | ② Import the header files. 30 | ③ Set the propertys of the variable named "config" with your own values. 31 | appID: the ID of your App on APMInsight 32 | channel: the channel you App will publish to 33 | groupID: should be consistent with the host APP in order to read the shared space 34 | */ 35 | /** 36 | 必要部分 !!! 37 | 可复制部分开始--- 38 | Necessary !!! 39 | Copyable section starts --- 40 | */ 41 | 42 | static dispatch_once_t onceToken; 43 | dispatch_once(&onceToken, ^{ 44 | /** 45 | 输出控制台日志 46 | Print console log 47 | */ 48 | #if DEBUG 49 | [RangersAPMForAPPExtension allowDebugLogUsingLogger:^(NSString * _Nonnull log) { 50 | NSLog(@"APMInsight Debug Log : %@", log); 51 | }]; 52 | #endif 53 | RangersAPMForAPPExtensionConfig *extensionConfig = [RangersAPMForAPPExtensionConfig configWithAppID:@"233805" groupID:@"xxx"]; // 请先在 Capability 中添加 App Groups,然后修改 groupID 54 | [RangersAPMForAPPExtension startWithConfig:extensionConfig]; 55 | }); 56 | /** 57 | ---可复制部分结束 58 | --- Copyable section ends 59 | */ 60 | 61 | // Do validation of contentText and/or NSExtensionContext attachments here 62 | return YES; 63 | } 64 | 65 | - (void)didSelectPost { 66 | /** 67 | 测试崩溃捕获 68 | 点击分享扩展程序的 Post 选项后,会执行如下代码,触发崩溃。之后启动 |主程序| 即可上报扩展程序的崩溃。 69 | */ 70 | [[NSArray array] objectAtIndex:0]; 71 | 72 | // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments. 73 | 74 | // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context. 75 | [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil]; 76 | } 77 | 78 | - (NSArray *)configurationItems { 79 | // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. 80 | return @[]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Example/Gemfile: -------------------------------------------------------------------------------- 1 | gem 'cocoapods', '1.9.3' -------------------------------------------------------------------------------- /Example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | specs: 3 | CFPropertyList (3.0.6) 4 | rexml 5 | activesupport (4.2.11.3) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | algoliasearch (1.27.5) 11 | httpclient (~> 2.8, >= 2.8.3) 12 | json (>= 1.5.1) 13 | atomos (0.1.3) 14 | claide (1.1.0) 15 | cocoapods (1.9.3) 16 | activesupport (>= 4.0.2, < 5) 17 | claide (>= 1.0.2, < 2.0) 18 | cocoapods-core (= 1.9.3) 19 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 20 | cocoapods-downloader (>= 1.2.2, < 2.0) 21 | cocoapods-plugins (>= 1.0.0, < 2.0) 22 | cocoapods-search (>= 1.0.0, < 2.0) 23 | cocoapods-stats (>= 1.0.0, < 2.0) 24 | cocoapods-trunk (>= 1.4.0, < 2.0) 25 | cocoapods-try (>= 1.1.0, < 2.0) 26 | colored2 (~> 3.1) 27 | escape (~> 0.0.4) 28 | fourflusher (>= 2.3.0, < 3.0) 29 | gh_inspector (~> 1.0) 30 | molinillo (~> 0.6.6) 31 | nap (~> 1.0) 32 | ruby-macho (~> 1.4) 33 | xcodeproj (>= 1.14.0, < 2.0) 34 | cocoapods-core (1.9.3) 35 | activesupport (>= 4.0.2, < 6) 36 | algoliasearch (~> 1.0) 37 | concurrent-ruby (~> 1.1) 38 | fuzzy_match (~> 2.0.4) 39 | nap (~> 1.0) 40 | netrc (~> 0.11) 41 | typhoeus (~> 1.0) 42 | cocoapods-deintegrate (1.0.5) 43 | cocoapods-downloader (1.6.3) 44 | cocoapods-plugins (1.0.0) 45 | nap 46 | cocoapods-search (1.0.1) 47 | cocoapods-stats (1.1.0) 48 | cocoapods-trunk (1.6.0) 49 | nap (>= 0.8, < 2.0) 50 | netrc (~> 0.11) 51 | cocoapods-try (1.2.0) 52 | colored2 (3.1.2) 53 | concurrent-ruby (1.2.2) 54 | escape (0.0.4) 55 | ethon (0.16.0) 56 | ffi (>= 1.15.0) 57 | ffi (1.15.5) 58 | fourflusher (2.3.1) 59 | fuzzy_match (2.0.4) 60 | gh_inspector (1.1.3) 61 | httpclient (2.8.3) 62 | i18n (0.9.5) 63 | concurrent-ruby (~> 1.0) 64 | json (2.6.3) 65 | minitest (5.18.0) 66 | molinillo (0.6.6) 67 | nanaimo (0.3.0) 68 | nap (1.1.0) 69 | netrc (0.11.0) 70 | rexml (3.2.5) 71 | ruby-macho (1.4.0) 72 | thread_safe (0.3.6) 73 | typhoeus (1.4.0) 74 | ethon (>= 0.9.0) 75 | tzinfo (1.2.11) 76 | thread_safe (~> 0.1) 77 | xcodeproj (1.22.0) 78 | CFPropertyList (>= 2.3.3, < 4.0) 79 | atomos (~> 0.1.3) 80 | claide (>= 1.0.2, < 2.0) 81 | colored2 (~> 3.1) 82 | nanaimo (~> 0.3.0) 83 | rexml (~> 3.2.4) 84 | 85 | PLATFORMS 86 | ruby 87 | 88 | DEPENDENCIES 89 | cocoapods (= 1.9.3) 90 | 91 | BUNDLED WITH 92 | 2.1.4 93 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '10.0' 3 | 4 | target 'APMInsight_iOS' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # 可以把这部分代码copy到您的Podfile文件中,执行pod install,安装对应组件 9 | # You can copy the code as follows to your Podfile, and excute "pod install" in command line to install our SDKs. 10 | # 可复制部分开始--- 11 | # Copyable section starts --- 12 | source 'https://github.com/volcengine/volcengine-specs.git' 13 | 14 | pod 'RangersAPM', '5.2.0', :subspecs => [ 15 | 'Crash', 16 | 'WatchDog', 17 | 'OOM', 18 | 'LAG', 19 | 'UserException', 20 | 'Monitors', 21 | 'UITrackers', 22 | 'HybridPro', 23 | 'MemoryGraph', 24 | 'NetworkPro', 25 | 'EventMonitor', 26 | 'APMLog', 27 | 'CrashProtector', 28 | 'CPUException', 29 | 'CN', 30 | # 'Global', 31 | 'Zombie', 32 | 'BootingProtect', 33 | 'MetricKit', 34 | 'Disk', 35 | 'SessionTracker', 36 | 'GWPASan', 37 | 'Coredump', 38 | 'CloudCommand' 39 | ] 40 | 41 | pod 'RangersAPMDoctor', '2.0.0' 42 | 43 | # ---可复制部分结束 44 | # --- Copyable section ends 45 | 46 | # Pods for APMInsight_iOS 47 | 48 | # 按需接入部分开始--- 49 | # Use as needed section starts--- 50 | post_install do |installer| 51 | installer.pods_project.targets.each do |target| 52 | target.build_configurations.each do |config| 53 | if "#{target}" == 'RangersAPM-RangersAPMPrivacyInfo' 54 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' 55 | end 56 | end 57 | end 58 | end 59 | # ---按需接入部分结束 60 | # Use as needed section ends--- 61 | 62 | end 63 | 64 | target 'APMInsight_iOS_extension' do 65 | use_frameworks! 66 | 67 | # 可以把这部分代码copy到您的Podfile文件中,执行pod install,安装对应组件 68 | # You can copy the code as follows to your Podfile, and excute "pod install" in command line to install our SDKs. 69 | # 可复制部分开始--- 70 | # Copyable section starts --- 71 | source 'https://github.com/volcengine/volcengine-specs.git' 72 | 73 | pod 'RangersAPMForExtension', '1.0.5' 74 | 75 | # ---可复制部分结束 76 | # --- Copyable section ends 77 | 78 | end 79 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - RangersAPM/Above (5.2.0) 3 | - RangersAPM/APMLog (5.2.0): 4 | - RangersAPM/CloudCommand 5 | - RangersAPM/Core 6 | - RangersAPM/HMD 7 | - RangersAPM/Zip 8 | - RangersAPM/BootingProtect (5.2.0): 9 | - RangersAPM/BootingProtectLite 10 | - RangersAPM/OOM 11 | - RangersAPM/BootingProtectLite (5.2.0): 12 | - RangersAPM/Crash 13 | - RangersAPM/WatchDog 14 | - RangersAPM/CloudCommand (5.2.0): 15 | - RangersAPM/Core 16 | - RangersAPM/HMD 17 | - RangersAPM/CN (5.2.0): 18 | - RangersAPM/Core 19 | - RangersAPM/Public 20 | - RangersAPM/Core (5.2.0): 21 | - RangersAPM/HMDLite 22 | - RangersAPM/Coredump (5.2.0): 23 | - RangersAPM/Core 24 | - RangersAPM/Crash 25 | - RangersAPM/HMD 26 | - RangersAPM/Zip 27 | - RangersAPM/CPUException (5.2.0): 28 | - RangersAPM/Core 29 | - RangersAPM/HMD 30 | - RangersAPM/SessionTracker 31 | - RangersAPM/Crash (5.2.0): 32 | - RangersAPM/Core 33 | - RangersAPM/HMD 34 | - RangersAPM/Public 35 | - RangersAPM/SessionTracker 36 | - RangersAPM/Zip 37 | - RangersAPM/CrashProtector (5.2.0): 38 | - RangersAPM/Core 39 | - RangersAPM/Crash 40 | - RangersAPM/SessionTracker 41 | - RangersAPM/Disk (5.2.0): 42 | - RangersAPM/Monitors 43 | - RangersAPM/SessionTracker 44 | - RangersAPM/EventMonitor (5.2.0): 45 | - RangersAPM/Core 46 | - RangersAPM/HMD 47 | - RangersAPM/Public 48 | - RangersAPM/GWPASan (5.2.0): 49 | - RangersAPM/Core 50 | - RangersAPM/Crash 51 | - RangersAPM/HMD 52 | - RangersAPM/HMD (5.2.0): 53 | - RangersAPM/Core 54 | - RangersAPM/Public 55 | - RangersAPM/HMDLite (5.2.0) 56 | - RangersAPM/HybridPro (5.2.0): 57 | - RangersAPM/Core 58 | - RangersAPM/HMD 59 | - RangersAPM/Public 60 | - RangersAPM/LAG (5.2.0): 61 | - RangersAPM/Core 62 | - RangersAPM/HMD 63 | - RangersAPM/Public 64 | - RangersAPM/SessionTracker 65 | - RangersAPM/MemoryGraph (5.2.0): 66 | - RangersAPM/Core 67 | - RangersAPM/HMD 68 | - RangersAPM/Public 69 | - RangersAPM/Zip 70 | - RangersAPM/MetricKit (5.2.0): 71 | - RangersAPM/Core 72 | - RangersAPM/HMD 73 | - RangersAPM/SessionTracker 74 | - RangersAPM/Monitors (5.2.0): 75 | - RangersAPM/Core 76 | - RangersAPM/HMD 77 | - RangersAPM/UITrackers 78 | - RangersAPM/NetworkBasic (5.2.0): 79 | - RangersAPM/Core 80 | - RangersAPM/HMD 81 | - RangersAPM/Public 82 | - RangersAPM/NetworkPro (5.2.0): 83 | - RangersAPM/NetworkBasic 84 | - RangersAPM/OOM (5.2.0): 85 | - RangersAPM/Core 86 | - RangersAPM/Crash 87 | - RangersAPM/HMD 88 | - RangersAPM/Public 89 | - RangersAPM/SessionTracker 90 | - RangersAPM/WatchDog 91 | - RangersAPM/Public (5.2.0): 92 | - RangersAPM/Above 93 | - RangersAPM/Core 94 | - RangersAPM/HMDLite 95 | - RangersAPM/Zyone 96 | - RangersAPM/SessionTracker (5.2.0): 97 | - RangersAPM/Core 98 | - RangersAPM/HMD 99 | - RangersAPM/Public 100 | - RangersAPM/UITrackers (5.2.0): 101 | - RangersAPM/Core 102 | - RangersAPM/HMD 103 | - RangersAPM/Public 104 | - RangersAPM/UserException (5.2.0): 105 | - RangersAPM/Core 106 | - RangersAPM/HMD 107 | - RangersAPM/Public 108 | - RangersAPM/SessionTracker 109 | - RangersAPM/WatchDog (5.2.0): 110 | - RangersAPM/Core 111 | - RangersAPM/HMD 112 | - RangersAPM/Public 113 | - RangersAPM/SessionTracker 114 | - RangersAPM/Zip (5.2.0) 115 | - RangersAPM/Zombie (5.2.0): 116 | - RangersAPM/Core 117 | - RangersAPM/Crash 118 | - RangersAPM/Zyone (5.2.0) 119 | - RangersAPMDoctor (2.0.0): 120 | - RangersAPM/HMD (>= 4.1.0) 121 | - RangersAPM/Public (>= 4.1.0) 122 | - RangersAPMForExtension (1.0.5): 123 | - RangersAPMForExtension/Crash (= 1.0.5) 124 | - RangersAPMForExtension/Core (1.0.5) 125 | - RangersAPMForExtension/Crash (1.0.5): 126 | - RangersAPMForExtension/Core 127 | - RangersAPMForExtension/Public 128 | - RangersAPMForExtension/Public (1.0.5) 129 | 130 | DEPENDENCIES: 131 | - RangersAPM/APMLog (= 5.2.0) 132 | - RangersAPM/BootingProtect (= 5.2.0) 133 | - RangersAPM/CloudCommand (= 5.2.0) 134 | - RangersAPM/CN (= 5.2.0) 135 | - RangersAPM/Coredump (= 5.2.0) 136 | - RangersAPM/CPUException (= 5.2.0) 137 | - RangersAPM/Crash (= 5.2.0) 138 | - RangersAPM/CrashProtector (= 5.2.0) 139 | - RangersAPM/Disk (= 5.2.0) 140 | - RangersAPM/EventMonitor (= 5.2.0) 141 | - RangersAPM/GWPASan (= 5.2.0) 142 | - RangersAPM/HybridPro (= 5.2.0) 143 | - RangersAPM/LAG (= 5.2.0) 144 | - RangersAPM/MemoryGraph (= 5.2.0) 145 | - RangersAPM/MetricKit (= 5.2.0) 146 | - RangersAPM/Monitors (= 5.2.0) 147 | - RangersAPM/NetworkPro (= 5.2.0) 148 | - RangersAPM/OOM (= 5.2.0) 149 | - RangersAPM/SessionTracker (= 5.2.0) 150 | - RangersAPM/UITrackers (= 5.2.0) 151 | - RangersAPM/UserException (= 5.2.0) 152 | - RangersAPM/WatchDog (= 5.2.0) 153 | - RangersAPM/Zombie (= 5.2.0) 154 | - RangersAPMDoctor (= 2.0.0) 155 | - RangersAPMForExtension (= 1.0.5) 156 | 157 | SPEC REPOS: 158 | https://github.com/volcengine/volcengine-specs.git: 159 | - RangersAPM 160 | - RangersAPMDoctor 161 | - RangersAPMForExtension 162 | 163 | SPEC CHECKSUMS: 164 | RangersAPM: 695cf4c4b5fe70b93f7565b482e927da2cea0ff3 165 | RangersAPMDoctor: ee8bcbff62c36b52a43e322cfd819669412aea5c 166 | RangersAPMForExtension: 3c0f2dcf30ac0c52280561a5f7f9e90327c0a9a2 167 | 168 | PODFILE CHECKSUM: 6e1f309bcc43b2dec9373b4a95e6911646abe2dd 169 | 170 | COCOAPODS: 1.9.3 171 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Volcengine Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APMInsight_iOS 2 | [中文版本](https://github.com/volcengine/APMInsight_iOS/blob/master/说明.md) 3 | APMPlus SDK. Enter official website to read the introduction of SDK capabilities and access. [APMPlus](https://www.volcengine.com/products/apmplus) 4 | 5 | ## Example 6 | ### Download & Installation 7 | 1. git clone https://github.com/volcengine/APMInsight_iOS.git --branch master 8 | 2. cd APMInsight_iOS/Example 9 | 3. bundle exec pod install 10 | 4. open APMInsight_iOS.xcworkspace 11 | 12 | ### Usage 13 | 1. The demo APP has integrated all capabilities of APMPlus. 14 | 2. You can make errors and performance data in the demo APP. 15 | 3. Reset the appID and appToken in APMInitialViewController.m, then the performance data will be uploaded to your own console. 16 | 17 | ## Environment 18 | * iOS 10.0+ 19 | * Xcode 14.0+ 20 | 21 | ## License 22 | APMInsight_iOS is available under the MIT license. See the [LICENSE](https://github.com/volcengine/APMInsight_iOS/blob/master/LICENSE) for more info. 23 | 24 | ## Change Log 25 | ### 5.2.0 (※Recommended※) 26 | * feature: update full-link monitoring protocol 27 | * feature: support BytePlus 28 | * feature: support configurating the limit of custom error 29 | 30 | ### 5.1.8 31 | * bugfix: fix the failure to manually report alog 32 | * optimization: compatible with Xcode 16.3 33 | 34 | ### 5.1.7 35 | * feature: support ignoring logs of abnormal devices 36 | 37 | ### 5.1.6 38 | * ~~feature: support ignoring crash log of abnormal devices~~ 39 | * feature: support more flexible reporting domain name configuration 40 | * bugfix: fix the issue of failure to start tracing in some scenarios 41 | * bugfix: fix the problem of uploading dsym 42 | * optimization: optimize some warnings 43 | 44 | ### 5.1.3 45 | * feature: support custom disk usage and expiration time of alog 46 | * bugfix: stability issues 47 | 48 | ### 5.1.2 49 | * optimization: optimize the abnormal data processing logic of page loading time 50 | 51 | ### 5.1.1 52 | * feature: support manual configuration of automation scenarios to prevent false positives of OOM in automation scenarios 53 | * feature: SDK configuration delivery support SIGPIPE 54 | 55 | ### 5.1.0 56 | * feature: support data reporting overseas 57 | 58 | ### 5.0.0 59 | * bugfix: fix the problem of OOM when reporting data if the network is abnormal 60 | * optimization(**Incompatible**): the minimum system version supported by the SDK is changed to iOS 10 61 | * optimization: optimize the processing logic when the disk space is insufficient, and throw exceptions in advance 62 | * optimization: optimize the verification logic of userID and deviceID (offline verification plug-in) 63 | 64 | ### 4.1.0 65 | * feature: access offline verification plug-in, which can visually verify the SDK access status. For details, please refer to [Link](https://www.volcengine.com/docs/6431/1298022) 66 | * bugfix: fix custom filters losing in some modules 67 | * bugfix: fix MemoryGraph crash on iOS 18 68 | 69 | ### 4.0.0 70 | * feature(**Incompatible**): remove armv7 71 | * bugfix: UITrackers supports multiple UIWindow 72 | 73 | ### 3.10.6 74 | * bugfix: fix symbol duplicate 75 | 76 | ### 3.10.5 77 | * bugfix: fix the OOM misjudgment problem when using UIWindowSceneDelegate 78 | 79 | ### 3.10.4 80 | * bugfix: fix custom log report error 81 | 82 | ### 3.10.3 83 | * bugfix: fix data migration crash 84 | * bugfix: fix BootingProtect occasionally failing to return OOM data 85 | 86 | ### 3.10.1 87 | * bugfix: fix alog crash 88 | 89 | ### 3.10.0 90 | * feature: add APMPlus log, and support cloud command or actively reporting 91 | * feature: new version of Hybrid monitoring 92 | * bugfix: fix the issue of missing nodes in MemoryGraph 93 | * optimization: optimize the reporting strategy of some logs to reduce the risk of ANR 94 | 95 | ### 3.9.2 96 | * optimization: support filtering of test scenarios in OOM crash determination 97 | 98 | ### 3.9.1 99 | * bugfix: fix function issue when calling in C++ files 100 | * optimization: optimize the reporting strategy of startup time to ignore abnormal data 101 | 102 | ### 3.9.0 103 | * feature: add Apple Privacy manifest 104 | 105 | ### 3.8.4 106 | * bugfix: fix the OOM misjudgment problem in some scenarios 107 | * bugfix: fix alog crash 108 | * bugfix: fix UITracker crash 109 | 110 | ### 3.8.0 111 | * bugfix: fix occasional crash 112 | * feature: support injecting custom network monitoring records 113 | 114 | ### 3.7.1 115 | * bugfix: fix priority inversion 116 | 117 | ### 3.7.0 118 | * feature: virtual memory monitoring 119 | * optimization: improve disk monitoring logs 120 | * bugfix: fix watchdog in MemoryGraph 121 | 122 | ### 3.6.4 123 | * bugfix: fix incompatibility issues with historical data 124 | 125 | ### 3.6.3 (Deprecated) 126 | * bugfix: fix missing data on page load 127 | * bugfix: fix symbol conflict 128 | * optimization: optimize console log output text 129 | 130 | ### 3.6.1 (Deprecated) 131 | * optimization: optimize data encryption logic 132 | * optimization: optimize library dependencies 133 | * feature: add compatibility APIs for page loading 134 | 135 | ### 3.5.4 (Deprecated) 136 | * bugfix: fix the crash of network monitoring in some scenarios 137 | * optimization: optimize the console log output logic 138 | 139 | ### 3.5.3 (Deprecated) 140 | * feature: add custom cloudCommand 141 | * feature: add battery metrics from MetricKit 142 | * feature: support custom log for component 143 | 144 | ### 3.4.2 145 | * bugfix: fix repeated reporting of CPU exception logs in some cases 146 | * bugfix: fix the problem that some crash logs in iOS 16 could not obtain valid stacks 147 | * bugfix: fix the crash after triggered a memory problem at debugging environment 148 | 149 | ### 3.3.2 150 | * feature: add CDN information 151 | * bugfix: fix symbol conflict 152 | 153 | ### 3.3.1 154 | * feature: actively report the alog file to support the sampling rate 155 | * bugfix: fix the problem that Crash Binary Image is missing 156 | * bugfix: fix the stuck problem that may be caused by getting the disk size 157 | 158 | ### 3.3.0 159 | * bugfix: fixed the problem of circular call in Hybrid 160 | * optimization: change the SessionTracker to the default dependency. If the SessionTracker module has not been integrated before, the event volume consumption may increase after the upgrade 161 | 162 | ### 3.2.3 163 | * feature: support ignoring the crash when the application exits 164 | 165 | ### 3.2.1 166 | * bugfix: fixed the OOM misjudgment problem in some scenarios 167 | 168 | ### 3.2.0 169 | * feature: Coredump 170 | 171 | ### 3.1.1 172 | * feature: support using custom network library to report data 173 | * optimization: limit the frequency of network status interface calls 174 | 175 | ### 3.1.0 176 | * feature: GWPASan 177 | 178 | ### 3.0.5 179 | * bugfix: stability issues 180 | * bugfix: fix mistake of OS version in some logs 181 | 182 | ### 3.0.4 183 | * feature: support fetching configuration for userID 184 | 185 | ### 3.0.3 186 | * feature: custom dimension for PV logs 187 | * optimization: optimize the trigger timing of some requests 188 | 189 | ### 3.0.2 190 | * bugfix: fix some stability issues 191 | * bugfix: Improve compatibility with dynamic libraries 192 | 193 | ### 3.0.1 194 | * bugfix: fix NSURLSession.sharedSession becoming unavailable in some cases 195 | * bugfix: fix MemoryGraph stuck in some cases 196 | * optimization: optimize the log reporting logic when the event balance is insufficient 197 | 198 | ### 3.0.0 199 | * feature: disk monitoring 200 | * feature(**Incompatible**): interface authentication 201 | 202 | ### 2.13.2 203 | * bugfix: fix memory leak 204 | * bugfix: fix some data collection error 205 | 206 | ### 2.13.1 207 | * bugfix: fix memory leak 208 | * bugfix: fix page records error 209 | 210 | ### 2.13.0 211 | * feature: support configuring performance data reporting interval 212 | * bugfix: fix Global subspec protocol mismatch 213 | 214 | ### 2.12.4 215 | * bugfix: fix network decision crash on iOS 15 216 | * bugfix: fix the misjudgment problem of OOM 217 | 218 | ### 2.12.3 219 | * bugfix: fix crash when network unavailable for SDK monitor 220 | * feature: custom configuration support Prewarm threshold 221 | 222 | ### 2.12.2 223 | * bugfix: fix Flutter configuration not taking effect 224 | 225 | ### 2.12.1 226 | * feature: add MetricKit subspec 227 | 228 | ### 2.12.0 229 | * feature: full link tracing - connect client network log with server log 230 | 231 | ### 2.11.1 232 | * optimization: optimize the problem that the launch time uploaded is too large 233 | * optimization: optimize console error log 234 | 235 | ### 2.10.1 236 | * bugfix: fix problem that network monitoring unavailable on low system 237 | 238 | ### 2.10.0 239 | * feature: add BootingProtect subspec 240 | 241 | ### 2.9.7 242 | * bugfix: fix memory leak in network monitor 243 | * optimization: time-consuming of network monitor during initialization 244 | * bugfix: fix the compatibility issue of network monitor in swift 245 | 246 | ### 2.9.3 247 | * bugfix: fix the problem that the device may fail to report the custom log when using the 12-hour clock 248 | 249 | ### 2.9.2 250 | * optimization: compatible with custom log reporting protocol 251 | 252 | ### 2.9.1 253 | * optimization: optimize custom log reporting time disorder 254 | * optimization: optimize the problem that log retrieval using user_id cannot be delivered normally 255 | * optimization: optimize console log to reduce duplicate messages 256 | 257 | ### 2.9.0 258 | * bugfix: fix crash when use class object as network delegate 259 | * feature: deadlock detector 260 | 261 | ### 2.8.1 262 | * bugfix: fix crash when working with Firebase Performance 263 | 264 | ### 2.8.0 265 | * feature: zombie object detection online 266 | * feature: userException interface supports passing in NSException 267 | * optimization: change the domain for Saas 268 | 269 | ### 2.7.4 270 | * bugfix: performance data reporting is not timely 271 | * feature: add debug log for CPU Monitor 272 | 273 | ### 2.7.3 274 | * feature: CPU Monitor 275 | 276 | ### 2.7.2 277 | * optimization: compliance requirements 278 | * feature: watchdog monitor supports component perspective 279 | * optimization: split device registration module 280 | * bugfix: network monitor may lead to OOM in some cases 281 | 282 | ### 2.6.5 283 | * optimization: destruction process 284 | * optimization: hook scheme 285 | 286 | ### 2.6.3 287 | * bugfix: missing fields 288 | 289 | ### 2.6.2 290 | * bugfix: symbol conflicts 291 | 292 | ### 2.6.1 293 | * bugfix: uploading apmlogs may crash 294 | * feature: add OneKit start task 295 | * optimization: dsym-uploading script 296 | * bugfix: network monitoring may cause some callbacks to fail 297 | 298 | ### 2.5.7 299 | * optimization: crash protection logic 300 | 301 | ### 2.5.6 302 | * bugfix: header lost 303 | 304 | ### 2.5.5 305 | * bugfix: crash when app exits 306 | 307 | ### 2.5.3 308 | * bugfix: network config 309 | * bugfix: start module api 310 | 311 | ### 2.5.2 312 | * feature: crash protector 313 | * feature: extension crash monitor 314 | * optimization: network monitor refactor 315 | 316 | ### 2.4.6 317 | * feature: enable default monitors 318 | 319 | ### 2.4.3 320 | * optimization: start analysis 321 | 322 | ### 2.4.1 323 | * bugfix: fix compiler error 324 | 325 | ### 2.4.0 326 | * feature: custom log and cloudCommand 327 | * bugfix: OOM log lose 328 | 329 | ### 2.3.2 330 | * bugfix: deviceID may be null in some case 331 | 332 | ### 2.3.0 333 | * feature: report launch log 334 | * bugfix: fix crash in iOS 15 and arm64e device 335 | 336 | ### 2.2.7 337 | * optimization: optimize the judgment for network error log 338 | 339 | ### 2.2.6 340 | * bugfix: fix symbol conflict 341 | 342 | ### 2.2.5 343 | * feature: component crash monitor support dynamic library 344 | 345 | ### 2.2.4 346 | * bugfix: fix the loss of network error log 347 | 348 | ### 2.2.3 349 | * bugfix: fix config not fetch 350 | 351 | ### 2.2.2 352 | * bugfix: fix symbol conflict 353 | 354 | ### 2.2.1 355 | * feature: add flutter monitor 356 | 357 | ### 2.1.10 358 | * bugfix: fix network code error 359 | 360 | ### 2.1.8 361 | * optimization: update OneKit to 1.1.13 362 | 363 | ### 2.1.7 364 | * bugfix: fix the loss of header files 365 | 366 | ### 2.1.6 367 | * optimization: optimize regular matching 368 | 369 | ### 2.1.5 370 | * optimization: optimize regular matching 371 | 372 | ### 2.1.4 373 | * bugfix: fix symbol conflict 374 | 375 | ### 2.1.3 376 | * optimization: remove some hook 377 | 378 | ### 2.1.2 379 | * optimization: modified UITracker to start synchronously 380 | 381 | ### 2.1.1 382 | * optimization: update OneKit to 1.1.9 383 | 384 | ### 2.1.0 385 | * optimization: component monitor support custom deviceID 386 | 387 | ### 2.0.6 388 | * bugfix: Custom-error add the validity of incoming parameters 389 | 390 | ### 2.0.5 391 | * bugfix: fix incorrect judgment of component crash 392 | 393 | ### 2.0.3 394 | * bugfix: fix compile error 395 | 396 | ### 2.0.2 (Obsolete) 397 | * bugfix: deviceID service error 398 | 399 | ### 2.0.0 400 | * feature: event analysis 401 | * feature: custom deviceID 402 | 403 | ### 1.5.10 404 | * bugfix: solve some category conflict 405 | 406 | ### 1.5.8 407 | * bugfix: solve conflict with BGFMDB 408 | 409 | ### 1.5.7 410 | * feature: enable Bitcode 411 | * bugfix: fix addScriptMessageHandler crash in Hybrid module 412 | 413 | ### 1.5.5 414 | * feature: viewControllers tracing 415 | * feature: more NSNotifications 416 | * optimization: bundle resources search path 417 | * feature: add custom information into MemoryGraph log 418 | 419 | ### 1.5.4 420 | * bugfix: solve conflicts with SSZipArchive 421 | * feature: some NSNotifications 422 | 423 | ### 1.5.3 424 | * bugfix: network monitor certificates verification failed in particular scenes 425 | 426 | ### 1.5.2 427 | * bugfix: request settings not effective 428 | 429 | ### 1.5.1 430 | * feature: network type support 5G 431 | * bugfix: solve conflicts with zipArchive 432 | 433 | ### 1.5.0 434 | * feature: update MemoryGraph config 435 | * feature: DSYM uploader script 436 | 437 | ### 1.4.0 438 | * feature: add debug log 439 | 440 | 441 | -------------------------------------------------------------------------------- /RangersAPM.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'RangersAPM' 3 | 4 | s.version = '2.10.0' 5 | 6 | s.summary = 'RangersAPM by Volcengine' 7 | 8 | s.description = 'APMInsight iOS SDK, a tool to monitor APP performance.' 9 | 10 | s.homepage = 'https://github.com/volcengine/APMInsight_iOS' 11 | 12 | s.license = { :type => 'MIT', :file => 'RangersAPM/LICENSE' } 13 | 14 | s.authors = 'Volcengine' 15 | 16 | s.ios.deployment_target = '9.0' 17 | 18 | s.source = { :http => "https://lf1-ttcdn-tos.pstatp.com/obj/heimdallr/RangersAPM/2.10.0/RangersAPM.zip" } 19 | 20 | s.frameworks = 'UIKit' 21 | 22 | s.pod_target_xcconfig = { 23 | 'OTHER_CPLUSPLUSFLAGS' => '-fno-c++-static-destructors', 24 | 'DEFINES_MODULE' => 'YES', 25 | } 26 | 27 | s.user_target_xcconfig = {'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',} 28 | 29 | s.static_framework = true 30 | 31 | s.subspec 'Above' do |above| 32 | above.vendored_libraries = "RangersAPM/Above/**/*.a" 33 | end 34 | 35 | s.subspec 'Zyone' do |zyone| 36 | zyone.vendored_libraries = "RangersAPM/Zyone/**/*.a" 37 | end 38 | 39 | s.subspec 'Public' do |public| 40 | public.vendored_libraries = "RangersAPM/Public/**/*.a" 41 | public.source_files = 'RangersAPM/Public/**/*.{h,m}' 42 | public.public_header_files = 'RangersAPM/Public/**/*.h' 43 | public.dependency 'RangersAPM/Core' 44 | public.dependency 'RangersAPM/Above' 45 | public.dependency 'RangersAPM/Zyone' 46 | public.dependency 'RARegisterKit/Core' 47 | end 48 | 49 | s.subspec 'Core' do |core| 50 | core.vendored_libraries = "RangersAPM/Core/**/*.a" 51 | core.libraries = 'c++','z','sqlite3' 52 | core.frameworks = 'SystemConfiguration','CoreTelephony','CoreFoundation' 53 | core.preserve_paths = 'RangersAPM/*.sh' 54 | core.resources = ['RangersAPM/Assets/Core/**/APMInsightCore.bundle'] 55 | core.dependency 'OneKit/BaseKit', '>=1.1.19' 56 | end 57 | 58 | s.subspec 'Crash' do |crash| 59 | crash.source_files = 'RangersAPM/Crash/**/*.{h,m}' 60 | crash.public_header_files = 'RangersAPM/Crash/**/*.h' 61 | crash.vendored_libraries = "RangersAPM/Crash/**/*.a" 62 | crash.dependency 'RangersAPM/Core' 63 | crash.dependency 'RangersAPM/Public' 64 | crash.dependency 'RangersAPM/HMD' 65 | crash.resources = ['RangersAPM/Assets/Crash/**/APMInsightCrash.bundle'] 66 | crash.libraries = 'c++abi' 67 | end 68 | 69 | s.subspec 'WatchDog' do |watchdog| 70 | watchdog.vendored_libraries = "RangersAPM/WatchDog/**/*.a" 71 | watchdog.dependency 'RangersAPM/Core' 72 | watchdog.dependency 'RangersAPM/HMD' 73 | watchdog.dependency 'RangersAPM/Public' 74 | end 75 | 76 | s.subspec 'OOM' do |oom| 77 | oom.vendored_libraries = "RangersAPM/OOM/**/*.a" 78 | oom.dependency 'RangersAPM/Core' 79 | oom.dependency 'RangersAPM/Crash' 80 | oom.dependency 'RangersAPM/WatchDog' 81 | oom.dependency 'RangersAPM/HMD' 82 | oom.dependency 'RangersAPM/Public' 83 | end 84 | 85 | s.subspec 'HMD' do |hmd| 86 | hmd.vendored_libraries = "RangersAPM/HMD/**/*.a" 87 | hmd.dependency 'RangersAPM/Core' 88 | hmd.dependency 'OneKit/Database', '>=1.1.19' 89 | end 90 | 91 | s.subspec 'LAG' do |lag| 92 | lag.vendored_libraries = "RangersAPM/LAG/**/*.a" 93 | lag.dependency 'RangersAPM/Core' 94 | lag.dependency 'RangersAPM/HMD' 95 | lag.dependency 'RangersAPM/Public' 96 | end 97 | 98 | s.subspec 'UserException' do |user| 99 | user.source_files = 'RangersAPM/UserException/**/*.{h,m}' 100 | user.public_header_files = "RangersAPM/UserException/**/*.h" 101 | user.vendored_libraries = "RangersAPM/UserException/**/*.a" 102 | user.dependency 'RangersAPM/Core' 103 | user.dependency 'RangersAPM/HMD' 104 | user.dependency 'RangersAPM/Public' 105 | end 106 | 107 | s.subspec 'UITrackers' do |uitrackers| 108 | uitrackers.source_files = 'RangersAPM/UITrackers/**/*.{h,m}' 109 | uitrackers.vendored_libraries = 'RangersAPM/UITrackers/**/*.a' 110 | uitrackers.public_header_files = 'RangersAPM/UITrackers/**/*.h' 111 | uitrackers.dependency 'RangersAPM/Core' 112 | uitrackers.dependency 'RangersAPM/HMD' 113 | uitrackers.dependency 'RangersAPM/Public' 114 | end 115 | 116 | s.subspec 'Monitors' do |monitors| 117 | monitors.source_files = 'RangersAPM/Monitors/**/*.{h,m}' 118 | monitors.vendored_libraries = 'RangersAPM/Monitors/**/*.a' 119 | monitors.public_header_files = "RangersAPM/Monitors/**/*.h" 120 | monitors.dependency 'RangersAPM/UITrackers' 121 | end 122 | 123 | s.subspec 'Hybrid' do |hybrid| 124 | hybrid.vendored_libraries = 'RangersAPM/Hybrid/**/*.a' 125 | hybrid.resources = ['RangersAPM/Assets/Hybrid/**/APMInsightHybrid.bundle'] 126 | hybrid.dependency 'RangersAPM/Core' 127 | hybrid.dependency 'RangersAPM/HMD' 128 | hybrid.dependency 'RangersAPM/Public' 129 | hybrid.frameworks = 'WebKit' 130 | end 131 | 132 | s.subspec 'MemoryGraph' do |memorygraph| 133 | memorygraph.vendored_libraries = 'RangersAPM/MemoryGraph/**/*.a' 134 | memorygraph.dependency 'RangersAPM/Core' 135 | memorygraph.dependency 'RangersAPM/HMD' 136 | memorygraph.dependency 'RangersAPM/Public' 137 | memorygraph.dependency 'RangersAPM/Zip' 138 | end 139 | 140 | s.subspec 'Zip' do |zip| 141 | zip.vendored_libraries = 'RangersAPM/Zip/**/*.a' 142 | zip.libraries = 'z' 143 | end 144 | 145 | s.subspec 'EventMonitor' do |eventmonitor| 146 | eventmonitor.source_files = 'RangersAPM/EventMonitor/**/*.{h,m}' 147 | eventmonitor.vendored_libraries = 'RangersAPM/EventMonitor/**/*.a' 148 | eventmonitor.public_header_files = 'RangersAPM/EventMonitor/**/*.h' 149 | eventmonitor.libraries = 'c++' 150 | eventmonitor.dependency 'RangersAPM/Core' 151 | eventmonitor.dependency 'RangersAPM/HMD' 152 | eventmonitor.dependency 'RangersAPM/Public' 153 | end 154 | 155 | s.subspec 'CN' do |cn| 156 | cn.vendored_libraries = 'RangersAPM/CN/**/*.a' 157 | cn.dependency 'RangersAPM/Core' 158 | cn.dependency 'RangersAPM/Public' 159 | end 160 | 161 | s.subspec 'Global' do |global| 162 | global.vendored_libraries = 'RangersAPM/Global/**/*.a' 163 | global.dependency 'RangersAPM/Core' 164 | global.dependency 'RangersAPM/Public' 165 | end 166 | 167 | s.subspec 'Flutter' do |flutter| 168 | flutter.source_files = 'RangersAPM/Flutter/**/*.{h,m}' 169 | flutter.vendored_libraries = 'RangersAPM/Flutter/**/*.a' 170 | flutter.public_header_files = 'RangersAPM/Flutter/**/*.h' 171 | flutter.dependency 'RangersAPM/EventMonitor' 172 | end 173 | 174 | s.subspec 'SessionTracker' do |st| 175 | st.vendored_libraries = 'RangersAPM/SessionTracker/**/*.a' 176 | st.dependency 'RangersAPM/Core' 177 | st.dependency 'RangersAPM/HMD' 178 | st.dependency 'RangersAPM/Public' 179 | end 180 | 181 | s.subspec 'APMLog' do |alog| 182 | alog.source_files = 'RangersAPM/APMLog/**/*.{h,m}' 183 | alog.vendored_libraries = 'RangersAPM/APMLog/**/*.a' 184 | alog.public_header_files = 'RangersAPM/APMLog/**/*.h' 185 | alog.dependency 'RangersAPM/Core' 186 | alog.dependency 'RangersAPM/HMD' 187 | alog.dependency 'RangersAPM/Zip' 188 | alog.dependency 'RangersAPM/Public' 189 | alog.libraries = 'c++','z', 'resolv' 190 | end 191 | 192 | s.subspec 'NetworkBasic' do |nb| 193 | nb.source_files = 'RangersAPM/NetworkBasic/**/*.{h,m}' 194 | nb.vendored_libraries = 'RangersAPM/NetworkBasic/**/*.a' 195 | nb.public_header_files = 'RangersAPM/NetworkBasic/**/*.h' 196 | nb.dependency 'RangersAPM/Core' 197 | nb.dependency 'RangersAPM/HMD' 198 | nb.dependency 'RangersAPM/Public' 199 | nb.libraries = 'c++' 200 | end 201 | 202 | s.subspec 'Network' do |net| 203 | net.source_files = 'RangersAPM/Network/**/*.{h,m}' 204 | net.vendored_libraries = 'RangersAPM/Network/**/*.a' 205 | net.public_header_files = 'RangersAPM/Network/**/*.h' 206 | net.dependency 'RangersAPM/NetworkBasic' 207 | end 208 | 209 | s.subspec 'NetworkPro' do |np| 210 | # np.source_files = 'RangersAPM/NetworkPro/**/*.{h,m}' 211 | np.vendored_libraries = 'RangersAPM/NetworkPro/**/*.a' 212 | # np.public_header_files = 'RangersAPM/NetworkPro/**/*.h' 213 | np.dependency 'RangersAPM/NetworkBasic' 214 | end 215 | 216 | s.subspec 'CrashProtector' do |cp| 217 | # cp.source_files = 'RangersAPM/CrashProtector/**/*.{h,m}' 218 | cp.vendored_libraries = 'RangersAPM/CrashProtector/**/*.a' 219 | # cp.public_header_files = 'RangersAPM/CrashProtector/**/*.h' 220 | cp.dependency 'RangersAPM/Core' 221 | cp.dependency 'RangersAPM/HMD' 222 | cp.dependency 'RangersAPM/Public' 223 | cp.dependency 'RangersAPM/Crash' 224 | cp.libraries = 'c++' 225 | end 226 | 227 | s.subspec 'OKKit' do |ok| 228 | ok.vendored_libraries = 'RangersAPM/OKKit/**/*.a' 229 | ok.dependency 'OneKit/StartUp' 230 | ok.dependency 'OneKit/Service', '>=1.1.39-rc.0' 231 | ok.dependency 'RangersAPM/Public' 232 | end 233 | 234 | s.subspec 'CPUException' do |cpu_exception| 235 | cpu_exception.vendored_libraries = 'RangersAPM/CPUException/**/*.a' 236 | cpu_exception.dependency 'RangersAPM/Core' 237 | cpu_exception.dependency 'RangersAPM/HMD' 238 | end 239 | 240 | s.subspec 'Zombie' do |zombie| 241 | zombie.vendored_libraries = 'RangersAPM/Zombie/**/*.a' 242 | zombie.dependency 'RangersAPM/Core' 243 | zombie.dependency 'RangersAPM/HMD' 244 | zombie.dependency 'RangersAPM/Public' 245 | zombie.dependency 'RangersAPM/Crash' 246 | zombie.pod_target_xcconfig = { 247 | 'GCC_PREPROCESSOR_DEFINITIONS[config=Release]' => '$(inherited) NS_BLOCK_ASSERTIONS' 248 | } 249 | end 250 | 251 | s.subspec 'BootingProtectLite' do |bpl| 252 | bpl.vendored_libraries = 'RangersAPM/BootingProtectLite/**/*.a' 253 | bpl.source_files = 'RangersAPM/BootingProtectLite/**/*.{h,m}' 254 | bpl.public_header_files = 'RangersAPM/BootingProtectLite/**/*.h' 255 | bpl.dependency 'RangersAPM/Crash' 256 | bpl.dependency 'RangersAPM/WatchDog' 257 | end 258 | 259 | s.subspec 'BootingProtect' do |bp| 260 | bp.vendored_libraries = 'RangersAPM/BootingProtect/**/*.a' 261 | bp.dependency 'RangersAPM/BootingProtectLite' 262 | bp.dependency 'RangersAPM/OOM' 263 | end 264 | end 265 | -------------------------------------------------------------------------------- /说明.md: -------------------------------------------------------------------------------- 1 | # APMInsight_iOS 2 | 应用性能监控全链路版 SDK。 进入官网获取更多SDK功能和接入相关信息。 [应用性能监控全链路版](https://www.volcengine.com/products/apmplus) 3 | 4 | ## 示例 5 | ### 下载安装 6 | 1. git clone https://github.com/volcengine/APMInsight_iOS.git --branch master 7 | 2. cd APMInsight_iOS/Example 8 | 3. bundle exec pod install 9 | 4. open APMInsight_iOS.xcworkspace 10 | 11 | ### 使用指南 12 | 1. 本demo已经接入了SDK的所有能力。 13 | 2. 你可以通过demo来制造一些崩溃和性能数据。 14 | 3. 修改 APMInitialViewController.m 中的 appID 和 appToken 可以把性能数据上报到你的控制台以查看。 15 | 16 | ## 环境要求 17 | * iOS 10.0+ 18 | * Xcode 14.0+ 19 | 20 | ## 证书 21 | APMInsight_iOS 使用 MIT 协议. 具体内容查看 [LISENSE](https://github.com/volcengine/APMInsight_iOS/blob/master/LICENSE). 22 | 23 | ## 更新日志 24 | ### 5.2.0 (※推荐※) 25 | * feature: 新的全链路监控协议 26 | * feature: 支持 BytePlus 27 | * feature: 自定义错误客户端限流支持自主配置 28 | 29 | ### 5.1.8 30 | * bugfix: 修复主动上报自定义日志失败问题 31 | * optimization: 兼容 Xcode 16.3 32 | 33 | ### 5.1.7 34 | * feature: 支持屏蔽越狱设备生产的日志 35 | 36 | ### 5.1.6 37 | * ~~feature: 支持忽略越狱设备崩溃日志~~ 38 | * feature: 支持更灵活的上报域名配置 39 | * bugfix: 修复部分场景下启动日志记录失败的问题 40 | * bugfix: 修复海外机房符号表上传问题 41 | * optimization: 优化一些警告 42 | 43 | ### 5.1.3 44 | * feature: 自定义日志支持自定义磁盘占用和过期时间 45 | * bugfix: 稳定性问题修复 46 | 47 | ### 5.1.2 48 | * optimization: 优化页面加载耗时的异常数据处理 49 | 50 | ### 5.1.1 51 | * feature: 支持手动配置自动化场景,防止 OOM 在自动化场景的误报 52 | * feature: SDK 下发配置支持 SIGPIPE 的控制 53 | 54 | ### 5.1.0 55 | * feature: 支持数据上报到海外 56 | 57 | ### 5.0.0 58 | * bugfix: 修复网络异常时数据上报发生 OOM 的问题 59 | * optimization(**Incompatible**): SDK 支持最低系统版本变更为 iOS 10 60 | * optimization: 优化磁盘空间不足时的处理逻辑,前置抛出异常 61 | * optimization: 优化 userID 和 deviceID 的验证逻辑(线下验证插件) 62 | 63 | ### 4.1.0 64 | * feature: 接入线下验证插件,可以直观验证 SDK 接入状态,详情可参考 [使用线下插件验证数据上报](https://www.volcengine.com/docs/6431/1298022) 65 | * bugfix: 修复部分模块缺失自定义维度的问题 66 | * bugfix: 修复 MemoryGraph 在 iOS 18 中的崩溃问题 67 | 68 | ### 4.0.0 69 | * feature(**Incompatible**): 不再支持 armv7 架构 70 | * bugfix: UITrackers 兼容多 UIWindow 的业务场景 71 | 72 | ### 3.10.6 73 | * bugfix: 修复符号冲突问题 74 | 75 | ### 3.10.5 76 | * bugfix: 修复使用 UIWindowSceneDelegate 时的 OOM 误报问题 77 | 78 | ### 3.10.4 79 | * bugfix: 修复自定义日志上报失败问题 80 | 81 | ### 3.10.3 82 | * bugfix: 稳定性问题修复 83 | * bugfix: 解决连续崩溃保护偶尔不能返回 OOM 数据的问题 84 | 85 | ### 3.10.1 86 | * bugfix: 稳定性问题修复 87 | 88 | ### 3.10.0 89 | * feature: 添加 APMPlus log,支持回捞或主动上报 90 | * feature: 新版 Hybrid 监控 91 | * bugfix: 修复 MemoryGraph 部分节点缺失问题 92 | * optimization: 优化部分日志时上报策略,降低卡死风险 93 | 94 | ### 3.9.2 95 | * optimization: 优化OOM崩溃判定策略,支持测试场景的过滤 96 | 97 | ### 3.9.1 98 | * bugfix: 修复部分接口在 C++ 文件中的调用问题 99 | * optimization: 优化启动时间上报策略,忽略异常数据 100 | 101 | ### 3.9.0 102 | * feature: 适配 Apple Privacy manifest 103 | 104 | ### 3.8.4 105 | * bugfix: 修复一些场景下 OOM 误报问题 106 | * bugfix: 修复 alog 的稳定性问题 107 | * bugfix: 修复 UITracker 的稳定性问题 108 | 109 | ### 3.8.0 110 | * bugfix: 修复主副卡切换时偶现的稳定性问题 111 | * feature: 支持注入自定义网络监控日志 112 | 113 | ### 3.7.1 114 | * bugfix: 修复优先级反转问题 115 | 116 | ### 3.7.0 117 | * feature: 虚拟内存监控 118 | * optimization: 完善磁盘监控日志 119 | * bugfix: 修复 MemoryGraph 中的卡死问题 120 | 121 | ### 3.6.4 122 | * bugfix: 修复历史数据兼容问题 123 | 124 | ### 3.6.3 (废弃) 125 | * bugfix: 修复页面加载部分数据缺失问题 126 | * bugfix: 修复符号冲突问题 127 | * optimization: 优化控制台日志输出文本 128 | 129 | ### 3.6.1 (废弃) 130 | * optimization: 优化数据加密逻辑 131 | * optimization: 优化依赖库 132 | * feature: 页面加载增加兼容性接口 133 | 134 | ### 3.5.4 (废弃) 135 | * bugfix: 修复网络监控在某些场景下的崩溃问题 136 | * optimization: 优化控制台日志输出逻辑 137 | 138 | ### 3.5.3 (废弃) 139 | * feature: 添加自定义回捞功能 140 | * feature: 添加 MetricKit 性能数据消费功能 141 | * feature: 支持组件视角自定义日志回捞功能 142 | 143 | ### 3.4.2 144 | * bugfix: 修复部分场景 CPU 异常日志重复上报问题 145 | * bugfix: 修复 iOS 16 部分崩溃日志无法获取有效堆栈问题 146 | * bugfix: 修复调试环境触发内存问题后的崩溃问题 147 | 148 | ### 3.3.2 149 | * feature: 添加 CDN 相关信息上报 150 | * bugfix: 修复符号冲突问题 151 | 152 | ### 3.3.1 153 | * feature: 主动上报 alog 文件支持采样率 154 | * bugfix: 修复 Crash Binary Image 缺失的问题 155 | * bugfix: 修复获取磁盘大小可能导致的卡死问题 156 | 157 | ### 3.3.0 158 | * bugfix: 修复 Hybrid 中循环调用的问题 159 | * optimization: 将 SessionTracker 改为默认依赖,若之前没有接入 SessionTracker 模块,升级后可能会造成事件量消耗增加 160 | 161 | ### 3.2.3 162 | * feature: 支持忽略应用退出时发生的崩溃 163 | 164 | ### 3.2.1 165 | * bugfix: 修复一些场景下 OOM 误报问题 166 | 167 | ### 3.2.0 168 | * feature: Coredump 169 | 170 | ### 3.1.1 171 | * feature: 数据上报支持自定义网络库 172 | * optimization: 限制网络状态接口调用频率 173 | 174 | ### 3.1.0 175 | * feature: GWPASan 176 | 177 | ### 3.0.5 178 | * bugfix: 稳定性问题修复 179 | * bugfix: 修复部分日志系统版本号错误问题 180 | 181 | ### 3.0.4 182 | * feature: 配置拉取参数支持 userID 183 | 184 | ### 3.0.3 185 | * feature: 支持 PV 日志上报自定义维度 186 | * optimization: 优化部分请求触发时机 187 | 188 | ### 3.0.2 189 | * bugfix: 修复一些稳定性问题 190 | * bugfix: 提高对动态库的兼容性 191 | 192 | ### 3.0.1 193 | * bugfix: 修复 NSURLSession.sharedSession 在某些场景下变得不可用的问题 194 | * bugfix: 修复 MemoryGraph 在某些场景下卡死的问题 195 | * optimization: 优化事件量不足时的日志上报逻辑 196 | 197 | ### 3.0.0 198 | * feature: 磁盘监控 199 | * feature(**Incompatible**): 接口鉴权 200 | 201 | ### 2.13.2 202 | * bugfix: 修复内存泄漏问题 203 | * bugfix: 修复部分数据采集错误问题 204 | 205 | ### 2.13.1 206 | * bugfix: 修复内存泄漏问题 207 | * bugfix: 修复页面追踪数据异常问题 208 | 209 | ### 2.13.0 210 | * feature: 支持配置性能数据上报间隔 211 | * bugfix: 修复 Global 子库协议不匹配问题 212 | 213 | ### 2.12.4 214 | * bugfix: 修复 iOS 15上的网络判定崩溃问题 215 | * bugfix: 修复 OOM 的误判问题 216 | 217 | ### 2.12.3 218 | * bugfix: 修复组件监控网络异常时可能发生崩溃的问题 219 | * feature: Prewarm 判定阈值支持平台配置 220 | 221 | ### 2.12.2 222 | * bugfix: 修复 Flutter 配置未生效问题 223 | 224 | ### 2.12.1 225 | * feature: 添加 MetricKit 模块 226 | 227 | ### 2.12.0 228 | * feature: 全链路 tracing,打通客户端网络日志和服务端日志 229 | 230 | ### 2.11.1 231 | * optimization: 优化统计到的启动时间过大问题 232 | * optimization: 优化控制台错误日志输出 233 | 234 | ### 2.10.1 235 | * bugfix: 修复低系统版本网络监控可能失效问题 236 | 237 | ### 2.10.0 238 | * feature: 新增连续崩溃保护模块 239 | 240 | ### 2.9.7 241 | * bugfix: 修复网络监控内存泄漏问题 242 | * optimization: 优化网络监控初始化耗时问题 243 | * bugfix: 修复网络监控在 swift 中的兼容问题 244 | 245 | ### 2.9.3 246 | * bugfix: 修复设备使用12小时制,自定义日志上报可能失败问题 247 | 248 | ### 2.9.2 249 | * optimization: 兼容自定义日志上报协议 250 | 251 | ### 2.9.1 252 | * optimization: 优化自定义日志上报时间错乱问题 253 | * optimization: 优化使用 user_id 进行日志回捞无法正常下发问题 254 | * optimization: 优化控制台日志,减少重复信息 255 | 256 | ### 2.9.0 257 | * bugfix: 修复网络代理使用类对象时引发的崩溃问题 258 | * feature: 死锁检测 259 | 260 | ### 2.8.1 261 | * bugfix: 修复同时接入Firebase性能监控概率崩溃问题 262 | 263 | ### 2.8.0 264 | * feature: 线上 Zombie 对象检测 265 | * feature: 自定义异常接口支持传入 NSException 对象 266 | * optimization: 更换 Saas 默认上报域名 267 | 268 | ### 2.7.4 269 | * bugfix: 修复性能数据部分上报时机丢失问题 270 | * feature: CPU 监控添加调试日志 271 | 272 | ### 2.7.3 273 | * feature: CPU 监控 274 | 275 | ### 2.7.2 276 | * optimization: 合规问题 277 | * feature: 组件视角支持卡死监控 278 | * optimization: 设备注册模块拆分 279 | * bugfix: 网络监控某些场景可能 OOM 280 | 281 | ### 2.6.5 282 | * optimization: 优化析构过程 283 | * optimization: 优化 hook 方案 284 | 285 | ### 2.6.3 286 | * bugfix: 修复字段缺失问题 287 | 288 | ### 2.6.2 289 | * bugfix: 修复符号冲突问题 290 | 291 | ### 2.6.1 292 | * bugfix: 修复崩溃后上传自定义日志可能崩溃问题 293 | * feature: 添加 OneKit 注册任务 294 | * optimization: 优化符号表上传脚本 295 | * bugfix: 修复网络监控可能导致部分回调失效问题 296 | 297 | ### 2.5.7 298 | * optimization: 优化崩溃防护兜底逻辑 299 | 300 | ### 2.5.6 301 | * bugfix: 修复头文件缺失问题 302 | 303 | ### 2.5.5 304 | * bugfix: 修复一个 APP 退出时可能出现的崩溃问题 305 | 306 | ### 2.5.3 307 | * bugfix: 修复网络监控配置问题 308 | * bugfix: 兼容启动分析历史接口 309 | 310 | ### 2.5.2 311 | * feature: 新增崩溃防护功能 312 | * feature: 新增 Extension 崩溃监控 313 | * optimization: 网络监控能力优化 314 | 315 | ### 2.4.6 316 | * feature: 允许配置默认启动模块,用于首次启动配置 317 | 318 | ### 2.4.3 319 | * optimization: 启动分析优化 320 | 321 | ### 2.4.1 322 | * bugfix: 修复编译问题 323 | 324 | ### 2.4.0 325 | * feature: 自定义日志与云控 326 | * bugfix: 修复 OOM 日志丢失问题 327 | 328 | ### 2.3.2 329 | * bugfix: 修复 deviceID 为空问题 330 | 331 | ### 2.3.0 332 | * feature: 上报 launch 事件 333 | * bugfix: 修复 iOS 15 arm64e 机型下的崩溃 334 | 335 | ### 2.2.7 336 | * optimization: 优化网络错误日志上报判定 337 | 338 | ### 2.2.6 339 | * bugfix: 修复符号冲突 340 | 341 | ### 2.2.5 342 | * feature: 组件崩溃监控支持动态库 343 | 344 | ### 2.2.4 345 | * bugfix: 修复网络错误日志丢失问题 346 | 347 | ### 2.2.3 348 | * bugfix: 修复配置为拉取问题 349 | 350 | ### 2.2.2 351 | * bugfix: 修复符号冲突 352 | 353 | ### 2.2.1 354 | * feature: 支持 flutter 监控 355 | 356 | ### 2.1.10 357 | * bugfix: 修复网络类型错误问题 358 | 359 | ### 2.1.8 360 | * optimization: 更新 OneKit 1.1.13 361 | 362 | ### 2.1.7 363 | * bugfix: 修复头文件丢失问题 364 | 365 | ### 2.1.6 366 | * optimization: 优化正则匹配 367 | 368 | ### 2.1.5 369 | * optimization: 优化正则匹配 370 | 371 | ### 2.1.4 372 | * bugfix: 修复符号冲突 373 | 374 | ### 2.1.3 375 | * optimization: 移除一些 hook 376 | 377 | ### 2.1.2 378 | * optimization: UITracker 修改为同步启动 379 | 380 | ### 2.1.1 381 | * optimization: 更新 OneKit 1.1.9 382 | 383 | ### 2.1.0 384 | * optimization: 组件监控支持自定义设备ID 385 | 386 | ### 2.0.6 387 | * bugfix: 自定义错误添加参数校验 388 | 389 | ### 2.0.5 390 | * bugfix: 修复组件崩溃判定错误问题 391 | 392 | ### 2.0.3 393 | * bugfix: 修复编译问题 394 | 395 | ### 2.0.2 (Obsolete) 396 | * bugfix: 修复设备ID服务问题 397 | 398 | ### 2.0.0 399 | * feature: 事件分析 400 | * feature: 支持自定义设备ID 401 | 402 | ### 1.5.10 403 | * bugfix: 修复分类冲突问题 404 | 405 | ### 1.5.8 406 | * bugfix: 修复和 BGFMDB 的冲突问题 407 | 408 | ### 1.5.7 409 | * feature: 支持 Bitcode 410 | * bugfix: 修复稳定性问题 411 | 412 | ### 1.5.5 413 | * feature: 支持 viewControllers 链路跟踪 414 | * feature: 添加一些通知 415 | * optimization: 优化资源查找路径 416 | * feature: 添加自定义信息 417 | 418 | ### 1.5.4 419 | * bugfix: 修复符号冲突 420 | * feature: 新增通知 421 | 422 | ### 1.5.3 423 | * bugfix: 修复一些场景下网络证书校验失败问题 424 | 425 | ### 1.5.2 426 | * bugfix: 修复配置未生效问题 427 | 428 | ### 1.5.1 429 | * feature: 网络类型支持 5G 430 | * bugfix: 修复和 zipArchive 的冲突 431 | 432 | ### 1.5.0 433 | * feature: 添加符号表上传脚本 434 | 435 | ### 1.4.0 436 | * feature: 添加 debug 日志 437 | 438 | 439 | --------------------------------------------------------------------------------