Skip to content

Commit 4829a86

Browse files
authored
Improve System Trace Implementation #trivial (TextureGroup#368)
* Improve the kdebug, system trace integration * Remove superseded subsystem * Address review comments * Please the license header gods * Address harder * Fix node block retaining collection view
1 parent 5f42f93 commit 4829a86

16 files changed

+209
-102
lines changed

AsyncDisplayKit.xcodeproj/project.pbxproj

+1
Original file line numberDiff line numberDiff line change
@@ -2627,6 +2627,7 @@
26272627
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
26282628
GCC_NO_COMMON_BLOCKS = YES;
26292629
GCC_PRECOMPILE_PREFIX_HEADER = YES;
2630+
GCC_PREPROCESSOR_DEFINITIONS = "PROFILE=1";
26302631
INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
26312632
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
26322633
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
167 KB
Binary file not shown.

Source/ASCollectionView.mm

+1-1
Original file line numberDiff line numberDiff line change
@@ -1675,7 +1675,7 @@ - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAt
16751675
if (node.interactionDelegate == nil) {
16761676
node.interactionDelegate = strongSelf;
16771677
}
1678-
if (_inverted) {
1678+
if (strongSelf.inverted) {
16791679
node.transform = CATransform3DMakeScale(1, -1, 1) ;
16801680
}
16811681
return node;

Source/ASDisplayNode.mm

+17-1
Original file line numberDiff line numberDiff line change
@@ -957,9 +957,25 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
957957
restrictedToSize:(ASLayoutElementSize)size
958958
relativeToParentSize:(CGSize)parentSize
959959
{
960+
// Use a pthread specific to mark when this method is called re-entrant on same thread.
961+
// We only want one calculateLayout signpost interval per thread.
962+
// This is fast enough to do it unconditionally.
963+
auto key = ASPthreadStaticKey(NULL);
964+
BOOL isRootCall = (pthread_getspecific(key) == NULL);
965+
if (isRootCall) {
966+
pthread_setspecific(key, kCFBooleanTrue);
967+
ASSignpostStart(ASSignpostCalculateLayout);
968+
}
969+
960970
ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize);
961971
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize);
962-
return [self calculateLayoutThatFits:resolvedRange];
972+
ASLayout *result = [self calculateLayoutThatFits:resolvedRange];
973+
974+
if (isRootCall) {
975+
pthread_setspecific(key, NULL);
976+
ASSignpostEnd(ASSignpostCalculateLayout);
977+
}
978+
return result;
963979
}
964980

965981
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize

Source/ASRunLoopQueue.mm

+68-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#import <AsyncDisplayKit/ASRunLoopQueue.h>
2020
#import <AsyncDisplayKit/ASThread.h>
2121
#import <AsyncDisplayKit/ASLog.h>
22-
22+
#import <QuartzCore/QuartzCore.h>
2323
#import <cstdlib>
2424
#import <deque>
2525
#import <vector>
@@ -78,16 +78,20 @@ - (void)threadMain
7878
return;
7979
}
8080
// The scope below is entered while already locked. @autorelease is crucial here; see PR 2890.
81+
NSInteger count;
8182
@autoreleasepool {
8283
#if ASRunLoopQueueLoggingEnabled
8384
NSLog(@"ASDeallocQueue Processing: %lu objects destroyed", weakSelf->_queue.size());
8485
#endif
8586
// Sometimes we release 10,000 objects at a time. Don't hold the lock while releasing.
8687
std::deque<id> currentQueue = weakSelf->_queue;
88+
count = currentQueue.size();
89+
ASSignpostStartCustom(ASSignpostDeallocQueueDrain, self, count);
8790
weakSelf->_queue = std::deque<id>();
8891
weakSelf->_queueLock.unlock();
8992
currentQueue.clear();
9093
}
94+
ASSignpostEndCustom(ASSignpostDeallocQueueDrain, self, count, ASSignpostColorDefault);
9195
});
9296

