Сегодня мы поговорим о LLDB — замечательном инструменте для отладки и тестирования ваших проектов.

Поймай их всех

Наверное, каждому из нас доводилось иметь дело с исправлением различных багов в своих проектах. Зачастую, мы даже не задумываемся о систематизации этого процесса, просто стараемся быстрее найти и исправить ошибку. Такой подход может дорого обойтись в больших проектах с нетривиальной архитектурой, где уже сложно прослеживаются зависимости между конкретными участками кода, из-за высокого уровня абстракции.

Отладка в среде Xcode

Тестирование и отладка довольной сложный и трудоемкий процесс, программист зачастую должен держать огромный контекст из различных блоков кода прямо у себя в голове. Вам наверняка уже приходилось слышать о существовании программ, помогающих разработчикам отлаживать и тестировать код. Xcode здесь не стал исключением, среда имеет встроенный отладчик по умолчанию расположенный в нижней панели IDE, он поддерживает несколько видов взаимодействий, о которых мы и поговорим в этой статье.

Впрочем, Xcode не единственное место откуда вы можете управлять процессом отладки вашего приложения, в некоторых ситуациях будет удобней воспользоваться отладчиком прямо из консоли:

Немного о LLDB

LLDB это кроссплатформенный, высокопроизводительный отладчик, написанный на C++. На текущий момент он поддерживает i386, x86 и ARM архитектуры и может работать под управлением  Unix-like и Linux операционных систем. В Xcode LLDB появился начиная с версии 4.3, где и остался стандартным отладчиком по сегодняшний день.

Виды ошибок

Существует бесчисленное множество ошибок, которое мы можем допустить при написании программного кода. Что-бы упростить себе задачу, систематизируем это множество, разделив его на:

  • Ошибки компиляции
  • Ошибки исполнения
  • Ошибки логики

Пока что нас будут интересовать, только ошибки исполнения, они характерны тем, что возникают во время исполнения программы, приводят к ее остановке и оставляют программиста с глазу на глаз со стэком вызовов. Как правило, такие ошибки обнаруживаются на низком уровне, где ни о каком семантическом и уж тем более лексическом анализе говорить не приходится. В подавляющем большинстве случаев вы будете встречать:

  • EXC_BAD_INSTRUCTION
  • SIGABRT
  • EXC_BAD_ACCESS

Начнем по порядку, EXC_BAD_INSTRUCTION означает, что вашу программу остановил один из Assertion’ов стандартной библиотеки по соображениям безопасности, вы точно знакомы с этой ошибкой если пишите на Swift’е, что-бы ее воспроизвести достаточно развернуть пустой опционал там, где этого делать не следует.

SIGABRT, если вы когда-нибудь слышали о POSIX, возможно вы уже знакомы с этим сигналом. Sigabrt говорит о невозможности дальнейшего выполнения программы. Обычно, в Cocoa такой сигнал будет говорить о необработанном исключении и может показаться, что это не сильно относится к Swift программистам, которые в добровольно-принудительном порядке обязаны написать обработчик для исключения, если надобность в таковом заявлена в сигнатуре словом throws, но стоит помнить, что необработанное исключение может прилететь к вам из Objective-C библиотеки, если вы в той или иной мере пользуетесь бриджингом.

EXC_BAD_ACCESS, ну а с этой ошибкой, думаю, знакомы без исключения все Си-программисты, вы попытались обратиться к области в памяти, которая была освобождена.

Для примера напишем пару строчек, чтобы воспроизвести все рассмотренные ошибки:


import Foundation

class CBDebugSource {
    
    var data = Array(
        arrayLiteral: 1,2,3
    )
    
    var objc_data = NSArray()
    
    var computedProperty: Int {
        return self.computedProperty
    }
    
    func getBadInstructionExeption() -> Void {
        for index in 0...self.data.count {
            _ = self.data[index]
        }
    }
    
    func getSignalAbort() -> Void {
        _ = self.objc_data[0]
    }
    
    func getBadAccessExeption() -> Void {
        _ = self.computedProperty
    }
}

Этим кодом мы декларируем класс CBDebugSource, содержащий три метода, соответствующих вышеприведенным типам ошибок. В контексте этого кода, на примере отладки одного из методов мы и рассмотрим простейшие виды работы с отладчиком.

Запускаем LLDB

Итак, подключаем нашу «игровую площадку» к точке входа:


import Foundation

let source = CBDebugSource()

source.getBadInstructionExeption()
source.getSignalAbort()
source.getBadAccessExeption()

Готово, можно начинать. Собираем проект, видим, что статический анализатор действительно молчит, значит наш код синтаксически и семантически валиден, давай-те запускаться:

Это ожидаемое поведение, давай-те узнаем больше информации о том, что случилось с нашим потоком, введя команду bt в терминале:

Нас интересует строчка stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0), итак мы узнали причину остановки, сработал assertion. Если внимательно посмотреть на стэк, можно увидеть, что наша логика последний раз вызывалась во втором фрэйме, давайте ее детализируем, набрав команду f 2 в терминале:

В этом месте мы и вылезаем за границы диапазона, давайте посмотрим чему в данный момент у нас равны индекс и размерность массива:

Помня, что массивы в Swift индексируются начиная с нуля, подтверждаем свою гипотезу и исправляем нашу функцию, используя оператор полуоткрытого диапазона:


func getBadInstructionExeption() -> Void {
        for index in 0..<self.data.count {
            _ = self.data[index]
        }
    }

Перед тем как вносить изменения в код, проверим нашу задумку на месте, не выходя из отладчика. Откроем read-eval-print loop, набрав repl в консоли:

Отлично, мы последовательно определили причину ошибки, локализовали ее место, проверили текущее состояние некорректного выражения и наконец исправили его, попутно проверив ход своих мыслей в REPL.

Заключение

Сегодня мы рассмотрели несколько простых взаимодействий с LLDB, а так же обсудили возможные типы фатальных ошибок и причины их возникновения, оставив довольно много интересных и полезных вещей за рамками данной статьи, например работу с breakpoint’ами. Мы обязательно вернемся и рассмотрим их в следующих статьях.

 

Автор фото: @madeawkward

 

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.