Інкрементальний аналіз в PVS-Studio: тепер і на складальному сервері


Введення
При впровадженні статичного аналізатора в існуючий процес розробки, команда може зіткнутися з певними труднощами. Наприклад, дуже корисно перевіряти змінений код перед тим, як він потрапить в систему контролю версій. Однак, виконання статичного аналізу в цьому випадку може вимагати досить тривалого часу, особливо на проектах з великою кодовою базою. У цій статті ми розглянемо режим інкрементального аналізу в статичному аналізаторі PVS-Studio, що дозволяє перевіряти тільки змінені файли, що значно скорочує час, необхідний для аналізу коду. Отже, розробники зможуть використовувати статичний аналіз так часто, як це необхідно, і мінімізувати ризики потрапляння код, що містить помилку, систему контролю версій. Приводом для написання статті стало, по-перше, бажання ще раз розповісти про такий корисної функції нашого аналізатора, а по-друге, той факт, що ми повністю переписали механізм інкрементального аналізу і додали підтримку цього режиму у версію нашого аналізатора для командного рядка.

Сценарії використання статичного аналізатора
Будь-який підхід до підвищення якості програмного забезпечення передбачає, що дефекти повинні виявлятися як можна раніше. В ідеальній ситуації, код потрібно писати без помилок, але в наш час ця практика успішно впроваджена тільки в одній корпорації:

<img src=«habrastorage.org/getpro/habr/post_images/fad/515/f76/fad515f767d3875e7673eb80448d0e0b.png» alt=«Picture » 1"/>

Чому ж так важливо виявляти і усувати помилки як можна раніше? Я не буду тут говорити про такі банальні речі як репутаційні ризики, які неминуче виникнуть, якщо ваші користувачі почнуть масово виявляти дефекти у вашому програмному забезпеченні. Давайте зосередимося саме на економічній складовій виправлення помилки в коді. У нас немає статистики по середній ціні помилки. Помилки бувають дуже різні, виявляються на різних етапах життєвого циклу програмного забезпечення, програмне забезпечення може використовуватися у різних предметних областях, як до критичних помилок, так і не дуже. Тому, хоча середня по індустрії вартість виправлення дефекту в залежності від стадії життєвого циклу невідома, оцінити зміну цієї вартості можна, використовуючи відоме правило «1-10-100». Стосовно до розробки програмного забезпечення, це правило стверджує, що якщо вартість усунення дефекту на етапі розробки дорівнює 1 ?, після передачі цього коду тестування складе вже 10 ?, і виростає до 100? після того, як код з дефектом пішов на продакшен:

<img src=«habrastorage.org/getpro/habr/post_images/858/1d8/f49/8581d8f49a61568209bc59bd323d84a1.png» alt=«Picture » 2"/>

Існує безліч причин для такого швидкого зростання ціни виправлення дефекту, наприклад:
  • Зміни в одній частині коду можуть торкнутися безліч інших частин програми.
  • Повторне виконання вже зроблених завдань: зміни в дизайні, кодування, внесення коригувань документацію і т. д.
  • Доставка виправленої версії користувачам, необхідність переконати користувачів оновитися.
Розуміючи важливість того, що слід усувати помилки на найбільш ранніх етапах життєвого циклу ПЗ, ми пропонуємо нашим клієнтам використовувати дворівневу схему перевірки коду статичним аналізатором. Перший рівень – це перевірка коду на комп'ютері розробника перед тим, як код буде закладено в систему контролю версій. Тобто розробник пише якийсь фрагмент коду і відразу інспектує його статичним аналізатором. Для цієї задачі у нас є плагін для Microsoft Visual Studio (підтримуються всі версії з 2010 до 2015 включно). Плагін дозволяє перевірити один або кілька файлів вихідного коду, один або кілька проектів або всі рішення цілком.

Другий рівень захисту — це запуск статичного аналізу у нічний час складання проекту. Це дозволяє переконатися, що нові помилки не були додані в систему контролю версій, або ж почати необхідні дії, щоб ці помилки виправити. Для того, щоб мінімізувати ризик проникнення помилок на більш пізні етапи життєвого циклу ПЗ, ми пропонуємо використовувати обидва рівня виконання статичного аналізу: локально на машинах розробників і на централізованому сервері безперервної інтеграції.

