線下監(jiān)控
- Xcode Instruments
- 使用 Instruments 的 os_signpost API 來完成自定義的性能數(shù)據(jù)監(jiān)控工具開發(fā)步责。
線上監(jiān)控
原則
- 監(jiān)控代碼不要侵入到業(yè)務(wù)代碼中返顺;
- 采用性能消耗最小的監(jiān)控方案。
監(jiān)控內(nèi)容
- CPU使用率
- FPS幀率
- 內(nèi)存
CPU使用率
方案
APP對CPU使用率蔓肯,等于APP中所有線程對CPU使用率的總和遂鹊。
在iOS系統(tǒng)中,usr/include/mach/thread_info.h 里可以看到線程基本信息的結(jié)構(gòu)體:
struct thread_basic_info {
time_value_t user_time; // 用戶運行時長
time_value_t system_time; // 系統(tǒng)運行時長
integer_t cpu_usage; // CPU 使用率
policy_t policy; // 調(diào)度策略
integer_t run_state; // 運行狀態(tài)
integer_t flags; // 各種標(biāo)記
integer_t suspend_count; // 暫停線程的計數(shù)
integer_t sleep_time; // 休眠的時間
};
實現(xiàn)
使用定時器蔗包,每2s去遍歷所有線程秉扑,累加線程的cpu_usage字段的值,獲得當(dāng)前APP對CPU的使用率调限。
+ (integer_t)cpuUsage {
thread_act_array_t threads; //int 組成的數(shù)組比如 thread[1] = 5635
mach_msg_type_number_t threadCount = 0; //mach_msg_type_number_t 是 int 類型
const task_t thisTask = mach_task_self();
//根據(jù)當(dāng)前 task 獲取所有線程
kern_return_t kr = task_threads(thisTask, &threads, &threadCount);
if (kr != KERN_SUCCESS) {
return 0;
}
integer_t cpuUsage = 0;
// 遍歷所有線程
for (int i = 0; i < threadCount; i++) {
thread_info_data_t threadInfo;
thread_basic_info_t threadBaseInfo;
mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) {
// 獲取 CPU 使用率
threadBaseInfo = (thread_basic_info_t)threadInfo;
if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {
cpuUsage += threadBaseInfo->cpu_usage;
}
}
}
assert(vm_deallocate(mach_task_self(), (vm_address_t)threads, threadCount * sizeof(thread_t)) == KERN_SUCCESS);
return cpuUsage;
}
FPS監(jiān)控
通過注冊 CADisplayLink 得到屏幕的同步刷新率舟陆,記錄每次刷新時間,然后就可以得到 FPS旧噪。
- (void)start {
self.dLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsCount:)];
[self.dLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
// 方法執(zhí)行幀率和屏幕刷新率保持一致
- (void)fpsCount:(CADisplayLink *)displayLink {
if (lastTimeStamp == 0) {
lastTimeStamp = self.dLink.timestamp;
} else {
total++;
// 開始渲染時間與上次渲染時間差值
NSTimeInterval useTime = self.dLink.timestamp - lastTimeStamp;
if (useTime < 1) return;
lastTimeStamp = self.dLink.timestamp;
// fps 計算
fps = total / useTime;
total = 0;
}
}
內(nèi)存監(jiān)控
phys_footprint是實際使用的物理內(nèi)存吨娜。
內(nèi)存信息存在task_info.h(完整路徑 usr/include/mach/task.info.h)文件的task_vm_info結(jié)構(gòu)體中,其中phys_footprint就是物理內(nèi)存的使用淘钟,而不是駐留內(nèi)存resident_size宦赠。結(jié)構(gòu)體里和內(nèi)存相關(guān)的代碼如下:
struct task_vm_info {
mach_vm_size_t virtual_size; // 虛擬內(nèi)存大小
integer_t region_count; // 內(nèi)存區(qū)域的數(shù)量
integer_t page_size;
mach_vm_size_t resident_size; // 駐留內(nèi)存大小
mach_vm_size_t resident_size_peak; // 駐留內(nèi)存峰值
...
/* added for rev1 */
mach_vm_size_t phys_footprint; // 物理內(nèi)存
...
類似于對 CPU 使用率的監(jiān)控陪毡,我們只要從這個結(jié)構(gòu)體里取出 phys_footprint 字段的值,就能夠監(jiān)控到實際物理內(nèi)存的使用情況了勾扭。
uint64_t memoryUsage() {
task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if (result != KERN_SUCCESS)
return 0;
return vmInfo.phys_footprint;
}