Painted painted levels: fostered fostered fostered
Tags: iOS performance Monitoring Tools RunLoop
* preface:
Recently, I learned a lot from Mr. Daiming’s technical sharing about “performance monitoring”. Based on recent learning, this article summarizes some performance monitoring related practices and plans to write a series of performance monitoring related articles. * The following directories are available: iOS performance monitoring (1) CPU power monitoring iOS performance monitoring (2) Main thread lag monitoring iOS performance monitoring (3) Method time monitoring
This article will introduce the iOS performance monitoring tool (QiLagMonitor) and “thread lag monitoring” related functional modules.
Understand the state of threads
The main thread runloop registers five modes by default: KCFRunLoopDefaultMode, UITrackingRunLoopMode, UIInitializationRunLoopMode, GSEventReceiveRunLoopMode, kCFRunLoopCommonModes.
The name of the | role |
---|---|
kCFRunLoopDefaultMode | The default Mode of the App in which the main thread is normally run. |
UITrackingRunLoopMode | Interface tracking Mode, used for ScrollView tracking touch sliding, to ensure that the interface sliding is not affected by other modes. |
UIInitializationRunLoopMode | The first Mode entered at the beginning of App startup will not be used after the completion of startup. |
GSEventReceiveRunLoopMode | Internal modes that accept system events are usually not needed. |
kCFRunLoopCommonModes | This is a placeholder Mode. In fact, it is used to switch between Default mode and UI mode. |
APPLE publicly provides two modes: NSDefaultRunLoopMode (kCFRunLoopDefaultMode) and NSRunLoopCommonModes (kCFRunLoopCommonModes).
In the next section, NSRunLoopCommonModes are used to monitor the main thread.
Then, runloop Observer: The Runloop Observer has seven states.
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
Copy the code
Respectively represent:
state | meaning |
---|---|
kCFRunLoopEntry | Entry to run runloop. |
kCFRunLoopBeforeTimers | Inside the event processing loop before any timers are processedTimer Before the timer) |
kCFRunLoopBeforeSources | Inside the event processing loop before any sources are processedSources Before the source) |
kCFRunLoopBeforeWaiting | Inside the event processing loop before the run loop sleeps, waiting for a source or timer to fire. This activity does not occur if CFRunLoopRunInMode is called with a timeout of 0 Seconds. It also does not occur in a particular iteration of the event processing loop if a version 0 source firesSource And the timerTimer Before) |
kCFRunLoopAfterWaiting | Inside the event processing loop after the run loop wakes up, but before processing the event that woke it up. This activity occurs only if the run loop did in fact go to sleep During the current loopSource And the timerTimer After, and before being awakened. |
kCFRunLoopExit | The exit of the run loop, after exiting the event processing loop. This activity occurs once for each call to CFRunLoopRun and CFRunLoopRunInMode. (Exit of runloop) |
kCFRunLoopAllActivities | All states of runloop. |
PS: For more information on Runloop, check out bin’s previous blog.
How does iOS monitor thread congestion?
Talk about the general implementation ideas in QiLagMonitor.
- First, create an observer
runLoopObserver
, used to observe the main threadrunloop
State.
A dispatchSemaphore is also created to ensure synchronous operations.
-
Second, add the runLoopObserver to the main thread runloop to observe.
-
Then, a child thread is opened and a continuous loop is opened in the child thread to monitor the status of the main thread runloop.
-
If the status of the main thread runloop is stuck in BeforeSources or AfterWaiting for more than 88 milliseconds, the main thread is currently stuck.
In this case, we save the main thread’s current call stack for monitoring purposes.
Schematic principle:
- Under normal conditions:
- Under abnormal circumstances:
Iii. Specific implementation of QiLagMonitor
- First, create a semaphore
dispatchSemaphore
And the observerrunLoopObserver
.
/ /! DispatchSemaphore = dispatch_semaphoRE_create (0); / /! Dispatch Semaphore // CFRunLoopObserverContext Context = {0,(__bridge void*)self,NULL,NULL}; runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runLoopObserverCallBack, &context);Copy the code
Meanwhile, when the main thread’s runloop status changes, the runLoopObserverCallBack method is called, which stores the current Runloop status internally. At the same time, control semaphore.
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
QiLagMonitor *lagMonitor = (__bridge QiLagMonitor*)info;
lagMonitor->runLoopActivity = activity;
dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore;
dispatch_semaphore_signal(semaphore);
}
Copy the code
- Second, add the observer to the main thread
runloop
thecommon
Observe in mode.
/ /! CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);Copy the code
- Third, create a child thread and start a persistent thread
loop
(Actually, it’s awhile
Infinite loop) to monitor the main threadrunloop
State. whenrunloop
The state of continues to beBeforeSources
,AfterWaiting
If the status is two, the main thread is stuck, and the current main thread call stack is recorded.
/ /! Dispatch_async (dispatch_get_global_queue(0, 0), ^{//! While (YES) {long semaphoreWait = dispatch_semaphore_wait(self->dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, STUCKMONITORRATE * NSEC_PER_MSEC)); if (semaphoreWait ! = 0) { if (! self->runLoopObserver) { self->timeoutCount = 0; self->dispatchSemaphore = 0; self->runLoopActivity = 0; return; } / /! The state of the two runloops, BeforeSources and AfterWaiting can detect whether the two state interval time caton the if (self - > runLoopActivity = = kCFRunLoopBeforeSources | | self->runLoopActivity == kCFRunLoopAfterWaiting) { //! If (++self->timeoutCount < 3) {continue; } NSLog(@"monitor trigger"); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSString *stackStr = [QiCallStack callStackWithType:QiCallStackTypeMain]; QiCallStackModel *model = [[QiCallStackModel alloc] init]; model.stackStr = stackStr; model.isStuck = YES; [[[QiLagDB shareInstance] increaseWithStackModel:model] subscribeNext:^(id x) {}]; }); } // end activity }// end semaphore wait self->timeoutCount = 0; }// end while });Copy the code
Recommended articles:
New Year’s Day benefits! Add animation to the view with SwiftUI. Write a simple page with SwiftUI. Optimize iOS App launch Use the “Time Profiler” tool to monitor the startup Time of your App. Understand the startup process of your App