Peter 9bc996374f Add 'submodules/AsyncDisplayKit/' from commit '02bedc12816e251ad71777f9d2578329b6d2bef6'
git-subtree-dir: submodules/AsyncDisplayKit
git-subtree-mainline: d06f423e0ed3df1fed9bd10d79ee312a9179b632
git-subtree-split: 02bedc12816e251ad71777f9d2578329b6d2bef6
2019-06-11 18:42:43 +01:00

122 lines
3.6 KiB
Plaintext

//
// ASEventLog.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASEventLog.h>
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASTraceEvent.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
@implementation ASEventLog {
AS::RecursiveMutex __instanceLock__;
// The index of the most recent log entry. -1 until first entry.
NSInteger _eventLogHead;
// A description of the object we're logging for. This is immutable.
NSString *_objectDescription;
}
/**
* Even just when debugging, all these events can take up considerable memory.
* Store them in a shared NSCache to limit the total consumption.
*/
+ (NSCache<ASEventLog *, NSMutableArray<ASTraceEvent *> *> *)contentsCache
{
static NSCache *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [[NSCache alloc] init];
});
return cache;
}
- (instancetype)initWithObject:(id)anObject
{
if ((self = [super init])) {
_objectDescription = ASObjectDescriptionMakeTiny(anObject);
_eventLogHead = -1;
}
return self;
}
- (instancetype)init
{
// This method is marked unavailable so the compiler won't let them call it.
ASDisplayNodeFailAssert(@"Failed to call initWithObject:");
return nil;
}
- (void)logEventWithBacktrace:(NSArray<NSString *> *)backtrace format:(NSString *)format, ...
{
va_list args;
va_start(args, format);
ASTraceEvent *event = [[ASTraceEvent alloc] initWithBacktrace:backtrace
format:format
arguments:args];
va_end(args);
AS::MutexLocker l(__instanceLock__);
NSCache *cache = [ASEventLog contentsCache];
NSMutableArray<ASTraceEvent *> *events = [cache objectForKey:self];
if (events == nil) {
events = [NSMutableArray arrayWithObject:event];
[cache setObject:events forKey:self];
_eventLogHead = 0;
return;
}
// Increment the head index.
_eventLogHead = (_eventLogHead + 1) % ASEVENTLOG_CAPACITY;
if (_eventLogHead < events.count) {
[events replaceObjectAtIndex:_eventLogHead withObject:event];
} else {
[events insertObject:event atIndex:_eventLogHead];
}
}
- (NSArray<ASTraceEvent *> *)events
{
NSMutableArray<ASTraceEvent *> *events = [[ASEventLog contentsCache] objectForKey:self];
if (events == nil) {
return nil;
}
AS::MutexLocker l(__instanceLock__);
NSUInteger tail = (_eventLogHead + 1);
NSUInteger count = events.count;
NSMutableArray<ASTraceEvent *> *result = [NSMutableArray array];
// Start from `tail` and go through array, wrapping around when we exceed end index.
for (NSUInteger actualIndex = 0; actualIndex < ASEVENTLOG_CAPACITY; actualIndex++) {
NSInteger ringIndex = (tail + actualIndex) % ASEVENTLOG_CAPACITY;
if (ringIndex < count) {
[result addObject:events[ringIndex]];
}
}
return result;
}
- (NSString *)description
{
/**
* This description intentionally doesn't follow the standard description format.
* Since this is a log, it's important for the description to look a certain way, and
* the formal description style doesn't allow for newlines and has a ton of punctuation.
*/
NSArray *events = [self events];
if (events == nil) {
return [NSString stringWithFormat:@"Event log for %@ was purged to conserve memory.", _objectDescription];
} else {
return [NSString stringWithFormat:@"Event log for %@. Events: %@", _objectDescription, events];
}
}
@end