Отладочные программы

Самая твердая часть отладки кодирует — особенно, код ядра — не изучает, как использовать инструменты, но как проанализировать данные, сгенерированные инструментами. Успех, который Вы имеете со своим анализом, зависит в значительной степени от навыков и опыта, который Вы приносите к нему, и они могут всегда улучшаться путем изучения от других, “заработавших их дорожки” отладка кода ядра.

Эта глава предлагает набор отлаживающих драйвер подсказок и методов от инженеров 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 представляет некоторые более важные инструменты.

Табличная 7-1  Отладка помогает для расширений ядра

Инструмент или API

Описание

gdb

Отладчик GNU, используемый в отладке ядра с двумя машинами. Посмотрите Подсказки относительно Использования gdb для небольшого количества полезной информации о gdb.

Макросы отладки ядра

Мощные макросы, разработанные для использования в gdb при отладке Дарвинского ядра. Макросы содержатся в файле .gdbinit в расположении каталога Open Source /xnu/osfmk/. Посмотрите Используя Макросы Отладки Ядра для получения дополнительной информации.

kextload

Утилита, делающая множество вещей с расширениями ядра. Это загружает расширения ядра и, до загрузки, проверяет расширения, предоставляя диагностическую информацию, связанную с ошибками зависимости и разрешением файла. Это также включает отладку расширений ядра (использование gdb) во время соответствия и загрузки фаз. Посмотрите Загрузку Вашего Драйвера для больше на kextload опции.

kextstat

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

ioreg

Обеспечивает снимок Реестра I/O, показывая иерархическую структуру отношений клиентского провайдера среди формирователей тока и кусков в системе. С необходимыми опциями это показывает свойства, связанные с каждым узлом в реестре. Проводник Реестра I/O показывает ту же информацию в графическом интерфейсе. И инструмент и приложение особенно полезны для отладки проблем с соответствием. Посмотрите, что Отладка Соответствует проблемы.

ioalloccount ioclasscount

Прежний инструмент выводит на экран сводку выделения памяти типом средства выделения (экземпляр, контейнер, и IOMalloc). Последний инструмент показывает число экземпляров, выделенных для каждого указанного класса. Оба инструмента полезны для разыскивания утечек памяти. Посмотрите Разгрузку Вашего Драйвера для получения дополнительной информации.

IOKitDebug.h

Определяет ряд значений для свойства 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. Это обычно - компьютер, на котором Ваш драйвер разрабатывается — следовательно, это также упоминается как компьютер разработки. Целевой компьютер является системой, в которой выполняется драйвер быть отлаженным. Ваш узел и целевые компьютеры должны выполнять ту же версию Дарвинского ядра, или максимально близко к той же версии. (Конечно, при отладке склонной к панике версии ядра Вы захотите, чтобы главный компьютер выполнил новую стабильную версию Дарвина.) Для оптимальной отладки на уровне исходного кода главный компьютер должен иметь исходный код драйвера, любые расширения ядра, связанные с Вашим драйвером (такие как его клиент или провайдер), и возможно даже само ядро (/xnu).

Для отладки с двумя машинами, чтобы быть выполнимым, следующее должно быть истиной:

  • Для версий OS X перед версией 10.2 OS X оба компьютера должны быть на той же подсети.

  • У Вас должен быть доступ входа в систему к обоим компьютерам как администратор (группа admin), потому что Вам будут нужны полномочия пользователя root загрузить Ваш KEXT (можно использовать sudo команда).

  • Необходимо быть в состоянии скопировать файлы между компьютерами с помощью FTP, scp (SSH), rsync, AFP, или подобный протокол или инструмент.