9397
CFRunLoopRef runloop = CFRunLoopGetCurrent();
@@ -196,8 +200,69 @@ @interface ASRunLoopQueue () {
196200

197201
@end
198202

203+
#if AS_KDEBUG_ENABLE
204+
/**
205+
* This is real, private CA API. Valid as of iOS 10.
206+
*/
207+
typedef enum {
208+
kCATransactionPhasePreLayout,
209+
kCATransactionPhasePreCommit,
210+
kCATransactionPhasePostCommit,
211+
} CATransactionPhase;
212+
213+
@interface CATransaction (Private)
214+
+ (void)addCommitHandler:(void(^)(void))block forPhase:(CATransactionPhase)phase;
215+
+ (int)currentState;
216+
@end
217+
#endif
218+
199219
@implementation ASRunLoopQueue
200220

221+
#if AS_KDEBUG_ENABLE
222+
+ (void)load
223+
{
224+
[self registerCATransactionObservers];
225+
}
226+
227+
+ (void)registerCATransactionObservers
228+
{
229+
static BOOL privateCAMethodsExist;
230+
static dispatch_block_t preLayoutHandler;
231+
static dispatch_block_t preCommitHandler;
232+
static dispatch_block_t postCommitHandler;
233+
static dispatch_once_t onceToken;
234+
dispatch_once(&onceToken, ^{
235+
privateCAMethodsExist = [CATransaction respondsToSelector:@selector(addCommitHandler:forPhase:)];
236+
privateCAMethodsExist &= [CATransaction respondsToSelector:@selector(currentState)];
237+
if (!privateCAMethodsExist) {
238+
NSLog(@"Private CA methods are gone.");
239+
}
240+
preLayoutHandler = ^{
241+
ASSignpostStartCustom(ASSignpostCATransactionLayout, 0, [CATransaction currentState]);
242+
};
243+
preCommitHandler = ^{
244+
int state = [CATransaction currentState];
245+
ASSignpostEndCustom(ASSignpostCATransactionLayout, 0, state, ASSignpostColorDefault);
246+
ASSignpostStartCustom(ASSignpostCATransactionCommit, 0, state);
247+
};
248+
postCommitHandler = ^{
249+
ASSignpostEndCustom(ASSignpostCATransactionCommit, 0, [CATransaction currentState], ASSignpostColorDefault);
250+
// Can't add new observers inside an observer. rdar://problem/31253952
251+
dispatch_async(dispatch_get_main_queue(), ^{
252+
[self registerCATransactionObservers];
253+
});
254+
};
255+
});
256+
257+
if (privateCAMethodsExist) {
258+
[CATransaction addCommitHandler:preLayoutHandler forPhase:kCATransactionPhasePreLayout];
259+
[CATransaction addCommitHandler:preCommitHandler forPhase:kCATransactionPhasePreCommit];
260+
[CATransaction addCommitHandler:postCommitHandler forPhase:kCATransactionPhasePostCommit];
261+
}
262+
}
263+
264+
#endif // AS_KDEBUG_ENABLE
265+
201266
- (instancetype)initWithRunLoop:(CFRunLoopRef)runloop retainObjects:(BOOL)retainsObjects handler:(void (^)(id _Nullable, BOOL))handlerBlock
202267
{
203268
if (self = [super init]) {
@@ -275,7 +340,7 @@ - (void)processQueue
275340
return;
276341
}
277342

278-
ASProfilingSignpostStart(0, self);
343+
ASSignpostStart(ASSignpostRunLoopQueueBatch);
279344

280345
// Snatch the next batch of items.
281346
NSInteger maxCountToProcess = MIN(internalQueueCount, self.batchSize);
@@ -329,7 +394,7 @@ - (void)processQueue
329394
CFRunLoopWakeUp(_runLoop);
330395
}
331396

332-
ASProfilingSignpostEnd(0, self);
397+
ASSignpostEnd(ASSignpostRunLoopQueueBatch);
333398
}
334399

335400
- (void)enqueue:(id)object

Source/Base/ASAssert.m

+1-7
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,9 @@
1313
#import <AsyncDisplayKit/ASAssert.h>
1414
#import <Foundation/Foundation.h>
1515

16-
// pthread_key_create must be called before the key can be used. This function does that.
1716
static pthread_key_t ASMainThreadAssertionsDisabledKey()
1817
{
19-
static pthread_key_t ASMainThreadAssertionsDisabledKey;
20-
static dispatch_once_t onceToken;
21-
dispatch_once(&onceToken, ^{
22-
pthread_key_create(&ASMainThreadAssertionsDisabledKey, NULL);
23-
});
24-
return ASMainThreadAssertionsDisabledKey;
18+
return ASPthreadStaticKey(NULL);
2519
}
2620

2721
BOOL ASMainThreadAssertionsAreDisabled() {

Source/Base/ASBaseDefines.h

+18
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,24 @@
215215
#define AS_SUBCLASSING_RESTRICTED
216216
#endif
217217

218+
#define ASPthreadStaticKey(dtor) ({ \
219+
static dispatch_once_t onceToken; \
220+
static pthread_key_t key; \
221+
dispatch_once(&onceToken, ^{ \
222+
pthread_key_create(&key, dtor); \
223+
}); \
224+
key; \
225+
})
226+
227+
#define ASCreateOnce(expr) ({ \
228+
static dispatch_once_t onceToken; \
229+
static __typeof__(expr) staticVar; \
230+
dispatch_once(&onceToken, ^{ \
231+
staticVar = expr; \
232+
}); \
233+
staticVar; \
234+
})
235+
218236
/// Ensure that class is of certain kind
219237
#define ASDynamicCast(x, c) ({ \
220238
id __val = x;\

