KKBOX WWDC17 Xcode debug - Oliver

Post on 22-Jan-2018

2.106 views 0 download

Transcript of KKBOX WWDC17 Xcode debug - Oliver

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