>
>
>
Интеграция PVS-Studio в PlatformIO

Алексей Говоров
Статей: 6

Интеграция PVS-Studio в PlatformIO

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

Что такое PlatformIO?

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 более подробно, советую посмотреть следующие статьи: