update
This commit is contained in:
113
Pods/CocoaDebug/Sources/Monitor/FPSCounter.swift
generated
Normal file
113
Pods/CocoaDebug/Sources/Monitor/FPSCounter.swift
generated
Normal file
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
/*
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class FPSCounter: NSObject {
|
||||
|
||||
internal class DisplayLinkProxy: NSObject {
|
||||
|
||||
@objc weak var parentCounter: FPSCounter?
|
||||
|
||||
@objc func updateFromDisplayLink(_ displayLink: CADisplayLink) {
|
||||
parentCounter?.updateFromDisplayLink(displayLink)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
private var displayLink: CADisplayLink
|
||||
private var displayLinkProxy: DisplayLinkProxy
|
||||
|
||||
|
||||
public override init() {
|
||||
self.displayLinkProxy = DisplayLinkProxy()
|
||||
self.displayLink = CADisplayLink(
|
||||
target: self.displayLinkProxy,
|
||||
selector: #selector(DisplayLinkProxy.updateFromDisplayLink(_:))
|
||||
)
|
||||
|
||||
super.init()
|
||||
|
||||
self.displayLinkProxy.parentCounter = self
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.displayLink.invalidate()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
public weak var delegate: FPSCounterDelegate?
|
||||
|
||||
@objc public var notificationDelay: TimeInterval = 1.0
|
||||
|
||||
|
||||
// MARK: - Tracking
|
||||
|
||||
private var runloop: RunLoop?
|
||||
private var mode: RunLoop.Mode?
|
||||
|
||||
|
||||
@objc public func startMonitoring(inRunLoop runloop: RunLoop = .main, mode: RunLoop.Mode = .common) {
|
||||
if CocoaDebugSettings.shared.enableFpsMonitoring == false {return}
|
||||
|
||||
self.stopMonitoring()
|
||||
|
||||
self.runloop = runloop
|
||||
self.mode = mode
|
||||
self.displayLink.add(to: runloop, forMode: mode)
|
||||
}
|
||||
|
||||
|
||||
@objc public func stopMonitoring() {
|
||||
guard let runloop = self.runloop, let mode = self.mode else { return }
|
||||
|
||||
self.displayLink.remove(from: runloop, forMode: mode)
|
||||
self.runloop = nil
|
||||
self.mode = nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Handling Frame Updates
|
||||
|
||||
private var lastNotificationTime: CFAbsoluteTime = 0.0
|
||||
private var numberOfFrames = 0
|
||||
|
||||
private func updateFromDisplayLink(_ displayLink: CADisplayLink) {
|
||||
if self.lastNotificationTime == 0.0 {
|
||||
self.lastNotificationTime = CFAbsoluteTimeGetCurrent()
|
||||
return
|
||||
}
|
||||
|
||||
self.numberOfFrames += 1
|
||||
|
||||
let currentTime = CFAbsoluteTimeGetCurrent()
|
||||
let elapsedTime = currentTime - self.lastNotificationTime
|
||||
|
||||
if elapsedTime >= self.notificationDelay {
|
||||
self.notifyUpdateForElapsedTime(elapsedTime)
|
||||
self.lastNotificationTime = 0.0
|
||||
self.numberOfFrames = 0
|
||||
}
|
||||
}
|
||||
|
||||
private func notifyUpdateForElapsedTime(_ elapsedTime: CFAbsoluteTime) {
|
||||
let fps = Int(round(Double(self.numberOfFrames) / elapsedTime))
|
||||
self.delegate?.fpsCounter(self, didUpdateFramesPerSecond: fps)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol FPSCounterDelegate: NSObjectProtocol {
|
||||
func fpsCounter(_ counter: FPSCounter, didUpdateFramesPerSecond fps: Int)
|
||||
}
|
||||
*/
|
||||
25
Pods/CocoaDebug/Sources/Monitor/_BacktraceLogger.h
generated
Executable file
25
Pods/CocoaDebug/Sources/Monitor/_BacktraceLogger.h
generated
Executable file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/*!
|
||||
* @brief 线程堆栈上下文输出
|
||||
*/
|
||||
@interface _BacktraceLogger : NSObject
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfAllThread;
|
||||
+ (NSString *)cocoadebug_backtraceOfMainThread;
|
||||
+ (NSString *)cocoadebug_backtraceOfCurrentThread;
|
||||
+ (NSString *)cocoadebug_backtraceOfNSThread:(NSThread *)thread;
|
||||
|
||||
+ (void)cocoadebug_logMain;
|
||||
+ (void)cocoadebug_logCurrent;
|
||||
+ (void)cocoadebug_logAllThread;
|
||||
|
||||
@end
|
||||
420
Pods/CocoaDebug/Sources/Monitor/_BacktraceLogger.m
generated
Executable file
420
Pods/CocoaDebug/Sources/Monitor/_BacktraceLogger.m
generated
Executable file
@@ -0,0 +1,420 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_BacktraceLogger.h"
|
||||
#import <mach/mach.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/nlist.h>
|
||||
|
||||
#import "CocoaDebugTool.h"
|
||||
#import "_OCLogHelper.h"
|
||||
|
||||
|
||||
/*!
|
||||
* @brief 适配不同CPU的宏定义
|
||||
*
|
||||
* @thx 代码出自https://github.com/bestswifter/BSBacktraceLogger 加工修改
|
||||
*/
|
||||
#if defined(__arm64__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(3UL))
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE ARM_THREAD_STATE64
|
||||
#define COCOADEBUG_FRAME_POINTER __fp
|
||||
#define COCOADEBUG_STACK_POINTER __sp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __pc
|
||||
|
||||
#elif defined(__arm__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(1UL))
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT ARM_THREAD_STATE_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE ARM_THREAD_STATE
|
||||
#define COCOADEBUG_FRAME_POINTER __r[7]
|
||||
#define COCOADEBUG_STACK_POINTER __sp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __pc
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) (A)
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE x86_THREAD_STATE64
|
||||
#define COCOADEBUG_FRAME_POINTER __rbp
|
||||
#define COCOADEBUG_STACK_POINTER __rsp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __rip
|
||||
|
||||
#elif defined(__i386__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) (A)
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE x86_THREAD_STATE32
|
||||
#define COCOADEBUG_FRAME_POINTER __ebp
|
||||
#define COCOADEBUG_STACK_POINTER __esp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __eip
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define _TRACE_FMT "%-4d%-31s 0x%016lx %s + %lu"
|
||||
#define _POINTER_FMT "0x%016lx"
|
||||
#define _POINTER_SHORT_FMT "0x%lx"
|
||||
#define COCOADEBUG_NLIST struct nlist_64
|
||||
#else
|
||||
#define _TRACE_FMT "%-4d%-31s 0x%08lx %s + %lu"
|
||||
#define _POINTER_FMT "0x%08lx"
|
||||
#define _POINTER_SHORT_FMT "0x%lx"
|
||||
#define COCOADEBUG_NLIST struct nlist
|
||||
|
||||
#endif
|
||||
|
||||
#define _MAX_FRAME_NUMBER 30
|
||||
#define _LOG_SEPERATE printf("\n");
|
||||
#define _FAILED_UINT_PTR_ADDRESS 0
|
||||
#define _CALL_INSTRUCTION_FROM_RETURN_ADDRESS(A) (_DETAG_INSTRUCTION_ADDRESS((A)) - 1)
|
||||
|
||||
|
||||
typedef struct COCOADEBUGStackFrameEntry{
|
||||
const struct COCOADEBUGStackFrameEntry * const previous;
|
||||
const uintptr_t return_address;
|
||||
} COCOADEBUGStackFrameEntry;
|
||||
|
||||
static mach_port_t main_thread_id;
|
||||
|
||||
|
||||
|
||||
@implementation _BacktraceLogger
|
||||
|
||||
+ (void)load {
|
||||
main_thread_id = mach_thread_self();
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Public
|
||||
+ (NSString *)cocoadebug_backtraceOfAllThread {
|
||||
thread_act_array_t threads;
|
||||
mach_msg_type_number_t thread_count = 0;
|
||||
const task_t this_task = mach_task_self();
|
||||
|
||||
kern_return_t kr = task_threads(this_task, &threads, &thread_count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
return @"Failed to get information of all threads";
|
||||
}
|
||||
NSMutableString * result = @"".mutableCopy;
|
||||
for (int idx = 0; idx < thread_count; idx++) {
|
||||
[result appendString: _cocoadebug_backtraceOfThread(threads[idx])];
|
||||
}
|
||||
return result.copy;
|
||||
}
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfMainThread {
|
||||
return [self cocoadebug_backtraceOfNSThread: [NSThread mainThread]];
|
||||
}
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfCurrentThread {
|
||||
return [self cocoadebug_backtraceOfNSThread: [NSThread currentThread]];
|
||||
}
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfNSThread:(NSThread *)thread {
|
||||
return _cocoadebug_backtraceOfThread(cocoadebug_machThreadFromNSThread(thread));
|
||||
}
|
||||
|
||||
+ (void)cocoadebug_logMain {
|
||||
// _LOG_SEPERATE
|
||||
// NSLog(@"Detected UI Blocking %@", [self cocoadebug_backtraceOfMainThread]);
|
||||
// _LOG_SEPERATE
|
||||
|
||||
[CocoaDebugTool logWithString:[NSString stringWithFormat:@"\nDetected UI Blocking\n%@\n", [self cocoadebug_backtraceOfMainThread]] color:[UIColor redColor]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"CocoaDebug_Detected_UI_Blocking" object:nil];
|
||||
}
|
||||
|
||||
+ (void)cocoadebug_logCurrent {
|
||||
_LOG_SEPERATE
|
||||
NSLog(@"%@", [self cocoadebug_backtraceOfCurrentThread]);
|
||||
_LOG_SEPERATE
|
||||
}
|
||||
|
||||
+ (void)cocoadebug_logAllThread {
|
||||
_LOG_SEPERATE
|
||||
NSLog(@"%@", [self cocoadebug_backtraceOfAllThread]);
|
||||
_LOG_SEPERATE
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Generate
|
||||
thread_t cocoadebug_machThreadFromNSThread(NSThread * nsthread) {
|
||||
char name[256];
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
task_threads(mach_task_self(), &list, &count);
|
||||
|
||||
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
|
||||
NSString * originName = nsthread.name;
|
||||
[nsthread setName: [NSString stringWithFormat: @"%f", timeStamp]];
|
||||
|
||||
if ([nsthread isMainThread]) { return (thread_t)main_thread_id; }
|
||||
|
||||
for (int idx = 0; idx < count; idx++) {
|
||||
pthread_t pt = pthread_from_mach_thread_np(list[idx]);
|
||||
if ([nsthread isMainThread] && list[idx] == main_thread_id) { return list[idx]; }
|
||||
if (pt) {
|
||||
name[0] = '\0';
|
||||
pthread_getname_np(pt, name, sizeof(name));
|
||||
if (!strcmp(name, [nsthread name].UTF8String)) {
|
||||
[nsthread setName: originName];
|
||||
return list[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
[nsthread setName: originName];
|
||||
return mach_thread_self();
|
||||
}
|
||||
|
||||
NSString * _cocoadebug_backtraceOfThread(thread_t thread) {
|
||||
uintptr_t backtraceBuffer[_MAX_FRAME_NUMBER];
|
||||
int idx = 0;
|
||||
NSMutableString * result = [NSString stringWithFormat: @"Backtrace of Thread %u:\n======================================================================================\n", thread].mutableCopy;
|
||||
|
||||
_STRUCT_MCONTEXT machineContext;
|
||||
if (!cocoadebug_fillThreadStateIntoMachineContext(thread, &machineContext)) {
|
||||
return [NSString stringWithFormat: @"Failed to get information abount thread: %u", thread];
|
||||
}
|
||||
const uintptr_t instructionAddress = cocoadebug_mach_instructionAddress(&machineContext);
|
||||
backtraceBuffer[idx++] = instructionAddress;
|
||||
|
||||
uintptr_t linkRegister = cocoadebug_mach_linkRegister(&machineContext);
|
||||
if (linkRegister) {
|
||||
backtraceBuffer[idx++] = linkRegister;
|
||||
}
|
||||
if (instructionAddress == _FAILED_UINT_PTR_ADDRESS) { return @"Failed to get instruction address"; }
|
||||
|
||||
COCOADEBUGStackFrameEntry frame = { 0 };
|
||||
const uintptr_t framePtr = cocoadebug_mach_framePointer(&machineContext);
|
||||
if (framePtr == _FAILED_UINT_PTR_ADDRESS ||
|
||||
cocoadebug_mach_copyMem((void *)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) {
|
||||
return @"failed to get frame pointer";
|
||||
}
|
||||
|
||||
for (; idx < _MAX_FRAME_NUMBER; idx++) {
|
||||
backtraceBuffer[idx] = frame.return_address;
|
||||
if (backtraceBuffer[idx] == _FAILED_UINT_PTR_ADDRESS ||
|
||||
frame.previous == NULL ||
|
||||
cocoadebug_mach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int backtraceLength = idx;
|
||||
Dl_info symbolicated[backtraceLength];
|
||||
cocoadebug_symbolicate(backtraceBuffer, symbolicated, backtraceLength, 0);
|
||||
for (int idx = 0; idx < backtraceLength; idx++) {
|
||||
[result appendFormat: @"%@", cocoadebug_logBacktraceEntry(idx, backtraceBuffer[idx], &symbolicated[idx])];
|
||||
}
|
||||
[result appendString: @"\n"];
|
||||
[result appendString: @"======================================================================================"];
|
||||
return result.copy;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - operate machine context
|
||||
bool cocoadebug_fillThreadStateIntoMachineContext(thread_t thread, _STRUCT_MCONTEXT * machineContext) {
|
||||
mach_msg_type_number_t state_count = COCOADEBUG_THREAD_STATE_COUNT;
|
||||
kern_return_t kr = thread_get_state(thread, COCOADEBUG_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count);
|
||||
return (kr == KERN_SUCCESS);
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_mach_linkRegister(_STRUCT_MCONTEXT * const machineContext){
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
return _FAILED_UINT_PTR_ADDRESS;
|
||||
#else
|
||||
return machineContext->__ss.__lr;
|
||||
#endif
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_mach_framePointer(_STRUCT_MCONTEXT * const machineContext) {
|
||||
return machineContext->__ss.COCOADEBUG_FRAME_POINTER;
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_mach_instructionAddress(_STRUCT_MCONTEXT * const machineContext) {
|
||||
return machineContext->__ss.COCOADEBUG_INSTRUCTION_ADDRESS;
|
||||
}
|
||||
|
||||
kern_return_t cocoadebug_mach_copyMem(const void * src, const void * dst, const size_t numBytes) {
|
||||
vm_size_t bytesCopied = 0;
|
||||
return vm_read_overwrite(mach_task_self(), (vm_address_t)src, (vm_size_t)numBytes, (vm_address_t)dst, &bytesCopied);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - handle symbolicate
|
||||
void cocoadebug_symbolicate(const uintptr_t * const backtraceBuffer, Dl_info * const symbolsBuffer, const int numEntries, const int skippedEntries) {
|
||||
int idx = 0;
|
||||
if (!skippedEntries && idx < numEntries) {
|
||||
cocoadebug_dladdr(backtraceBuffer[idx], &symbolsBuffer[idx]);
|
||||
idx++;
|
||||
}
|
||||
|
||||
for (; idx < numEntries; idx++) {
|
||||
cocoadebug_dladdr(_CALL_INSTRUCTION_FROM_RETURN_ADDRESS(backtraceBuffer[idx]), &symbolsBuffer[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
bool cocoadebug_dladdr(const uintptr_t address, Dl_info * const info) {
|
||||
info->dli_fname = NULL;
|
||||
info->dli_fbase = NULL;
|
||||
info->dli_sname = NULL;
|
||||
info->dli_saddr = NULL;
|
||||
|
||||
const uint32_t idx = cocoadebug_imageIndexContainingAddress(address);
|
||||
if (idx == UINT_MAX) { return false; }
|
||||
|
||||
const struct mach_header * header = _dyld_get_image_header(idx);
|
||||
const uintptr_t imageVMAddressSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx);
|
||||
const uintptr_t addressWithSlide = address - imageVMAddressSlide;
|
||||
const uintptr_t segmentBase = cocoadebug_segmentBaseOfImageIndex(idx) + imageVMAddressSlide;
|
||||
if (segmentBase == _FAILED_UINT_PTR_ADDRESS) { return false; }
|
||||
|
||||
info->dli_fbase = (void *)header;
|
||||
info->dli_fname = _dyld_get_image_name(idx);
|
||||
|
||||
const COCOADEBUG_NLIST * bestMatch = NULL;
|
||||
uintptr_t bestDistance = ULONG_MAX;
|
||||
uintptr_t cmdPtr = cocoadebug_firstCmdAfterHeader(header);
|
||||
if (cmdPtr == _FAILED_UINT_PTR_ADDRESS) { return false; }
|
||||
|
||||
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
|
||||
const struct load_command * loadCmd = (struct load_command *)cmdPtr;
|
||||
if (loadCmd->cmd == LC_SYMTAB) {
|
||||
const struct symtab_command * symtabCmd = (struct symtab_command *)cmdPtr;
|
||||
const COCOADEBUG_NLIST * symbolTable = (COCOADEBUG_NLIST *)(segmentBase + symtabCmd->symoff);
|
||||
const uintptr_t stringTable = segmentBase + symtabCmd->stroff;
|
||||
|
||||
for (uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++) {
|
||||
if (symbolTable[iSym].n_value == _FAILED_UINT_PTR_ADDRESS) { continue; }
|
||||
uintptr_t symbolBase = symbolTable[iSym].n_value;
|
||||
uintptr_t currentDistance = addressWithSlide - symbolBase;
|
||||
if ( (addressWithSlide >= symbolBase && currentDistance <= bestDistance) ) {
|
||||
bestMatch = symbolTable + iSym;
|
||||
bestDistance = currentDistance;
|
||||
}
|
||||
}
|
||||
if (bestMatch != NULL) {
|
||||
info->dli_saddr = (void *)(bestMatch->n_value + imageVMAddressSlide);
|
||||
info->dli_sname = (char *)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx);
|
||||
if (*info->dli_sname == '_') {
|
||||
info->dli_sname++;
|
||||
}
|
||||
if (info->dli_saddr == info->dli_fbase && bestMatch->n_type == 3) {
|
||||
info->dli_sname = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_firstCmdAfterHeader(const struct mach_header * const header) {
|
||||
switch (header->magic) {
|
||||
case MH_MAGIC:
|
||||
case MH_CIGAM:
|
||||
return (uintptr_t)(header + 1);
|
||||
case MH_MAGIC_64:
|
||||
case MH_CIGAM_64:
|
||||
return (uintptr_t)(((struct mach_header_64*)header) + 1);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_segmentBaseOfImageIndex(const uint32_t idx) {
|
||||
const struct mach_header * header = _dyld_get_image_header(idx);
|
||||
|
||||
uintptr_t cmdPtr = cocoadebug_firstCmdAfterHeader(header);
|
||||
if (cmdPtr == _FAILED_UINT_PTR_ADDRESS) { return _FAILED_UINT_PTR_ADDRESS; }
|
||||
for (uint32_t idx = 0; idx < header->ncmds; idx++) {
|
||||
const struct load_command * loadCmd = (struct load_command *)cmdPtr;
|
||||
if (loadCmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command * segCmd = (struct segment_command *)cmdPtr;
|
||||
if (strcmp(segCmd->segname, SEG_LINKEDIT) == 0) {
|
||||
return segCmd->vmaddr - segCmd->fileoff;
|
||||
}
|
||||
} else if (loadCmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64 * segCmd = (struct segment_command_64 *)cmdPtr;
|
||||
if (strcmp(segCmd->segname, SEG_LINKEDIT) == 0) {
|
||||
return segCmd->vmaddr - segCmd->fileoff;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
return _FAILED_UINT_PTR_ADDRESS;
|
||||
}
|
||||
|
||||
uint32_t cocoadebug_imageIndexContainingAddress(const uintptr_t address) {
|
||||
const uint32_t imageCount = _dyld_image_count();
|
||||
const struct mach_header * header = _FAILED_UINT_PTR_ADDRESS;
|
||||
|
||||
for (uint32_t iImg = 0; iImg < imageCount; iImg++) {
|
||||
header = _dyld_get_image_header(iImg);
|
||||
if (header != NULL) {
|
||||
uintptr_t addressWSlide = address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg);
|
||||
uintptr_t cmdPtr = cocoadebug_firstCmdAfterHeader(header);
|
||||
if (cmdPtr == _FAILED_UINT_PTR_ADDRESS) { continue; }
|
||||
|
||||
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
|
||||
const struct load_command * loadCmd = (struct load_command *)cmdPtr;
|
||||
if (loadCmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command * segCmd = (struct segment_command *)cmdPtr;
|
||||
if (addressWSlide >= segCmd->vmaddr &&
|
||||
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
|
||||
return iImg;
|
||||
}
|
||||
} else if (loadCmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64 * segCmd = (struct segment_command_64 *)cmdPtr;
|
||||
if (addressWSlide >= segCmd->vmaddr &&
|
||||
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
|
||||
return iImg;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - generate backtrace entry
|
||||
const char * cocoadebug_lastPathEntry(const char * const path) {
|
||||
if (path == NULL) { return NULL; }
|
||||
char * lastFile = strrchr(path, '/');
|
||||
return lastFile == NULL ? path: lastFile + 1;
|
||||
}
|
||||
|
||||
NSString * cocoadebug_logBacktraceEntry(const int entryNum, const uintptr_t address, const Dl_info * const dlInfo) {
|
||||
char faddrBuffer[20];
|
||||
char saddrBuffer[20];
|
||||
|
||||
const char * fname = cocoadebug_lastPathEntry(dlInfo->dli_fname);
|
||||
if (fname == NULL) {
|
||||
sprintf(faddrBuffer, _POINTER_FMT, (uintptr_t)dlInfo->dli_fbase);
|
||||
fname = faddrBuffer;
|
||||
}
|
||||
|
||||
uintptr_t offset = address - (uintptr_t)dlInfo->dli_saddr;
|
||||
const char * sname = dlInfo->dli_sname;
|
||||
if (sname == NULL) {
|
||||
sprintf(saddrBuffer, _POINTER_SHORT_FMT, (uintptr_t)dlInfo->dli_fbase);
|
||||
sname = saddrBuffer;
|
||||
offset = address - (uintptr_t)dlInfo->dli_fbase;
|
||||
}
|
||||
return [NSString stringWithFormat: @"%-30s 0x%08" PRIxPTR " %s + %lu\n", fname, (uintptr_t)address, sname, offset];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
18
Pods/CocoaDebug/Sources/Monitor/_DebugConsoleLabel.h
generated
Normal file
18
Pods/CocoaDebug/Sources/Monitor/_DebugConsoleLabel.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
/*
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface _DebugConsoleLabel : UILabel
|
||||
|
||||
- (void)updateLabelWithValue:(float)value;
|
||||
|
||||
- (NSAttributedString *)uiBlockingAttributedStringWith:(float)uiBlocking;
|
||||
|
||||
@end
|
||||
*/
|
||||
75
Pods/CocoaDebug/Sources/Monitor/_DebugConsoleLabel.m
generated
Normal file
75
Pods/CocoaDebug/Sources/Monitor/_DebugConsoleLabel.m
generated
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
/*
|
||||
#import "_DebugConsoleLabel.h"
|
||||
|
||||
@interface _DebugConsoleLabel ()
|
||||
|
||||
@property (nonatomic, strong) UIFont *mainFont;
|
||||
@property (nonatomic, strong) UIFont *subFont;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _DebugConsoleLabel
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setDefault];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setDefault {
|
||||
self.textAlignment = NSTextAlignmentCenter;
|
||||
self.userInteractionEnabled = NO;
|
||||
self.adjustsFontSizeToFitWidth = YES;
|
||||
|
||||
self.mainFont = [UIFont fontWithName:@"Menlo" size:14];
|
||||
if (self.mainFont) {
|
||||
self.subFont = [UIFont fontWithName:@"Menlo" size:4];
|
||||
} else {
|
||||
self.mainFont = [UIFont fontWithName:@"Courier" size:14];
|
||||
self.subFont = [UIFont fontWithName:@"Courier" size:4];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateLabelWithValue:(float)value {
|
||||
self.attributedText = [self uiBlockingAttributedStringWith:value];
|
||||
}
|
||||
|
||||
#pragma mark - NSAttributedString
|
||||
|
||||
- (NSAttributedString *)uiBlockingAttributedStringWith:(float)uiBlocking {
|
||||
CGFloat progress = uiBlocking / 60.0;
|
||||
UIColor *color = [UIColor colorWithHue:0.27 * (progress - 0.2) saturation:1 brightness:0.9 alpha:1];
|
||||
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d UIBlocking",(int)round(uiBlocking)]];
|
||||
[text addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, text.length - 3)];
|
||||
[text addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(text.length - 3, 3)];
|
||||
[text addAttribute:NSFontAttributeName value:self.mainFont range:NSMakeRange(0, text.length)];
|
||||
[text addAttribute:NSFontAttributeName value:self.subFont range:NSMakeRange(text.length - 4, 1)];
|
||||
return text;
|
||||
}
|
||||
|
||||
#pragma mark - Color
|
||||
|
||||
- (UIColor*)getColorByPercent:(CGFloat)percent {
|
||||
NSInteger r = 0, g = 0, one = 255 + 255;
|
||||
if (percent < 0.5) {
|
||||
r = one * percent;
|
||||
g = 255;
|
||||
}
|
||||
if (percent >= 0.5) {
|
||||
g = 255 - ((percent - 0.5 ) * one) ;
|
||||
r = 255;
|
||||
}
|
||||
return [UIColor colorWithRed:r/255.0 green:g/255.0 blue:0 alpha:1];
|
||||
}
|
||||
|
||||
@end
|
||||
*/
|
||||
23
Pods/CocoaDebug/Sources/Monitor/_RunloopMonitor.h
generated
Normal file
23
Pods/CocoaDebug/Sources/Monitor/_RunloopMonitor.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface _RunloopMonitor : NSObject
|
||||
//@property (nonatomic, copy, class) NSString * version;
|
||||
+ (instancetype)shared;
|
||||
|
||||
- (void)beginMonitor;
|
||||
|
||||
- (void)endMonitor;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
147
Pods/CocoaDebug/Sources/Monitor/_RunloopMonitor.m
generated
Normal file
147
Pods/CocoaDebug/Sources/Monitor/_RunloopMonitor.m
generated
Normal file
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_RunloopMonitor.h"
|
||||
#import "_BacktraceLogger.h"
|
||||
|
||||
// 定义延迟时间 毫秒
|
||||
static int64_t const OUT_TIME = 100 * NSEC_PER_MSEC;
|
||||
// before wait 的超时时间
|
||||
static NSTimeInterval const WAIT_TIME = 0.5;
|
||||
|
||||
@interface _RunloopMonitor () {
|
||||
@public
|
||||
CFRunLoopObserverRef observer;
|
||||
CFRunLoopActivity currentActivity;
|
||||
dispatch_semaphore_t semaphore;
|
||||
BOOL isMonitoring;
|
||||
}
|
||||
@end
|
||||
|
||||
static void runloopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
|
||||
//_RunloopMonitor * monitor = (__bridge _RunloopMonitor*)info;
|
||||
[_RunloopMonitor shared]->currentActivity = activity;
|
||||
|
||||
// switch (activity) {
|
||||
// case kCFRunLoopEntry:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopEntry");
|
||||
// break;
|
||||
// case kCFRunLoopBeforeTimers:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopBeforeTimers");
|
||||
// break;
|
||||
// case kCFRunLoopBeforeSources:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopBeforeSources");
|
||||
// break;
|
||||
// case kCFRunLoopBeforeWaiting:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopBeforeWaiting");
|
||||
// break;
|
||||
// case kCFRunLoopAfterWaiting:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopAfterWaiting");
|
||||
// break;
|
||||
// case kCFRunLoopExit:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopExit");
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
dispatch_semaphore_t sema = [_RunloopMonitor shared]->semaphore;
|
||||
dispatch_semaphore_signal(sema);
|
||||
}
|
||||
|
||||
@implementation _RunloopMonitor
|
||||
|
||||
+ (instancetype)shared {
|
||||
static id ins = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
ins = [[super allocWithZone:NSDefaultMallocZone()] init];
|
||||
});
|
||||
return ins;
|
||||
}
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
return [self shared];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self endMonitor];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)beginMonitor {
|
||||
|
||||
if ([_RunloopMonitor shared]->isMonitoring) return;
|
||||
|
||||
[_RunloopMonitor shared]->isMonitoring = YES;
|
||||
|
||||
// 创建观察者
|
||||
CFRunLoopObserverContext context = {
|
||||
0,
|
||||
(__bridge void*)self,
|
||||
&CFRetain,
|
||||
&CFRelease,
|
||||
NULL
|
||||
};
|
||||
//static CFRunLoopObserverRef observer;
|
||||
observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runloopObserverCallback, &context);
|
||||
|
||||
// 观察主线程
|
||||
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
|
||||
|
||||
// 在子线程中监控卡顿
|
||||
semaphore = dispatch_semaphore_create(0); //同步?
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
// 开启持续的loop来监控
|
||||
while ([_RunloopMonitor shared]->isMonitoring) {
|
||||
if ([_RunloopMonitor shared]->currentActivity == kCFRunLoopBeforeWaiting)
|
||||
{
|
||||
// 处理休眠前事件观测
|
||||
__block BOOL timeOut = YES;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
timeOut = NO; // timeOut任务
|
||||
});
|
||||
[NSThread sleepForTimeInterval:WAIT_TIME];
|
||||
// WAIT_TIME 时间后,如果 timeOut任务 任未执行, 则认为主线程前面的任务执行时间过长导致卡顿
|
||||
if (timeOut) {
|
||||
[_BacktraceLogger cocoadebug_logMain];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 处理 Timer,Source,唤醒后事件
|
||||
// 同步等待时间内,接收到信号result=0, 超时则继续往下执行并且result!=0
|
||||
long result = dispatch_semaphore_wait([_RunloopMonitor shared]->semaphore, dispatch_time(DISPATCH_TIME_NOW, OUT_TIME));
|
||||
if (result != 0) { // 超时
|
||||
if (![_RunloopMonitor shared]->observer) {
|
||||
[[_RunloopMonitor shared] endMonitor];
|
||||
continue;
|
||||
}
|
||||
if ([_RunloopMonitor shared]->currentActivity == kCFRunLoopBeforeSources ||
|
||||
[_RunloopMonitor shared]->currentActivity == kCFRunLoopAfterWaiting ||
|
||||
[_RunloopMonitor shared]->currentActivity == kCFRunLoopBeforeTimers) {
|
||||
|
||||
[_BacktraceLogger cocoadebug_logMain];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
- (void)endMonitor {
|
||||
if (!observer) return;
|
||||
if (!isMonitoring) return;
|
||||
isMonitoring = NO;
|
||||
|
||||
CFRunLoopRemoveObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
|
||||
CFRelease(observer);
|
||||
observer = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user