Когда все это будет существовать, завершите следующие шаги:

  1. Target установил NVRAM debug переменная к 0x144, то, которое позволяет Вам заскочить в отладчик после немаскируемого прерывания (NMI) и, если Вы выполняете OS X v. 10.2 или позже, позволяет Вам отладить два компьютера не на той же подсети. Можно использовать setenv для установки флага в Открывают сам Firmware (на основанных на PowerPC компьютерах Macintosh), или можно использовать nvram утилита. Для последнего введите следующий как корень в командной строке:

    1. nvram boot-args="debug=0x144"

    Это - хорошая идея войти nvram boot-args (никакой параметр) сначала для получения любых текущих переменных NVRAM в действительности; тогда включайте эти переменные вместе с флагом отладки, когда Вы дадите nvram boot-args управляйте во второй раз. Перезагрузите систему.

  2. Узел или Копия Target драйвер (или любое другое расширение ядра) к рабочему каталогу на целевом компьютере.

  3. Узел Установил постоянное сетевое соединение с целевым компьютером через 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 управляйте во второй раз для проверки этого.

  4. Target Создает файлы символов для драйвера и любых других расширений ядра, от которых это зависит. Сначала создайте каталог для содержания символов; тогда работайте kextload инструмент командной строки, указывая каталог как параметр -s опция:

    $ kextload -l -s /tmp/symbols /tmp/MyDriver.kext

    Эта команда загрузки MyDriver.kext но, из-за -l опция, еще не запускает процесс соответствия (который происходит на более позднем шаге). Если Вы не хотите, чтобы драйвер загрузился просто все же, укажите -n опция вместе с -s опция. Посмотрите Используя kextload, kextunload, и kextstat для kextload процедура для отладки водительского кода запуска.

  5. Target или Копия Узла файлы символов к главному компьютеру.

  6. Разместите Дополнительно, если Вы хотите отладить свой драйвер с доступом ко всем символам в ядре, получить или создать symboled ядро. Для получения дополнительной информации свяжитесь с Технической поддержкой Разработчика Apple. Можно найти инструкции для создания Дарвинского ядра от Открытого исходного кода в Здании и Отладке Ядер в Руководстве по программированию Ядра.

  7. Выполнение узла gdb на ядре.

    $ gdb /mach_kernel

    Если у Вас есть symboled ядро, укажите путь к нему, а не /mach_kernel. Важно, чтобы Вы работали gdb на ядре той же версии и сборки как та, работающая на целевом компьютере. Если версии отличаются, необходимо получить symboled копию ядра и использования цели это.

  8. Узел в gdb, добавьте файл символов своего драйвера.

    (gdb) add-symbol-file /tmp/symbols/com.acme.driver.MyDriver.sym

    Добавьте файлы символов других расширений ядра в Вашей водительской цепочке зависимости.

  9. Узел говорит gdb то, что Вы будете отлаживать удаленно.

    (gdb) target remote-kdp
  10. Target Врывается в режим отладки ядра. В зависимости от модели Вашей целевой системы или выпустите надлежащую команду клавиатуры или нажмите кнопку программиста. На клавиатурах USB удержите Командную клавишу и кнопку питания; на клавиатурах ADB удержите Клавишу CTRL и кнопку питания. Если Вы выполняете версию 10.4 OS X или позже, удерживаете следующие пять клавиш: Команда, Опция, Управление, Сдвиг и Escape.

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

  11. Присоединение узла к целевому компьютеру и точкам останова набора.

    (gdb) attach target.goober.com
    (gdb) break 'MyDriverClass::WriteData(* char)'
    (gdb) continue

    Убедитесь, что Вы даете continue команда; иначе целевой компьютер безразличен.

  12. Target Запускает выполнение драйвера.

    $ kextload -m -t /tmp/MyDriver.kext

    -m опция запускает процесс соответствия для драйвера. -t говорящая опция, kextload для осуществления обширных проверок проверки, является действительно дополнительным здесь; идеально, Ваш драйвер должен был передать эти проверки во время более раннего этапа отладки (см. Используя kextload, kextunload, и kextstat). После запуска драйвера выполните действия, необходимые для инициирования точки останова.

  13. Узел, Когда точка останова Вы устанавливаете, инициирован, можно начать отлаживать использование драйвера 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 файл Вы получили из репозитория С открытым исходным кодом.), Поскольку макросы отладки ядра могут измениться между версиями ядра, удостоверьтесь, что Вы используете макросы, которые соответствуют максимально близко версию ядра, которое Вы отлаживаете.

Табличные 7-2  макросы отладки Ядра

Макрос

Описание

showalltasks

Выводит на экран сводное перечисление задач

showallacts

Выводит на экран сводное перечисление всех активаций

showallstacks

Выводит на экран штабели ядра для всех активаций

showallvm

Выводит на экран сводное перечисление всех карт VM

showallvme

Выводит на экран сводное перечисление всех записей карты VM

showallipc

Выводит на экран сводное перечисление всех пробелов IPC

showallrights

Выводит на экран сводное перечисление всех прав IPC

showallkmods

Выводит на экран сводное перечисление всех двоичных файлов расширения ядра

showtask

Состояние дисплеев указанной задачи

showtaskacts

Выводит на экран состояние всех активаций в задаче

showtaskstacks

Дисплеи все ядро складывают для всех активаций в задаче

showtaskvm

Состояние дисплеев карты VM указанной задачи

showtaskvme

Выводит на экран сводный список записей карты VM задачи

showtaskipc

Состояние дисплеев пространства IPC указанной задачи

showtaskrights

Выводит на экран сводный список записей пространства IPC задачи

showact

Состояние дисплеев указанной активации потока

showactstack

Выводит на экран штабель ядра для указанной активации

showmap

Выводит на экран состояние указанной карты VM

showmapvme

Выводит на экран сводный список записей указанной карты VM

showipc

Выводит на экран состояние указанного пространства IPC

showrights

Выводит на экран сводный список всех прав в пространстве IPC

