forked from TextureGroup/Texture
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathASEventLog.mm
121 lines (105 loc) · 3.64 KB
/
ASEventLog.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//
// 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 {
ASDN::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);
ASDN::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;
}
ASDN::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