Такий підхід, однак, не позбавлений недоліків і не гарантує, що помилки, які можуть бути виявлені статичним аналізатором, не потраплять в систему контролю версій або, в крайньому випадку, будуть виправлені до того, як білд піде тестування. Примус розробників обов'язково виконувати вручну статичний аналіз перед комітом, напевно зіткнеться з сильним опором. По-перше, на проектах з великою кодовою базою ніхто не захоче сидіти і чекати, поки проект буде перевірений. Або ж, якщо розробник вирішить заощадити час і перевірити тільки ті файли, код в яких він змінював, йому доведеться вести облік змінених файлів, що, природно, теж ніхто не буде робити. Якщо ж ми розглянемо складальний сервер, на якому, крім нічних збірок, також налаштована збірка після виявлення змін у системі контролю версій, то виконання статичного аналізу всієї кодової бази під час численних денних збірок також не застосовується із-за того, що статичний аналіз потребує багато часу.

Режим інкрементального аналізу, що дозволяє перевіряти тільки останні змінені файли, допомагає вирішити ці проблеми. Розглянемо більш докладно, які переваги може принести режим інкрементального аналізу при використанні на комп'ютерах розробників і на складальному сервері.

Інкрементальний аналіз на комп'ютері розробника — бар'єр на шляху багів в систему контролю версій
Якщо команда розробників прийняла рішення використовувати статичний аналіз коду, і аналіз виконується лише на складальному сервері, під час, наприклад, нічних збірок, рано чи пізно розробники почнуть статичний аналізатор сприймати як ворога. Не дивно, адже всі члени команди будуть бачити, які помилки допускають їх колеги. Ми прагнемо до того, щоб всі учасники проекту сприймали статичний аналізатор як друга і як корисний інструмент, який допомагає покращувати якість коду. Для того, щоб помилки, які може виявити статичний аналізатор, не потрапляли в систему контролю версій і на загальний огляд, статичний аналіз повинен виконуватися також і на комп'ютерах розробників, щоб виявляти можливі проблеми в коді як можна раніше.

Як я вже згадував, ручний запуск статичного аналізу на всій кодової базі може потребувати досить тривалого часу. Якщо ж розробник буде змушений запам'ятовувати, над якими файлами з вихідним кодом він працював, це також буде дуже сильно дратувати.

Інкрементальний аналіз дозволяє скоротити час, який потрібен для виконання статичного аналізу, і позбутися від необхідності вручну запускати аналіз. Інкрементальний аналіз запускається автоматично після складання проекту або рішення. Ми вважаємо цей тригер найбільш підходящим для запуску аналізу. Цілком логічно перевірити, що проект збирається, перед тим, як внести зміни в систему контролю версій. Таким чином, режим інкрементального аналізу дозволяє позбутися від дратівливих дій, пов'язаних з необхідністю виконувати вручну статичний аналіз на комп'ютері розробника. Інкрементальний аналіз виконується у фоновому режимі, тому розробник може продовжувати працювати над кодом, не чекаючи закінчення перевірки.

Включити режим послесборочного інкрементального аналізу можна в меню PVS-Studio/Incremental Analysis After Build (Modified Files Only), даний пункт активований PVS-Studio за замовчуванням:

<img src=«habrastorage.org/getpro/habr/post_images/933/d84/ca7/933d84ca763c02042cec4cb7ee5c2e7d.png» alt=«Picture » 3"/>

Після активації режиму інкрементного аналізу PVS-Studio стане автоматично у фоновому режимі проводити аналіз всіх порушених модифікаціями файлів відразу після закінчення складання проекту. Якщо PVS-Studio виявить такі модифікації, інкрементальний аналіз буде автоматично запущений, а в області сповіщень Windows з'явиться анімована іконка PVS-Studio:

<img src=«habrastorage.org/getpro/habr/post_images/bf0/3af/165/bf03af165e9124e96d47a65165d740b4.png» alt=«Picture » 4"/>

Подробиці, пов'язані з використанням інкрементального аналізу, розглянуті в статті Режим інкрементального аналізу PVS-Studio.

Інкрементальний аналіз на сервері безперервної інтеграції — додатковий бар'єр на шляху багів
Практика безперервної інтеграції передбачає, що проект збирається на складальному сервері після кожного коміта в систему контролю версій. Як правило, крім складання проекту також виконується існуючий набір юніт-тестів. На додаток до юніт-тестів багато команд, які застосовують практику безперервної інтеграції, використовують складальний сервер для забезпечення процесів безперервного контролю якості. Наприклад, крім прогону модульних та інтеграційних тестів, ці процеси можуть включати виконання статичного і динамічного аналізу, вимірювання продуктивності і т. д.

