Co się stanie, jeśli wystąpi błąd w czasie wykonywania?

17

Co się stanie, jeśli w programie wystąpi błąd czasu wykonywania? Czy wykonanie programu się po prostu zatrzyma? Czy jest jakiś sposób, aby skłonić Arduino do powiedzenia mi, jaki jest błąd?

Facet z kapeluszem
źródło

Odpowiedzi:

21

Najpierw zobaczmy kilka przykładów tego, co może pójść nie tak.

Niezainicjowane zmienne lokalne

void setup() {
  int status;
  pinMode(13, OUTPUT);
  digitalWrite(13, status);
} 

Jak zauważył Edgar Bonet w komentarzach, zmienne lokalne, takie jak statusw powyższym kodzie, nie są domyślnie inicjowane przez kompilator C ++. Tak więc wynik powyższego kodu jest nieokreślony. Aby tego uniknąć, upewnij się, że zawsze przypisujesz wartości do zmiennych lokalnych.

W przypadku zmiennych globalnych i statycznych sytuacja wygląda nieco inaczej:

Gwarantuje się, że zmienne globalne i statyczne zostaną zainicjowane do wartości 0 przez standard C.

Źródło: Podręcznik referencyjny AVR Libc - Często zadawane pytania - Czy nie powinienem inicjować wszystkich moich zmiennych?

Oznacza to, że nie powinieneś się martwić o zainicjowanie ich na 0 w swoim kodzie. W rzeczywistości powinieneś tego unikać, ponieważ inicjalizacja może marnować pamięć. Zainicjuj je tylko na wartości inne niż 0.

Przepełnienie pamięci

int array[10];
int v = array[100];
array[-100] = 10;

Pierwszym problemem jest to, że nie wiesz, co zostanie przypisane do v, ale gorsze jest to, że nie wiesz, co pomyliłeś przy przypisaniu do pozycji -100 z array.

Przejdź do nielegalnej instrukcji

void doSomething( void ) { 
    for (int i = 0; i < 1000; i++); 
}

void setup () 
{
    void (*funcPtr)( void );

    funcPtr = &doSomething;
    funcPtr(); // calls doSomething();

    funcPtr = NULL;
    funcPtr(); // undefined behavior
}

Pierwsze połączenie do funcPtr()będzie faktycznie połączeniem z doSomething(). Połączenia takie jak drugie mogą prowadzić do nieokreślonego zachowania.

Inne złe rzeczy, które mogą się zdarzyć

Na przykład możesz zabraknąć pamięci RAM. Co jeszcze. W każdym razie myślę, że twój program będzie działał, prawdopodobnie nie w zamierzony sposób.

Rodzaje ochrony

W systemach komputerowych takie problemy są zazwyczaj rozwiązywane na różnych poziomach:

  1. Przez kompilator
  2. Przez środowisko uruchomieniowe języka programowania (jak na przykład Java).
  3. Przez system operacyjny lub procesor (jeśli twoja pamięć uzyskuje dostęp do pozycji poza granicami przestrzeni adresowej zarezerwowanej dla twojego programu, system operacyjny lub procesor mogą mieć mechanizmy bezpieczeństwa, aby temu zapobiec)

Arduinos mają tylko ograniczoną ochronę kompilatora i prawdopodobnie nic więcej. Dobrą wiadomością jest to, że nie są wielozadaniowe, więc dotyczy to tylko twojego programu. W każdym razie każdy z tych błędów spowoduje nieprawidłowe zachowanie.

Odpowiedzi

Założenia są tymi wszystkimi problemami, które wskazałem powyżej, są problemami w czasie wykonywania.

Co się stanie, jeśli w programie wystąpi błąd czasu wykonywania?

Program będzie kontynuowany, a to, co się stanie, będzie zależeć od skutków ubocznych błędu czasu wykonywania. Wywołanie wskaźnika funkcji zerowej prawdopodobnie spowoduje, że program przeskoczy w nieznane miejsce.

Czy wykonanie programu się po prostu zatrzyma?

Nie, będzie tak, jakby nic nadzwyczajnego się nie wydarzyło, prawdopodobnie robiąc to, czego nie zamierzałeś. Może się zresetować lub działać nieprawidłowo. Może przekształcić niektóre wejścia w wyjścia i spalić czujnik lub dwa (ale jest to bardzo mało prawdopodobne ).

Czy jest jakiś sposób, aby Arduino powiedział mi, jaki jest błąd?

Nie wydaje mi się Jak powiedziałem wcześniej, mechanizmów ochronnych nie ma. Język nie obsługuje środowiska uruchomieniowego, nie ma systemu operacyjnego, nie sprawdza sprzętu pod kątem dostępu do pamięci poza zakresem (bootloader nie jest również liczony). Musisz tylko uważać na swój program i prawdopodobnie ustawić własne sieci bezpieczeństwa.

Przyczyną braku ochrony jest prawdopodobnie to, że kontrolery Arduino są zbyt tanie, mają za mało pamięci i nie powinny uruchamiać niczego zbyt ważnego (tak, wydaje się, że AVR zrzeka się gdzieś, aby nie używać MCU normalnie używanych przez Arduino w systemach podtrzymywania życia).

