Elektroda.pl
Elektroda.pl
X
Elektroda.pl
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

assert() czy "obsługa" błędu?

Freddie Chopin 05 Sty 2013 16:37 2019 7
  • #1 05 Sty 2013 16:37
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Witam!

    Jednym z głównych problemów z którym się borykam jest "podejście" do obsługi błędów w świecie mikrokontrolerów. W aplikacji na PC specjalnie nie ma nad czym debatować, dowolny błąd można sobie propagować aż do samej góry, jeśli błąd jest krytyczny to można wyjść do systemu operacyjnego, można sobie używać wyjątków C++ żeby sprawę ułatwić - słowem - można robić wszystkie fantastyczne rzeczy które sobie człowiek wymarzy.

    Tymczasem w świecie embedded jak wiadomo działa to trochę inaczej - np. nie ma systemy operacyjnego do którego można "wyjść", a nawet jeśli jest, to raczej "wyjście" tutaj wiele nie pomoże, bo załóżmy, że debatujemy nad aplikacją która nie jest interaktywną konsolą dla żyjącego użytkownika, tylko typowym sterownikiem zintegrowanym do "czegoś" - wyjdzie taki firmware do OSa i co z tego wynika dobrego? W toku takich rozważań dochodzę często do wniosku, że większość błędów w zasadzie nie ma prawa wystąpić w takim systemie, a wiec najlepszym podejściem jest Design By Contract, wstawienie wszędzie assert() i w trakcie tworzenia/testowania wyłapywanie rzeczy które można poprawić. Problem jest tego typu, że na moje oko jakieś 95% funkcji w takim wypadku będzie "bezbłędnych", pozostałe 5% pozostawiając na funkcje które dotyczą jakiejś interakcji z użytkownikiem lub z medium do którego można jakiś błąd zgłosić i ma to jakikolwiek sens... Zwykle też obsługa błędu jest mało sensowna, bo jakby na to nie patrzeć istotą działania danego urządzenia jest wykonywanie przez niego BEZBŁĘDNIE swojej funkcji, a w środowisku embedded ciężko raczej o jakieś mechanizmy "przywracania" funkcjonalności lepsze niż reset...

    Patrząc od drugiej strony - skoro 95% funkcjonalności nie wynika z nastaw/danych/czegokolwiek wprowadzonego przez użytkownika, a więc np parametry funkcji czy sposób użycia (kolejność, kontekst, ...) zwykle są "generowane" tylko przez programistę, to również sprawdzanie parametrów i obsługa takiego błędu ma mało sensu, bo to nie jest "błąd", tylko "bug" - wniosek => assert(), ale czy aby na pewno?

    Wywód może trochę chaotyczny, ale chętnie poznam przemyślenia innych osób na temat "obsługa błędów" - jak wy to robicie? Jakie jest wasze podejście? Jakie jest wasze zdanie? Jakby na to nie patrzeć programy na PC w 95% przypadków wchodzą w interakcję z użytkownikiem, natomiast w środowisku embedded raczej jest zupełnie odwrotnie - interakcja jest praktycznie zerowa...

    Może zna ktoś fajną literaturę o takich tematach - przypuszczam że to byłoby coś na styl "architektura oprogramowania embedded", ale ja osobiście nie spotkałem się z taką pozycją (;

    Z góry dzięki za odpowiedzi/wypowiedzi!

    4\/3!!

    0 7
  • #2 05 Sty 2013 17:07
    tymon_x
    Poziom 30  

    U mnie to jest tak, że daje z reguły assert w takiej postaci:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    W bibliotekach od Xilinxa stosują rozróżnienie między assert_void() i assert_nonvoid() w zależności jakiej funkcji się znajdują (bez i z return wartością). Większość rzeczy jesteś w stanie przewidzieć, że coś się nie wykrzaczy, jedynie przykładowo zapomniałeś zainicjować wskaźnik, który przekazujesz jako obiekt między funkcjami, assert pomoże to pięknie wskazać bo i tak Tobie on jest potrzebny niż skomplikowana obsługa błędów :) A jak przetestowane, można zawsze wyłączyć assertowanie.

    W technice assert_void i assert_nonvoid zawsze masz możliwość wyboru w razie czego i może warto w tą stronę pójść. Takie tryby do wyboru podczas kompilacji assert, error handling i none :)

    Jestem za, nie zaszkodzi, a zawsze można wyłączyć z kompilacji dla oszczędności pamięci i szybkości wchodzenia w ciało :P

    Troszkę bardziej skomplikowane:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Kod: c
    Zaloguj się, aby zobaczyć kod

    W sprawdzam w assert maskę dla FIFO, jak są same jedynki obok Siebie przechodzi to assertowanie. Bo w moim module FIFO wielkość pamięci jest związana z maską dla liczników read/write. A zawsze mogę się pomylić, a po co mi to :) Nie lubię dziur w pamięci, mimo że też będzie działać.

    0
  • #3 11 Sty 2013 11:46
    Freddie Chopin
    Specjalista - Mikrokontrolery

    No tak, ale czy nie zauważyłeś (Ty lub ktoś inny - zapraszam do dyskusji każdego (; ) tego "efektu", że przestawiając się na assert() czasem ciężko stwierdzić który błąd powinien być obsługiwany (zostawiony do obsługi błędu) a który powinien być traktowany jako bug, czyli wyłapany przez assert()? Bo jak dla mnie w programie którym się teraz zajmuję to na oko jakieś 99% wyjątkowych sytuacji potraktowałbym za pomocą assert(), a co za tym idzie program stałby się "bezbłędny" - czy to jest normalne?

    No bo np. bawię się emulacją EEPROMu we flashu - kusi mnie aby potraktować każdy błąd sprzętu (nieudany zapis, kasowanie, brak wolnego miejsca) przy pomocy assert(), no bo tak naprawdę jak miałbym niby "obsłużyć" taką sytuację? W teorii akurat te operacje mogłby zwracać błąd, bo zapis wywoływany jest przez MODBUS, a więc mam możliwość zwrócenia np błędu że jest low-level hardware problem, tylko że tutaj dochodzimy do dyskusji o tym co jest "gwarantowane" a co nie - skoro w MCU udostępniona jest możliwość zapisu/kasowania pamięci flash, to można podejść do sprawy tak, że jeśli jest z nią jakiś problem to wynika on albo z kodu (niewłaściwe użycie - assert()), albo z tego że sprzęt właśnie umiera (i tak nie ma co tego obsługiwać, bo i po co - assert() w takiej sytuacji spowoduje timeout na MODBUSie, więc sprawa nie przejdzie "niezauważona" - w teorii...).

    Ach te dylematy...

    P.S. czym się różni assert_void() od assert_nonvoid()?

    4\/3!!

    0
  • #5 12 Sty 2013 11:33
    Luklukowaty
    Poziom 19  

    Ja kwestię rozpatruje troszkę inaczej. Właśnie wielokrotnie miałem duże problemy (i pewnie nie ja) ze względu na sposób działania urządzeń tak jak Ty to napisałeś - jest jakiś problem? a to wywalić ten problem do kosza.

    Niestety - każde urządzenie nie będzie działać bezbłędnie ze względu na różne fizyczne niedociągnięcia (zużycie Flasha, zaniki napięcia, zwarcia, wytarcie łożysk, zamarznięcie siłownika, etc., etc.).

    Ja uważam inaczej - ZAWSZE w pewnym momencie każdego układu jest gdzieś jakieś urządzenie służące do interakcji z użytkownikiem. Samo urządzenie może nie posiadać takowego, ale komunikuje się ze światem zewnętrznym (jak Twoje urządzenie na Modbus) i brakuje mi w większości urządzeń zaawansowanej diagnostyki. Dlaczego? Otóż cały system regulacyjny/automatyki powinien działać bezbłędnie. W świecie realnym tak nie jest i istotne jest szybkie zweryfikowanie problemu i jego naprawa. W przypadku, gdy urządzenie przykładowo ignoruje błędy, które posiada (a mogą np. wyniknąć z błędów we Flashu - prosta cykliczna weryfikacja CRC softwaru załatwiła by sprawę) powoduje zakłócenia pracy całego układu. Ukrywając się w cieniu i udając, że jest wszystko OK takie urządzenie - moim zdaniem - więcej psuje niż daje na plus.

    W embedded o tyle trudniej jest z alarmami/awariami/etc. że użytkownika nie ma na bieżąco. Moim zdaniem:

    - urządzenie powinno wszystkie awarie/błędy/etc. monitorować i logować
    - dostęp do zalogowanych błędów powinien być możliwy za pomocą dostępnych interfejsów urządzenia
    - jeżeli urządzenie posiada wyświetlacz lub GUI powinien o błędach informować
    - jednocześnie urządzenie nie powinno przestawać działać znajdując błąd oprócz błędów krytycznych, które przewidzieliśmy i mogą co nieco uszkodzić (np. przetwornik Pt100 po wykryciu zwarcia/rozwarcia rezystora powinien wystawić odpowiedni błąd i nie udawać, że mierzy temperaturę wskazując ostatnio zmierzoną wartość - tylko odmówić właściwej odpowiedzi np. poprzez Modbus).

    oraz wiele innych. Uważam, że problematyka błędów i awarii jest pomijana przez automatyków oraz przez samych producentów sprzętu ponieważ czasami implementacja "a co by było gdyby" zajmuje 2 czy 3 razy tyle co właściwego oprogramowania. Zakłada się, że urządzenia mają działać bezbłędnie a tak nigdy nie było, nie jest i raczej nie będzie. Pamiętajmy, że często zignorowanie błędu może spowodować do poważnej awarii a nawet śmierci człowieka !!!

    Reasumując:
    - detekcja błędu, zalogowanie go i poinformowanie o tym urządzenia nadrzędnego/użytkownika
    - wznowienie działania programu z błędem jeżeli nie spowoduje to szkód, lub bezwzględna odmowa pracy w zależności od roli błędu i samego urządzenia

    P.S.
    do powyższych wywodów zaliczam również przykładowo błędy CRC podczas komunikacji z cyfrowymi czujnikami, które są obok procka (np. pomiar temp. i wilgotności). Tak samo błąd w oprogramowaniu, typowy BUG - jeżeli wystąpi może spowodować straty materialne i zdrowotne [przykładowo czujnik temperatury zamiast wskazać 1600 stopni wskaże 1000 stopni przez zignorowanego buga przepełnienia stosu, czy dzielenia przez zero, czy co innego].

    Może i się powtarzam, ale napiszę jeszcze raz - układy automatyki to nie są zabawki dla dzieci, albo fajfon dla wyzwolonej feministki...

    0
  • #6 12 Sty 2013 11:45
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Tylko że assert() to nie jest ignorowanie błędu, tylko zatrzymanie całego programu (lub ewentualnie aktualnego wątku) i zgłoszenie błędu, zamiast jego obsługi przez logowanie, interakcję i decydowanie co z nim zrobić - naprawić, zignorować, zresetować, zdiagnozować itd.

    4\/3!!

    0
  • #7 12 Sty 2013 12:19
    Luklukowaty
    Poziom 19  

    Nie odnosiłem się do "assert()" tylko do całej koncepcji awarii czy błędów, ich detekcji, etc.

    Przekładając to na assert() - można go dać podczas prototypu, dla urządzenia końcowego według mnie nie jest to akceptowalne w takiej "bezmyślnej" formie.

    0