Одним з важливих вимог до завдань, що виконуються на сервері безперервної інтеграції, є те, що складання проекту і виконання додаткових дій повинні проходити швидко, щоб команда могла оперативно відреагувати на виявлені проблеми. Виконання статичного аналізу на великий кодової базі після кожного коміта в систему контролю версій суперечить цій вимозі, оскільки це може вимагати досить тривалого часу. Ми були не згодні миритися з таким обмеженням, тому подивилися на наш плагін для Visual Studio, в якому вже давно існує режим інкрементального аналізу, і запитали себе: а чому б нам не реалізувати такий же режим в модулі для командного рядка PVS-Studio_Cmd.exe?

Сказано — зроблено, і в нашому модулі для командного рядка, який призначений для інтеграції статичного аналізатора в різні складальні системи, з'явився режим інкрементального аналізу. Цей режим працює так само, як інкрементальний аналіз в плагіні.

Таким чином, з додаванням підтримки інкрементального аналізу в PVS-Studio_Cmd.exe стало можливо використовувати наш статичний аналізатор в системі безперервної інтеграції під час численних денних збірок. За рахунок того, що будуть перевірені тільки змінені файли з моменту останнього оновлення коду системи контролю версій, статичний аналіз буде виконаний дуже швидко, і тривалість складання проекту практично не збільшиться.

Щоб активувати режим інкрементального аналізу в модулі для командного рядка PVS-Studio_Cmd.exe вкажіть ключ --incremental та виберіть один з наступних режимів роботи:
  • Scan – проаналізувати усі залежності для визначення того, на яких файлах повинен бути виконаний інкрементальний аналіз. Безпосередньо аналіз виконаний не буде.
  • Analyze – виконати інкрементальний аналіз. Цей крок повинен виконуватися після виконання кроку Scan, і може виконуватися як до, так і після складання рішення чи проекту. Статичний аналіз буде виконаний тільки змінених файлів з моменту останньої збірки.
  • ScanAndAnalyze — проаналізувати усі залежності для визначення того, на яких файлах повинен бути виконаний інкрементальний аналіз, і відразу ж виконати інкрементальний аналіз змінених файлів з вихідним кодом.
Для отримання більш докладної інформації про режим інкрементального аналізу в модулі для командного рядка PVS-Studio_Cmd.exe зверніться до статей Режим інкрементального аналізу PVS-Studio і Перевірка Visual C++.vcxproj) і Visual C# (.csproj) проектів з командного рядка за допомогою PVS-Studio.

Хочу також відзначити, що з функціональністю інкрементального аналізу відмінно поєднується використання утиліти BlameNotifier, що поставляється в дистрибутиві PVS-Studio. Ця утиліта взаємодіє з популярними системами контролю версій (на даний момент підтримуються Git, Svn і Mercurial), щоб отримати інформацію про те, хто з розробників закоммитил код, що містить помилки, і розіслати повідомлення цим розробникам.

Таким чином, ми рекомендуємо використовувати наступний сценарій застосування статичного аналізатора на сервері безперервної інтеграції:
  • для численних денних збірок виконувати інкрементальний аналіз, щоб контролювати якість коду тільки модифікованих файлів;
  • для нічної складання доцільно виконувати повний аналіз усієї кодової бази, щоб мати повну інформацію про дефекти в коді.
Особливості реалізації режиму інкрементального аналізу в PVS-Studio
Як я вже зазначив, режим інкрементального аналізу в плагіні PVS-Studio для Visual Studio існує вже давно. В плагіні визначення змінених файлів, для яких повинен виконуватися інкрементальний аналіз, було реалізовано з допомогою COM-обгорток Visual Studio. Такий підхід абсолютно непридатний для реалізації режиму інкрементального аналізу у версії нашого аналізатора для командного рядка, оскільки він повністю незалежний від внутрішньої інфраструктури Visual Studio. Підтримка різних реалізацій, які виконують одну й ту ж функцію в різних компонентах, — не найкраща ідея, тому ми відразу вирішили, що плагін для Visual Studio і утиліта командного рядка PVS-Studio_Cmd.exe будуть використовувати загальну кодову базу.

