Як уникнути типових багів вбудованого по


Розуміння помилок програмного і апаратного забезпечення на прикладі інших вбудованих систем може допомогти Вам ідентифікувати, діагностувати і виправити помилки в своїй власній системі.

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

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

В одному випадку ми з колегами знайшли помилку в коді, якої не було в попередній версії. Ми вивчили історію змін від попередньої версії до поточної і звузили пошуки до цієї опції компілятора. Більш детальне дослідження коду показало, що через неакуратно виконаної роботи ми стали незахищеними перед впливом оптимізації. У лістингу 1 представлена ​​спрощена версія того, що ми робили.

x = "hello"; // hello1

Тепер включите оптимізацію, і поведінка зміниться. Оскільки рядок в hello1 і hello2 ідентична, компілятор зберігає одну копію "hello", тому і hello1, і hello2 звернуться до цієї позиції. У цьому випадку виконання рядків x = "hello" і y = "hello" призводить до того, що результат порівняння x і y виявляється істина.

Суть в тому, що if (x == y) ні правильним типом порівняння. Завжди повинна використовуватися функція strcmp () або її еквівалент.

У фільмі «Парк юрського періоду» є сцена, в якій герой Джеффа Голдблюма виявляє, що їх метод перевірки того, чи всі динозаври присутні, полягає в підрахунку загального числа тварин, і як тільки воно досягає певної позначки, рахунок припиняється. Те, що число динозаврів збільшувалася, не було помічено тому, що герої фільму, досягаючи очікуваного числа динозаврів, ніколи не продовжували рахунок. Розумні тварини з'ясували, як розмножуватися, не дивлячись на те, що, імовірно, все клоновані динозаври були жіночої статі.

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

Чи може така помилка виникнути у Вашому програмному забезпеченні? Якщо Ви зберігаєте число в 8-ми розрядної змінної, Ви повинні зупинити рахунок на 255. Це значення можна розглядати як певне помилкове стан або як наближення максимально можливого значення змінної в системі. Який з цих варіантів краще, залежить від того, чи досягає лічильник 255 в помилковому стані або в нормальному.

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

В якості ілюстрації «помилки Парку юрського періоду» наведу приклад зі своєї практики. У нас була система, що вимірює потік газу. Зазвичай система контролювала потоки до 15 л / хв. Верхню межу вимірюваного потоку ми встановили в 25 л / хв. Ми вважали, що навіть якщо потоки газу перевищать 25 л / хв, можна розглядати їх рівними цієї величини.

У деяких випадках потік дійсно перевищував 25 л / хв, але ми вважали, що ці випадки можна не враховувати, оскільки система не перебувала в стані контролю. Один такий випадок був тестом, в якому порівнювалися потоки, виявлені двома ідентичними датчиками. За умови, що датчики функціонували правильно і давали точні свідчення, потоки в кожному датчику повинні були бути в межах допуску один одного. Коли зовнішній тиск в системі подачі газу було дуже високим, тестування могло відбуватися і з перевищенням планки в 25 л / хв. Якщо припустити, що калібрування одного датчика була зміщена, то швидкість потоку, виміряна цими двома датчиками, дорівнювала, наприклад, 26 л / хв і 29 л / хв. Обидві ці величини згодом округлювалися до 25 л / хв з описаних вище причин. Коли прийшов час порівняти обидва свідчення, вони були рівні 25л / хв і відповідно рівні один одному. Виявивши таку ситуацію, ми заборонили використання порівняльного тесту для потоків понад 25л / хв.

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

Я періодично бачу «помилку Парку юрського періоду» в журналах подій. Розглянемо пристрій, який записує виняткові події в журналі. Є обмежений простір, призначене для зберігання події, наприклад, створюється журнал з 30 записами. Кожен запис - це структура, яка містить деталі типу події, час і, можливо, поточні налаштування пристрою. У повному журналі містяться 30 подій. Однак якщо виникне 50 подій, то в журналі їх як і раніше буде 30, таким чином, дійсна кількість подій виявляється прихованим. У цьому випадку проблему можна полегшити, використовуючи одну заключну рядок, яка містить кількість незафіксованих подій. Хоча цей метод і не допоможе дізнатися вам деталі подій, по крайней мере, Ви будете знати, що щось пропущено. Під час використання пристрою цей лічильник вкаже вам на те, чи була величина вкладена в більший обсяг простору пам'яті журналу.

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

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

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

Ми багато використовували макрос assert (). Цей макрос використовує вбудований в компілятор макрос __ LINE __, який прирівнюється до поточної рядку скомпільованої файлу. Якби ми змінили номери рядків, то значення __ LINE __. що використовується кожним assert (), відрізнялося б, а ці значення зберігалися у вихідній програмі.

Niall Murphy "How to Avoid Common Firmware Bugs"

з певного часу цикли строю таким чином що б виключити можливість виходу змінної за можливий діапазон.
тобто наприклад цикл де змінна A змінюється від 0 до 10 і ми порівнюємо умова A = 10. якщо раптом з якихось причин змінна A вийде з границі 10 то ми виконаємо цю операцію незрозуміле число раз поки змінна не переповниться і відлік не почнеться занова з нуля. Якби ми перевіряли умова A => 10? то при такому збої цикл був би коротшим і збій не такий критичний, але знову ж таки залежить від конкретних умов.

Остання ситуація - дійсно цікавий і повчальний випадок. Все інше - тривіально і відомо навіть школярам.