ИНЖЕНЕРНАЯ ЗАМЕТКА

Диагностика подключения LLDB и загрузки символов на облачном Mac

Диагностика подключения LLDB и загрузки символов на облачном Mac

Xcode уже показывает статус «Подключение», однако загрузка CPU на облачном Mac остается почти нулевой, точки останова меняют цвет с синего на серый, а в окне переменных нет данных. Если в этот момент многократно нажимать кнопку остановки, перезапускать приложение или удалять DerivedData, можно лишь уничтожить сведения о сбое. Эффективнее разделить проблему на три уровня: допускает ли целевой процесс отладку, установлена ли сессия между LLDB и целевым процессом и удается ли найти подходящие символы для текущего артефакта сборки.

Сначала определите проблемный уровень

Вначале запишите время возникновения сбоя, коммит проекта, Scheme, Configuration, целевое устройство и способ запуска, а затем проверьте состояние Xcode. Не объединяйте медленное подключение, медленный запуск приложения и медленное разрешение символов в одну категорию.

Симптом Что проверить в первую очередь Типичный вывод
Приложение не запускается, а LLDB продолжает ждать Целевой процесс, параметры запуска Процесс завершился или был запущен другой артефакт сборки
PID процесса отображается, но интерфейс не отвечает Состояние процесса, служба отладки Целевой процесс приостановлен, заблокирован или не завершил отладочное рукопожатие
Точки останова пустые внутри или серые Модули и символы Модуль не загружен, его путь изменился или UUID не совпадает
Выполнение останавливается, но локальные переменные не видны Уровень оптимизации, отладочная информация Оптимизация Release объединила или удалила переменные

Сохраните снимок процессов в другом терминале:

mkdir -p "$HOME/lldb-case"
date -u > "$HOME/lldb-case/time.txt"
ps -axo pid,ppid,state,%cpu,etime,command \
  > "$HOME/lldb-case/processes.txt"
xcrun simctl list devices \
  > "$HOME/lldb-case/simulators.txt"

Если в поле state длительное время отображается U, процесс может находиться в непрерываемом ожидании. Постоянное значение T означает, что процесс приостановлен. По одному снимку нельзя определить динамику, поэтому полезнее через десять секунд сделать еще один.

Сначала сохраните данные, а затем выполняйте очистку. Успешный перезапуск восстанавливает работу, но не доказывает, что первопричина устранена.

Проверьте процесс и отладочную сессию

Убедитесь, что подключение выполнено к правильному артефакту

Приложения с одинаковым именем могут одновременно находиться в нескольких симуляторах, каталогах тестов или старых каталогах сборки. Сначала проверьте процесс и загруженные образы в LLDB:

(lldb) process status
(lldb) target list
(lldb) image list -o -f

target list должен указывать на исполняемый файл текущей сборки. В image list путь к целевому модулю должен находиться в ожидаемом каталоге Build Products. Если путь относится к другому каталогу DerivedData, не удаляйте сразу все данные. Сначала запишите старый путь и проверьте, не использует ли Scheme ошибочный артефакт сборки повторно.

Если командная строка LLDB по-прежнему отвечает, выполните:

(lldb) thread list
(lldb) thread backtrace all

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

sample <PID> 10 1 -file "$HOME/lldb-case/app-sample.txt"

Не запускайте несколько команд sample подряд. Десятисекундного образца достаточно, чтобы определить, ожидает ли главный поток блокировку, файловый ввод-вывод, сетевой вызов или системный фреймворк.

Сопоставьте dSYM по UUID

Одинаковые имена файлов не гарантируют совпадения символов. При каждой компоновке может создаваться новый UUID, а LLDB использует только тот dSYM, который соответствует файлу Mach-O.

dwarfdump --uuid "/path/to/MyApp.app/MyApp"
dwarfdump --uuid "/path/to/MyApp.app.dSYM"

UUID для одной и той же архитектуры должны совпадать с обеих сторон. Если приложение содержит arm64, необходимо как минимум сравнить строки arm64. Не подменяйте dSYM файлом из другого Archive, другого коммита или сборки после повторной компоновки.

Проверьте, загружен ли модуль

Найдите целевой модуль в LLDB:

(lldb) image lookup -n AppDelegate
(lldb) image lookup -r -n 'YourModule\..*'
(lldb) breakpoint list

