├── .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 |
--------------------------------------------------------------------------------