Теоретично, завдання з виявлення модифікованих файлів з моменту останньої складання проекту не представляє особливих труднощів. Для її рішення нам потрібно отримати час модифікації цільового бінарного файлу і час модифікації файлів, які брали участь у побудові цільового бінарного файлу. Ті файли з вихідним кодом, які були змінені пізніше, ніж цільовий файл, повинні бути додані в список файлів для інкрементального аналізу. Реальний світ, однак, набагато складніше. Зокрема, для проектів, реалізованих на C чи C++, досить складно визначити всі файли, що брали участь у побудові цільового файлу, наприклад, ті відмінності файли, які були підключені безпосередньо в коді, і відсутні в проектному файлі. Тут я хочу зазначити, що під Windows і наш плагін для Visual Studio (що очевидно), і версія для командного рядка PVS-Studio_Cmd.exe підтримують тільки аналіз проектів MSBuild. Цей факт суттєво спростив наше завдання. Варто також згадати, що в Linux версії PVS-Studio теж можливе використання інкрементального аналізу — там це працює «з коробки»: при використанні моніторингу компіляції будуть аналізуватися тільки зібрані файли. Відповідно, при инкрементальной збірці запуститься інкрементальний аналіз; при прямої інтеграції в складальну систему (наприклад, у файли make) ситуація буде аналогічною.

У MSBuild реалізований механізм відстеження звернень до файлової системи (File Tracking). Для инкрементальной складання проектів, реалізованих на C і C++, відповідності між вихідними файлами (наприклад, cpp-файлами, заголовочными файлами) і цільовими файлами записуються в *.tlog-файли. Наприклад, для завдання CL шляхи до всіх вхідних файлів, прочитаним компілятором, будуть записані у файл CL.read.{ID}.tlog, а шляху до цільових файлів будуть збережені в файлі CL.write.{ID}.tlog.

Отже, у файлах CL.*.tlog у нас вже є вся інформація про вихідні файли, які були скомпільовані, і про цільових файлах. Завдання поступово спрощується. Однак, все одно залишається завдання обійти всі вихідні і цільові файли і порівняти дати їх модифікації. Можна спростити ще? Звичайно! У просторі імен Microsoft.Build.Utilities знаходимо класи CanonicalTrackedInputFiles і CanonicalTrackedOutputFiles, які відповідають за роботу з файлами CL.read.*.tlog і CL.write.*.tlog відповідно. Створивши примірники цих класів і використовуючи метод CanonicalTrackedInputFiles.ComputeSourcesNeedingCompilation, отримуємо список вихідних файлів, які потребують компіляції, на підставі аналізу цільових файлів і графа залежностей вихідних файлів.

Наведемо приклад коду, який дозволяє отримати список файлів, на яких повинен бути виконаний інкрементальний аналіз, з допомогою вибраного нами підходу. У цьому прикладі sourceFiles – це колекція повних нормалізованих шляхів до всіх вхідних файлів проекту, tlogDirectoryPath – шлях до директорії, в якій перебувають *.tlog-файли:
var sourceFileTaskItems =
new ITaskItem[sourceFiles.Count];

for (var index = 0; index < sourceFiles.Count; index++)
sourceFileTaskItems[index] =
new TaskItem(sourceFiles[index]);

var tLogWriteFiles =
GetTaskItemsFromPath("CL.write.*", tlogDirectoryPath);
var tLogReadFiles =
GetTaskItemsFromPath("CL.read.*", tlogDirectoryPath);

var trackedOutputFiles =
new CanonicalTrackedOutputFiles(tLogWriteFiles);
var trackedInputFiles =
new CanonicalTrackedInputFiles(tLogReadFiles,
sourceFileTaskItems, trackedOutputFiles, false, false);

ITaskItem[] sourcesForIncrementalBuild =
trackedInputFiles.ComputeSourcesNeedingCompilation(true);

Таким чином, використовуючи стандартні засоби MSBuild, ми домоглися того, що механізм визначення файлів для інкрементального аналізу ідентичний внутрішнього механізму MSBuild для инкрементальной складання, що забезпечує дуже високу надійність цього підходу.

Висновок
У цій статті ми розглянули переваги, які дає використання інкрементального аналізу на комп'ютерах розробників і на складальному сервері. Також ми заглянули під капот і дізналися, як, використовуючи можливості MSBuild, визначити, для яких файлів потрібно виконувати інкрементальний аналіз. Пропоную всім, кого зацікавили можливості нашого продукту, завантажити пробну версію аналізатора PVS-Studio і подивитися, а що ж може виявитися в ваших проектах. Всім якісного коду!



Якщо хочете поділитися цією статтею з англомовної аудиторією, то прошу використовувати посилання на переклад: Pavel Kuznetsov. Incremental analysis in PVS-Studio: now on the build server

Прочитали статтю і є питання?Часто до наших статей задають одні і ті ж питання. Відповіді на них ми зібрали тут: Відповіді на питання читачів статей про PVS-Studio, версія 2015. Будь ласка, ознайомтеся зі списком.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.