LXR: Навигация по исходникам

Дмитрий Кузнецов не страшится отправиться в путешествие даже по коду ядра Linux. А ведь в нем миллионы строк!

Анализ творений мастеров всегда был одним из лучших методов обучения любому ремеслу или искусству. Для программирования он особенно эффективен благодаря доступности миллионов проектов с открытым исходным кодом. Но разбираться в них непросто. Кроме главной проблемы — понять проектные решения автора — наваливается лавина рутинных задач поиска и навигации по исходному коду. Они доставляют неудобства даже в небольших проектах, а когда речь заходит о громадинах в несколько миллионов строк кода, неудобства превращаются почти в катастрофу. В таких случаях уже не отбиться: нужно привлекать «тяжелую артиллерию». Наш урок посвящен одному такому «орудию» — Linux Cross Referencer (LXR).

LXR с высоты 10 000 метров

Принцип работы LXR прост. Сначала пользователь с помощью специальных автоматизирующих скриптов конфигурирует систему. На этом этапе от него требуется предоставить исходный код изучаемых проектов и задать конфигурационные параметры. Система анализирует полученную информацию и сохраняет ее в базе данных (БД). После этого LXR готов обрабатывать навигационные запросы пользователя. Для реализации этого взаимодействия разработчики не стали изобретать ничего нового, а мудро решили воспользоваться протоколом HTTP. Кроме того, что эта технология знакома большинству разработчиков, а потому делает LXR более понятной и легкой для изучения, она дает еще два огромных плюса. Во-первых, с системой можно работать удаленно. Для демонстрации этого на сайте проекта организована удаленная навигация по исходному коду ядра Linux (http://lxr.linux.no/linux). А во-вторых, в качестве пользовательского интерфейса (GUI) можно использовать любой web-браузер. Таким образом, для пользователя навигация по исходному коду сводится к просмотру web-страниц и переходам по гиперссылкам.

Архитектура LXR

Теперь попробуем разобраться во внутреннем устройстве. Главное, что сразу стоит отметить: LXR — типичный проект в стиле *nix. Это значит, что если для некой задачи уже есть готовое решение, то оно и используется, а не изобретается очередной «велосипед». Как ни удивительно, LXR ничего не делает сам: весь его код состоит всего лишь из нескольких десятков Perl-скриптов, применяющих другое ПО для решения своих задач. Архитектура LXR распадается на две подсистемы:

» Подсистема конфигурации Ее задачи — проанализировать предоставленный исходный код, сохранить результаты в БД и настроить систему так, чтобы обслуживание навигационных запросов работало быстро и корректно. Для пользователя работа с ней сводится к вызову соответствующих скриптов с нужными параметрами или исправлению конфигурационных файлов вручную.

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

Понимание архитектуры делает очевидным список вспомогательного ПО:

» Любой web-браузер для просмотра исходного кода и навигации по нему.

» Web-сервер для взаимодействия web-браузера с LXR. Разработчики ориентировались в первую очередь на Apache, но также поддерживается lighttpd. Возможны и другие варианты, однако проблемы с настройками придется решать самостоятельно. » СУБД для хранения результатов анализа исходного кода. Здесь выбор велик: MySQL, PostgeSQL, SQLite, Oracle. Но следует учитывать, что SQLite будет адекватным выбором только для небольших проектов. При серьезных объемах кода она сильно отстает по производительности.

» Exuberant ctags для анализа исходного кода и сбора информации об именах и позициях встречающихся в нем переменных, констант, функций и т. д.

» Glimpse или swish-e для индексации исходного кода и поиска по нему. LXR может использовать любую из этих программ. Тем не менее, нужно иметь в виду, что они немного различаются в деталях. Glimpse более точна при поиске, но и более медлительна на больших объемах текста. Она ищет с точностью до номера строки, тогда как swish-e — только до файла. Кроме того, glimpse бесплатна только для некоммерческого использования.

» Интерпретатор Perl версии 5.10 или выше c модулями File::MMagic, DBI и соответствующим образом выбранной СУБД с DBD-драйвером.

Анатомия конфигурации

Глупо отправляться в путь, не зная конечного пункта маршрута. Поэтому, прежде чем пускаться в рассуждения о процессе конфигурирования, сосредоточим внимание на его цели. Из чего состоит правильная конфигурация? Понимая архитектуру LXR, предположить состав конфигурации нетрудно:

» Глобальный конфигурационный файл (lxr.conf).

» Дополнительный конфигурационный файл для web-сервера.

» БД в одной из поддерживаемых СУБД, заполненная результатами анализа исходного кода.

» Результаты индексации исходного кода с помощью glimpse или swish-e.

Файл lxr.conf иногда приходится редактировать вручную, поэтому его структуру стоит рассмотреть более детально. Она довольно проста и имеет следующий вид:

(

{блок глобальных параметров},

{блок параметров для проекта 1},

{блок параметров для проекта N}

)

Каждый блок параметров — список разделенных запятыми элементов вида ‘имя’ => ‘значение’. Любители Perl без труда узнают в lxr.conf знакомый синтаксис. Действительно, это просто фрагмент кода на Perl, в котором определяется огромная структура данных — массив хэшей. Такое решение избавляет разработчиков LXR от необходимости разбора lxr.conf. Достаточно просто включить его в другие Perl-скрипты, чтобы все конфигурационные параметры стали доступны.

Создание конфигурации

Это самая сложная операция конфигурирования, поэтому для ее автоматизации предусмотрен специальный скрипт — ./scripts/ configure-lxr.pl. Вопреки всем опасениям, параметров у него не так много. Большинство из них определяют расположение результатов работы. По умолчанию, если параметры не указаны, для этой цели будет создан каталог ./custom.d. Тем не менее, очевидно, что для нормальной работы скрипта дополнительная информация все же необходима. Варианты использования LXR настолько разнообразны, что только человек может дать разумные ответы на некоторые вопросы. К счастью, разработчики тоже так считают, поэтому configure-lxr.pl не делает нелепых предположений о пожеланиях пользователя, а первым делом предлагает ему ответить на несколько десятков вопросов. Какую СУБД использовать? Какие имя, логин и пароль дать БД? Где лежат исходные коды добавляемого проекта и сколько у него версий? Вот только некоторые из них. Такой допрос может показаться довольно суровым, зато в итоге получается практически готовая конфигурация, а использование параметра — verbose и наличие ответов по умолчанию для большинства вопросов превращает его почти в приятную беседу.

Итак, что же получается в результате?

» ./custom.d/lxr.conf, содержащий два блока параметров (глобальных и для первого проекта). Его нужно скопировать в каталог LXR.

» Конфигурационный файл для web-сервера. Например, для Apache это будет ./custom.d/apache-lxrserver.conf. Его нужно скопировать в каталог, где лежат все конфигурационные файлы web-сервера. В Ubuntu 12.04 это будет ./etc/apache2/conf.d.

» Скрипт для создания БД — ./custom.d/initdb.sh. Чтобы он сделал свою часть работы, его нужно просто запустить без параметров. С этим скриптом связан один неприятный момент, незнание которого может привести к потере данных. Дело в том, что initdb. sh создается каждый раз при запуске configure-lxr.pl. Но при этом он не замещает старую версию, а объединяется с ней. Это значит, что при запуске будут не только создаваться новые БД, но и пересоздаваться старые. А если там были какие-то данные, это приведет к их потере. Поэтому нужно не забывать вовремя удалять старые версии initdb.sh.

Почти вся работа сделана. Чтобы завершить создание конфигурации и довести ее до работоспособного состояния, остается только проанализировать исходный код и сохранить результаты в БД. Для этого служит скрипт ./genxref. Что ему нужно для работы? На первый взгляд может показаться, что все необходимое есть в lxr.conf. Действительно, там можно найти подробнейшую информацию обо всех проектах. Но их может оказаться неподъемно много. Какие именно нужно анализировать? Все? Даже при малейшем изменении в любом из них? Это может занять часы. Например, на одно ядро Linux уйдет часа полтора. Очевидно, требуется более гибкое решение, позволяющее ограничить поле деятельности ./genxref. Для этого и предназначены его параметры. Их можно поделить на три группы. Первая служит для выбора анализируемого проекта:

» —url=<URL нужного проекта> позволяет указать конкретный проект. Значение этого параметра складывается из двух частей: одного из элементов host_names и virtroot. Обе переменные определены в lxr.conf: host_names — в блоке в глобальных параметров, virtroot — в блоке параметров проекта. Например, при локальном использовании LXR значение url будет примерно таким:

—url=//localhost/lxr/my_project.

» —allurls заставляет анализировать все проекты, найденные в lxr.conf.

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

» —version=<id версии> позволяет указать конкретную версию проекта. Список идентификаторов версий можно найти в lxr.conf. Он находится в блоке параметров проекта (variables => v => range).

» —allversions заставляет анализировать все версии.

Параметры третьей группы нужны для управления процессом анализа. Если ни один из них не указан, то ранее обработанные файлы исходного кода пропускаются, что дает существенную экономию времени, когда изменения незначительны. Параметр —reindexall отключает эту оптимизацию. А —checkonly заставляет ./genxref только проверить корректность конфигурации, не выполняя никакого анализа исходного кода вообще.

Модификация конфигурации

Остальные операции конфигурирования не должны вызвать затруднений:

» Добавление нового проекта Выполняется уже знакомым скриптом ./scripts/configure-lxr.pl, нужно только добавить параметр —add. Для создания и заполнения БД нового проекта используются те же initdb.sh и genxref.

» Удаление проекта и того проще. Требуется всего лишь удалить блок параметров соответствующего проекта в lxr.conf.

» Добавление и удаление новых версий проекта Осуществляется редактированием списка идентификаторов версий в блоке параметров проекта в lxr.conf (variables => v => range).

Особый случай: ядро Linux

Ядро Linux мало чем отличается от других крупных проектов, поэтому с ним вполне можно работать обычными методами. Но есть одна особенность, затрудняющая такое применение LXR. Дело в том, что ядро Linux может быть собрано для множества различных аппаратных архитектур, для каждой из которых отдельные части кода имеют свою независимую реализацию. Это значит, что если весь исходный код считать одним целым, то разобраться в нем будет довольно сложно. При навигации было бы очень удобно работать с какой-то одной архитектурой, а при необходимости иметь возможность переключаться на другую.

Чтобы решить эту проблему, для ядра Linux в LXR предусмотрены специальные средства. К счастью, они лишь дополняют стандартные, радикально не меняя принцип работы. Дополнений всего два:

» Перед ./scripts/configure-lxr.pl нужно вызвать ./scripts/kernel-vars-grab.sh —erase <путь к каталогу с деревом исходных кодов ядра Linux>. В результате в каталоге ./custom.d будет создан набор файлов, имена которых заканчиваются на _list.txt. В них содержатся списки поддерживаемых архитектур, платформ и т.д. Этой информацией пользуется configure-lxr.pl.

» Вызвать ./scripts/configure-lxr.pl, указав специальный конфигурационный файл для ядра Linux:

./scripts/configure-lxr.pl —add —conf-out=lxr.conf lxrkernel.conf

Использование на практике

Постановка задачи

Теории довольно. Пора попробовать LXR в деле. Создадим рабочий каталог /home/lxr, а в нем подкаталог src для исходного кода подопытных проектов. Поскольку у каждого из них может быть несколько версий, структура src будет двухуровневая: каталоги проектов, в каждом из которых подкаталоги для версий. Для экспериментов возьмем по две версии busybox и grep, а также одну версию ядра Linux. Тогда src будет выглядеть так:

» src/bb, в котором ver_1.20.2 и ver_1.21.0.

» src/grep, в котором ver_2.12, ver_2.13. » src/linux_kernel, в котором ver_3.2.42.

Попробуем организовать навигацию по этим проектам. Затем добавим по одной версии grep (ver_2.14) и ядра Linux (ver_3.2.44), а для демонстрации удаления проектов и версий избавимся от всего проекта busybox и одной версии grep (ver_2.12). Задача будет выполняться с использованием MySQL, glimpse и Apache на ОС Linux Ubuntu 12.04 LTS 64-bit с правами суперпользователя.

Установка LXR и вспомогательного ПО

Установка LXR очень проста. Поскольку он написан на Perl, компилировать там нечего. Достаточно загрузить архив с исходным кодом и распаковать его: cd /home/lxr wget -nd -P . http://sourceforge.net/projects/lxr/files/stable/ lxr-1.2.0.tgz tar -xvf ./lxr-1.2.0.tgz

Для удобства переименуем полученный каталог в lxr: mv ./lxr-1.2.0 ./lxr

Для установки вспомогательного ПО удобнее всего воспользоваться пакетным менеджером. В Ubuntu для этого служит GUI-приложение Synaptic или команда apt-get: apt-get install <список пакетов через пробел>

Вероятно, glimpse среди пакетов нет. Не беда, ее легко собрать самостоятельно. Чтобы не засорять систему, сделаем это в локальном каталоге /home/lxr/glimpse.

Загружаем и распаковываем архив с исходным кодом: wget -nd -P /home/lxr/glimpse http://webglimpse.net/trial/ glimpse-latest.tar.gz cd /home/lxr/glimpse tar -xzvf ./glimpse-latest.tar.gz cd /home/lxr/glimpse/glimpse-4.18.6/

Сборка оригинальностью не отличается (—prefix задает ката¬лог для ее результатов):

./configure —prefix=»/home/lxr/glimpse/_install» make && make check && make install

Для полной интеграции в систему остается только добавить /home/lxr/glimpse/_install/bin в $PATH.

Создание конфигурации

Из каталога с исходным кодом LXR запустим скрипт configure-lxr. pl для создания начальной конфигурации (на этом этапе добавим только один проект — busybox):

./scripts/configure-lxr.pl

Ответив на ряд вопросов, в custom.d находим готовую конфигурацию. Осталось несколько завершающих штрихов:

1 Как просил configure-lxr.pl, закомментируем в ./custom.d/lxr.conf переменную swishbin.

2 Скопируем ./custom.d/lxr.conf в каталог с исходным кодом LXR: cp ./custom.d/lxr.conf ./

3 Скопируем конфигурационный файл web-сервера в системный каталог: cp ./custom.d/apache-lxrserver.conf /etc/apache2/conf.d/

4 Создадим БД, после чего сразу же удалим скрипт:

./custom.d/initdb.sh rm ./custom.d/initdb.sh

5 Выполним проверку конфигурации, а в случае успеха запустим анализ исходного кода:

./genxref —url=//localhost/lxr/busybox —checkonly

./genxref —url=//localhost/lxr/busybox —allversions

Можно проверять. Вводим в адресную строку web-браузера localhost/lxr/busybox/source. Ошибка?! Как всегда, практика с теорией немного разошлись. Нужно еще внести пару исправлений в apache-lxrserver.conf:

6 Добавим строчку Servername loacalhost.

7 Содержимое раздела Perl security checks поместим внутрь условного оператора: <IfModule modperl.c> … </IfModule>. Вот теперь заработало!

Добавление проекта

Добавим две версии проекта grep (ver_2.12 и ver_2.13). Все необходимые для этого команды уже знакомы:

./scripts/configure-lxr.pl -add cp ./custom.d/lxr.conf ./

./custom.d/initdb.sh rm ./custom.d/initdb.sh

./genxref —url=//localhost/lxr/grep -allversions

Добавление ядра Linux

1 Генерируем списки поддерживаемых платформ и архитектур:

./scripts/kernel-vars-grab.sh —erase /home/lxr/src/linux_kernel/

2 Запускаем конфигурационный скрипт c соответствующими параметрами:

./scripts/configure-lxr.pl —add —conf-out=lxr.conf lxrkernel.conf

3 И далее уже знакомое cp ./custom.d/lxr.conf ./

./custom.d/initdb.sh rm ./custom.d/initdb.sh

./genxref —url=//localhost/lxr/kernel —allversions

Добавление версии

Добавим версию 2.14 к проекту grep.

1 Создаем каталог src/grep/ver_2.14 и копируем туда исходный код.

2 Открываем lxr.conf и в блоке параметров проекта grep добавляем элемент ver_2.14 в начало списка идентификаторов версий (variables => v => range).

Остается только проанализировать добавленный код:

./genxref —url=//localhost/lxr/grep —version=ver_2.14

Добавление версии ядра Linux

Добавим версию 3.2.44, создав каталог src/linux_kernel/ver_3.2.44 и скопировав в него исходный код.

./scripts/kernel-vars-grab.sh —erase /home/lxr/src/linux_kernel

./genxref —url=//localhost/lxr/kernel -version=ver_3.2.44

Удаление проекта

Для этого нужно лишь удалить соответствующий раздел в lxr. conf. В нашем примере за проект busybox отвечает второй раздел (в первом определяются глобальные параметры). Этого достаточно, чтобы проект пропал из системы, но его БД все равно останется и будет занимать место на диске. Чтобы его освободить, необходимо удалить БД и результаты работы glimpse вручную. В нашем случае это можно сделать так:

1 Заходим в MySQL: mysql -u root -p

2 Выполняем SQL-запрос для удаления БД проекта busybox:

DROP DATABASE lxr_bb;

3 Выходим из MySQL: quit

4 Удаляем каталог /home/lxr/glimpse_db/lxr/busybox.

Удаление версии

Для этого достаточно в lxr.conf удалить идентификатор версии в блоке параметров соответствующего проекта. Например, для удаления версии 2.12 в grep нужно убрать строчку ver_2.12 во втором блоке (или в третьем, если пожалели busybox).

Чтобы зря не занимать место на диске, удаляем ненужные данные из БД:

./genxref —url=//localhost/lxr/grep —reindexall

Заключение

К сожалению, тема слишком обширна для одной статьи. Многое осталось за кадром: интеграция с системами контроля версий, иные комбинации вспомогательного ПО, удаленная навигация и многое другое. Надеюсь, эта статья станет для кого-то первой ступенькой в дальнейшем изучении LXR.

SQL - 17 | 0,267 сек. | 7.46 МБ