КОДИНГОтчет, созданный scan-buildция вызывает другую функцию несколько раз, то будут напечатанывсе вызовы), но для ознакомительных целей сойдет.Проверим работу скрипта на файле workandsolve.cc:int work(int data);int solve(int data);int work(int data) {int res = solve(data);return res;}int solve(int data) {int res = work(data);return res;}int main() {solve(10);return 0;}Запускаем Dehydra:g++ -fplugin=~/dehydra/gcc_dehydra.so -fplugin-arg-gcc_\dehydra-script=callgraph.js workandsolve.cc -o /dev/nullПолучаем на выходе:work(int):solve(int)solve(int):work(int)main():solve(int)work(int)Полное описание функций-хендлеров и их параметров, а такжепримеры использования можно найти на странице проекта: mzl.la/DWbf4.Dehydra «из коробки» умеет достаточно мало. Она в основномпредназначена для написания собственных процедур проверки.В репозитории mozilla-central (bit.ly/vJEl1B) лежит несколько готовыхскриптов, использовавшихся для проверки кода программ от Mozilla,но они не очень интересные, так как требуют дополнительной аннотациикода. Например, скрипт final.js проверяет, нет ли наследникову класса, помеченного пользовательским атрибутом final.В общем, это очень интересный инструмент, но он требует дополнительногопрограммирования.Поисковая строка DXRCLANG STATIC ANALYZERClang — это компилятор C/C++/Objective-C, который являются частьюпроекта LLVM(llvm.org). В Сlang встроен статический анализатор, длязапуска которого нужно передать утилите clang (или clang++) опцию--analyze. На его сайте (bit.ly/11xRuQ) опубликован список проверок,которые выполняются в ходе анализа. Проверки разделены на несколькогрупп: core, deadcode, osx, unix. Примерно половина приходитсяна core — сюда входят проверки на наличие базовых ошибок, вомногом схожие с предупреждениями в GCC:• деление на ноль;• разыменование нулевого указателя;• использование неинициализированных переменных в различныхситуациях;• проверки, специфичные для Objective-C.Другая большая группа — osx, где собраны проверки для Mac OS X.Вообще, видно, что именно эта среда является целевой для разработчиковclang static analyzer. Рассмотрим работу clang analyzer на примере.Пусть у нас есть код clantest.c:#include int div_by_zero() {int x = 0;int y = 5 / x;return y;}int null_dereference() {int x = 0;int* p = NULL;if (x > 0)p = &x;return *p;}int main() {return 0;}Компилируем и одновременно проверяем код:clang --analyze clangtest.c -o clangtestПолучаем на выходе:clangtest.c:5:13: warning: Division by zeroint y = 5 / x;^090ХАКЕР 02 /157/ 2012
Ищем ошибки в программах на C/C++clangtest.c:15:10: warning: Dereference of null pointer...return *p;^~2 warnings generated.Если заменить «x > 0» на «x >= 0» в функции null_dereference, товторое предупреждение, что приятно, исчезнет.Вместе с clang поставляется удобная утилита scan-build, котораяпозволяет проводить статический анализ всего проекта вовремя его сборки. Этой утилите нужно передать команду, котораяначинает процесс сборки проекта. Она заменит все вызовыкомпилятора вызовами со статическим анализатором. Утилитане выполняет сложный анализ процесса сборки, а просто заменяетпеременные окружения CC и CXX своим анализатором, что несколькоограничивает применение утилиты.Для рассмотренного в качестве примера clangtest.c можносделать такой Makefile:clangtest : clangtest.c$(CC) -o clangtest clangtest.cТеперь можно запустить scan-build make. Будет создан отчет,который можно просмотреть с помощью утилиты scan-view.На основе Clang создано ещё несколько интересных проектов,например проект DXR, который представляет собой интеллектуальныйбраузер исходного кода. DXR позволяет удобно искатьразличные конструкции в коде. Например, с его помощью можнонайти все функции, которые вызывают заданную функцию, или всетипы, которые являются потомками заданного типа. Насколько японял, раньше DXR работал на основе Dehydra. Как настроить DXRдля своего проекта, описано на странице mzl.la/tEyXCL.CPPCHECKCppcheck — это специализированная программа для поиска ошибокв коде на C++. Cppcheck работает с кодом как с последовательностьюстрок, особо не вдаваясь в их смысл. Сначала он всяческиупрощает код и приводит его к виду последовательности токенов,разделенных пробелами. После этого он ищет типичные ошибкис помощью регулярных выражений и набора эвристических правил,написанных на C++.Вот так выглядит простейшие правило для поиска ошибокделения на нуль:cppcheck --rule="/ 0"То же самое правило на C++:void CheckOther::divisionByZero() {// Loop through all tokensfor (const Token *tok = _tokenizer->tokens();tok; tok = tok->next()) {// check if there is a division by zeroif (Token::Match(tok, "/ 0")) {// report errordivisionByZeroError(tok);}}}Для Cppcheck уже написано огромное количество правил.На странице проекта перечислены типы детектируемых ошибок:• различные сценарии утечки памяти и других ресурсов;• использование устаревших функций;• неправильное использование STL и Boost;• наличие неиспользуемых или неинициализированных переменных;• разыменование нулевого указателя;• неправильное использование классов;• и т. д.Cppcheck имеет как консольный, так и графический интерфейс.Пользоваться и тем и другим одинаково просто — достаточноуказать директорию, которую нужно проверить. Разберёмпример с утечкой памяти: пусть она выделяется в конструкторе,но не освобождается в деструкторе.class Newbie {public:Newbie() {resource = new int[256];}private:int* resource;};int main() {Newbie noob;return 0;}Успешное обнаружение утечки в этой программе с помощьюCppcheck проиллюстрировано на скрине на следующем развороте.!Страница bit.ly/s6RQoH содержит внушительный список известныхи не очень проектов, в которых с помощью Cppcheck удалосьобнаружить баги. Что интересно, в основном это баги, найденныев коде на чистом C. Подавляющее большинство из них связанос утечкой памяти или какого-либо другого ресурса.COCCINELLECoccinellе (произносится как [кОксэнэл]) — это инструмент дляавтоматического преобразования С-кода. Преобразования описываютсяв виде семантического патча на языке SmPL (SemanticPatch Language). По сути, такой патч — это набор правил для заменыодних конструкций языка C другими. Изначально coccinelleразрабатывался для решения следующей задачи. Представь, чтоу нас есть библиотека и клиентский код, который эту библиотекуиспользует. Как нужно изменить клиентский код при измененииинтерфейса библиотеки? Coccinelle позволяет записать необходимыеизменения в виде компактного семантического патча. Потомэтот патч можно наложить на клиентский код, чтобы получитьего актуальную версию. Так как coccinelle «понимает» С, то этотинструмент можно использовать для поиска определённых шаблоновв коде. Нас интересуют шаблоны, потенциально содержащиеошибку. Для того чтобы понять, как искать шаблоны, рассмотримструктуру семантического патча.Патч состоит из набора правил следующего вида:@[имя параметры]@объявление метапеременных@@описание преобразованийКаждое правило начинается с заголовка, заключённого в двазначка @. Заголовок может быть пустым или содержать имя правилаи какие-либо параметры для него (например, список другихправил, от выполнения которых оно зависит). За ним следуетраздел объявления метапеременных. Каждая метапеременнаяпредставляет собой типизированный шаблон для сопоставленияс конструкциями в C. Далее идёт основной раздел, в которомуказывается, какие конструкции нужно найти и на что их следуетзаменить (можно просто искать определенные конструкции, не заменяяих). Рассмотрим несколько примеров.Первый пример, касающийся так называемой проблемы«!x & y», очень популярен в статьях и презентациях разработчиковХАКЕР 02 /157/ 2012 091