Source/Base/ASLog.h

+58-15
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,55 @@
1616
//
1717

1818
#import <AsyncDisplayKit/ASAvailability.h>
19+
#import <Foundation/Foundation.h>
1920

20-
#pragma once
21+
/// The signposts we use. Signposts are grouped by color. The SystemTrace.tracetemplate file
22+
/// should be kept up-to-date with these values.
23+
typedef NS_ENUM(uint32_t, ASSignpostName) {
24+
// Collection/Table (Blue)
25+
ASSignpostDataControllerBatch = 300, // Alloc/layout nodes before collection update.
26+
ASSignpostRangeControllerUpdate, // Ranges update pass.
27+
ASSignpostCollectionUpdate, // Entire update process, from -endUpdates to [super perform…]
2128

29+
// Rendering (Green)
30+
ASSignpostLayerDisplay = 325, // Client display callout.
31+
ASSignpostRunLoopQueueBatch, // One batch of ASRunLoopQueue.
32+
33+
// Layout (Purple)
34+
ASSignpostCalculateLayout = 350, // Start of calculateLayoutThatFits to end. Max 1 per thread.
35+
36+
// Misc (Orange)
37+
ASSignpostDeallocQueueDrain = 375, // One chunk of dealloc queue work. arg0 is count.
38+
ASSignpostCATransactionLayout, // The CA transaction commit layout phase.
39+
ASSignpostCATransactionCommit // The CA transaction commit post-layout phase.
40+
};
41+
42+
typedef NS_ENUM(uintptr_t, ASSignpostColor) {
43+
ASSignpostColorBlue,
44+
ASSignpostColorGreen,
45+
ASSignpostColorPurple,
46+
ASSignpostColorOrange,
47+
ASSignpostColorRed,
48+
ASSignpostColorDefault
49+
};
50+
51+
static inline ASSignpostColor ASSignpostGetColor(ASSignpostName name, ASSignpostColor colorPref) {
52+
if (colorPref == ASSignpostColorDefault) {
53+
return (ASSignpostColor)((name / 25) % 4);
54+
} else {
55+
return colorPref;
56+
}
57+
}
2258

2359
#define ASMultiplexImageNodeLogDebug(...)
2460
#define ASMultiplexImageNodeCLogDebug(...)
2561

2662
#define ASMultiplexImageNodeLogError(...)
2763
#define ASMultiplexImageNodeCLogError(...)
2864

29-
// Note: `<sys/kdebug_signpost.h>` only exists in Xcode 8 and later.
30-
#if defined(PROFILE) && __has_include(<sys/kdebug_signpost.h>)
65+
#define AS_KDEBUG_ENABLE defined(PROFILE) && __has_include(<sys/kdebug_signpost.h>)
66+
67+
#if AS_KDEBUG_ENABLE
3168

3269
#import <sys/kdebug_signpost.h>
3370

@@ -45,21 +82,27 @@
4582
#define APPSDBG_CODE(SubClass,code) KDBG_CODE(DBG_APPS, SubClass, code)
4683
#endif
4784

48-
#define ASProfilingSignpost(x) \
49-
AS_AT_LEAST_IOS10 ? kdebug_signpost(x, 0, 0, 0, (uint32_t)(x % 4)) \
50-
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, x) | DBG_FUNC_NONE, 0, 0, 0, (uint32_t)(x % 4));
85+
// Currently we'll reserve arg3.
86+
#define ASSignpost(name, identifier, arg2, color) \
87+
AS_AT_LEAST_IOS10 ? kdebug_signpost(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \
88+
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_NONE, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color));
89+
90+
#define ASSignpostStartCustom(name, identifier, arg2) \
91+
AS_AT_LEAST_IOS10 ? kdebug_signpost_start(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0) \
92+
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_START, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0);
93+
#define ASSignpostStart(name) ASSignpostStartCustom(name, self, 0)
5194

52-
#define ASProfilingSignpostStart(x, y) \
53-
AS_AT_LEAST_IOS10 ? kdebug_signpost_start((uint32_t)x, (uintptr_t)y, 0, 0, (uint32_t)(x % 4)) \
54-
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, x) | DBG_FUNC_START, (uintptr_t)y, 0, 0, (uint32_t)(x % 4));
95+
#define ASSignpostEndCustom(name, identifier, arg2, color) \
96+
AS_AT_LEAST_IOS10 ? kdebug_signpost_end(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \
97+
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_END, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color));
98+
#define ASSignpostEnd(name) ASSignpostEndCustom(name, self, 0, ASSignpostColorDefault)
5599

56-
#define ASProfilingSignpostEnd(x, y) \
57-
AS_AT_LEAST_IOS10 ? kdebug_signpost_end((uint32_t)x, (uintptr_t)y, 0, 0, (uint32_t)(x % 4)) \
58-
: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, x) | DBG_FUNC_END, (uintptr_t)y, 0, 0, (uint32_t)(x % 4));
59100
#else
60101