Ricardo
źródło
1
Świetny! Najlepsza odpowiedź, jaką do tej pory widziałem na Arduino.SE!
Facet z kapeluszem
1
Dzięki!! Myślę, że powinniśmy starać się udzielać jak najlepszych odpowiedzi. Ale martwi mnie trochę fakt, że nie mamy tylu PRAWDZIWYCH EKSPERTÓW, którzy mogliby spojrzeć na takie odpowiedzi jak moje i znaleźć rażące błędy. Właściwie to dlatego opublikowałem odpowiedź, chociaż nie wiem zbyt wiele na temat mikrokontrolerów AVR. To sprawdza, czy uda nam się kogoś naprawić. Z pewnością nie chcemy, aby inteligentne pensy, takie jak ja, mówiały rzeczy, które nie są właściwe i nie potrafiły tego uniknąć. Ale to prawdopodobnie dyskusja na stronie Meta.
Ricardo
5
@ Ricardo - Jeden komentarz, który chciałbym napisać, jest taki, że nieinicjalizowane jawnie zmienne niekoniecznie są niezainicjowane. Zmienne zdefiniowane poza funkcjami mają zazwyczaj tak zwany „automatyczny czas przechowywania”, który następnie jest domyślnie inicjowany na zero. Aby uzyskać więcej informacji, zobacz en.cppreference.com/w/cpp/language/default_initialization . Zachowanie inicjalizacyjne jest na tyle złożone, że prawdopodobnie nie można na nim polegać, ale tworzenie ogólnych instrukcji nie jest prawdopodobnie dobrym pomysłem.
Connor Wolf
1
Co więcej, SRAM jest inicjowany na 0 podczas resetowania lub uruchamiania, więc możesz podjąć pewne świadome domysły na temat niezainicjowanych zmiennych, jeśli chcesz żyć niebezpiecznie. Nie powinieneś polegać na tym zachowaniu, ale jest to interesujące.
Connor Wolf
1
Istnieje ciekawy przykład tego, co dzieje się, gdy zabraknie SRAM tutaj: electronics.stackexchange.com/questions/42049/… . Zasadniczo stos blokuje część stosu lub odwrotnie. Może to robić interesujące rzeczy, takie jak uszkodzenie części ramki stosu (przerywanie funkcji zwraca itd.) Lub zapisywanie niepoprawnych danych do zmiennych.
Connor Wolf
9

Nie ma wyjątków czasu wykonywania. Istnieje tylko niezdefiniowane zachowanie.

Naprawdę, nie ma wyjątków w ogóle . Jeśli spróbujesz wykonać niepoprawną operację, jej wyniki będą nieznane.

W ogóle nie ma sprawdzania środowiska wykonawczego, z wyjątkiem tego , co implementujesz. Twój program działa na sprzęcie typu bare-metal. Jest to komputerowy odpowiednik ciągłego działania w pierścieniu 0 , ponieważ ATmega nie ma dzwonków .

Connor Wolf
źródło
6

Istnieje jeden mechanizm, który może uzyskać MCU ze stanu nieregularnego i jest to zegar nadzorujący . Jeśli implementujesz jakiś kod, który wielokrotnie będzie działał w pętli, który nie będzie działał dłużej niż jakiś ustalony czas, możesz ustawić ten czas jako okres nadzoru i włączyć timer.

Następnie musisz wielokrotnie resetować stoper w pętli. Jeśli kod zawiesi się w pętli warunku, która nigdy się nie skończy, wówczas watchdog policzy do zera i ostatecznie zresetuje MCU.

W ten sposób tracisz dane, ale jeśli uruchomisz AVR WDT w trybie przerwania, możesz zapisać niektóre dane przed zresetowaniem MCU.

Tak więc zegar nadzoru może chronić Twój kod przed okazjonalnymi niezamierzonymi niekończącymi się pętlami.

Dokumentacja: AVR132: Korzystanie z ulepszonego timera nadzoru

nio
źródło
5

Potrzebujesz czegoś takiego jak debugger sprzętowy. Zazwyczaj jednak program nie działa tak, jak tego oczekujesz, i musisz spojrzeć na tę sekcję kodu, aby zidentyfikować problem.

Typowym / szybkim / łatwym sposobem na to jest dodanie instrukcji print, aby wydrukować wartości zmiennych lub cokolwiek innego, abyś wiedział, że program bez problemu dotrze do tego miejsca w kodzie. Pomoże to w dalszej izolacji problemu.

Wierzę, że VisualMicro ma wbudowane funkcje debugowania.

sachleen
źródło
3

Zakładam, że procesor AVR nie ma żadnych narzędzi do wykrywania błędów ani odzyskiwania. Może po prostu przestać lub ignorować błąd i konsekwencje. Jak powiedział sachleen, powinieneś dodać do swojego programu kilka instrukcji debugowania, które drukują dane w trakcie operacji, aby sprawdzić, czy działa. Jeśli używasz emulatora i ustawiasz punkty przerwania, możesz łatwo znaleźć problem.

Doktor
źródło
-2

Arduino zrestartuje (czyli będzie to ożywienie setup()i loop()).

użytkownik 28739
źródło
1
Niekoniecznie. Błąd w czasie wykonywania może spowodować, że program przejdzie w pętlę bez ponownego uruchamiania.
Nick Gammon