This commit is contained in:
DDIsFriend
2023-08-23 09:24:40 +08:00
parent 6bd037c5dd
commit 63ca919ed5
494 changed files with 35308 additions and 6623 deletions

View 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)
}
*/

View 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

View 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

View 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
*/

View 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
*/

View 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

View 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