showpid

Выводит на экран состояние процесса, идентифицированного PID

showproc

Выводит на экран состояние процесса, идентифицированного proc указателем

showkmod

Информация о дисплеях о двоичном файле расширения ядра

showkmodaddr

Учитывая адрес, выводит на экран двоичный файл расширения ядра и смещение

zprint

Информация о зоне дисплеев

paniclog

Выводит на экран паническую информацию о журнале

switchtoact

Контекст потока переключателя

switchtoctx

Контекст переключателя

resetctx

Контекст сброса

Подмножество макросов отладки ядра особенно полезно для писателей драйвера: 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 описывает возможные типы исключений.

Табличные 7-3  Типы исключений ядра

Значение прерывания

Тип прерывания ядра

0x100

Система сбрасывается

0x200

Компьютерная проверка

0x300

Доступ к данным

0x400

Доступ инструкции

0x500

Внешнее прерывание

0x600

Исключение выравнивания

0x700

Запрещенная команда

  • Регистры. Когда паника произошла, под первым “Состоянием исключения” снимок содержания регистров CPU.

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

  • Расширения ядра. Под “Загружаемыми модулями ядра в следе” идентификаторы пакета (CFBundleIdentifier свойство) всех расширений ядра, на которые ссылаются в следе и всех других расширений ядра, от которых эти расширения имеют зависимости. Это расширения ядра, для которых Вы, вероятно, захотите генерировать файлы символов до отладки паники.

  • Другие состояния исключения. При “Продолжении назад через цепочку исключения”: предыдущее исключение, утверждает ядро, испытанное, разделенное снимками содержания регистров CPU в то время, когда произошли исключения. Большую часть времени первое состояние исключения (сразу после первой строки панического журнала) дает Вам достаточно информации для определения то, что вызвало панику. Иногда, однако, паника является результатом более раннего исключения, и можно исследовать цепочку исключений для получения дополнительной информации.

  • Версия ядра. Версия Дарвинского ядра и, что еще более важно, версия сборки xnu проект (базовая часть Дарвинского ядра). Если Вы отлаживаете с symboled ядром (как рекомендуется), необходимо получить или создать symboled ядро из этой версии xnu.

Общая процедура

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

  1. Получите как можно больше двоичных файлов с отладочной информацией.

    Обратите внимание на все расширения ядра, перечисленные под “Загружаемыми модулями ядра в следе”. Если у Вас нет отладочной информации для некоторых из них, попробуйте к полученному symboled версию их или получите источник и создайте один с отладочной информацией. Это включало бы mach_kernel, семьи I/O Kit и другие KEXTs, которые являются частью установки по умолчанию. У Вас должна быть та же версия ядра и двоичных файлов KEXT, которые делает испуганный компьютер, или символы не выстроятся в линию правильно.

  2. Генерируйте и добавьте файлы символов для каждого расширения ядра в следе.

    Как только Вы имеете двоичные файлы расширения ядра с (или без) отладочная информация, генерируете перемещенные файлы символов для каждого KEXT в следе. Использовать kextload с -s и -n опции сделать это; kextload предлагает Вам адрес загрузки каждого расширения ядра, которое можно получить от следа. Также можно указать -a опция с -s при использовании kextload указать KEXTs и их адреса загрузки. Несмотря на то, что Вы не должны перемещать файлы символов для всех расширений ядра, можно только декодировать стековые фреймы в ядре или в KEXTs, для которого Вы сделали это. После выполнения gdb на mach_kernel (предпочтительно symboled), использовать gdb add-symbol-file команда для каждого перемещаемые файлы символов Вы генерировали; посмотрите Установку для Отладки С двумя машинами для подробных данных.

  3. Декодируйте адреса в паническом журнале.

    Запустите с регистра PC и возможно LR (Регистр Ссылки). (Содержание LR должно быть похожим на допустимый текстовый адрес, обычно немного меньший, чем адрес регистра PC.) Тогда обрабатывают каждый адрес в следе, не забывать вычесть четыре из каждого штабеля адресуется для выполнения последней инструкции в том кадре. Один возможный способ пойти об этом состоит в том, чтобы использовать пару gdb команды для каждого адреса:

    (gdb) x/i <address>-4
    ...
    (gdb) info line *<address>-4

    Вам нужна звездочка перед адресом в info команда, потому что Вы передаете необработанный адрес, а не символ gdb ожидает. x команда, с другой стороны, ожидает необработанный адрес, таким образом, никакая звездочка не будет необходима.

    Перечисление 7-11 дает пример символьного следа, сгенерированного от x/i <address>-4. Вы будете знать, что успешно выполнились, когда все стековые фреймы декодируют к своего рода команде перехода в ассемблере.

  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. Содержание буфера, однако, потребует, чтобы больше усилия с Вашей стороны интерпретировало.