kkbox wwdc17 xcode debug - oliver
TRANSCRIPT
Xcode - DebugKKBOX WWDC 2017 Study
Oliver Huang iOS Engineer
Related Video Sessions
404 - Debugging with Xcode 9https://developer.apple.com/videos/play/wwdc2017/404/
406 - Finding Bugs Using Xcode Runtime Toolshttps://developer.apple.com/videos/play/wwdc2017/406/
407 - Understanding Undefined Behaviorhttps://developer.apple.com/videos/play/wwdc2017/407/
411 - What's New in LLVMhttps://developer.apple.com/videos/play/wwdc2017/411/
Debugging with Xcode 9
• Development: Unplugged (Wireless Development)
• Breakpoint Workflow Enhancements
• Debug View Hierarchy Improvements
Wireless Development NEW
Wireless Development
• Minimum requirement
- iOS 11, tvOS 11, macOS 10.12.4+
• Tools support
- Xcode, Instruments, Accessibility Inspector, Console, Configurator
- (tvOS only) Safari Web Inspector for TVMLKit, QuickTime Screen Recording
Demo
Wireless Development tvOS device pairing
Breakpoint Workflow Enhancements
• Code Completion in Text Field
• Options Indicator
• Deep Filtering
Breakpoints
Demo
Debug View Hierarchy Enhancements
• View Controller Debugging
• SpriteKit Debugging
• SceneKit Debugging
View Controller Debugging NEW
SpriteKit Debugging NEW
SpriteKit Debugging
SceneKit Debugging NEW
Finding Bugs Using Xcode Runtime ToolsImprovements in Runtime Checking
Improvements in Runtime Checking
Runtime Issues
Finding Bugs Using Xcode Runtime Tools
Improvements in Runtime Checking
• Main Thread Checker (New)
• Address Sanitizer
• Thread Sanitizer
• Undefined Behavior Sanitizer (New)
4
Main Thread Checker
Designing Asynchronous APIs
Let API user specify callback queue
DeepThought.asyncComputeAnswer(to: theQuestion) { reply in … }
Designing Asynchronous APIs
Let API user specify callback queue
DeepThought.asyncComputeAnswer(to: theQuestion, completionQueue: queue) { reply in … }
Address Sanitizer
• Detects use-after-scope
• Detects use-after-return (opt-in)
• Compatible with Malloc Scribble
Finding Memory Issues
Security critical bugs • Use-after-free and buffer overflows
Diagnoses hard-to-reproduce crashes
Advanced Debugging and the Address Sanitizer WWDC 2015
Use of out of scope stack memory
// Use of Stack Memory Out of Scope
int *integer_pointer = NULL; if (is_some_condition_true()) { int value = calculate_value(); integer_pointer = &value; } *integer_pointer = 42;
// Use of Stack Memory after Return
int *returns_address_of_stack() { int a = 42; return &a; }
int *integer_pointer = returns_address_of_stack(); *integer_pointer = 43;
Thread Sanitizer
• Race on collections
• Swift access races
What is Thread Sanitizer
Multithreading issues
Finds races even if they did not manifest
64-bit macOS, 64-bit simulators
Thread Sanitizer and Static Analysis WWDC 2016
// Thread 1 eventLog.log(source: networkingSubsystem, message: "Download finished")
// Thread 2 eventLog.log(source: databaseSubsystem, message: "Query complete")
Thread 2: Data race in EventLog.log(source:message:)
// Swift Data Race Example
class EventLog { private var lastEventSource: LogSource?
func log(source: LogSource, message: String) { print(message) lastEventSource = source } }
// Use DispatchQueue to Synchronize Access
class EventLog { private var lastEventSource: LogSource? private var queue = DispatchQueue(label: "com.example.EventLog.queue")
func log(source: LogSource, message: String) { queue.async { print(message) lastEventSource = source } } }
// Swift Access Race with Mutating Methods
struct BluePoliceBoxLocation { private var x, y, z: Int private var time: Int
}
mutating func teleport(toPlanet: String) { … } mutating func fly(toCity: String) { … } mutating func travelToEndOfTime() { … }
Thread 2: Swift access race
Thread 1: Previous access
// Thread 1 location.teleport(toPlanet: "Mars")
// Thread 2 location.travelToEndOfTime()
changes x, y, z
changes time
Undefined Behavior Sanitizer
Alignment Violation
Nonnull Return Value Violation
Integer Overflow
C++ Dynamic Type Violation Invalid Float Cast
Invalid Shift Exponent
Invalid Boolean Invalid EnumInvalid Variable-Length Array Integer Division by Zero
Invalid Shift BaseInvalid Integer Cast
Out-of-Bounds Array Access
Invalid Object SizeMissing Return Value
Reached Unreachable Code
Nonnull Parameter ViolationNonnull Assignment Violation
Null Dereference
“undefined behavior:behavior for which this International Standard imposes no requirements.”
•ISO C++14 Standard
Undefined Behavior Is About Tradeoffs
Performance over safety
(INT_MAX + 1) ≯ INT_MAX
Integer Overflow
Null pointer returned from function declared to never return null
// Nonnull Return Value Violation
@implementation SolarSystem + (nonnull NSDictionary *)planetMoons { return @{@"Earth": @[@"Moon"], @"Mars" : @[@"Phobos", @"Deimos"], // … }; } - (nonnull NSArray *)moonsOfPlanet:(nonnull NSString *)planet { return [[self class] planetMoons][planet]; } @end
// Find the biggest moon for each planet NSMutableArray *biggestMoons = [NSMutableArray new]; [biggestMoons addObject:[solarSystem moonsOfPlanet:@"Pluto"][0]];
Nonnull Return Value Violation
Compiler 2
Source code
.c, .m, .cpp, .mm
Object file
.o
Let’s Experiment: A Very Simple Optimization Pipeline Compiler 1
Dead Code Elimination
Redundant Null Check Elimination
Redundant Null Check Elimination
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
*P = 4;
} Keep the brace in MM
if (P == NULL) return;
Dead Code Elimination
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
if (P == NULL)
return;
*P = 4;
} Keep the closing brace MM
Compiler Optimization
Compiler 2
Source code
.c, .m, .cpp, .mm
Object file
.o
Let’s Experiment: A Very Simple Optimization Pipeline Compiler 1
Dead Code Elimination
Redundant Null Check Elimination
Redundant Null Check Elimination
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
*P = 4;
} Keep the brace in MM
Dead Code Elimination
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
if (P == NULL)
return;
*P = 4;
} Keep the closing brace MM
Compiler Optimization 1
Compiler 2
Source code
.c, .m, .cpp, .mm
Object file
.o
Let’s Experiment: A Very Simple Optimization Pipeline Compiler 1
Dead Code Elimination
Redundant Null Check Elimination
Dead Code Elimination
int unused = *P;
void contains_null_check(int *P) {
…Hidden text for MM
*P = 4;
} Keep closing brace in MM
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
*P = 4;
} Keep the brace in MM
Compiler Optimization 1
Compiler 2
Source code
.c, .m, .cpp, .mm
Object file
.o
Let’s Experiment: A Very Simple Optimization Pipeline Compiler 1
Dead Code Elimination
Redundant Null Check Elimination
Dead Code Elimination
void contains_null_check(int *P) {
…Hidden text for MM
*P = 4;
} Keep closing brace in MM
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
*P = 4;
} Keep the brace in MM
Compiler Optimization 1
Compiler 2
Source code
.c, .m, .cpp, .mm
Object file
.o
Let’s Experiment: A Very Simple Optimization Pipeline Compiler 2
Redundant Null Check Elimination
Dead Code Elimination
Dead Code Elimination
void contains_null_check(int *P) { …Hidden text for MM if (P == NULL) return; *P = 4; } Please keep the brace MM
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
if (P == NULL)
return;
*P = 4;
} Keep brace during MM
int unused = *P;
Compiler Optimization 2
Compiler 2
Source code
.c, .m, .cpp, .mm
Object file
.o
Let’s Experiment: A Very Simple Optimization Pipeline Compiler 2
Redundant Null Check Elimination
Dead Code Elimination
Dead Code Elimination
void contains_null_check(int *P) { …Hidden text for MM if (P == NULL) return; *P = 4; } Please keep the brace MM
void contains_null_check(int *P) {
int unused = *P; …Hidden text for MM
if (P == NULL)
return;
*P = 4;
} Keep brace during MM
Compiler Optimization 2
Source code
.c, .m, .cpp, .mm
Object file
.o Compiler 2
Let’s Experiment: A Very Simple Optimization Pipeline Compiler 2
Redundant Null Check Elimination
Dead Code Elimination
Redundant Null Check Elimination
void contains_null_check(int *P) {
…Hidden text for MM
if (P == NULL)
return;
*P = 4;
} Please keep me MM!
void contains_null_check(int *P) { …Hidden text for MM if (P == NULL) return; *P = 4; } Please keep the brace MM
Compiler Optimization 2
Let’s Experiment: A Very Simple Optimization Pipeline A surprising result
void contains_null_check(int *P) {
int unused = *P; …
if (P == NULL)
return;
*P = 4;
} Keep brace during MM
Compiler 1
void contains_null_check(int *P) {
…
*P = 4;
} Keep closing brace in MMvoid contains_null_check(int *P) {
…
if (P == NULL)
return;
*P = 4;
}
void contains_null_check(int *P) {
int unused = *P; …
if (P == NULL)
return;
*P = 4;
} Keep brace during MM
Compiler 2
Compiler Optimization
Using Runtime Tools Effectively
• Exercise more code
• Use the tools together
Runtime Tool Overhead
Execution overhead Memory overhead
Main Thread Checker 1.02x negligible
Undefined Behavior Sanitizer 1.2x negligible
Address Sanitizer 2–3x 2x
Thread Sanitizer 5–10x 4x
What's New in LLVM• API Availability Checking for Objective-C
• Static Analyzer Checks
• New Warnings
• C++ Refactoring & Features from C++17
• Link-Time Optimization (LTO)
• API Availability Checking for Objective-C
• Static Analyzer Checks
• New Warnings
• C++ Refactoring & Features from C++17
• Link-Time Optimization (LTO)
API Availability Checking for Objective-C
[UIView instancesRespondToSelector:@selector(addInteraction:)]
[UIDragInteraction class]
[NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]
&ARErrorDomain != NULL futimens != NULL
[UIView instancesRespondToSelector:@selector(addInteraction:)]
[UIDragInteraction class]
[NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]
&ARErrorDomain != NULL futimens != NULL
[UIView instancesRespondToSelector:@selector(addInteraction:)]
[UIDragInteraction class]
[NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]
&ARErrorDomain != NULL futimens != NULL
[UIView instancesRespondToSelector:@selector(addInteraction:)]
[UIDragInteraction class]
[NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]
&ARErrorDomain != NULL futimens != NULL
[UIView instancesRespondToSelector:@selector(addInteraction:)]
[UIDragInteraction class]
[NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]
&ARErrorDomain != NULL futimens != NULL
API Availability Checking in Objective-C
if (@available(iOS 11,x*)) { r = [VNDetectFaceRectanglesRequest new]; if ([handler performRequests:@[r] error:&error]) { // Draw rectangles } } else { // Fall back when API not available }
Compiler warns about unguarded uses of new API
Use @available to query API availability at run time
API Availability Checking for Objective-C
Factor out Code with API_AVAILABILITY()
Convenient to write entire methods with limited availability
@interface MyAlbumController : UIViewController
- (void)showFaces
@end
API_AVAILABILITY(ios(11.0));
Factor out Code with API_AVAILABILITY()
Convenient to write entire methods with limited availability
@interface MyAlbumController : UIViewController
- (void)showFaces
@end
API_AVAILABILITY(ios(11.0))
;
Can apply to entire classes
Factor out Code with API_AVAILABILITY()
API Availability Checking in C/C++
Use __builtin_available to check availability at runtime
if (__builtin_available(iOS 11, macOS 10.13, *)) { CFNewAPIOniOS11(); }
API Availability Checking in C/C++
Use __builtin_available to check availability at runtime
Include <os/availability.h> for the API_AVAILABILITY macro
#include <os/availability.h>
void myFunctionForiOS11OrNewer(int i) API_AVAILABILITY(ios(11.0), macos(10.13));
Do Not Compare Number Objects to Scalars
Comparing NSNumber pointer value to 0 checks for nil – not zero number
@property NSNumber *photoCount;
- (BOOL)hasPhotos {
} Comparing pointer value to a scalar integer valuereturn self.photoCount > 0;
Do Not Auto-Synthesize NSMutable copy Properties
Setter calls -copy, which yields an immutable copy
- (void)replaceWithStockPhoto:(NSImage *)stockPhoto { self.photos = [NSMutableArray<NSImage *> new]; [self.photos addObject:stockPhoto]; }
-[__NSArray0 addObject:]: unrecognized selector sent to instance
@property (copy) NSMutableArray<NSImage *> *photos;
Static Analyzer Checks
Run Analyzer on Your Code! Supports Objective-C, C, C++
Analyze during build
Q & A