Опыт статического анализа Qt-программы с использованием PVS-Studio

imageДанная статья – результат моего первого опыта статического анализа достаточно большой программы (1665 файлов с исходными текстами на данный момент). Кроме того, это мой первый опыт использования среды Microsoft Visual Studio. Разработка анализируемой программы велась исключительно в Ubuntu, Eclipse CDT, компилятор GCC.

Подготовка проекта

Если вы свободно владеете “студией” и давно научились разрабатывать в ней Qt-проекты, то смело пропускайте этот раздел, т.к. здесь приведен мой способ подготовки проекта для VS2010.

Поскольку я разрабатываю программу базирующуюся на Qt-фреймворке, то в моем проекте есть файл .pro. Этот файл является основой для генерации проекта. На сайте Qt (http://doc.qt.nokia.com/stable/qmake-project-files.html) указано, что qmake позволяет генерировать проекты “студии”. Для этого необходимо воспользоваться командой:

${QTDIR}binqmake.exe -t vcapp -spec ${QTDIR}mkspecswin32-msvc2010

После чего появится файл проекта *.vcxproj, который и открывается “студией”. Больше манипуляций не потребовалось – проект успешно компилируется.

Установка и настройка PVS-Studio

Установка проста до невозможности: скачал, установил, перезапустил и вот уже есть нужная вкладка в меню – PVS-Studio. С настройкой пришлось немного повозится. По неизвестным мне причинам при анализе проекта (PVS-Studio->Check solution) PVS не может найти заголовочные файлы Qt (например, QWidget.h или QObject.h), хотя, повторюсь, проект компилируется без ошибок. Чтобы исправить эту ситуацию, нужно зайти в настройки проекта (правой кнопкой на проекте -> Properties), выбрать “VC++ Directories” и в разделе “Include directories” указать пути до заголовочных файлов Qt. Причем, нужно указать не просто корневую папку Qtinclude, а включить помодульно подпапки, которые вы используете. Т.е., если в .pro-файле указано, что ваш проект использует модули core, gui, xml, sql, network, webkit, opengl, то, значит, вам нужно добавить include, includeqtcore, inclideqtgui, includeqtxml, includeqtxmlpatterns, includeqtsql, includeqtnetwork, includeqtwebkit, includeqtopengl. После реализации этих действий PVS-Studio начинает работать правильно.

Статический анализ

Анализ проходил порядка 4 часов, я в это время катался на сноуборде. Итак ,было обнаружено:

  • 120 предупреждений категории 1
  • 164 предупреждения категории 2
  • 287 предупреждений категории 3

Категория 3

В данной категории ~250 (87% процентов от общего числа) предупреждений “V001. A code fragment from ‘file’ cannot be analyzed”, сообщающих о том, что PVS не справился с анализом определенного файла. Многие предупреждения ссылаются на автогенерируемые файлы, вроде moc*.cpp. Было бы удобнее для меня, если бы PVS не пытался анализировать эти файлы.

Следующее по популярности предупреждение “V550. An odd precise comparison. It’s probably better to use a comparison with defined precision: fabs(A — B) < Epsilon or fabs(A — B) > Epsilon”. Анализ предупреждений показал 8 ложных срабатываний и 1 истинное.

Следующее предупреждение в этом блоке: “V126. Be advised that the size of the type ‘long’ varies between LLP64/LP64 data models”:

This diagnostic message lets you find all the ‘long’ types used in a program.
Of course, presence of the ‘long’ type in a program is not an error in itself. But you may need to review all the fragments of the program text where this type is used when you create portable 64-bit code that must work well in Windows and Linux.
Windows and Linux use different data models for the 64-bit architecture. A data model means correlations of sizes of base data types such as int, float, pointer, etc. Windows uses the LLP64 data model while Linux uses the LP64 data model. In these models, the sizes of the ‘long’ type are different.
In Windows (LLP64), the size of the ‘long’ type is 4 bytes.
In Linux (LP64), the size of the ‘long’ type is 8 bytes.
The difference of the ‘long’ type’s sizes may make files’ formats incompatible or cause errors when developing code executed in Linux and Windows. So if you want, you may use PVS-Studio to review all the code fragments where the ‘long’ type is used.

Напомню, что я использую Qt-фреймворк и поэтому использовать тип long вообще плохой тон, нужно использовать qint16, qint32 и qint64 для кросс-платформенной разработки. Вопрос лишь в том, чем использование PVS в данном случае будет лучше поиска по всему проекту. Ведь эти предупреждения относятся к Viva64 – платному продукту. Для этих предупреждений отключена навигация по коду (понимать и править становится очень тяжело).

Предупреждение которое я не смог проанализировать из за триальности программы:
“V111. Call function ‘foo’ with variable number of arguments. N argument has memsize type”

Категория 2

В этой категории сложилась удручающая ситуация: предупреждений, связанных с General Analysis (бесплатный анализ) – 4 штуки (2% от общего числа) и 160 триальных предупреждений из Viva64.

Парадоксально, но львиная доля триальных предупреждений относятся к “V112. Dangerous magic number N used”, которые так же встречаются и в категории 3. По какому принципу PVS положил часть в категорию 2, а часть – в категорию 3 для меня загадка. Я считаю, анализатор данного предупреждения следовало бы улучшить, потому что беглый осмотр показал ~90% ложных срабатываний. Я бы даже сказал, что ни одного истинного срабатывания не нашлось. Но проверил я не всю выборку, поскольку триальность сделала свое дело – я устал. По данному предупреждению в основном были обнаружены магические цифры в процедурах рисования, как то: установка различных отступов и смещений.

Предупреждения бесплатного доступа:

“V537. Consider reviewing the correctness of ‘X’ item’s usage”:


The analyzer detected a potential misprint in code. This rule tries to diagnose an error of the following type using the heuristic method:
int x = static_cast&lt int&gt(GetX()) * n;
int y = static_cast&lt int&gt(GetX()) * n;
In the second line, the GetX() function is used instead of GetY(). This is the correct code:
int x = static_cast&lt int&gt(GetX()) * n;
int y = static_cast&lt int&gt(GetY()) * n;

– ложное срабатывание. Вот код:

  int width = img_in->width();
  int height = img_in->height();

  if (width > height) {
    if (width > maxWidth) {
      height = (int) ((float) maxWidth / width * height);
      width = maxWidth;
    }
  } else {
    if (height > maxHeight) {
  /*здесь*/  width = (int) ((float) maxHeight / height * width);
      height = maxHeight;
    }
  }

* This source code was highlighted with Source Code Highlighter.

“V525. The code containing the collection of similar blocks. Check items X, Y, Z, … in lines N1, N2, N3,” – ложные срабатывания.

Предупреждения платного доступа:

“V401. The structure’s size can be decreased via changing the fields’ order. The size can be reduced from N to K bytes,”:


The analyzer detected a data structure which can lead to ineffective usage of main memory.
Let’s consider an example of a structure which Viva64 will announce ineffective:
struct LiseElement {
bool m_isActive;
char *m_pNext;
int m_value;
};
This structure will occupy 24 bytes in 64-bit code, what relates to data alignement. But if you change the order of the fields its size will occupy 16 bytes. The optimized variant of the structure will look as follows:
struct LiseElement {
char *m_pNext;
int m_value;
bool m_isActive;
};

На данный момент я не могу понять как это возможно, но если это действительно так, то это здорово – это предупреждение очень полезно для меня спасибо.

Категория 1

Ситуация схожа с категорией 2: всего 1 бесплатное предупреждение и 119 платных.

Бесплатное предупреждения:
“V524. It is odd that the ‘Foo_1′ function is fully equivalent to the »Foo_2’ function”– ложное срабатывание:

const qreal& getX() const {
throw std::runtime_error(«Unsupported operation error»);
}

const qreal& getY() const {
throw std::runtime_error(«Unsupported operation error»);
}

Платные предупреждения:

“V103. Implicit type conversion from memsize type to 32-bit type,
“V104. Implicit type conversion to memsize type in an arithmetic expression,
“V110. Implicit type conversion of return value from memsize type to 32-bit type,
“V302. Member operator[] of ‘foo’ class has a 32-bit type argument. Use memsize-type here – данные предупреждения были обнаружены в исходных кодах Qt, конкретно в файле qtessolator.cpp. Истинность проверить не удалось из за триальности.

Для некоторых ошибок, в частности V114, V121 PVS-Studio дублирует предупреждения. Так, в списке предупреждений встречаются в среднем по 4 экземпляра предупреждения одного типа, ссылающиеся на одну и ту же строчку.

Итоги

Ощущение от использования инструмента двоякое. Я предполагал, что инструмент позволит выявить большее число ошибок, поскольку я не причисляю себя к гениальным программистам. Фактически, нашелся один участок когда который действительно стоит подправить. Либо я слишком плохо о себе думаю, либо инструмент еще не до конца отточен.

Инструмент выдал 6 (2.11%) бесплатных и 278 (97.89%) платных предупреждений (без 3-ей категории). Очень уж маленькая бесплатность получается, даже не распробуешь. Предупреждения, связанные с V112 (магические цифры) дают одни ложные срабатывания, при этом их очень много – устаешь их проверять, тем более они платные.

Купил бы я этот инструмент? К сожалению, нет. Я потратил много времени на настройку и анализ из за большого числа ложных срабатываний. Одно не ложное срабатывание на почти 500 предупреждений – уж слишком жестоко. Что удивительно, полезное предупреждение было в 3 категории (как я понял в самой не критичной). 250 предупреждений обнаружены в автогенерируемых файлах. Хотелось бы видеть пре-кофнигурацию, которая бы исключала такие файлы из обработки (например moc-файлы Qt). Больше всего меня порадовало предупреждение V401(можно уменьшить размер). Вот это я купил бы! Я купил бы все предупреждения, которые позволят мне уменьшить размер и увеличить скорость моего приложения.

Спасибо.

Реклама

Добавить комментарий

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

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s