Debugging is one of the most important skills of a developer. Debugging with Xcode is not too hard, basically it’s about how to use the Xcode breakpoint system and the LLDB. This post list the essentials for the objc.io Debugging technique post, but the GDB debugging technique can be found on Apple Technical Note 2239.

  1. LLDB   (run help to see all commands)

    1. UI Debugging:
      1. Use Chisel of Facebook (brew install chisel) – some commands like (pviews) or (pvc)
      2. pviews print Views Hierarchy – equivalent in lldb:

        po [[UIWindow keyWindow] recursiveDescription]

      3. pvc print View Controllers Hierarchy – equivalent in lldb:

        po [[UIWindow keyWindow] rootViewController] _printHierarchy] // ios8, private-helper of UIVC

      4. presponder  print the Responder Chain
      5. border/unborder : add border to view or layer
      6. wivar : set watchpoint on instance variable
      7. bmessage : this powerful command of Facebook Chisel has done a wonderful job for you in:
        1. sometimes you want to set breakpoint on [MyViewController viewDidAppear:] but if MyViewController doesn’t implement viewDidAppear but inherits it from somewhere then the breakpoint can not be set.
        2. If it inherits from UIViewController, the viewDidAppear method is an Apple method, so there is no symbols for it, there is no self in that method.
    2. Breakpoints commands
      1. (lldb) bt // print stack trace of current thread
      2. (lldb) bt all // print stack trace of all threads
      3. (lldb) help // list all commands
      4. (lldb) help <cmd> // help for the command
      5. Using the result :
        1. p count > $0 = 99;
        2. p $0 + 7 > 106
      6. Modify the value:
        1. e count = 42 // expression count = 42
        2. e -O // po // print object
        3. Stepping
          1. c – continue
          2. thread step-over // n
          3. thread step-in // s
          4. thread step-out // execute until reach return
      7. Setting bp:
        1. b main.m:17 // set breakpoint in the line 17 of main.m
        2. b isEven   // set bp on symbol
        3. b -[NSArray objectAtIndex:]   // set bp on function
        4. b +[NSSet setWithObject:]
    3. Symbolic Breakpoints can specify:
      1. condition
      2. action
      3. count
      4. automatic continue after actions
      5. Usage: using a symbolic bp on a function like [UIViewController dismissViewControllerAnimated:completion:], on each bp, list the caller object to see which object called it, we can actually resolve situations when a viewController is dismissed wrongly.
    4. Use private API functions
      1. Check private functions: https://github.com/nst/iOS-Runtime-Headers/blob/master/Readme.md
      2. Use Disassembler like Hopper:
        1. First, locate UIKit.framework – then use pseudocode view
        2. The pseudocode explains most of the logic details how a UIKit function behave.
    5. Logging Tips
      1. When we have access to self & _cmd, we can use

        1. NSStringFromSelector(_cmd) – %@ – Name of the current selector.
        2. NSStringFromClass([self class]) – %@ – Name of the current object’s class.
        3. [[NSString stringWithUTF8String:__FILE__] lastPathComponent] – %@ – Name of the source code file.
        4. [NSThread callStackSymbols] – %@ – NSArray of the current stack trace as programmer-readable strings. For debugging only, do not present it to end users or use to do any logic in your program.
      2. __func__ %s Current function signature.
        __LINE__ %d Current line number in the source code file.
        __FILE__ %s Full path to the source code file.
        __PRETTY_FUNCTION__ %s Like __func__, but includes verbose type information in C++ code.

iOS Support Matrix 

Accessing self and _cmd

  1. 32-bit processors Screen Shot 2015-03-17 at 2.55.53 pm
    1. Variables stack: $esp
    2. First value $esp is the return address
    3. $esp + 4 // the self

      po *(int*)($esp + 4)  –> self

    4. $esp + 8 // _cmd

      p (SEL)*(int*)($esp + 8) –> _cmd

    5. Intel 32-bit vs ARM

      Screen Shot 2015-03-17 at 2.49.30 pm

  2. x86-64   (Intel processors for iMac, MacBook) – Simulators is running on these processors
    1. $rdi, $rsi, $rdx, $rcx, $r8, $r9
    2. subsequent variables stack: $bp
  3. Arm7 (basically all ARM use $r0 for
    1. $r0, $r1, $r2, $r3 –> 4 parameters
      1. p (SEL) $r1 –> _cmd
    2. Subsequent variables stack: $sp  // sp = stack pointer
  4. Arm64   (iPhone5s+, iPad Air+, iPad Mini 2+)
    1. $r0, … $r7 –> similar to Arm7, but 8 parameters
    2. stack pointer $sp
  5. Terms
    1. ABI: Application Binary Interface
    2. SIMD: Single Instruction Multiple Data
    3. x86 – is Little-Endian (the reverse way, with smallest level first, as it’s Intel backward-compatibility) Screen Shot 2015-03-17 at 5.05.43 pm
    4. ARM is Bi-Endian (can switch between both, default is Little-Endian).
      1. More about ARM http://wanderingcoder.net/2010/07/19/ought-arm/
    5. Floating-point and Endianness: some historical machines use Big-Endian for floating point, and Little-Endian for integers. But it’s history.
    6. Registers:
      1. Accumulator register (AX). Stores return values.
      2. Counter register (CX).
      3. Data register (DX).
      4. Base register (BX). Used as a pointer to data.
      5. Stack Pointer register (SP). Pointer to the top of the stack.
      6. Stack Base Pointer register (BP). Used to point to the base of the stack.
      7. Source Index register (SI).
      8. Destination Index register (DI).
    7.  16-bit, 32-bit, 64-bit
      1. AX (Accumulator-Register 16-bit)
      2. EAX (Accumulator register 32-bit) – just add the E for 32-bit
      3. RAX (Accumulator register 64-bit) – add R for 64-bit
      4. x86_64:
        • R0 is RAX.
        • R1 is RCX.
        • R2 is RDX.
        • R3 is RBX.
        • R4 is RSP.
        • R5 is RBP.
        • R6 is RSI.
        • R7 is RDI.
    8. self
      1. using self in a class method means the class itself.
      2. using self in an instance method means the object instance receiving this message
    9.  isa: a pointer to the Class

      1. self->isa == [self class]

Example:

  1. When I want to see which classes are loaded in the Simulator for the app:
    1. Use Symbolic Breakpoint:

      [NSObject initialize]

    2. Condition:

      [(NSString*)NSStringFromClass($rdi) hasPrefix:@”MCP”]

    3. Action:

      p (NSString*)NSStringFromClass($rdi)

Advertisements