Если image lookup не находит символ, хотя модуль уже присутствует в image list, сначала проверьте формат отладочной информации. Сборки для разработки обычно должны генерировать DWARF или DWARF with dSYM. При высоком уровне оптимизации функции могут встраиваться, а локальные переменные — становиться недоступными. В такой ситуации создайте отдельную диагностическую Configuration, а не изменяйте временно общую для команды конфигурацию Release.

Статус точки останова pending не обязательно означает ошибку. Если динамический фреймворк еще не загружен, точка останова ожидает добавления соответствующего образа в процесс. Установите точку останова во входной функции, которая заведомо загружена, а затем отслеживайте последующие модули вместо многократного удаления одной и той же точки.

Найдите сбой рукопожатия в системных журналах

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

log stream --style compact --info \
  --predicate 'process == "debugserver" OR process == "lldb-rpc-server"' \
  > "$HOME/lldb-case/debug-session.log"

После однократного воспроизведения остановите сбор с помощью Control-C. Журнал может содержать имена пользователей, пути к проектам и идентификаторы устройств, поэтому перед отправкой обращения или передачей данных команде его следует обезличить. Не собирайте без необходимости общесистемные данные --debug в течение длительного времени: это создает много шума и усложняет фильтрацию результатов.

Если журнал показывает, что целевой процесс завершается в момент подключения, сначала запустите приложение отдельно от LLDB и убедитесь, что оно продолжает стабильно работать. Если завершается только тестовый процесс, отдельно проверьте хост тестов, тестовый Bundle и тестируемое приложение, а не только PID основного приложения.

Установите воспроизводимую последовательность восстановления

Восстановление следует начинать с действий, оказывающих минимальное влияние:

  1. Завершите текущую отладочную сессию, но сохраните целевое приложение и журналы.
  2. Проверьте Scheme, Configuration, целевое устройство запуска и путь к исполняемому файлу.
  3. Сравните UUID Mach-O и dSYM.
  4. Повторно соберите текущий Target без очистки всего проекта.
  5. Удаляйте каталог DerivedData соответствующего проекта только после подтверждения повторного использования старого артефакта.
  6. Если проблема по-прежнему воспроизводится, сохраните стеки вызовов потоков, образец процесса, системные журналы и минимальные шаги воспроизведения.

Сначала выведите список каталогов и время их изменения с помощью следующей команды, чтобы случайно не удалить данные других задач:

find "$HOME/Library/Developer/Xcode/DerivedData" \
  -maxdepth 1 -mindepth 1 -type d -print

На облачном Mac могут одновременно выполняться сборка, тестирование и графическая отладка. Перед очисткой убедитесь, что другие конвейеры не используют тот же каталог. Более надежный вариант — назначить каждой задаче отдельный -derivedDataPath, отделив данные отладки от кэша автоматической сборки.

Превратите результаты диагностики в критерии приемки

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

Команда может добавить проверку UUID, пути к артефактам и Configuration в контрольный список сборки, но не следует жестко задавать личные каталоги в скриптах. По-настоящему надежный процесс отладки — это не «очистить и повторить». Он позволяет любому участнику команды на основании одного и того же набора данных определить, на каком уровне возник сбой — процесса, сессии или символов, — и исправить только тот уровень, где появилось несоответствие.

Часто задаваемые вопросы

Что проверить, если LLDB подключился, но точка останова не срабатывает?

Сначала командой image list убедитесь, что нужный модуль загружен, затем сравните UUID исполняемого файла и dSYM. Незагруженный модуль оставляет точку в ожидании, а несовпадающий UUID ломает привязку к исходному коду.

Нужно ли сразу удалять DerivedData при зависании отладки?

Нет. Сначала сохраните список процессов, вывод LLDB, системные журналы и UUID артефактов. Удаляйте каталог конкретного проекта только после подтверждения, что причиной стали устаревшие данные сборки.

Нужно ли открывать отладочный порт в интернет?

Обычно нет. Запускайте Xcode и LLDB на облачном Mac и работайте через контролируемый удалённый рабочий стол или SSH. При необходимости перенаправления ограничьте туннель и адрес прослушивания.

VMDebug облачные Mac

Нужен физический Mac mini, выделенный под один заказ?

Сравните конфигурации M4 и M4 Pro, четыре периода аренды и 5 доступных узлов, а затем выберите устройство под свой рабочий процесс.

Выбрать конфигурацию и заказать