Недавно в среде разработки встраиваемых систем PlatformIO появилась поддержка PVS-Studio. В этой статье вы узнаете, как проверить свой код статическим анализатором на примере открытого проекта.
PlatformIO – это кроссплатформенный инструмент для программирования микроконтроллеров. Ядром PlatformIO является инструмент с интерфейсом командной строки, однако рекомендуется использовать его в виде плагина для Visual Studio Code. Поддерживается большое количество современных чипов и плат на их основе. Умеет автоматически загружать подходящие системы сборки, а на сайте собрана большая коллекция библиотек для управления подключаемыми электронными компонентами. Есть поддержка нескольких статических анализаторов кода, в том числе и PVS-Studio.
Для демонстрации возьмем программу управления гексаподом ArduPod на плате Arduino Mega.
Создадим новый проект для подходящей платы и скопируем исходный код:
В папке /arduino/AP_Utils/examples/ находятся несколько примеров программ для настройки и запуска гексапода, воспользуемся servo_test.ino. Программа для Arduino, как правило, создается в виде скетчей в формате INO, который в данном случае не совсем подходит. Для того чтобы сделать из него правильный .cpp файл обычно достаточно поменять расширение имени файла, добавить в начало заголовок #include <Arduino.h>, и убедиться, что функции и глобальные переменные объявлены до обращения к ним.
В процессе сборки могут возникнуть ошибки об отсутствии необходимых сторонних библиотек. Однако PlatformIO поможет найти их в своём репозитории.
In file included from src\servo_test.cpp:20:0:
src/AP_Utils.h:10:37: fatal error: Adafruit_PWMServoDriver.h:
No such file or directory
*******************************************************************************
* Looking for Adafruit_PWMServoDriver.h dependency? Check our library registry!
*
* CLI> platformio lib search "header:Adafruit_PWMServoDriver.h"
* Web> https://platformio.org/lib/search?query=header:Adafruit_PWMServoDriver.h
*
*******************************************************************************
compilation terminated.
По ссылке будут показаны подходящие варианты, а установка зависимости производится одной командой в терминале:
pio lib install "Adafruit PWM Servo Driver Library"
Для настройки анализаторов нужно отредактировать конфиг platformio.ini примерно таким образом:
[env:megaatmega2560]
platform = atmelavr
board = megaatmega2560
framework = arduino
check_tool = pvs-studio
check_flags =
pvs-studio:
--analysis-mode=4 ; General analysis mode. Set to 32 for MISRA
--exclude-path=/.pio/libdeps ; Ignore dependency libraries
Параметр check_tool указывает, какие анализаторы кода применять, а их настройка производится в параметре check_flags. Более подробные инструкции находятся в документации на официальном сайте: https://docs.platformio.org/en/latest/plus/check-tools/pvs-studio.html
Наконец, можно запустить проверку проекта командой в терминале. Перед первой проверкой среда сама скачает актуальный дистрибутив анализатора.
pio check
В этот раз целью статьи является демонстрация интеграции PVS-Studio с PlatformIO, а не демонстрация диагностических возможностей анализатора. Однако, раз проект проверен, рассмотрим парочку найденных ошибок, чтобы показать, что проект удалось успешно проанализировать.
V519 There are identical sub-expressions to the left and to the right of the '-' operator: pow(t, 2) - pow(t, 2). AP_Utils.cpp 176
pointLeg* AP_Utils::traceLeg(uint8_t leg, float phi, float z,
int resolution, uint8_t shape) {
....
if(shape == ELLIPTIC) {
....
float v = sqrt(pow(phi - legs[leg].phi, 2) + pow(z - legs[leg].z, 2));
float u = sqrt(pow(phi - phi0, 2) + pow(z - z0, 2));
float t = sqrt(pow(phi0 - legs[leg].phi, 2) + pow(z0 - legs[leg].z, 2));
theta = acos((pow(t, 2) - pow(t, 2) - pow(v, 2))/(-2.0*t*u));
....
}
....
}
Два одинаковых выражения вычитаются одно из другого. Непонятно, каков математический смысл этой разности. Возможно, программист просто не стал сокращать выражение. А возможно, допущена опечатка, и на месте одной из этих t должен находиться другой аргумент.
V550 An odd precise comparison: value != - 1. It's probably better to use a comparison with defined precision: fabs(A - B) > Epsilon. AP_Utils.cpp 574
float AP_Utils::sr04_average(uint8_t trig, uint8_t echo,
int unit, int samples, int time) {
....
float average, pause, value;
....
for(int i=0; i<samples; i++) {
value = sr04(trig, echo, unit);
if(value != -1) { // <=
total += value;
delay(pause);
} else {
i--;
}
}
average = total/samples;
....
return average;
}
Предупреждение указывает на неаккуратное сравнение чисел с плавающей запятой. Из-за невозможности точного представления вещественных чисел конечным количеством бит, безопаснее устанавливать равенство дробных чисел через сравнение их разности с заданным показателем точности. Например, как-то так:
bool is_equal(double x, double y) {
return std::fabs(x - y) < 0.001f;
}
Единственным безопасным вариантом прямого сравнения нецелых чисел является присвоение переменным значений констант, и последующее сравнение их значений с этими константами. В данном случае значению переменной value нигде конкретно не присваивается -1. Вот как устроен вызываемый метод AP_Utils::sr04, который и возвращает проверяемое значение:
float AP_Utils::sr04(uint8_t trig, uint8_t echo, int unit) {
....
float duration, distance;
....
duration = pulseIn(echo, HIGH);
distance = (346.3*duration*0.000001*unit)/2; // <=
if((distance >= 0.02*unit) && (distance <= 4*unit)) {
....
return(distance);
} else {
....
return 0;
}
}
Как видно, в value запишется результат некоторых вычислений. Присваивания -1 нигде не видно, зато AP_Utils::sr04 может вернуть 0, и это наводит на мысль, что сравнение производится не с тем результатом.
В этой статье мы рассмотрели процесс проверки проектов на микроконтроллерах статическим анализатором кода в среде для программирования встраиваемых систем PlatformIO. Напомню, что все желающие опробовать PVS-Studio могут воспользоваться ознакомительным режимом, а для открытых проектов есть возможность получить бесплатную лицензию.
Тем, кто желает узнать о возможностях PVS-Studio более подробно, советую посмотреть следующие статьи: