Отладочные программы
Самая твердая часть отладки кодирует — особенно, код ядра — не изучает, как использовать инструменты, но как проанализировать данные, сгенерированные инструментами. Успех, который Вы имеете со своим анализом, зависит в значительной степени от навыков и опыта, который Вы приносите к нему, и они могут всегда улучшаться путем изучения от других, “заработавших их дорожки” отладка кода ядра.
Эта глава предлагает набор отлаживающих драйвер подсказок и методов от инженеров Apple. Это запускается путем перечисления нескольких общих предложений для отладки драйвера и детализации инструментов в вашем распоряжении. Это тогда обсуждает эти общие области отладки драйвера:
Отладочные программы во время соответствия и загрузки этапов
Отладка с двумя машинами, включая процедуру установки, макросы отладки ядра и отладку паники и зависает
Журналирование, включая пользовательскую регистрацию событий
Некоторые основы отладки
Прежде, чем копаться в основных элементах отладки драйвера, это могло бы стоить Вашего, в то время как рассмотреть несколько общих подсказок по отладке и рассмотреть средства отладки, доступные Вам.
Общие советы
Чтобы помочь Вам начать, вот, несколько подсказок по отладке на основе опыта инженеров ядра Apple.
Разработайте на инкрементных шагах. Другими словами, не пишите большой блок кода драйвера прежде, чем отладить его. И на каждом этапе проверяют, что драйвер может разгрузиться, а также загрузиться; драйвер, который не может разгрузиться, предлагает утечку памяти (наиболее вероятно вызванный отсутствием
release
где-нибудь).Как предложение, получите соответствие драйвера и основные методы жизненного цикла драйвера, работающие сначала (особенно
start
). Тогда начните говорить с Вашими аппаратными средствами. Как только Вы уверены, что это работает должным образом, затем запишите код, говорящий с остальной частью системы.Сохраните более ранние версии своего кода. Если ошибки начинают появляться в Вашем коде, Вы можете «разность» текущая версия с предыдущей версией для определения местоположения точно, какой код является новым (и таким образом вероятный источник проблемы).
Получите исходный код для всех других частей ядра, с которым Ваш драйвер взаимодействует или зависит от (прямо или косвенно). Когда у Вас есть полная отладка на уровне исходного кода, дела идут намного проще.
Получите символы для каждого расширения ядра, которое могло бы влиять или быть затронуто, Ваш драйвер. Это включало бы Вашего водительского провайдера и его клиенты. Для полной палитры символов получите symboled ядро. В частности symboled ядро требуется, чтобы использовать макросы отладки ядра (описанный в Использовании Макросов Отладки Ядра).
Посмотрите Установку для Отладки С двумя машинами для инструкций при генерации файлов символов для расширений ядра. Для получения symboled ядра Вам, возможно, придется создать Дарвинское ядро из открытого исходного кода. Посмотрите Создание и Отладку Ядер в Руководстве по программированию Ядра для подробных данных.
Учиться
gdb
полностью. Запустите путем чтения Отлаживающий с GDB, предоставленным как часть Документации Инструментов.Познакомьтесь с как компьютерный взгляд инструкций в ассемблере (см. Компьютерные Инструкции Исследования).
Время от времени, одноступенчатый через каждую строку кода для проверки водительского выполнения точно, к чему Вы хотите его.
Не чрезмерно полагайтесь на вызовы к
IOLog
предоставлять Вам отладочную информацию (см. Используя IOLog).Удостоверьтесь, что Ваш KEXT включает только заголовочные файлы от
Kernel.framework
, в дополнение к заголовочным файлам Вы определяете. При включении других заголовков несмотря на то, что их определения могут быть в объеме во время компиляции, функциях и службах, они определяют, не будет доступно в среде ядра.
Проблемы с 64-разрядной архитектурой
В версии 10.3 OS X Apple представил некоторые изменения для поддержки новой 64-разрядной архитектуры. Поскольку изменения реализованы в самой операционной системе, некоторые драйверы могут быть затронуты, даже если они не работают на 64-разрядном компьютере. Для лучше объяснения этих изменений этот раздел сначала предоставляет краткое описание преобразования адресов PCI.
Устройства на шине PCI могут обработать 32-разрядные адреса, что означает, что устройство PCI имеет окно на 4 гигабайта в оперативную память. Когда устройство PCI выполняет транзакцию данных к или от оперативной памяти, драйвер устройства подготавливает эту память к I/O. В системах, где и подсистема памяти и драйверы устройств PCI используют 32-разрядное обращение, нет никаких трудностей. В системах, где подсистема памяти использует 64-разрядное обращение, однако, устройство PCI видит только четыре гигабайта оперативной памяти за один раз. Для решения этой проблемы Apple принял решение реализовать преобразование адресов. В этой схеме блоки памяти отображаются в 32-разрядное адресное пространство устройств PCI. Устройство PCI все еще имеет окно на 4 гигабайта в оперативную память, но то окно может содержать блоки состоящие из нескольких несмежных участков оперативной памяти. Преобразование адресов выполняется частью контроллера памяти, названного DART (таблица разрешения адреса устройства). DART сохраняет таблицу переводов для использования при отображении между физическими адресами, которые процессор видит и адреса, которые устройство PCI видит (названный адресами I/O).
Если Ваш драйвер придерживается задокументированного, предоставленного Apple APIs, этот процесс преобразования адресов прозрачен. Например, когда Ваш драйвер вызывает IOMemoryDescriptor prepare
метод, отображение автоматически помещается в DART. С другой стороны, когда Ваш драйвер вызывает IOMemoryDescriptor release
метод, отображение удалено. Несмотря на то, что это всегда было рекомендуемой процедурой, отказ сделать так мог не привести к нежелательному поведению в предыдущих версиях OS X. Начинаясь в версии 10.3 OS X, однако, отказ выполнить эту процедуру может привести к случайному повреждению данных или панике.
Если Ваш драйвер испытывает трудность на версии 10.3 OS X или более поздней системе, необходимо проверить, что Вы следуете этим инструкциям:
Всегда вызывайте
IOMemoryDescriptor::prepare
подготавливать физическую память к передаче I/O (в версии 10.3 OS X и позже, это также помещает отображение в DART).Сбалансируйте каждый вызов к
prepare
с вызовом кcomplete
не соединять память проводом.Всегда вызывайте
IOMemoryDescriptor::release
удалить отображение из DART.На аппаратных средствах, включающих DART, обратите внимание на направление DMA для чтений и записей. В 64-разрядной системе драйвер, пытающийся записать в область памяти, направление DMA которой устанавливается для чтения, вызовет панику ядра.
Побочный эффект изменений в подсистеме памяти состоит в том, что OS X, намного более вероятно, возвратит физически непрерывные диапазоны страницы в областях памяти. В более ранних версиях OS X система возвратила многостраничные области памяти в обратном порядке, начавшись с последней страницы и продолжившись к первой странице. Из-за этого многостраничная область памяти редко содержала физически непрерывный диапазон страниц.
Значительно увеличенная вероятность наблюдения физически непрерывных диапазонов страницы в областях памяти могла бы представить скрытые ошибки в драйверах, которые ранее не должны были обрабатывать физически непрерывные страницы. Если Ваш драйвер ведет себя неправильно или паникует, несомненно, исследуют эту возможность.
Другой результат изменений подсистемы памяти касается физических адресов, которые некоторые драйверы получают непосредственно из pmap уровня. Поскольку нет взаимно-однозначного соответствия между физическими адресами и адресами I/O, физические адреса, полученные из pmap уровня, не имеют никакой цели вне самой системы виртуальной памяти. Драйверы, использующие вызовы pmap для получения таких адресов (такой как pmap_extract
) не будет функционировать в системах с DART. Для предотвращения использования этих вызовов версия 10.3 OS X откажется загружать расширение ядра, использующее их, даже в системах без DART.
Средства отладки драйвера
Apple обеспечивает много инструментов, которые можно использовать для отладки драйверов устройств Набора I/O. Таблица 7-1 представляет некоторые более важные инструменты.
Инструмент или API | Описание |
---|---|
Отладчик GNU, используемый в отладке ядра с двумя машинами. Посмотрите Подсказки относительно Использования gdb для небольшого количества полезной информации о | |
Макросы отладки ядра | Мощные макросы, разработанные для использования в |
Утилита, делающая множество вещей с расширениями ядра. Это загружает расширения ядра и, до загрузки, проверяет расширения, предоставляя диагностическую информацию, связанную с ошибками зависимости и разрешением файла. Это также включает отладку расширений ядра (использование | |
Утилита, распечатывающая столбцы информации о расширениях ядра, в настоящее время загружающихся в системе. Информацией, самой полезной для отладки драйвера, является водительский адрес загрузки, ссылки, сохраненные драйвером к другим расширениям ядра, числу ссылок, которые другие расширения имеют к нему, и версия расширения ядра. Посмотрите Разгрузку Вашего Драйвера для получения дополнительной информации. | |
| Обеспечивает снимок Реестра I/O, показывая иерархическую структуру отношений клиентского провайдера среди формирователей тока и кусков в системе. С необходимыми опциями это показывает свойства, связанные с каждым узлом в реестре. Проводник Реестра I/O показывает ту же информацию в графическом интерфейсе. И инструмент и приложение особенно полезны для отладки проблем с соответствием. Посмотрите, что Отладка Соответствует проблемы. |
| Прежний инструмент выводит на экран сводку выделения памяти типом средства выделения (экземпляр, контейнер, и |
| Определяет ряд значений для свойства IOKitDebug. Когда это свойство определяется, система пишет состояние различных событий драйвера, таких как присоединение, соответствие и зондирование, к системному журналу. Посмотрите, что Отладка Соответствует проблемы для получения дополнительной информации. |
Отладка соответствия и загрузки проблем
Прежде чем Вы начнете отлаживать свою водительскую специфичную для аппаратных средств функциональность, необходимо сначала гарантировать, что KEXT допустим и что это загружается, разгружается и соответствует должным образом. В этом разделе описывается использовать инструменты OS X, такой как kextload
и kextstat
, чтобы аутентифицировать и проверить Ваш KEXT, разрешите его зависимости и протестируйте его соответствие.
Зависимости от драйвера
Каждый драйвер объявляет свои зависимости от других загружаемых расширений ядра (таких как семья I/O Kit), субкомпоненты ядра (такой как com.apple.kernel.iokit
), или KPIs (такой как com.apple.kpi.libkern
) в Info.plist
файл. На верхнем уровне Info.plist
файл, драйвер перечисляет каждую зависимость в форме пары ключ/значение в OSBundleLibraries
словарь; ключ является KEXT или именем субкомпонента ядра, и значение является номером версии KEXT или субкомпонента.
В OS X v10.2, объявляя зависимость от субкомпонента ядра Набора I/O com.apple.kernel.iokit
(версия 6.x) неявно позволяет Вашему драйверу получать доступ к символам в двух других субкомпонентах ядра: com.apple.kernel.mach
и com.apple.kernel.bsd
.
В OS X v10.3, драйвер, объявляющий зависимость от com.apple.kernel.iokit
версия 6.x (10,2 версий) будет все еще обладать автоматическим доступом к символам BSD и Маху.
В OS X v10.4, Apple представил поддерживаемые интерфейсы программирования ядра или KPIs. В частности KPIs поддерживают разработку NKEs (сетевые расширения ядра), файловая система KEXTs и другой Набор non-I/O KEXTs. KPIs также рекомендуются для чистого Набора I/O KEXTs (KEXTs, использующие только I/O, Предоставленный набором APIs), что целевой OS X v10.4 и позже. Знайте, однако, что никакой KEXT не может объявить зависимости от комбинации и субкомпонентов ядра и KPIs. Для узнавания больше о KPIs посмотрите Ссылку Платформы Ядра; чтобы узнать, как объявить зависимости от них, посмотрите Зависимости от Расширения ядра.
Используя kextload, kextunload, и kextstat
Во время процесса разработки это - хорошая идея загрузить и разгрузить Ваш драйвер регулярно для ловли проблем в этих процессах, прежде чем они станут очень трудными найти. kextload
инструмент позволяет Вам загружать свой драйвер в командной строке и выполняет широкий диапазон тестов аутентификации и проверки. kextload
также позволяет Вам отлаживать свое водительское start
и probe
подпрограммы перед соответствием начинаются. kextunload
инструмент завершает и не регистрирует объекты Набора I/O, связанные с Вашим KEXT, и разгружает код и лица для этого KEXT. Для полной информации на kextload
, kextunload
, и kextstat
, просмотрите man
страницы для этих инструментов в Окне терминала.
Можно использовать kextload
загрузить и запустить Ваш KEXT с или без стартового соответствия Набора I/O, но kextload
не предоставляет информации о самом процессе соответствия. Если Ваш драйвер не соответствует должным образом, посмотрите, что Отладка Соответствует проблемы для получения дополнительной информации.
Загрузка Вашего драйвера
kextload
инструмент проверяет Ваше водительское Info.plist
файл полностью, прежде чем это попытается загрузить Ваш драйвер в ядро. При необходимости свойства отсутствуют или неправильно идентифицированные, если Ваши заявленные зависимости отсутствуют или несовместимые, или если Ваш драйвер имеет неправильные полномочия или владение, kextload
отказывается загружать Ваш KEXT. Для обнаружения, почему KEXT не загружается работать kextload
с -t
или -tn
опция. Это говорит kextload
чтобы выполнить все его тесты на Вашем KEXT и предоставить Вам словарь ошибок, это находит (добавление -n
опция предотвращает kextload
от загрузки KEXT, даже если это не находит ошибок). Например, Перечисление 7-1 показывает Info.plist
из KEXT под названием BadKEXT, содержащий три ошибки.
Пример перечисления 7-1 Info.plist
содержа ошибки
<key>IOKitPersonalities</key> |
<dict> |
<key>BadKEXT</key> |
<dict> |
<key>CFBundleIdentifier</key> |
<string>com.MySoftwareCompany.driver.BadKEXT</string> |
<key>IOClass></key> |
<string>com_MySoftwareCompany_driver_BadKEXT</string> |
<!-- No floating point numbers are allowed in the kernel. --> |
<key>IOKitDebug</key> |
<number>0.0</number> |
<key>IOMatchCategory</key> |
<string>com_MySoftwareCompany_driver_BadKEXT</string> |
<key>IOResourceMatch</key> |
<string>IOKit</string> |
<!-- The personality is missing its IOProviderClass key-value pair. --> |
</dict> |
</dict> |
<key>Libraries</key> |
<dict> |
<key>com.apple.kernel.iokit</key> |
<string>1.1</string> |
<!-- com.apple.kernel.libkern is misspelled. --> |
<key>com.apple.kernel.libker</key> |
<string>1.1</string> |
<key>com.apple.kernel.mach</key> |
<string>1.1</string> |
</dict> |
Кроме того, BadKEXT имеет проблемы полномочий и владение: Это принадлежит пользователю user_name
и группа staff
и его папки имеют полномочия rwxrwxr-x
(775 восьмеричных). По причинам безопасности KEXTs должен принадлежать полностью пользователь и группа колеса. Далее, никакой компонент KEXT не может быть перезаписываем никаким пользователем кроме корня. (Для получения дополнительной информации о корректных полномочиях и владении KEXTs, посмотрите Тестирование и Развертывание Драйверов). Принятие полномочий пользователя root при помощи sudo
команда и выполнение kextload -t
на BadKEXT предоставляет следующую информацию, показанную в Перечислении 7-2.
Перечисление 7-2 kextload -t
выходные ошибки найдены в KEXT
[computer_name:] user_name% sudo kextload -t BadKEXT.kext |
Password: |
can't add kernel extension BadKEXT.kext (validation error) (run kextload |
on this kext with -t for diagnostic output) |
kernel extension BadKEXT.kext has problems: |
Validation failures |
{ |
"Info dictionary missing required property/value" = { |
"IOKitPersonalities:BadKEXT:IOProviderClass" |
} |
"Info dictionary property value is of illegal type" = { |
"IOKitPersonalities:BadKEXT:IOKitDebug" |
} |
} |
Authentication failures |
{ |
"File owner/permissions are incorrect" = { |
"/Users/user_name/Projects/BadKEXT/build/BadKEXT.kext" |
"/Users/user_name/Projects/BadKext/build/BadKEXT.kext/Contents/Info.plist" |
"/Users/user_name/Projects/BadKext/build/BadKEXT.kext/Contents" |
"/Users/user_name/Projects/BadKext/build/BadKEXT.kext/Contents/MacOS/BadKEXT" |
"/Users/user_name/Projects/BadKext/build/BadKEXT.kext/Contents/MacOS" |
} |
} |
Missing dependencies |
{ |
"com.apple.kernel.libker" = |
"No valid version of this dependency can be found" |
} |
Проверки kextload
выполняет упоминаются ниже.
Info.plist
словарь должен присутствовать и должен иметь типdictionary
.CFBundleIdentifier
свойство должно иметь типstring
и больше, чем 63 символа.CFBundleVersion
свойство должно быть надлежащим стилем «vers»string
.Если дополнительное
OSBundleCompatibleVersion
свойство присутствует, это должна быть надлежащая строка стиля «vers» со значением, меньше чем или равнымCFBundleVersion
значение.CFBundleExecutable
должен иметь типstring
.Если
IOKitPersonalities
свойство присутствует (IOKitPersonalities
свойство требуется для соответствия драйвера, но не требуется для KEXT быть допустимым), все его значения должны иметь типdictionary
.kextload
выполняет следующее, проверяет лица.IOClass
свойство должно иметь типstring
.IOProviderClass
свойство должно иметь типstring
.CFBundleIdentifier
должен иметь типstring
.IOKitDebug
свойство, если есть должен иметь типinteger
(и не число с плавающей точкой).Все типы свойства должны быть допустимыми для использования в ядре; например,
date
тип не позволяется в ядре.
OSBundleLibraries
должен присутствовать и иметь типdict
.OSBundleLibraries
значения должны существовать и иметь действительные версии.Если все лица имеют
IOKitDebug
набор свойств к ненулевому значению,kextload
отмечает KEXT неподходящее на безопасную начальную загрузку, даже еслиOSBundleRequired
свойство присутствует и допустимо.
Если -t
опция не предоставляет Вам достаточно информации, можно сказать kextload
сделать Вам поэтапный отчет его действий при помощи -v
опция, сопровождаемая числом от 1 до 6 указаний, сколько информации Вы хотите. Таким образом Вы видите в какой точка kextload
сбои.
Разгрузка Вашего драйвера
kextunload
инструмент просто пытается разгрузить Ваш водительский код и лица, завершаясь и не регистрируя все объекты Набора I/O, связанные с Вашим драйвером. К сожалению, kextunload
предоставляет очень мало информации о том, почему мог бы не разгрузиться Ваш драйвер. Возможно, что системный журнал (в котором можно просмотреть /var/log/system.log
) выведет на экран некоторую информацию об отказе разгрузиться, но Вы получите наиболее полезную информацию с помощью других методов и инструментов, описанных в этом разделе.
Драйвер, имеющий несогласованное retain
или release
, тот, зависящийся от другими модулями ядра или тем, имеющим сбой stop
метод не разгрузится. Безотносительно причины, если Вашему драйверу не удается разгрузиться, процесс отладки становится трудным, потому что Вы вынуждены перезапустить компьютер каждый раз, когда необходимо загрузить и разгрузить KEXT.
В целом, если драйвер внезапно отказывается разгружаться, необходимо внести только инкрементные изменения в водительском коде, проверив его возможность загрузиться и разгрузиться после каждого изменения, сузить возможных преступников. Таким образом можно вернуться к предыдущей, незагружаемой версии драйвера и исследовать изменения, которые Вы внесли в него.
Можно определить, не может ли драйвер разгрузиться, потому что другой модуль в зависимости от него при помощи kextstat
инструмент для отображения информации обо всех в настоящее время загружал KEXTs. Перечисление 7-3 показывает несколько строк вывода от kextstat
.
Перечисление 7-3 Частичное перечисление kextstat
вывод
Index Refs Address Size Wired Name (Version) <Linked Against> |
1 1 0x0 0x0 0x0 com.apple.kernel (6.0) |
2 8 0x0 0x0 0x0 com.apple.kernel.bsd (6.0) |
3 33 0x0 0x0 0x0 com.apple.kernel.iokit (6.0) |
4 32 0x0 0x0 0x0 com.apple.kernel.libkern (6.0) |
// Some lines not shown... |
11 7 0xaadd000 0x9000 0x8000 com.apple.iokit.IOPCIFamily (1.2) <4 3> |
// Some lines not shown... |
26 6 0xb14c000 0x1a000 0x19000 com.apple.iokit.IOUSBFamily (1.2) <4 3 2> |
// Some lines not shown... |
Первый столбец kextstat
вывод является индексом загрузки KEXT. kextstat
использование этот индекс в последнем столбце, который покажет, против которого соединяется KEXTs определенный KEXT. Например, IOPCIFamily KEXT, индекс 11 в Перечислении 7-3, зависит от обоих com.apple.kernel.iokit
(индекс 3) и com.apple.kernel.libkern
(индекс 4).
Второй столбец является суммой всех ссылок на выбранный KEXT другим в настоящее время загруженным KEXTs. Проверьте этот столбец, если Вашему драйверу не удается разгрузиться: Если его число ссылок является ненулевым, kextunload
не может разгрузить его.
Если еще нет никаких других KEXTs в зависимости от Вашего драйвера, ему не удается разгрузиться, проблемой часто является несогласованное retain
или release
на объекте, который инстанцируют от одного из Ваших классов. Если Вам определили многократные классы в Вашем KEXT, можно использовать ioclasscount
узнать, какой класс имеет выдающиеся экземпляры. В Окне терминала ввести
ioclasscount MyClassName |
ioclasscount
инструмент выводит количество экземпляра указанного объекта, смещенного числом прямых подклассов, имеющих по крайней мере один выделенный экземпляр. С этой информацией Вы тогда проходите через свой водительский код, ища a retain
на Вашем объекте это не сбалансировано с a release
. Несогласованное retain
может быть трудным найти, потому что часто это не находится явно в Вашем коде, но в контейнерном классе Вы поместили свой объект в в методе, который Вы вызвали, или во все еще рабочем пользовательском приложении, содержащем io_object_t
ссылка на объекте. Например, вызов
addEventSource ( My_Event_Source_Object ) |
неявно помещает сохранение в My_Event_Source_Object, остающийся, пока Вы не вызываете
removeEventSource ( My_Event_Source_Object ) |
Отладка Водительского запуска и тестовых Методов
Когда Вы решили, что Ваш KEXT может загрузиться и разгрузиться успешно, можно хотеть отладить водительское start
и probe
методы. Чтобы сделать это, Вы используете kextload
. Это вызвано тем, что устанавливая Ваш KEXT в /System/Library/Extensions
и разрешение kextd
загрузить его при необходимости не дарит Вам возможность присоединить к компьютеру в a gdb
сеанс перед Вашим драйвером уже соответствовал и запустился.
При разработке драйвера Набора I/O можно использовать kextload
с -l
опция загрузить Ваш двоичный файл KEXT на целевом компьютере, но воздержаться от отправки любых лиц к ядру. Путем разделения загружающийся от соответствия, kextload -l
позволяет Вам задерживать соответствие (и вызов Вашего водительского start
и probe
методы) после установки сеанса отладки с двумя машинами (для получения дополнительной информации о том, как установить такой сеанс, посмотрите Установку для Отладки С двумя машинами).
Чтобы избежать соответствовать перед чтением можно вызвать kextunload
на Вашем драйвере перед использованием kextload -l
для проверки ни одно из водительских лиц не остается в ядре от более раннего сеанса отладки. Знайте, однако, что, включая новое устройство после использования kextload -l
загрузить Ваш двоичный файл KEXT может заставить некоторый другой драйвер соответствовать на устройстве. Если это не желательно, заботьтесь для не изменения конфигурации системы, пока Вы не готовы.
После того, как Вы присоединили к целевому компьютеру и установили точки останова на start
и probe
методы, Вы используете kextload
с -m
опция отправить лица в ядро, инициировавшее соответствие. Если Ваш KEXT’s start
и probe
методы не выполняются, успешное соответствие не происходило, и необходимо исследовать лица драйвера для обнаружения почему. Посмотрите, что Отладка Соответствует проблемы для уведомления относительно того, как отладить соответствие проблем.
При разработке Набора non–I/O KEXT, такой как NKE (сетевое расширение ядра) или файловая система KEXT, Вы реализуете MODULE_START
метод вместо start
метод драйвер Набора I/O реализации. Отладить MODULE_START
метод, Вы используете kextload
с -i
или -I
опции в интерактивном режиме загрузить KEXT и его зависимости, приостанавливаясь после каждого шага для Вашего разрешения продолжаться.
Используя -i
опция говорит kextload
автоматически загрузить зависимости Вашего KEXT, но попросить разрешение продолжаться на каждом шаге в процессе загрузки самого KEXT. -I
причины опции kextload
приостанавливаться для ввода на каждом шаге каждого этапа загрузки, включая загрузку зависимостей. Если Вы хотите отладить свой KEXT’s MODULE_START
метод, Вы используете kextload -i
или -I
загрузить Ваш двоичный файл KEXT и затем установить сеанс отладки с двумя машинами перед предоставлением разрешения вызвать MODULE_START
метод. Перечисление 7-4 показывает загрузку примера KEXT по имени использование MyKEXT kextload -i
.
Перечисление 7-4 Используя kextload -i
для интерактивной загрузки KEXT
[computer_name:/tmp] user_name% sudo kextload -i MyKEXT.kext |
Password: |
Load extension MyKEXT.kext and its dependencies [Y/n]? y |
Load module /tmp/MyKEXT.kext/Contents/MacOS/MyKEXT [Y/n]? y |
kextload: module com.MySoftwareCompany.driver.MyKEXT created as #70 at |
address 0xaf5c000, size 8192 |
kextload: You can now break to the debugger and set breakpoints for this |
extension. |
Start module /tmp/MyKEXT.kext/Contents/MacOS/MyKEXT (answering no will abort |
the load) [Y/n]? y |
kextload: started module /tmp/MyKEXT.kext/Contents/MacOS/MyKEXT |
kextload: MyKEXT.kext loaded successfully |
Send personality "MyKEXT" to the kernel [Y/n]? y |
kextload: matching started for MyKEXT.kext |
[computer_name:/tmp] user_name% |
Отладка соответствия проблем
Получение Вашего драйвера соответствовать правильно может быть одним из более сложных аспектов разработки драйвера Набора I/O. Если Ваш драйвер загружается и разгружается должным образом, но не соответствует, самой важной вещью, которую можно сделать, становятся полностью знакомой с соответствием семьи языка. Несмотря на то, что Набор I/O реализует драйвер, соответствующий в трехфазном, отнимающем процессе (описанный подробно inIOKit Основные принципы), каждая семья I/O Kit свободна определить произвольно сложный собственный язык соответствия.
После того, как Набор I/O отбрасывает драйверы неправильного класса в соответствующей класс фазе, Набор I/O исследует остающиеся драйверы на специфичные для семьи свойства соответствия в пассивно соответствующей фазе. Если семья реализует специфичное для семьи соответствие, она рассматривает кандидатуру водительская индивидуальность и повышает или понижает водительское probe
выиграйте на основе соответствий, которые это находит.
Несмотря на то, что некоторые семьи не определяют специфичного для семьи соответствия, вместо этого предпочитая, чтобы драйверы зондировали устройство, многие другие осуществляют некоторый контроль над пассивно соответствующей фазой. Семья USB, например, требует, чтобы ее драйверы использовали определенные комбинации свойств, определенных USB Общая Спецификация Класса. Для Вашего драйвера USB для соответствия необходимо включать все элементы точно одной комбинации свойства. Другим примером является семья PCI, определяющая много ключей соответствия, значения которых являются содержанием различных регистров пространства конфигурации PCI. Кандидат драйвер PCI может тогда соответствовать на единственном значении регистра, списке значений, или даже частичного значения регистра, обозначенного предоставленной битовой маской.
Если Ваш драйвер не соответствует, сначала удостоверьтесь, что значения свойств в Вашей водительской индивидуальности соответствуют соответствующие свойства, которые Ваше устройство публикует в Реестре I/O. Можно использовать приложение Проводника Реестра I/O (доступный в /Developer/Applications
) или инструмент командной строки ioreg
для просмотра свойств устройство публикует. При использовании Проводника Реестра I/O выберите Find, чтобы искать имя устройства, следовать за путем к объекту, представляющему устройство и просмотреть его свойства в более низком окне. Поочередно, для просмотра Реестра I/O в Окне терминала ввести
ioreg -bl |
-b
опция выводит на экран имена объектов полужирным и -l
опция выводит на экран свойства объектов.
Если нет никаких расхождений между опубликованными свойствами Вашего устройства и Вашими водительскими свойствами соответствия, затем удостоверьтесь, что Вы знаете, как Ваша водительская семья реализует соответствие. Семьи, определяющие их собственный язык соответствия, делают так путем реализации matchPropertyTable
метод. Путем просмотра кода для этого метода можно определить, как водительская семья использует свойства, которые это находит в водительском словаре индивидуальности для корректировки probe
счет.
В очень редких случаях драйвер мог бы объявить IOResources
как значение IOProviderClass
ключ. IOResouces
специальный кусок, присоединенный к корню Реестра I/O, делающего ресурсы, такие как ядро BSD, доступное по всей системе. Традиционно, драйверы виртуальных устройств соответствуют на IOResources
потому что виртуальные устройства не публикуют собственные куски. Другим примером такого драйвера является HelloIOKit KEXT (описанный в Привет Наборе I/O: Создание Драйвера устройства С XCode), который соответствует на IOResources
потому что это не управляет никакими аппаратными средствами.
Наконец, можно поместить IOKitDebug
свойство в Вашем водительском словаре индивидуальности. Когда Вы даете этому свойству ненулевое значение, оно помещает состояние различных событий, таких как присоединение, соответствие и зондирование, в системный журнал (доступный в /var/log/system.log
). Значение IOKitDebug
свойство определяет, какие события регистрируются; посмотрите IOKitDebug.h
(доступный в /System/Library/Frameworks/Kernel.framework/Headers/IOKit
) для I/O Определенные с помощью набора значения. Если Вы не интересуетесь только единственным событием или определенным набором событий, установите значение в 65535
получить информацию обо всех событиях IOKitDebug
покрытия. С этой информацией Вы видите, который имели место события и успешно выполнились ли они или перестали работать, но не, почему они перестали работать.
Несмотря на то, что IOKitDebug
свойство может быть полезным во время разработки, несомненно, сможет установить ее значение в 0
(или удалите свойство в целом) прежде, чем поставить Ваш драйвер, потому что ненулевое значение будет препятствовать тому, чтобы Ваш драйвер загрузился во время безопасной начальной загрузки. Для получения дополнительной информации о безопасной начальной загрузке посмотрите раздел Loading Kernel Extensions at Boot Time Тем Программирования Расширения ядра.
Отладка с двумя машинами
Для отладки драйвера устройства или действительно любой код, находящийся в ядре, два компьютера, является необходимостью. Ошибочный код ядра имеет противную тенденцию разрушить или подвесить систему и так непосредственно отладку, что система часто невозможна.
Отладка с двумя машинами с gdb
основная трасса к нахождению ошибок в коде драйвера. Этот раздел берет Вас через процедуру для установки двух компьютеров для отладки, предлагает несколько подсказок относительно использования gdb
в коде ядра, представляет Вас макросам отладки ядра и обсуждает методы для нахождения, что ошибки, вызывающие ядро, паникуют и зависают.
Установка для отладки с двумя машинами
Этот раздел суммирует шаги, требуемые устанавливать два компьютера для отладки ядра. Это рисует в большой степени на следующих документах, к которым необходимо обратиться для более подробной информации:
Учебное руководство Привет Отладчик: Отладка Драйвера устройства с GDB. (Это учебное руководство является частью Тем Программирования Расширения ядра.)
Раздел «When Things Go Wrong» Создания и Отладки Ядер в Руководстве по программированию Ядра. (Этот документ доступен онлайн в Дарвинской Документации.)
В отладке с двумя машинами одну систему вызывают целевым компьютером и другим узлом (или разработка) компьютер. Главный компьютер является фактически работающим компьютером gdb
. Это обычно - компьютер, на котором Ваш драйвер разрабатывается — следовательно, это также упоминается как компьютер разработки. Целевой компьютер является системой, в которой выполняется драйвер быть отлаженным. Ваш узел и целевые компьютеры должны выполнять ту же версию Дарвинского ядра, или максимально близко к той же версии. (Конечно, при отладке склонной к панике версии ядра Вы захотите, чтобы главный компьютер выполнил новую стабильную версию Дарвина.) Для оптимальной отладки на уровне исходного кода главный компьютер должен иметь исходный код драйвера, любые расширения ядра, связанные с Вашим драйвером (такие как его клиент или провайдер), и возможно даже само ядро (/xnu
).
Для отладки с двумя машинами, чтобы быть выполнимым, следующее должно быть истиной:
Для версий OS X перед версией 10.2 OS X оба компьютера должны быть на той же подсети.
У Вас должен быть доступ входа в систему к обоим компьютерам как администратор (группа
admin
), потому что Вам будут нужны полномочия пользователя root загрузить Ваш KEXT (можно использоватьsudo
команда).Необходимо быть в состоянии скопировать файлы между компьютерами с помощью FTP,
scp
(SSH),rsync
, AFP, или подобный протокол или инструмент.
Когда все это будет существовать, завершите следующие шаги:
Target установил NVRAM
debug
переменная к0x144
, то, которое позволяет Вам заскочить в отладчик после немаскируемого прерывания (NMI) и, если Вы выполняете OS X v. 10.2 или позже, позволяет Вам отладить два компьютера не на той же подсети. Можно использоватьsetenv
для установки флага в Открывают сам Firmware (на основанных на PowerPC компьютерах Macintosh), или можно использоватьnvram
утилита. Для последнего введите следующий как корень в командной строке:nvram boot-args="debug=0x144"
Это - хорошая идея войти
nvram boot-args
(никакой параметр) сначала для получения любых текущих переменных NVRAM в действительности; тогда включайте эти переменные вместе с флагом отладки, когда Вы дадитеnvram boot-args
управляйте во второй раз. Перезагрузите систему.Узел или Копия Target драйвер (или любое другое расширение ядра) к рабочему каталогу на целевом компьютере.
Узел Установил постоянное сетевое соединение с целевым компьютером через ARP. Следующий пример предполагает, что Ваш тестовый компьютер
target.goober.com
:$ ping -c 1 target.goober.com
ping results: ....
$ arp -an
target.goober.com (10.0.0.69): 00:a0:13:12:65:31
$ arp -s target.goober.com 00:a0:13:12:65:31
$ arp -an
target.goober.com (10.0.0.69) at00:a0:13:12:65:31 permanent
Эта последовательность команд устанавливает соединение с целевым компьютером (через
ping
), выводит на экран информацию о недавних соединениях, о которых знает ARP (arp -an
), делает соединение с целевым компьютером постоянным путем указания адреса оборудования Ethernet (arp -s
), и проблемыarp -an
управляйте во второй раз для проверки этого.Target Создает файлы символов для драйвера и любых других расширений ядра, от которых это зависит. Сначала создайте каталог для содержания символов; тогда работайте
kextload
инструмент командной строки, указывая каталог как параметр-s
опция:$ kextload -l -s /tmp/symbols /tmp/MyDriver.kext
Эта команда загрузки
MyDriver.kext
но, из-за-l
опция, еще не запускает процесс соответствия (который происходит на более позднем шаге). Если Вы не хотите, чтобы драйвер загрузился просто все же, укажите-n
опция вместе с-s
опция. Посмотрите Используя kextload, kextunload, и kextstat дляkextload
процедура для отладки водительского кода запуска.Target или Копия Узла файлы символов к главному компьютеру.
Разместите Дополнительно, если Вы хотите отладить свой драйвер с доступом ко всем символам в ядре, получить или создать symboled ядро. Для получения дополнительной информации свяжитесь с Технической поддержкой Разработчика Apple. Можно найти инструкции для создания Дарвинского ядра от Открытого исходного кода в Здании и Отладке Ядер в Руководстве по программированию Ядра.
$ gdb /mach_kernel
Если у Вас есть symboled ядро, укажите путь к нему, а не
/mach_kernel
. Важно, чтобы Вы работалиgdb
на ядре той же версии и сборки как та, работающая на целевом компьютере. Если версии отличаются, необходимо получить symboled копию ядра и использования цели это.Узел в
gdb
, добавьте файл символов своего драйвера.(gdb) add-symbol-file /tmp/symbols/com.acme.driver.MyDriver.sym
Добавьте файлы символов других расширений ядра в Вашей водительской цепочке зависимости.
Узел говорит
gdb
то, что Вы будете отлаживать удаленно.(gdb) target remote-kdp
Target Врывается в режим отладки ядра. В зависимости от модели Вашей целевой системы или выпустите надлежащую команду клавиатуры или нажмите кнопку программиста. На клавиатурах USB удержите Командную клавишу и кнопку питания; на клавиатурах ADB удержите Клавишу CTRL и кнопку питания. Если Вы выполняете версию 10.4 OS X или позже, удерживаете следующие пять клавиш: Команда, Опция, Управление, Сдвиг и Escape.
Вам, вероятно, придется удержать клавиши или кнопки в течение нескольких секунд, пока Вы не видите “Ожидание удаленного сообщения” соединения отладчика.
Присоединение узла к целевому компьютеру и точкам останова набора.
(gdb) attach target.goober.com
(gdb) break 'MyDriverClass::WriteData(* char)'
(gdb) continue
Убедитесь, что Вы даете
continue
команда; иначе целевой компьютер безразличен.Target Запускает выполнение драйвера.
$ kextload -m -t /tmp/MyDriver.kext
-m
опция запускает процесс соответствия для драйвера.-t
говорящая опция,kextload
для осуществления обширных проверок проверки, является действительно дополнительным здесь; идеально, Ваш драйвер должен был передать эти проверки во время более раннего этапа отладки (см. Используя kextload, kextunload, и kextstat). После запуска драйвера выполните действия, необходимые для инициирования точки останова.Узел, Когда точка останова Вы устанавливаете, инициирован, можно начать отлаживать использование драйвера
gdb
команды. Если Вы «получаете» макросы отладки ядра (см. следующий раздел, Используя Макросы Отладки Ядра), можно использовать тех также.
Используя макросы отладки ядра
Apple включает ряд макросов отладки ядра как часть Дарвина. Они были записаны инженерами с глубокими знаниями того, как работает Дарвинское ядро. Несмотря на то, что это возможно к коду отладочной программы без них макросы, они сделают задачу намного проще.
Ядро, отлаживая макросы зондирует внутренние структуры рабочей системы OS X в значительной глубине. С ними можно получить сводные и подробные снимки задач и их потоков в ядре, включая такую информацию как приоритет потока, исполнимые имена и вызванные функции. Макросы отладки ядра также приводят к информации о штабелях ядра для всех или выбранных активаций потока, на пробелах IPC и правах порта, на картах виртуальной памяти и записях карты, и на зонах выделения. Посмотрите Таблицу 7-2 для сводки макросов отладки ядра.
Можно получить макросы отладки ядра из Дарвинского репозитория Открытого исходного кода. Они находятся в .gdbinit
файл в /xnu/osfmk
ответвление исходного дерева. Поскольку .gdbinit
стандартное имя файла инициализации для gdb
, у Вас могло бы уже быть свое собственное .gdbinit
файл для установки сеансов отладки. Если это верно, можно объединить содержание файлов или иметь «исходный» оператор в одном .gdbinit
файл, ссылающийся на другой файл. Включать макросы в a .gdbinit
файл для сеанса отладки, укажите следующий gdb
команда вскоре после выполнения gdb
на mach_kernel
:
(gdb) source /tmp/.gdbinit |
(В этом примере, /tmp
представляет любой каталог, содержащий копию .gdbinit
файл Вы получили из репозитория С открытым исходным кодом.), Поскольку макросы отладки ядра могут измениться между версиями ядра, удостоверьтесь, что Вы используете макросы, которые соответствуют максимально близко версию ядра, которое Вы отлаживаете.
Макрос | Описание |
---|---|
| Выводит на экран сводное перечисление задач |
| Выводит на экран сводное перечисление всех активаций |
| Выводит на экран штабели ядра для всех активаций |
| Выводит на экран сводное перечисление всех карт VM |
| Выводит на экран сводное перечисление всех записей карты VM |
| Выводит на экран сводное перечисление всех пробелов IPC |
| Выводит на экран сводное перечисление всех прав IPC |
| Выводит на экран сводное перечисление всех двоичных файлов расширения ядра |
| Состояние дисплеев указанной задачи |
| Выводит на экран состояние всех активаций в задаче |
| Дисплеи все ядро складывают для всех активаций в задаче |
| Состояние дисплеев карты VM указанной задачи |
| Выводит на экран сводный список записей карты VM задачи |
| Состояние дисплеев пространства IPC указанной задачи |
| Выводит на экран сводный список записей пространства IPC задачи |
| Состояние дисплеев указанной активации потока |
| Выводит на экран штабель ядра для указанной активации |
| Выводит на экран состояние указанной карты VM |
| Выводит на экран сводный список записей указанной карты VM |
| Выводит на экран состояние указанного пространства IPC |
| Выводит на экран сводный список всех прав в пространстве IPC |
| Выводит на экран состояние процесса, идентифицированного PID |
| Выводит на экран состояние процесса, идентифицированного proc указателем |
| Информация о дисплеях о двоичном файле расширения ядра |
| Учитывая адрес, выводит на экран двоичный файл расширения ядра и смещение |
| Информация о зоне дисплеев |
| Выводит на экран паническую информацию о журнале |
| Контекст потока переключателя |
| Контекст переключателя |
| Контекст сброса |
Подмножество макросов отладки ядра особенно полезно для писателей драйвера: showallstacks
, switchtoact
, showkmodaddr
, showallkmods
, и switchtoctx
. Вывод showallstacks
списки все задачи в системе и, для каждой задачи, потоков и штабелей связались с каждым потоком. Перечисление 7-5 показывает информацию о паре задач, как испускается showallstacks
.
Пример перечисления 7-5 распараллеливает штабели, показанные showallstacks
(gdb) showallstacks |
... |
task vm_map ipc_space #acts pid proc command |
0x00c1e620 0x00a79a2c 0x00c10ce0 2 51 0x00d60760 kextd |
activation thread pri state wait_queue wait_event |
0x00c2a1f8 0x00ccab0c 31 W 0x00c9fee8 0x30a10c <ipc_mqueue_rcv> |
continuation=0x1ef44 <ipc_mqueue_receive_continue> |
activation thread pri state wait_queue wait_event |
0x00c29a48 0x00cca194 31 W 0x00310570 0x30a3a0 <kmod_cmd_queue> |
kernel_stack=0x04d48000 |
stacktop=0x04d4bbe0 |
0x04d4bbe0 0xccab0c |
0x04d4bc40 0x342d8 <thread_invoke+1104> |
0x04d4bca0 0x344b4 <thread_block_reason+212> |
0x04d4bd00 0x334e0 <thread_sleep_fast_usimple_lock+56> |
0x04d4bd50 0x81ee0 <kmod_control+248> |
0x04d4bdb0 0x45f1c <_Xkmod_control+192> |
0x04d4be00 0x2aa70 <ipc_kobject_server+276> |
0x04d4be50 0x253e4 <mach_msg_overwrite_trap+2848> |
0x04d4bf20 0x257e0 <mach_msg_trap+28> |
0x04d4bf70 0x92078 <.L_syscall_return> |
0x04d4bfc0 0x10000000 |
stackbottom=0x04d4bfc0 |
task vm_map ipc_space #acts pid proc command |
0x00c1e4c0 0x00a79930 0x00c10c88 1 65 0x00d608c8 update |
activation thread pri state wait_queue wait_event |
0x00ddaa50 0x00ddbe34 31 W 0x00310780 0xd608c8 <rld_env+10471956> |
continuation=0x1da528 <_sleep_continue> |
Типичное число штабелей, показанных showallstacks
сталкивается с десятками. Большинство потоков, связанных с этими штабелями, спит, блокируется на продолжении (как то, что для второй задачи, показанной в вышеупомянутом примере). Штабели, такие как они можно обычно игнорировать. Остающиеся штабели являются значительными, потому что они отражают действие, продолжающееся в системе в определенный момент и контекст (как это происходит, когда паника NMI или ядра происходит).
Активации потока и штабели в ядре — включая те из драйверов — принадлежат названной задаче kernel_task
(под command
столбец). При отладке драйвера Вы смотрите в активных штабелях в kernel_task
для любой индикации относительно Вашего драйвера или его провайдера, клиента или любого другого объекта это связывается с. Если Вы добавите файлы символов для этих объектов драйвера перед началом сеанса отладки то индикация будет намного более ясной. Перечисление 7-6 показывает активный связанный с драйвером поток в kernel_task
в контексте смежных потоков.
Поток ядра перечисления 7-6 складывает как показано showallstacks
activation thread pri state wait_queue wait_event |
0x0101ac38 0x010957e4 80 UW 0x00311510 0x10b371c <rld_env+13953096> |
continuation=0x2227d0 <_ZN10IOWorkLoop22threadMainContinuationEv> |
activation thread pri state wait_queue wait_event |
0x0101aaf0 0x01095650 80 R |
stack_privilege=0x07950000 |
kernel_stack=0x07950000 |
stacktop=0x07953b90 |
0x07953b90 0xdf239e4 <com.apple.driver.AppleUSBProKeyboard + 0x19e4> |
0x07953be0 0xe546694 <com.apple.iokit.IOUSBFamily + 0x2694> |
0x07953c40 0xe5a84b4 <com.apple.driver.AppleUSBOHCI + 0x34b4> |
0x07953d00 0xe5a8640 <com.apple.driver.AppleUSBOHCI + 0x3640> |
0x07953d60 0xe5a93bc <com.apple.driver.AppleUSBOHCI + 0x43bc> |
0x07953df0 0x2239a8 <_ZN22IOInterruptEventSource12checkForWorkEv+18> |
0x07953e40 0x222864 <_ZN10IOWorkLoop10threadMainEv+104> |
0x07953e90 0x2227d0 <_ZN10IOWorkLoop22threadMainContinuationEv> |
stackbottom=0x07953e90 |
activation thread pri state wait_queue wait_event |
0x0101b530 0x0101c328 80 UW 0x00311500 0x10b605c <rld_env+13963656> |
continuation=0x2227d0 <_ZN10IOWorkLoop22threadMainContinuationEv> |
Можно использовать showallstacks
в отладке паники, зависает, и клинья. Например, это могло бы показать пару потоков, заведенных в тупик друг против друга, или это могло бы помочь идентифицировать поток, не обрабатывающий прерывания должным образом, таким образом вызывая зависание системы.
Другой общий метод с помощью макросов отладки ядра должен работать showallstacks
макрос и находит штабель или штабели, которые являются большей частью интереса. Тогда работайте switchtoact
макрос, давая ему адрес активации потока, для переключения на контекст того потока и его штабеля. Оттуда можно получить след, проверить кадры и переменные и т.д. Перечисление 7-7 показывает этот метод.
Перечисление 7-7 , Переключающееся для поточной обработки активацию и исследующий его
(gdb) switchtoact 0x00c29a48 |
(gdb) bt |
#0 0x00090448 in cswnovect () |
#1 0x0008f84c in switch_context (old=0xcca194, continuation=0, new=0xccab0c) at |
/SourceCache/xnu/xnu-327/osfmk/ppc/pcb.c:235 |
#2 0x000344b4 in thread_block_reason (continuation=0, reason=0) at |
/SourceCache/xnu/xnu-327/osfmk/kern/sched_prim.c:1629 |
#3 0x000334e0 in thread_sleep_fast_usimple_lock (event=0xeec500, lock=0x30a3ac, |
interruptible=213844) at /SourceCache/xnu/xnu-327/osfmk/kern/sched_prim.c:626 |
#4 0x00081ee0 in kmod_control (host_priv=0xeec500, id=4144, flavor=213844, |
data=0xc1202c, dataCount=0xc12048) at /SourceCache/xnu/xnu-327/osfmk/kern/kmod.c:602 |
#5 0x00045f1c in _Xkmod_control (InHeadP=0xc12010, OutHeadP=0xc12110) at |
mach/host_priv_server.c:958 |
#6 0x0002aa70 in ipc_kobject_server (request=0xc12000) at |
/SourceCache/xnu/xnu-327/osfmk/kern/ipc_kobject.c:309 |
#7 0x000253e4 in mach_msg_overwrite_trap (msg=0xf0080dd0, option=3, send_size=60, |
rcv_size=60, rcv_name=3843, timeout=12685100, notify=172953600, rcv_msg=0x0, |
scatter_list_size=0) at /SourceCache/xnu/xnu-327/osfmk/ipc/mach_msg.c:1601 |
#8 0x000257e0 in mach_msg_trap (msg=0xeec500, option=13410708, send_size=213844, |
rcv_size=4144, rcv_name=172953600, timeout=178377984, notify=256) at |
/SourceCache/xnu/xnu-327/osfmk/ipc/mach_msg.c:1853 |
#9 0x00092078 in .L_syscall_return () |
#10 0x10000000 in ?? () |
Cannot access memory at address 0xf0080d10 |
(gdb) f 4 |
#4 0x00081ee0 in kmod_control (host_priv=0xeec500, id=4144, flavor=213844, |
data=0xc1202c, dataCount=0xc12048) at /SourceCache/xnu/xnu-327/osfmk/kern/kmod.c:602 |
602 res = thread_sleep_simple_lock((event_t)&kmod_cmd_queue, |
(gdb) l |
597 simple_lock(&kmod_queue_lock); |
598 |
599 if (queue_empty(&kmod_cmd_queue)) { |
600 wait_result_t res; |
601 |
602 res = thread_sleep_simple_lock((event_t)&kmod_cmd_queue, |
603 &kmod_queue_lock, |
604 THREAD_ABORTSAFE); |
605 if (queue_empty(&kmod_cmd_queue)) { |
606 // we must have been interrupted! |
Помните что когда использование switchtoact
то, что Вы фактически изменили значение указателя вершины стека. Вы находитесь в различном контексте, чем прежде. Если Вы хотите возвратиться к прежнему контексту, используйте resetctx
макрос.
showallkmods
и showkmodaddr
макросы также полезны в отладке драйвера. Прежний макрос перечисляет все загруженные расширения ядра в формате, подобном kextstat
утилита командной строки (Перечисление 7-8 показывает несколько строк вывода). Если Вы даете showkmodaddr
макрос адрес «анонимного» кадра в штабеле, и если кадр принадлежит драйверу (или другое расширение ядра), макро-информация о печати о расширении ядра.
Вывод Listing 7-8 Sample от showallkmods
макрос
(gdb) showallkmods |
kmod address size id refs version name |
0x0ebc39f4 0x0eb7d000 0x00048000 71 0 3.2 com.apple.filesystems.afpfs |
0x0ea09480 0x0ea03000 0x00007000 70 0 2.1 com.apple.nke.asp_atp |
0x0e9e0c60 0x0e9d9000 0x00008000 69 0 3.0 com.apple.nke.asp_tcp |
0x0e22b13c 0x0e226000 0x00006000 68 0 1.2 com.apple.nke.IPFirewall |
0x0e225600 0x0e220000 0x00006000 67 0 1.2 com.apple.nke.SharedIP |
0x0df5d868 0x0df37000 0x00028000 62 0 1.2 com.apple.ATIRage128 |
0x0de96454 0x0de79000 0x0001e000 55 3 1.3 com.apple.iokit.IOAudioFamily |
... |
Подсказки относительно Использования gdb
Если Вы надеетесь стать опытными при отладке драйвера Набора I/O, необходимо будет стать опытными в использовании gdb
. Существует не обходящий это требование. Но даже если Вы уже знакомы с gdb
, можно всегда получать преимущества от понимания, собранного другими писателями драйвера от их опыта.
Исследование компьютерных инструкций
Если у Вас нет символов для двоичного файла драйвера — и даже если Вы делаете — необходимо попытаться исследовать компьютерные инструкции в памяти для получения подробного представления того, что продолжается в том двоичном файле. Вы используете gdb
команда x
исследовать память в текущем контексте; обычно, x
сопровождается наклонной чертой (“/”) и один - три параметра, один из которых i
. Параметры исследовать-памяти:
Повторный счет
Формат отображения:
s
(строка),x
(шестнадцатеричный), илиi
(компьютерная инструкция)Размер модуля:
b
(байт),h
(полуслово),w
(слово — четыре байта),g
(гигантское слово — восемь байтов)
Например, если Вы хотите исследовать 10 инструкций прежде и 10 инструкций после текущего контекста (как описано в Подсказках относительно Отладки Паники), Вы могли дать команду, такую как:
(gdb) x/20i $pc -40 |
Эта команда говорит, “показывают мне 20 инструкций, но стартовые 40 байтов” (4 байта за инструкцию) “перед текущим адресом в счетчике команд” ( $pc
переменная). Конечно, Вы могли быть менее тщательно продуманы и дать простую команду, такую как:
(gdb) x/10i 0x001c220c |
который показывает Вам 10 компьютерных инструкций, запускающихся в указанном адресе. Перечисление 7-9 показывает Вам типичный блок инструкций.
Вывод Listing 7-9 Typical gdb
“исследуйте память” команда
(gdb) x/20i $pc-40 |
0x8257c <kmod_control+124>: addi r3,r27,-19540 |
0x82580 <kmod_control+128>: bl 0x8d980 <get_cpu_data> |
0x82584 <kmod_control+132>: addi r0,r30,-19552 |
0x82588 <kmod_control+136>: lwz r31,-19552(r30) |
0x8258c <kmod_control+140>: cmpw r31,r0 |
0x82590 <kmod_control+144>: bne+ 0x825c0 <kmod_control+192> |
0x82594 <kmod_control+148>: mr r3,r31 |
0x82598 <kmod_control+152>: addi r4,r27,-19540 |
0x8259c <kmod_control+156>: li r5,2 |
0x825a0 <kmod_control+160>: bl 0x338a8 |
<thread_sleep_fast_usimple_lock> |
0x825a4 <kmod_control+164>: lwz r0,-19552(r30) |
0x825a8 <kmod_control+168>: cmpw r0,r31 |
0x825ac <kmod_control+172>: bne+ 0x825c0 <kmod_control+192> |
0x825b0 <kmod_control+176>: addi r3,r27,-19540 |
0x825b4 <kmod_control+180>: bl 0x8da00 <fast_usimple_lock+32> |
0x825b8 <kmod_control+184>: li r3,14 |
0x825bc <kmod_control+188>: b 0x82678 <kmod_control+376> |
0x825c0 <kmod_control+192>: lis r26,49 |
0x825c4 <kmod_control+196>: li r30,0 |
0x825c8 <kmod_control+200>: lwz r0,-19552(r26) |
Само собой разумеется, необходимо знать некоторый ассемблер для понимания вывода команды исследовать-памяти. Вы не должны быть экспертом в ассемблере, просто достаточно обоснованном для распознавания образцов. Например, это было бы выгодно, чтобы знать, как косвенность указателя с объектом смотрит в компьютерных инструкциях. С объектом существует две косвенности действительно, один для получения данных (и это могло быть нулем), и один к виртуальной таблице объекта (первое поле в объекте). Если то поле не указывает или на Ваш код или на код ядра, то существует что-то, что могло бы вызывать исключение нулевого указателя. Если Ваше ассемблерное знание ржаво или не существует, можно исследовать компьютерные инструкции на водительский код, который Вы знаете для звука. Путем знания, как «здоровый» код смотрит в ассемблере, Вы будете лучше подготовлены определить расхождения от образца.
Точки останова
Используя точки останова для отладки кода в ядре может быть расстраивающий опыт. Часто функции ядра вызываются так часто, что при помещении точки останова на функцию трудно определить, какой особый случай является тем с проблемой. Существует несколько вещей, которые можно сделать с точками останова для улучшения этого.
Условные точки прерывания. Условная точка прерывания говорит
gdb
инициировать точку останова, только если определенное выражение является истиной. Синтаксисcond
<устанавливают контрольные точки индекс> <выражение>. Примером является следующее:(gdb) cond 1 (num > 0)
Однако условные точки прерывания являются очень медленными в отладке с двумя машинами. Если Вы не ожидаете, что выражение точки останова будет оценено только пара дюжины времен или так, они, вероятно, слишком утомительны для доверия.
Совместные точки останова. К вещам скорости можно использовать две точки останова, сотрудничающие друг с другом. Одна точка останова является набором условной точки прерывания в критической, но часто вызываемой функции. Другая точка останова, которой присоединили список команд к нему, установлена в точке в коде, только достигнутом после того, как серия событий произошла. Вы первоначально отключаете условную точку прерывания, и вторая точка останова включает ее в некоторый момент позже, где контекст является более подходящим для проблемы, Вы занимаетесь расследованиями (и таким образом, Вы не возражаете против замедления вычисления выражения). Следующая серия
gdb
команды устанавливают обе точки останова:(gdb) cond 1 (num > 0)
(gdb) disable 1
(gdb) com 2
enable 1
continue
end
Если эта отладка - что-то, что Вы часто делаете, можно поместить команды установки точки останова в макрос и вставить тот макрос Ваш
.gdbinit
файл.Точки останова в фиктивных функциях. Можно использовать предыдущие два метода на коде, которым Вы не владеете. Однако, если это - Ваш код, который Вы отлаживаете, самый быстрый способ инициировать точку останова точно, когда и где Вы хотите, должен использовать фиктивные функции. Рассмотрите следование за разделенным фрагментом кода:
void dummy() {
;
}
void myFunction() {
// ...
if (num > 0)
dummy();
// ...
}
Выражение в
myFunction
то же точное выражение, которое Вы имели бы в условной точке прерывания. Просто установите точку останова на фиктивной функции. Когда точка останова инициирована, получите след, коммутируйте кадры, и Вы находитесь в желаемом контексте.
Единственное продвижение
Единственное продвижение через исходный код не обязательно берет Вас от одной строки до следующего. Можно возвратиться вокруг в исходном коде вполне немного, потому что компилятор делает различные вещи с символами для оптимизации их.
Существует две вещи, которые можно сделать для обхождения этого. Если это - Ваш код, Вы продвигаетесь через, можно выключить оптимизацию. Или Вы можете одноступенчатый через компьютерные инструкции в ассемблере, потому что одна строка исходного кода обычно генерирует несколько последовательных строк ассемблера. Так, если Вам трудно понять вещи единственным продвижением через источник, попробуйте единственное продвижение через ассемблер.
К одноступенчатому в gdb
, используйте stepi
команда (si
если коротко). Если Вы также используете, можно получить лучшее представление прогресса display
команда, как в этом примере:
(gdb) display/4i $pc |
Это выводит на экран счетчик команд и следующие три инструкции, как Вы продвигаетесь.
Отладка паники ядра
Вы могли бы быть знакомы с паникой ядра: те неожиданные события, наносящие вред системе, оставляя его абсолютно безразличным. Когда паника происходит на OS X, ядро распечатывает информацию о панике, которую можно проанализировать для нахождения причины паники. В системах перед Ягуаром эта информация появляется на экране как черно-белый текстовый дамп. Начиная с выпуска Ягуара паника ядра вызывает дисплей сообщения, сообщающего Вам, что проблема произошла и запросив перезапустить компьютер. После перезагрузки можно найти отладочную информацию о панике в файле panic.log
в /Library/Logs/
.
Если Вы никогда не видели его прежде, информация в panic.log
мог бы казаться загадочным. Перечисление 7-10 показывает типичную запись в паническом журнале.
Демонстрационная запись в журнале перечисления 7-10 для паники ядра
Unresolved kernel trap(cpu 0): 0x300 - Data access DAR=0x00000058 PC=0x0b4255b4 |
Latest crash info for cpu 0: |
Exception state (sv=0x0AD86A00) |
PC=0x0B4255B4; MSR=0x00009030; DAR=0x00000058; DSISR=0x40000000; LR=0x0B4255A0; |
R1=0x04DE3B50; XCP=0x0000000C (0x300 - Data access) |
Backtrace: |
0x0B4255A0 0x000BA9F8 0x001D41F8 0x001D411C 0x001D6B90 0x0003ACCC |
0x0008EC84 0x0003D69C 0x0003D4FC 0x000276E0 0x0009108C 0xFFFFFFFF |
Kernel loadable modules in backtrace (with dependencies): |
com.acme.driver.MyDriver(1.6)@0xb409000 |
Proceeding back via exception chain: |
Exception state (sv=0x0AD86A00) |
previously dumped as "Latest" state. skipping... |
Exception state (sv=0x0B2BBA00) |
PC=0x90015BC8; MSR=0x0200F030; DAR=0x012DA94C; DSISR=0x40000000; LR=0x902498DC; |
R1=0xBFFFE140; XCP=0x00000030 (0xC00 - System call) |
Kernel version: |
Darwin Kernel Version 6.0: |
Wed May 1 01:04:14 PDT 2002; root:xnu/xnu-282.obj~4/RELEASE_PPC |
Этот блок информации имеет несколько различных частей, каждого с его собственным значением для отладки проблемы.
Первая строка. Единственный самый важный бит информации о панике является первой строкой, кратко описывающей природу паники. В этом случае паника имеет некоторое отношение к исключению доступа к данным. Регистры, появляющиеся на той же строке как сообщение, являются теми с самыми подходящими данными; в этом случае они - DAR (Регистр Доступа к данным) и PC (Счетчик команд) регистры. Регистры, показанные на первой строке, варьируются согласно типу прерывания ядра. Шестнадцатеричный код перед описанием, определяющимся в
/xnu/osfmk/ppc_init.c
, указывает тип исключительной ситуации. Таблица 7-3 описывает возможные типы исключений.
Значение прерывания | Тип прерывания ядра |
---|---|
| Система сбрасывается |
| Компьютерная проверка |
| Доступ к данным |
| Доступ инструкции |
| Внешнее прерывание |
| Исключение выравнивания |
| Запрещенная команда |
Регистры. Когда паника произошла, под первым “Состоянием исключения” снимок содержания регистров CPU.
След. Каждый шестнадцатеричный адрес в следе указывает состояние реализации программы при определенном продвижении точки до паники. Поскольку каждый из этих адресов является фактически каждым функционального указателя возврата, необходимо вычесть четыре из этого адреса для наблюдения выполнявшейся инструкции.
Расширения ядра. Под “Загружаемыми модулями ядра в следе” идентификаторы пакета (
CFBundleIdentifier
свойство) всех расширений ядра, на которые ссылаются в следе и всех других расширений ядра, от которых эти расширения имеют зависимости. Это расширения ядра, для которых Вы, вероятно, захотите генерировать файлы символов до отладки паники.Другие состояния исключения. При “Продолжении назад через цепочку исключения”: предыдущее исключение, утверждает ядро, испытанное, разделенное снимками содержания регистров CPU в то время, когда произошли исключения. Большую часть времени первое состояние исключения (сразу после первой строки панического журнала) дает Вам достаточно информации для определения то, что вызвало панику. Иногда, однако, паника является результатом более раннего исключения, и можно исследовать цепочку исключений для получения дополнительной информации.
Версия ядра. Версия Дарвинского ядра и, что еще более важно, версия сборки
xnu
проект (базовая часть Дарвинского ядра). Если Вы отлаживаете с symboled ядром (как рекомендуется), необходимо получить или создать symboled ядро из этой версииxnu
.
Общая процедура
Существует много возможных способов отладить панику ядра, но следующий план действий оказался плодотворным на практике.
Получите как можно больше двоичных файлов с отладочной информацией.
Обратите внимание на все расширения ядра, перечисленные под “Загружаемыми модулями ядра в следе”. Если у Вас нет отладочной информации для некоторых из них, попробуйте к полученному symboled версию их или получите источник и создайте один с отладочной информацией. Это включало бы
mach_kernel
, семьи I/O Kit и другие KEXTs, которые являются частью установки по умолчанию. У Вас должна быть та же версия ядра и двоичных файлов KEXT, которые делает испуганный компьютер, или символы не выстроятся в линию правильно.Генерируйте и добавьте файлы символов для каждого расширения ядра в следе.
Как только Вы имеете двоичные файлы расширения ядра с (или без) отладочная информация, генерируете перемещенные файлы символов для каждого KEXT в следе. Использовать
kextload
с-s
и-n
опции сделать это; kextload предлагает Вам адрес загрузки каждого расширения ядра, которое можно получить от следа. Также можно указать-a
опция с-s
при использованииkextload
указать KEXTs и их адреса загрузки. Несмотря на то, что Вы не должны перемещать файлы символов для всех расширений ядра, можно только декодировать стековые фреймы в ядре или в KEXTs, для которого Вы сделали это. После выполненияgdb
наmach_kernel
(предпочтительно symboled), использоватьgdb
add-symbol-file
команда для каждого перемещаемые файлы символов Вы генерировали; посмотрите Установку для Отладки С двумя машинами для подробных данных.Декодируйте адреса в паническом журнале.
Запустите с регистра PC и возможно LR (Регистр Ссылки). (Содержание LR должно быть похожим на допустимый текстовый адрес, обычно немного меньший, чем адрес регистра PC.) Тогда обрабатывают каждый адрес в следе, не забывать вычесть четыре из каждого штабеля адресуется для выполнения последней инструкции в том кадре. Один возможный способ пойти об этом состоит в том, чтобы использовать пару
gdb
команды для каждого адреса:(gdb) x/i <address>-4
...
(gdb) info line *<address>-4
Вам нужна звездочка перед адресом в
info
команда, потому что Вы передаете необработанный адрес, а не символgdb
ожидает.x
команда, с другой стороны, ожидает необработанный адрес, таким образом, никакая звездочка не будет необходима.Перечисление 7-11 дает пример символьного следа, сгенерированного от
x/i <address>-4
. Вы будете знать, что успешно выполнились, когда все стековые фреймы декодируют к своего рода команде перехода в ассемблере.Интерпретируйте результаты.
Интерпретация результатов предыдущего шага является самой твердой фазой отладки паники, потому что это не механическое устройство в природе. Посмотрите следующий раздел, Подсказки относительно Отладки Паники, для некоторых предложений.
Пример перечисления 7-11 символьного следа
(gdb) x/i 0x001c2200-4 |
0x1c21fc <IOService::PMstop(void)+320>: bctrl |
0xa538260 <IODisplay::stop(IOService *)+36>: bctrl |
0x1bbc34 <IOService::actionStop(IOService *, IOService *)+160>: bctrl |
0x1ccda4 <runAction__10IOWorkLoopPFP8OSObjectPvn3_iPB2Pvn3+92>: bctrl |
0x1bc434 <IOService::terminateWorker(unsigned long)+1824>: bctrl |
0x1bb1f0 <IOService::terminatePhase1(unsigned long)+928>: bl |
0x1bb20c <IOService::scheduleTerminatePhase2(unsigned long)> |
0x1edfcc <IOADBController::powerStateWillChangeTo(unsigned long, unsigned long, IOService *)+88>: bctrl |
0x1c54e8 <IOService::inform(IOPMinformee *, bool)+204>: bctrl |
0x1c5118 <IOService::notifyAll(bool)+84>: bl |
0x1c541c <IOService::inform(IOPMinformee *, bool)> |
0x1c58b8 <IOService::parent_down_05(void)+36>: bl |
0x1c50c4 <IOService::notifyAll(bool)> |
0x1c8364 <IOService::allowCancelCommon(void)+356>: bl |
0x1c5894 <IOService::parent_down_05(void)> |
0x1c80a0 <IOService::serializedAllowPowerChange2(unsigned long)+84>: bl |
0x1c8200 <IOService::allowCancelCommon(void)> |
0x1ce198 <IOCommandGate::runAction(int (*)(OSObject *, void *, void *, void *, void *), void *, void *, void *, void *)+184>: bctrl |
0x1c802c <IOService::allowPowerChange(unsigned long)+72>: bctrl |
0xa52e6c ???? |
0x3dfe0 <_call_thread_continue+440>: bctrl |
0x333fc <thread_continue+144>: bctrl |
Подсказки относительно отладки паники
Следующие советы могли бы сделать отладку паники проще для Вас.
Как отмечено ранее, всегда обращайте внимание на первую строку панического сообщения и списка расширений ядра, вовлеченных в панику. Паническое сообщение дает главное представление о проблеме. С расширениями ядра генерируйте перемещенные файлы символов для сеанса отладки.
Не предполагайте, что паника не является Вашим водительским отказом просто, потому что это не обнаруживается в следе. Передача нулевого указателя семье I/O Kit или любой другой организации кода ядра вызовет панику в том коде. Поскольку ядро не имеет ресурсов для защиты себя от нулевых указателей, драйверы должны быть чрезвычайно бдительными против передачи их в.
showallstacks
макрос отладки ядра очень полезен для отладки паники. Посмотрите Используя Макросы Отладки Ядра для получения дополнительной информации.Использовать
gdb
$pc
переменная при отладке паники сgdb
.$pc
переменная содержит значение счетчика команд, идентифицирующего место, где было выдвинуто паническое возражение. Если Вы хотите исследовать контекст паники, Вы могли бы дать команду, такую как:(gdb) x/20i $pc -40
Это выводит на экран 10 инструкций в ассемблере прежде и после точки, где произошла паника. Если у Вас есть код соответствующего источника и символы, можно войти:
(gdb) l *$pc
Это показывает определенную строку кода, взявшего панику.
Если Вам вызвало панику Исключение Доступа Инструкции (
0x400
) и регистр PC является нулем, это означает, что что-то в ядре перешло для обнуления. Главный кадр в штабеле обычно является переходом через некоторый указатель функции, не инициализированный (или это так или иначе «топталось»).Паника вызывается Исключением Доступа к данным
0x300
довольно распространены. Эти типы паники обычно включают косвенность через нуль (другими словами, разыменованный нулевой указатель). Любое время нулевой указатель разыменовывается, паника, заканчивается. Когда Вы получите Исключение Доступа к данным, сначала проверьте регистр DAR; если значение является меньше, чем приблизительно 1 000, паника является, вероятно, результатом косвенности через нулевого указателя. Это вызвано тем, что большинство классов не больше, чем приблизительно 1 000 байтов и когда смещение добавляется к нулевому указателю, результат - приблизительно 1000 или меньше. Если результат намного больше, вероятно, что расположение указателя было повреждено (в противоположность его содержанию), и содержание неизвестного расположения используется в качестве указателя в Вашем коде.Нулевой указатель подразумевает возможность состояния состязания со значением завершения или завершением работы. Когда вызвано, подпрограмма завершения запускается к бесплатным ресурсам и если Ваш драйвер сошлется на эти ресурсы после того, как подпрограмму вызовут, то это, вероятно, вернет нулевых указателей.
Если Вы вкладываете панику
kalloc
,kmem
, илиIOMalloc
, это предполагает использование освобожденного указателя. Даже если Ваш код драйвера не обнаруживается в следе, Ваш драйвер мог бы быть преступником. Используя освобожденный указатель, вероятно, повредит внутренние структуры данных ядра для выделения.Паника может также быть вызвана путем случайного наброска на чьей-либо памяти. Эти «мины» может быть известно трудно отладить; след не показывает Вам очень, за исключением того, что что-то плохо произошло. Если паника восстанавливаема, однако, Вы имеете возможность разыскать незаконный код при помощи «тестового» макроса. Тестовый макрос помогает заключить в скобки точно, где в коде произошел набросок. По определению каракули являются байтом, не имеющим ожидаемого содержания (потому что они были изменены некоторым другим кодом). Путем знания, где паника произошла и где байт в последний раз содержал свое математическое ожидание, Вы знаете, где искать код наброска.
Часто имеет место, что Ваш драйвер является незаконным бумагомарателем. Для нахождения незаконного кода (если таковые имеются) определите тестовый макрос, тестирующий ли адрес памяти (
A
в примере ниже), имеет математическое ожидание (N
). (Обратите внимание на то, чтоA
адрес в регистре DAR исходной паники каракулей.), ЕслиA
не имеет математического ожидания, затем вызывает панику прямо тогда и там:Uint32 N = 123;
Uint32 *A;
A = &N;
// ...
#define PROBE() do {
if (A && *A != N)
*(Uint32)0 = 0;
} while (0)
Путем помещения тестового макроса в каждую функцию, где
A
иN
появитесь, можно сузить расположение каракулей. Если Ваш драйвер не является тем, делающим набросок, это все еще могло бы быть косвенно ответственно, потому что это могло заставлять другой код набрасывать. Например, Ваш драйвер мог бы просить, чтобы некоторый другой код записал в Вашем адресном пространстве; если это передало их неправильный адрес, это могло бы привести к данным, набрасываемым на внутренней части это.
Отладка зависаний системы
Зависания системы, после паники ядра, самое серьезное условие, вызванное плохо себя ведомым кодом ядра. Подвешенная система может не быть абсолютно безразличной, но это неприменимо, потому что Вы не можете эффективно щелкнуть кнопкой мыши или ввести ключ. Можно категоризировать зависания системы и их вероятную причину, поведением курсора мыши.
Курсор не вращается и не переместится. Этот признак указывает, что не поставляется основное прерывание. Мышь даже не вращается, потому что то поведение находится на основном прерывании; не вращение указывает, что система находится в очень жестком цикле. То, что, вероятно, произошло, - то, что объект драйвера отключил прерывание, заставив код где-нибудь в штабеле драйвера войти в бесконечный цикл. Другими словами, часть аппаратных средств повысила прерывание, но драйвер, который должен обработать его, не обрабатывает его и таким образом, аппаратные средства продолжают повышать его. Драйвер, вероятно, вызвал это «фантомное» прерывание, но не сознает, что это имеет и так не очищает его.
Вращения курсора, но не переместятся. Этот признак указывает, что вращается высокоприоритетный поток, такой как таймер.
Вращения курсора и перемещения, но ничто иное. Этот признак предполагает, что поток USB все еще планируется, таким образом указывая мертвую блокировку в некотором объекте драйвера, не связанном с USB или HI.
Для зависаний системы с первым признаком — курсор не вращается и не переместится — Ваша первая цель должна состоять в том, чтобы узнать то, что вызвало прерывание. Почему аппаратными средствами управляет Ваш драйвер, повышающий прерывание? Если Ваш драйвер использует источник события прерывания фильтра (IOFilterInterruptEventSource), Вы могли бы хотеть исследовать это, также. С источником события фильтра драйвер может проигнорировать прерывания, что он думает, не его ответственность.
С любым зависанием системы необходимо запуститься gdb
на ядре присоедините к подвешенной системе и работайте showallstacks
макрос. Отсканируйте вывод для потоков, заведенных в тупик друг против друга. Или, если это - необработанное основное прерывание, что Вы подозреваете, находите рабочий поток; если это - то, взявшее прерывание, это - вероятно, поток, это вошло в бесконечный цикл. Если драйвер находится в бесконечном цикле, можно установить точку останова в кадре штабеля потока, который является возможным преступником; когда Вы продолжаете и поражаете точку останова почти сразу, Вы знаете, что находитесь в бесконечном цикле. Вы можете одноступенчатый оттуда для нахождения проблемы.
Отладка драйверов начальной загрузки
OS X футболист BootX копирует драйверы для аппаратных средств, требуемых в процессе начальной загрузки в память для кода загрузки времени начальной загрузки ядра загружаться. Поскольку драйверы начальной загрузки уже загружаются к тому времени, когда система подходит, Вы не имеете столько контроля ими, сколько Вы делаете по драйверам неначальной загрузки. Кроме того, плохо себя ведущий драйвер начальной загрузки может заставить Вашу систему становиться неприменимой, пока Вы не в состоянии разгрузить его. По этим причинам методы отладки для драйверов начальной загрузки варьируются несколько от тех для других драйверов.
Самый важный шаг, который можно предпринять, должен обработать драйвер начальной загрузки как драйвер неначальной загрузки, в то время как Вы находитесь в фазу разработки. Удалите OSBundleRequired
свойство от Вашего водительского Info.plist
файл и использование методы, описанные в этой главе для проверки, драйвер выполняет все свои функции правильно, прежде чем Вы объявите, что он драйвер начальной загрузки.
После полного тестирования драйвера добавьте OSBundleRequired
свойство к Info.plist
(см., что документ Загружает Расширения ядра во Время начальной загрузки для определения, какое значение драйвер должен объявить). Это заставит футболиста BootX загружать Ваш драйвер в память во время процесса начальной загрузки.
Если Ваш драйвер начальной загрузки действительно имеет ошибки, Вы были неспособны найти прежде, Вы не можете использовать gdb
отладить его, потому что не возможно присоединить к компьютеру, в то время как это загружается. Вместо этого необходимо полагаться IOLog
вывод для обнаружения, что происходит. IOLog
синхронно, когда Вы выполняете многословную начальную загрузку, таким образом, можно использовать IOLog
операторы в течение Вашей начальной загрузки водительский код для разыскивания ошибок. Посмотрите Используя IOLog для получения дополнительной информации об этой функции.
Выполнять многословную начальную загрузку, удержание перезагрузки обоих Command
и V
ключи. Для получения еще большего количества подробности от Набора I/O можно установить a boot-args
флаг перед перезагрузкой. Принятие root
полномочия с sudo
команда, введите следование командной строки
%sudo nvram boot-args=”io=0xffff” |
Password: |
%shutdown -r now |
Несмотря на то, что этот метод производит пространный вывод, может быть трудно исследовать, потому что это прокручивает от экрана во время процесса начальной загрузки. Если Ваш драйвер начальной загрузки не препятствует тому, чтобы система завершила процесс начальной загрузки, можно просмотреть информацию полностью в системном журнале в /var/log/system.log
.
Журналирование
Журналирование, получение и дисплей информации в определенные моменты в коде, полезно в некоторых ситуациях как средство отладки, потому что это обеспечивает линейную запись того, что происходит во время выполнения кода драйвера. В этом разделе описываются основную функцию журналирования Набора I/O, IOLog
, и обсуждает пути, которыми можно пойти о создании собственного средства журналирования.
Используя IOLog
Естественно полагаться printf
операторы для отображения, что продолжается в коде приложения в течение фазы отладки. Для в драйверах ядра, Набор I/O, эквивалентный printf
оператор IOLog
функция, определенная в /System/Library/Frameworks/Kernel.framework/Headers/IOKit/IOLib.h
. К сожалению, IOLog
только незначительно полезно для отладки резидентных ядром драйверов.
Поскольку это выполняется в ядре, IOLog
обязательно скромно ресурсом. Если Вы используете, буфер сообщений не является большим и IOLog
для журналирования действия I/O, например, старые сообщения, вероятно, будут перезаписаны более новыми, стирая информацию, в которой Вы нуждаетесь для отладки. В целом Вы не можете успешно использовать IOLog
в любом жестком цикле в Вашем водительском коде, являются ли они циклами I/O или неумышленными циклами, вызванными ошибками в коде. Единственный способ гарантировать Вам получает всю информацию, в которой Вы нуждаетесь в такой ситуации, должен выделить Ваш собственный буфер и создать Ваше собственное средство журналирования для записи в него (для получения дополнительной информации об этом методе, посмотрите Пользовательскую Регистрацию событий).
Если Ваш драйвер вызывает панику, IOLog
не поможет многому в определении причины. Поскольку IOLog
когда компьютер паникует, последнее, функция является асинхронной IOLog
сообщение все еще ожидает, чтобы быть запланированным для вывода, таким образом, Вы пропустите сообщение, содержащее информацию о причине паники.
Вы не можете вызвать IOLog
от контекста прерывания. При создании объекта IOFilterInterruptEventSource Вы отправляете его, фильтр функционирует и функция действия. Вы не можете использовать IOLog
в Вашей подпрограмме фильтра, потому что это выполняет в основном (аппаратные средства) время прерывания, когда очень мало системы доступно. Можно, однако, вызвать IOLog
в Вашей подпрограмме действия, потому что это работает на работе, циклично выполняются, источник события присоединен. Обратите внимание на то, что при создании объекта IOInterruptEventSource Вы отправляете ему функцию действия, работающую на цикле работы источника события, но никакой функции фильтра. Как с действием функционируют для объекта IOFilterInterruptEventSource, можно вызвать IOLog
в действии функционируют для объекта IOInterruptEventSource.
Несмотря на то, что IOLog
функция не является подходящей для использования в процессе отладки, это полезно для журналирования нормального состояния и сообщений об ошибках. Например, класс PhantomAudioDevice (содержавшийся в проекте PhantomAudioDriver в /Developer/Examples/Kernel/IOKit/Audio/PhantomAudioDriver
) использование IOLog
зарегистрировать состояние в нескольких из его методов. Перечисление 7-12 показывает частичные списки PhantomAudioDevice createAudioEngines
и volumeChanged
методы.
Перечисление 7-12 Используя IOLog
в методах PhantomAudioDevice
bool PhantomAudioDevice::createAudioEngines() |
{ |
... |
IOLog("PhantomAudioDevice[%p]::createAudioEngines()\n", this); |
audioEngineArray = OSDynamicCast(OSArray, |
getProperty(AUDIO_ENGINES_KEY)); |
if (audioEngineArray) { |
/* Create new audio engine with AudioEngine array in personality. */ |
... |
} |
else { |
IOLog("PhantomAudioDevice[%p]::createAudioEngines() - Error: no |
AudioEngine array in personality.\n", this); |
goto Done; |
} |
... |
} |
IOReturn PhantomAudioDevice::volumeChanged(IOAudioControl *volumeControl, |
SInt32 oldValue, SInt32 newValue) |
{ |
IOLog("PhantomAudioDevice[%p]::volumeChanged(%p, %ld, %ld)\n", this, |
volumeControl, oldValue, newValue); |
... |
} |
Пользовательская регистрация событий
Можно наперчить код IOLog
или printf
вызовы как метод отладки, но, как отмечено в предыдущем разделе (Используя IOLog), этот подход имеет свои ограничения. Некоторые драйверы особенно чувствительны к любому влиянию на производительность и требуют более надежных и данных регистрации с более прекрасными зернами. Если Ваш драйвер попадает в эту категорию, можно создать собственное средство журналирования.
Существуют различные способы реализовать пользовательское регистрирующее отладку решение, избегающее недостатков IOLog
. Необходимо было бы выделить собственный буфер для Вас записи в журнале, функции реализации и макросы для записи тех записей в буфер, и разработать некоторые средние значения для исследования содержания буфера.
В целях иллюстрации этот раздел смотрит на функцию журналирования драйвера AppleGMACEthernet. Можно счесть исходный код для этого драйвера (включая инструменты журналирования) в Дарвинском исходном коде для версии 10.2.7 OS X и выше, доступным в http://www .opensource.apple.com/darwinsource/index.html. Для нахождения исходного кода выберите версию OS X, равного или больше, чем v10.2.7, и нажмите Source (выберите источник для версии PPC, если существует выбор). Это выводит на экран новую страницу, перечисляющую проекты с открытым исходным кодом, доступные для версии OS X, который Вы выбрали. Прокрутите вниз к AppleGMACEthernet и щелкните по нему для просмотра источника. Будьте подготовлены предоставить свое имя элемента ADC и пароль.
Драйвер AppleGMACEthernet реализует инфраструктуру журналирования резидентного объекта ядра, а также инструмент пространства пользователя для печати отформатированного содержания буфера журнала. Инструмент, используемый этим драйвером, распечатывает отчет к стандартному выводу, начинающемуся с информации заголовка, сопровождаемой записями в журнале с меткой времени. Можно перенаправить вывод к файлу и затем добавить комментарии к записям в файле. Перечисление 7-13 показывает первые несколько секунд водительской жизни (вместе с комментариями).
Вывод Listing 7-13 Sample пользовательского журнала событий
0 1376a020 13779fe0 1376a020 [ffffffe0] |
27 38e9000 0 1 |
20: 39 291159 0 0 eNet |
30: 39 291266 19aec90 181c200 Strt |
40: 39 293857 19aec90 18a4000 KeyL |
50: 39 293899 100 100 =txq |
60: 39 293903 100 100 =txe |
70: 39 293907 40 40 =rxe |
80: 39 293908 100 1000040 parm |
90: 39 307103 1000 135f0000 =TxR |
a0: 39 307111 400 198b000 =RxR |
b0: 39 307119 17f4400 19c8f00 arys |
c0: 39 307120 19aec90 0 AttI |
d0: 39 307232 19aec90 0 RwPM |
e0: 39 307247 19aec90 2 mx4d |
f0: 39 307249 19aec90 2 ip4d |
100: 39 307316 19aec90 1 Pwr! |
110: 39 307436 19aec90 198e400 cfig |
120: 39 307465 19aec90 0 AttD |
130: 39 307598 19aec90 0 powr |
140: 39 307599 19aec90 0 RegS |
150: 39 310366 19aec90 0 Exit end of start method |
160: 46 944895 191d400 198e400 NetE |
170: 46 944899 191d400 0 Wake wake up ethernet cell |
180: 46 944938 0 0 +Clk |
190: 46 945093 199a5c0 1ee21000 Adrs |
1a0: 46 945096 19c8f00 198b000 IRxR |
1b0: 46 946111 191d400 135f0000 ITxR |
1c0: 46 946127 3 1010 wReg |
1d0: 46 946135 0 ff ChpI chip initialization |
1e0: 46 946136 4 9050 |
Заголовок отчета состоит из двух строк. Первая строка идентифицирует любой флаг в действительности и дает запуск, окончание и текущие адреса буфера журнала. Вторая строка указывает период (в секундах) заполненный записями в журнале, показывает физический адрес буфера и соответствует числам предупреждений и буферных переносов, так как драйвер запустил.
Каждая запись в журнале идентифицируется смещением (в шестнадцатеричном) от запуска буфера и метки времени с двумя частями. Первая часть является временем в секундах, так как драйвер начал выполняться, и вторым является время в микросекундах (фактически наносекунды, смещенные на право на 10 для близкого приближения микросекунд). Последний столбец содержит четыре кодов символов со специальным значением для драйвера AppleGMACEthernet. Промежуточные параметры шоу на два столбца, значение которых зависит от контекста, обозначенного кодом.
Драйвер AppleGMACEthernet (в классе UniEnet) реализации, входящие в систему путь, минимизирующий его эффект на производительность. Существенные шаги, которые это предпринимает, являются следующим:
Это выделяет 64-килобайтный всеобъемлющий буфер для содержания данных регистрации и определяет глобальные указатели на тот буфер.
Это реализует две низкоуровневых функции для журналирования событий к буферу, один для общих событий и другого для предупреждений; предупреждения являются надмножествами событий, сначала выполняющих журнал, тогда
IOLog
, и затем независимо от того, что Вы настраиваете его, чтобы сделать.Для удобства это также определяет макросы, основывался на этих функциях.
Это вставляет макрос, заходит в критические точки в коде.
Это реализует инструмент пространства пользователя, чтобы отформатировать и распечатать содержание буфера журнала.
Это реализует пользовательский клиент для отправки буферных данных в инструмент.
Перечисление 7-14 показывает определения журнала событий и предупредительных функций и макросов.
Определение перечисления 7-14 журналирования макросов и функций
#if USE_ELG /* (( */ |
#define ELG(A,B,ASCI,STRING) EvLog( (UInt32)(A), (UInt32)(B), |
(UInt32)(ASCI), STRING ) |
#define ALERT(A,B,ASCI,STRING) Alert( (UInt32)(A), (UInt32)(B), |
(UInt32)(ASCI), STRING ) |
void EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str ); |
void Alert( UInt32 a, UInt32 b, UInt32 ascii, char* str ); |
#else /* ) not USE_ELG: ( */ |
#define ELG(A,B,ASCI,S) |
#define ALERT(A,B,ASCI,STRING) IOLog( "UniNEnet: %8x %8x " STRING "\n", |
(unsigned int)(A), (unsigned int)(B) ) |
#endif /* USE_ELG )) */ |
Если Вам любопытно, посмотрите источник класса UniEnet для реализаций EvLog
и Alert
.
Драйвер AppleGMACEthernet вызывает ELG
и ALERT
макросы в надлежащих точках в исходном коде. В примере в Перечислении 7-15, ELG
макрос вызывают сразу после configureInterface
функция членства вызывается; код события ‘cfig’
и параметры являются адресами текущего потока, и объект IONetworkInterface передал в. (Заключительный строковый параметр распечатан на предупреждениях и через IOLog
если отключена пользовательская функция отладки.)
Перечисление 7-15 Вызывая макрос журналирования
bool UniNEnet::configureInterface( IONetworkInterface *netif ) { |
IONetworkData *nd; |
ELG( IOThreadSelf(), netif, 'cfig', "configureInterface" ); |
// ... |
Отчет, показанный в Перечислении 7-13, включает в себя запись в журнале от этого макро-вызова, иллюстрируя, как это могло бы появиться (это - шестнадцатое).
Несмотря на то, что могло бы быть удобно иметь инструмент, приятно распечатывающий содержание буфера журнала, существует продолжительное время и усилие, вовлеченное в разработку этого инструмента и пользовательского объекта клиента, который должен подать его данные регистрации. Вместо инструмента пространства пользователя, можно исследовать содержание буфера журнала в gdb
. Содержание буфера, однако, потребует, чтобы больше усилия с Вашей стороны интерпретировало.