| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- // Copyright 2020 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #import "FirebasePerformance/Sources/Gauges/FPRGaugeManager.h"
- #import "FirebasePerformance/Sources/Gauges/FPRGaugeManager+Private.h"
- #import "FirebasePerformance/Sources/Common/FPRDiagnostics.h"
- #import "FirebasePerformance/Sources/Configurations/FPRConfigurations.h"
- #import "FirebasePerformance/Sources/FPRClient.h"
- #import "FirebasePerformance/Sources/Gauges/CPU/FPRCPUGaugeCollector.h"
- #import "FirebasePerformance/Sources/Gauges/Memory/FPRMemoryGaugeCollector.h"
- #import <UIKit/UIKit.h>
- // Number of gauge data information after which that gets flushed to Google Data Transport.
- NSInteger const kGaugeDataBatchSize = 25;
- NS_EXTENSION_UNAVAILABLE("Firebase Performance is not supported for extensions.")
- @interface FPRGaugeManager () <FPRCPUGaugeCollectorDelegate, FPRMemoryGaugeCollectorDelegate>
- /** @brief List of gauges that are currently being actively captured. */
- @property(nonatomic, readwrite) FPRGauges activeGauges;
- /** @brief List of gauge information collected. Intentionally this is not a typed collection. Gauge
- * data could be CPU Gauge data or Memory gauge data.
- */
- @property(nonatomic) NSMutableArray *gaugeData;
- /** @brief Currently active sessionID. */
- @property(nonatomic, readwrite, copy) NSString *currentSessionId;
- @end
- @implementation FPRGaugeManager
- + (instancetype)sharedInstance {
- static FPRGaugeManager *instance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- instance = [[FPRGaugeManager alloc] initWithGauges:FPRGaugeNone];
- });
- return instance;
- }
- - (instancetype)initWithGauges:(FPRGauges)gauges {
- self = [super init];
- if (self) {
- _activeGauges = FPRGaugeNone;
- _gaugeData = [[NSMutableArray alloc] init];
- _gaugeDataProtectionQueue =
- dispatch_queue_create("com.google.perf.gaugeManager.gaugeData", DISPATCH_QUEUE_SERIAL);
- _isColdStart = YES;
- [self startAppActivityTracking];
- }
- return self;
- }
- - (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:UIApplicationDidBecomeActiveNotification
- object:[UIApplication sharedApplication]];
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:UIApplicationWillResignActiveNotification
- object:[UIApplication sharedApplication]];
- }
- /**
- * Starts tracking the application state changes.
- */
- - (void)startAppActivityTracking {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(appStateChanged:)
- name:UIApplicationDidBecomeActiveNotification
- object:[UIApplication sharedApplication]];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(appStateChanged:)
- name:UIApplicationWillResignActiveNotification
- object:[UIApplication sharedApplication]];
- }
- - (void)appStateChanged:(NSNotification *)notification {
- FPRApplicationState applicationState = [FPRAppActivityTracker sharedInstance].applicationState;
- [self.cpuGaugeCollector updateSamplingFrequencyForApplicationState:applicationState];
- [self.memoryGaugeCollector updateSamplingFrequencyForApplicationState:applicationState];
- self.isColdStart = NO;
- }
- #pragma mark - Implementation methods
- - (BOOL)gaugeCollectionEnabled {
- // Allow gauge collection to happen during cold start. During dispatch time, we do another check
- // to make sure if gauge collection is enabled. This is to accomodate gauge metric collection
- // during app_start scenario.
- if (self.isColdStart) {
- return YES;
- }
- // Check if the SDK is enabled to collect gauge data.
- BOOL sdkEnabled = [[FPRConfigurations sharedInstance] sdkEnabled];
- if (!sdkEnabled) {
- return NO;
- }
- return [FPRConfigurations sharedInstance].isDataCollectionEnabled;
- }
- - (void)startCollectingGauges:(FPRGauges)gauges forSessionId:(NSString *)sessionId {
- // Dispatch the already available gauge data with old sessionId.
- [self prepareAndDispatchCollectedGaugeDataWithSessionId:self.currentSessionId];
- self.currentSessionId = sessionId;
- if (self.gaugeCollectionEnabled) {
- if ((gauges & FPRGaugeCPU) == FPRGaugeCPU) {
- self.cpuGaugeCollector = [[FPRCPUGaugeCollector alloc] initWithDelegate:self];
- }
- if ((gauges & FPRGaugeMemory) == FPRGaugeMemory) {
- self.memoryGaugeCollector = [[FPRMemoryGaugeCollector alloc] initWithDelegate:self];
- }
- self.activeGauges = self.activeGauges | gauges;
- }
- }
- - (void)stopCollectingGauges:(FPRGauges)gauges {
- if ((gauges & FPRGaugeCPU) == FPRGaugeCPU) {
- self.cpuGaugeCollector = nil;
- }
- if ((gauges & FPRGaugeMemory) == FPRGaugeMemory) {
- self.memoryGaugeCollector = nil;
- }
- self.activeGauges = self.activeGauges & ~(gauges);
- // Flush out all the already collected gauge metrics
- [self prepareAndDispatchCollectedGaugeDataWithSessionId:self.currentSessionId];
- }
- - (void)collectAllGauges {
- if (self.cpuGaugeCollector) {
- [self.cpuGaugeCollector collectMetric];
- }
- if (self.memoryGaugeCollector) {
- [self.memoryGaugeCollector collectMetric];
- }
- }
- - (void)dispatchMetric:(id)gaugeMetric {
- // If the gauge metric is of type CPU, then dispatch only if CPU collection is enabled.
- if ([gaugeMetric isKindOfClass:[FPRCPUGaugeData class]] &&
- ((self.activeGauges & FPRGaugeCPU) == FPRGaugeCPU)) {
- [self addGaugeData:gaugeMetric];
- }
- // If the gauge metric is of type memory, then dispatch only if memory collection is enabled.
- if ([gaugeMetric isKindOfClass:[FPRMemoryGaugeData class]] &&
- ((self.activeGauges & FPRGaugeMemory) == FPRGaugeMemory)) {
- [self addGaugeData:gaugeMetric];
- }
- }
- #pragma mark - Utils
- - (void)prepareAndDispatchCollectedGaugeDataWithSessionId:(nullable NSString *)sessionId {
- dispatch_async(self.gaugeDataProtectionQueue, ^{
- NSArray *dispatchGauges = [self.gaugeData copy];
- self.gaugeData = [[NSMutableArray alloc] init];
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- if (dispatchGauges.count > 0 && sessionId != nil) {
- [[FPRClient sharedInstance] logGaugeMetric:dispatchGauges forSessionId:sessionId];
- FPRLogInfo(kFPRGaugeManagerDataCollected, @"Logging %lu gauge metrics.",
- (unsigned long)dispatchGauges.count);
- }
- });
- });
- }
- /**
- * Adds the gauge to the batch and decide on when to dispatch the events to Google Data Transport.
- *
- * @param gauge Gauge data received from the collectors.
- */
- - (void)addGaugeData:(id)gauge {
- dispatch_async(self.gaugeDataProtectionQueue, ^{
- if (gauge) {
- [self.gaugeData addObject:gauge];
- if (self.gaugeData.count >= kGaugeDataBatchSize) {
- [self prepareAndDispatchCollectedGaugeDataWithSessionId:self.currentSessionId];
- }
- }
- });
- }
- #pragma mark - FPRCPUGaugeCollectorDelegate methods
- - (void)cpuGaugeCollector:(FPRCPUGaugeCollector *)collector gaugeData:(FPRCPUGaugeData *)gaugeData {
- [self addGaugeData:gaugeData];
- }
- #pragma mark - FPRMemoryGaugeCollectorDelegate methods
- - (void)memoryGaugeCollector:(FPRMemoryGaugeCollector *)collector
- gaugeData:(FPRMemoryGaugeData *)gaugeData {
- [self addGaugeData:gaugeData];
- }
- @end
|