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

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

![0714_platformio_ru/image1.png](https://import.viva64.com/docx/blog/0714_platformio_ru/image1.png)


> \*\*Примечание\\\.\*\* Интеграция с PlatformIO была реализована ранее, но на текущий момент официально не поддерживается\\\.

## Что такое PlatformIO?

[PlatformIO](https://platformio.org/) – это кроссплатформенный инструмент для программирования микроконтроллеров\. Ядром PlatformIO является инструмент с интерфейсом командной строки, однако рекомендуется использовать его в виде плагина для Visual Studio Code\. Поддерживается большое количество современных чипов и плат на их основе\. Умеет автоматически загружать подходящие системы сборки, а на сайте собрана большая коллекция библиотек для управления подключаемыми электронными компонентами\. Есть поддержка нескольких статических анализаторов кода, в том числе и PVS\-Studio\.

## Импорт проекта

Для демонстрации возьмем программу управления гексаподом [ArduPod](https://github.com/jgromes/ArduPod) на плате Arduino Mega\. 

<https://www.youtube.com/watch?v=UzcBlvJY_mQ>

Создадим новый проект для подходящей платы и скопируем исходный код:

![0714_platformio_ru/image2.png](https://import.viva64.com/docx/blog/0714_platformio_ru/image2.png)

В папке _/arduino/AP\_Utils/examples/_ находятся несколько примеров программ для настройки и запуска гексапода, воспользуемся servo\_test\.ino\. Программа для Arduino, как правило, создается в виде скетчей в формате INO, который в данном случае не совсем подходит\. Для того чтобы сделать из него правильный \.cpp файл обычно достаточно поменять расширение имени файла, добавить в начало заголовок _\#include <Arduino\.h\>_, и убедиться, что функции и глобальные переменные объявлены до обращения к ним\.

![0714_platformio_ru/image3.png](https://import.viva64.com/docx/blog/0714_platformio_ru/image3.png)

В процессе сборки могут возникнуть ошибки об отсутствии необходимых сторонних библиотек\. Однако PlatformIO поможет найти их в своём репозитории\.

```cpp
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.
```

По ссылке будут показаны подходящие варианты, а установка зависимости производится одной командой в терминале:

```cpp
pio lib install "Adafruit PWM Servo Driver Library"
```

## Настройка анализаторов и запуск проверки

Для настройки анализаторов нужно отредактировать конфиг platformio\.ini примерно таким образом:

```cpp
[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](https://docs.platformio.org/en/latest/plus/check-tools/pvs-studio.html)

Наконец, можно запустить проверку проекта командой в терминале\. Перед первой проверкой среда сама скачает актуальный дистрибутив анализатора\.

```cpp
pio check
```

## Результат проверки программы гексапода

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

[V519](https://pvs-studio.ru/ru/docs/warnings/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

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/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

```cpp
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;
}
```

Предупреждение указывает на неаккуратное сравнение чисел с плавающей запятой\. Из\-за невозможности точного представления вещественных чисел конечным количеством бит, безопаснее устанавливать равенство дробных чисел через сравнение их разности с заданным показателем точности\. Например, как\-то так:

```cpp
bool is_equal(double x, double y) {
  return std::fabs(x - y) < 0.001f;
}
```

Единственным безопасным вариантом прямого сравнения нецелых чисел является присвоение переменным значений констант, и последующее сравнение их значений с этими константами\. В данном случае значению переменной _value_ нигде конкретно не присваивается \-1\. Вот как устроен вызываемый метод _AP\_Utils::sr04_, который и возвращает проверяемое значение:

```cpp
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 могут воспользоваться [ознакомительным режимом](https://pvs-studio.ru/ru/docs/manual/0009/), а для открытых проектов есть возможность получить [бесплатную лицензию](https://pvs-studio.ru/ru/blog/posts/0614/)\.

Тем, кто желает узнать о возможностях PVS\-Studio более подробно, советую посмотреть следующие статьи:

* [Причины внедрить в процесс разработки статический анализатор кода PVS\-Studio](https://pvs-studio.ru/ru/blog/posts/0687/)
* [Знакомство со статическим анализатором кода PVS\-Studio на Windows](https://pvs-studio.ru/ru/docs/manual/0007/)
* [Почему embedded\-разработчикам следует использовать статический анализ кода](https://pvs-studio.ru/ru/blog/posts/0563/)
* [PVS\-Studio: поддержка стандартов кодирования MISRA C и MISRA C\+\+](https://pvs-studio.ru/ru/blog/posts/cpp/0596/)