61-
#define ASProfilingSignpost(x)
62-
#define ASProfilingSignpostStart(x, y)
63-
#define ASProfilingSignpostEnd(x, y)
102+
#define ASSignpost(name, identifier, arg2, color)
103+
#define ASSignpostStartCustom(name, identifier, arg2)
104+
#define ASSignpostStart(name)
105+
#define ASSignpostEndCustom(name, identifier, arg2, color)
106+
#define ASSignpostEnd(name)
64107

65108
#endif

Source/Details/ASDataController.mm

+5-45
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
//#define LOG(...) NSLog(__VA_ARGS__)
4141
#define LOG(...)
4242

43-
#define AS_MEASURE_AVOIDED_DATACONTROLLER_WORK 0
44-
4543
#define RETURN_IF_NO_DATASOURCE(val) if (_dataSource == nil) { return val; }
4644
#define ASSERT_ON_EDITING_QUEUE ASDisplayNodeAssertNotNil(dispatch_get_specific(&kASDataControllerEditingQueueKey), @"%@ must be called on the editing transaction queue.", NSStringFromSelector(_cmd))
4745

@@ -54,13 +52,6 @@
5452

5553
typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCollectionElement *> *elements, NSArray<ASCellNode *> *nodes);
5654

57-
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
58-
@interface ASDataController (AvoidedWorkMeasuring)
59-
+ (void)_didLayoutNode;
60-
+ (void)_expectToInsertNodes:(NSUInteger)count;
61-
@end
62-
#endif
63-
6455
@interface ASDataController () {
6556
id<ASDataControllerLayoutDelegate> _layoutDelegate;
6657

@@ -159,17 +150,14 @@ - (void)setLayoutDelegate:(id<ASDataControllerLayoutDelegate>)layoutDelegate
159150
- (void)batchAllocateNodesFromElements:(NSArray<ASCollectionElement *> *)elements andLayout:(BOOL)shouldLayout batchSize:(NSInteger)batchSize batchCompletion:(ASDataControllerCompletionBlock)batchCompletionHandler
160151
{
161152
ASSERT_ON_EDITING_QUEUE;
162-
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
163-
[ASDataController _expectToInsertNodes:elements.count];
164-
#endif
165-
153+
166154
if (elements.count == 0 || _dataSource == nil) {
167155
batchCompletionHandler(@[], @[]);
168156
return;
169157
}
170158

171-
ASProfilingSignpostStart(2, _dataSource);
172-
159+
ASSignpostStart(ASSignpostDataControllerBatch);
160+
173161
if (batchSize == 0) {
174162
batchSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
175163
}
@@ -182,8 +170,8 @@ - (void)batchAllocateNodesFromElements:(NSArray<ASCollectionElement *> *)element
182170
NSArray<ASCellNode *> *nodes = [self _allocateNodesFromElements:batchedElements andLayout:shouldLayout];
183171
batchCompletionHandler(batchedElements, nodes);
184172
}
185-
186-
ASProfilingSignpostEnd(2, _dataSource);
173+
174+
ASSignpostEndCustom(ASSignpostDataControllerBatch, self, 0, (_dataSource != nil ? ASSignpostColorDefault : ASSignpostColorRed));
187175
}
188176

189177
/**
@@ -228,10 +216,6 @@ - (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrai
228216
if (ASSizeRangeHasSignificantArea(sizeRange)) {
229217
[self _layoutNode:node withConstrainedSize:sizeRange];
230218
}
231-
232-
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
233-
[ASDataController _didLayoutNode];
234-
#endif
235219
}
236220

237221
allocatedNodeBuffer[i] = node;
@@ -836,27 +820,3 @@ - (void)_scheduleBlockOnMainSerialQueue:(dispatch_block_t)block
836820
}
837821

838822
@end
839-
840-
#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK
841-
842-
static volatile int64_t _totalExpectedItems = 0;
843-
static volatile int64_t _totalMeasuredNodes = 0;
844-
845-
@implementation ASDataController (WorkMeasuring)
846-
847-
+ (void)_didLayoutNode
848-
{
849-
int64_t measured = OSAtomicIncrement64(&_totalMeasuredNodes);
850-
int64_t expected = _totalExpectedItems;
851-
if (measured % 20 == 0 || measured == expected) {
852-
NSLog(@"Data controller avoided work (underestimated): %lld / %lld", measured, expected);
853-
}
854-
}
855-
856-
+ (void)_expectToInsertNodes:(NSUInteger)count
857-
{
858-
OSAtomicAdd64((int64_t)count, &_totalExpectedItems);
859-
}
860-
861-
@end
862-
#endif

0 commit comments

Comments
 (0)