Inżynierowie oprogramowania wbudowanego uwielbiają rozwiązywać problemy. To właśnie podstawa, tego, co robią. Niestety jednym z największych ich mankamentów jest to, że tworzą oni wiele niedoskonałości, aby następnie stać się bohaterami, spędzając ogromną ilość czasu na ich usprawnieniu (debugowanie!). To dość typowe, bez przeszkód można znaleźć firmy, w których inżynierowie oprogramowania wbudowanego wykorzystują 20-40% czasu na samo tylko debugowanie kodu! Na szczęście istnieje wiele potencjalnych rzeczy, które zespoły mogą zrobić inaczej, aby ten period skrócić, zamieniając w jednocyfrową wartość procentową. W tym artykule przeanalizujemy kilka wskazówek dotyczących skrócenia okresu debugowania.
Wskazówka nr 1 — uwzględnij rozwój oparty na testach (TDD)
Programowanie sterowane testami to paradygmat pozwalający deweloperom na przyrostowe budowanie oprogramowania produkcyjnego, w którym polegają na weryfikacji, aby oceniać napisany przez siebie kod. Na przykład TDD pozwala programistom najpierw napisać przypadek testowy, sprawić, że zakończy się niepowodzeniem. A następnie stworzyć kod, który umożliwia zaliczenie. Proces jest dalej powtarzany dla kolejnych funkcji.
Tradycyjnie twórcy oprogramowania wbudowanego piszą całe moduły kodu przed jego testowaniem. Możliwe jest wygenerowanie tysięcy linijek w ciągu kilku tygodni. Następnie, kiedy przychodzi czas na przetestowanie, gdzie jest problem, jeśli coś nie działa? Bóg wie! Deweloper musi skrupulatnie przejrzeć cały kod i odkryć, co stanowi mankament i oczywiście go naprawić. Wymagane może być do tego sporo czasu.
Z drugiej strony, w przypadku programistów korzystających z TDD, jeśli zostanie popełniony błąd i znajdzie się on w kodzie, najbliższy przypadek testowy natychmiast o tym poinformuje! Ponieważ stopniowo piszą i jednocześnie testują oni swój kod, jest bardziej prawdopodobne, że dokładnie wiedzą, co zmienili i mogą błyskawicznie rozwiązać niedogodność. TDD może wydawać się bardziej pracochłonne, ale tworzy zbiór przypadków, które można uruchomić w testach regresji, aby upewnić się, że wszystko działa zgodnie z oczekiwaniami. TDD dostarcza dwie pieczenie na jednym ogniu: skraca się czas debugowania i od razu zapewnia testy automatyczne dla całego kodu.
Wskazówka #2 — rozwijaj możliwie jak najwięcej kodu poza docelową platformą
Gdy projekt się rozpoczyna jednym z pierwszych odruchów niemal każdego dewelopera jest zaopatrzenie się w płytkę rozwojową i rozpoczęcie pisania kodu wbudowanego. Niestety w wielu przypadkach ten osadzony nie jest wyróżnikiem naszych aplikacji. Podczas gdy duża ilość kodu aplikacji ostatecznie musi wejść w interakcję ze sprzętem, wiele modułów można opracować poza platformą wbudowaną, tj. na komputerze, a nie mikrokontrolerze.
Tworzenie kodu poza docelowym urządzeniem daje programistom sporo możliwości skrócenia czasu poświęcanego na każdy cykl debugowania. Na przykład zazwyczaj, aby napisać i przetestować go dla docelowego mikrokontrolera, programista musi:
* Skompilować kod na docelową platformę (cross-kompilacja);
* Rozpocząć sesję debugowania;
* Zaprogramować urządzenie docelowe, np. przez SWD;
* Uruchomić kod na właściwym układzie;
* Sprawdzić, czy wszystko działa, uruchamiając całość w systemie (musi on również zawierać kod niskiego poziomu).
Jeśli kod jest opracowywany na komputerze, deweloper musi go skompilować dla adekwatnej maszyny, a następnie użyć zestawu testów jednostkowych, emulatora lub niestandardowego programu w ramach zainicjowania. Jeśli problem zostanie znaleziony, naprawienie, ponowna kompilacja i uruchomienie jest znacznie szybsze. W przypadku wbudowanego celu, samo zaprogramowanie może dodać dziesiątki sekund do każdego cyklu, nie mówiąc już o pokusie wykonywania kodu krok po kroku.
Istnieją określone błędy, które może spowodować tworzenie/debugowanie poza docelową platformą. Jednak mimo wszystko to podejście jest znacznie szybsze i wydajniejsze. Zamiast śledzić problemy przez system wbudowany, można je szybko wymusić lub określone zachowania w kodzie, określić przyczynę danych sytuacji, naprawić ją i przejść dalej. To prawda, że niektóre rzeczy pojawią się dopiero na właściwej platformie wbudowanej, a nie na komputerze, jednak nadal nie ma ich na tyle dużo, by czynić takie ujęcie bezsensownym.
Wskazówka #3 — opanuj strategie debugowania
Najmniej wydajną metodą debugowania znaną ludzkości jest wykonywanie pojedynczych kroków przez wiersze kodu. Oczywiście... jest czas i miejsce, aby tak robić, ale często jest to po prostu bezwartościowe. Niestety, twórcy oprogramowania wbudowanego domyślnie stosują debugowanie za pomocą punktów przerwania i przeprowadzania pojedynczych kroków, aby lepiej sobie radzić. Programiści muszą opanować inne strategie dostępne w nowoczesnych mikrokontrolerach.
Obecnie deweloperzy mają dostęp do co najmniej ośmiu różnych technik debugowania. Obejmują one od najprostszych do najbardziej złożonych:
* Watch/Expressions: pozwala programiście na zbadanie rejestrów procesora i urządzeń peryferyjnych. Często mogą być używane do szpiegowania zmiennych, przeprowadzania obliczeń lub zatrzymywania procesora w przypadku zmiany.
* Punkty przerwania: umożliwiają zastopowanie wykonywania procesora w określonym wierszu kodu. Mogą one służyć do ustawiania punktów z pomocą instrukcji warunkowych.
* printf: zapewniają możliwość drukowania danych znakowych na zmapowanym interfejsie szeregowym. W zależności od implementacji może to mieć wpływ na wydajność w czasie rzeczywistym lub nie.
* Asercje: są to instrukcje warunkowe wykorzystywane do weryfikacji założeń w określonym punkcie programu. Niepowodzenie często powoduje zatrzymanie procesora i podanie lokalizacji w kodzie i wiersza nieudanej asercji.
* Profilowanie statystyczne: okresowe próbkowanie różnych rejestrów w aplikacji, które występują jednocześnie z jej uruchomieniem. Często nie wpływa na wydajność w czasie rzeczywistym. Na przykład można sprawdzać licznik programu (PC), aby zrozumieć, jakie moduły kodu są wykonywane.
* Profilowanie danych: okresowe próbkowanie różnych lokalizacji pamięci, które zawierają zmienne dane. Profilowanie zasobów może być świetne, gdy jest używane z wizualizatorem w czasie rzeczywistym do monitorowania stanu systemu, zmian interesujących zmiennych i tak dalej.
* Śledzenie zadań i danych: gwarantuje programistom możliwość obserwowania zdarzeń w aplikacji systemu operacyjnego w czasie rzeczywistym. W rezultacie można uzyskać wgląd w wydajność, opóźnienia zadań, czasy wykonywania i wiele więcej.
* Śledzenie instrukcji: zapewnia sposobność rejestrowania każdej instrukcji przeprowadzanej na procesorze. Może to służyć do zrozumienia pokrycia kodu podczas testowania, debugowania problemów z kompilatorem i nie tylko.
Opanowanie wszystkich tych technik i wiedza, kiedy ich użyć może znacznie ograniczyć zakres pracy przeznaczanej na debugowanie, gdy błąd dostanie się do systemu.
Podsumowanie
Można poświęcić dużo czasu na debugowanie oprogramowania wbudowanego. I czasami nie da się tego uniknąć, jednak programiści mogą obecnie czynić to częściej niż to konieczne. Przedstawiono powyżej w zarysie kilka obszarów, które można dokładniej zbadać, aby skrócić ten period. Jeśli dedykujecie więcej niż 20% swojego czasu pracy na debugowanie, warto wydzielić godzinę tygodniowo, aby określić, jakie zmiany można zacząć natychmiast wprowadzać do procesu tworzenia oprogramowania, by zminimalizować tę wartość.
Źródło: https://www.embedded.com/3-tips-for-decreasing-time-spent-debugging/
Wskazówka nr 1 — uwzględnij rozwój oparty na testach (TDD)
Programowanie sterowane testami to paradygmat pozwalający deweloperom na przyrostowe budowanie oprogramowania produkcyjnego, w którym polegają na weryfikacji, aby oceniać napisany przez siebie kod. Na przykład TDD pozwala programistom najpierw napisać przypadek testowy, sprawić, że zakończy się niepowodzeniem. A następnie stworzyć kod, który umożliwia zaliczenie. Proces jest dalej powtarzany dla kolejnych funkcji.
Tradycyjnie twórcy oprogramowania wbudowanego piszą całe moduły kodu przed jego testowaniem. Możliwe jest wygenerowanie tysięcy linijek w ciągu kilku tygodni. Następnie, kiedy przychodzi czas na przetestowanie, gdzie jest problem, jeśli coś nie działa? Bóg wie! Deweloper musi skrupulatnie przejrzeć cały kod i odkryć, co stanowi mankament i oczywiście go naprawić. Wymagane może być do tego sporo czasu.
Z drugiej strony, w przypadku programistów korzystających z TDD, jeśli zostanie popełniony błąd i znajdzie się on w kodzie, najbliższy przypadek testowy natychmiast o tym poinformuje! Ponieważ stopniowo piszą i jednocześnie testują oni swój kod, jest bardziej prawdopodobne, że dokładnie wiedzą, co zmienili i mogą błyskawicznie rozwiązać niedogodność. TDD może wydawać się bardziej pracochłonne, ale tworzy zbiór przypadków, które można uruchomić w testach regresji, aby upewnić się, że wszystko działa zgodnie z oczekiwaniami. TDD dostarcza dwie pieczenie na jednym ogniu: skraca się czas debugowania i od razu zapewnia testy automatyczne dla całego kodu.
Wskazówka #2 — rozwijaj możliwie jak najwięcej kodu poza docelową platformą
Gdy projekt się rozpoczyna jednym z pierwszych odruchów niemal każdego dewelopera jest zaopatrzenie się w płytkę rozwojową i rozpoczęcie pisania kodu wbudowanego. Niestety w wielu przypadkach ten osadzony nie jest wyróżnikiem naszych aplikacji. Podczas gdy duża ilość kodu aplikacji ostatecznie musi wejść w interakcję ze sprzętem, wiele modułów można opracować poza platformą wbudowaną, tj. na komputerze, a nie mikrokontrolerze.
Tworzenie kodu poza docelowym urządzeniem daje programistom sporo możliwości skrócenia czasu poświęcanego na każdy cykl debugowania. Na przykład zazwyczaj, aby napisać i przetestować go dla docelowego mikrokontrolera, programista musi:
* Skompilować kod na docelową platformę (cross-kompilacja);
* Rozpocząć sesję debugowania;
* Zaprogramować urządzenie docelowe, np. przez SWD;
* Uruchomić kod na właściwym układzie;
* Sprawdzić, czy wszystko działa, uruchamiając całość w systemie (musi on również zawierać kod niskiego poziomu).
Jeśli kod jest opracowywany na komputerze, deweloper musi go skompilować dla adekwatnej maszyny, a następnie użyć zestawu testów jednostkowych, emulatora lub niestandardowego programu w ramach zainicjowania. Jeśli problem zostanie znaleziony, naprawienie, ponowna kompilacja i uruchomienie jest znacznie szybsze. W przypadku wbudowanego celu, samo zaprogramowanie może dodać dziesiątki sekund do każdego cyklu, nie mówiąc już o pokusie wykonywania kodu krok po kroku.
Istnieją określone błędy, które może spowodować tworzenie/debugowanie poza docelową platformą. Jednak mimo wszystko to podejście jest znacznie szybsze i wydajniejsze. Zamiast śledzić problemy przez system wbudowany, można je szybko wymusić lub określone zachowania w kodzie, określić przyczynę danych sytuacji, naprawić ją i przejść dalej. To prawda, że niektóre rzeczy pojawią się dopiero na właściwej platformie wbudowanej, a nie na komputerze, jednak nadal nie ma ich na tyle dużo, by czynić takie ujęcie bezsensownym.
Wskazówka #3 — opanuj strategie debugowania
Najmniej wydajną metodą debugowania znaną ludzkości jest wykonywanie pojedynczych kroków przez wiersze kodu. Oczywiście... jest czas i miejsce, aby tak robić, ale często jest to po prostu bezwartościowe. Niestety, twórcy oprogramowania wbudowanego domyślnie stosują debugowanie za pomocą punktów przerwania i przeprowadzania pojedynczych kroków, aby lepiej sobie radzić. Programiści muszą opanować inne strategie dostępne w nowoczesnych mikrokontrolerach.
Obecnie deweloperzy mają dostęp do co najmniej ośmiu różnych technik debugowania. Obejmują one od najprostszych do najbardziej złożonych:
* Watch/Expressions: pozwala programiście na zbadanie rejestrów procesora i urządzeń peryferyjnych. Często mogą być używane do szpiegowania zmiennych, przeprowadzania obliczeń lub zatrzymywania procesora w przypadku zmiany.
* Punkty przerwania: umożliwiają zastopowanie wykonywania procesora w określonym wierszu kodu. Mogą one służyć do ustawiania punktów z pomocą instrukcji warunkowych.
* printf: zapewniają możliwość drukowania danych znakowych na zmapowanym interfejsie szeregowym. W zależności od implementacji może to mieć wpływ na wydajność w czasie rzeczywistym lub nie.
* Asercje: są to instrukcje warunkowe wykorzystywane do weryfikacji założeń w określonym punkcie programu. Niepowodzenie często powoduje zatrzymanie procesora i podanie lokalizacji w kodzie i wiersza nieudanej asercji.
* Profilowanie statystyczne: okresowe próbkowanie różnych rejestrów w aplikacji, które występują jednocześnie z jej uruchomieniem. Często nie wpływa na wydajność w czasie rzeczywistym. Na przykład można sprawdzać licznik programu (PC), aby zrozumieć, jakie moduły kodu są wykonywane.
* Profilowanie danych: okresowe próbkowanie różnych lokalizacji pamięci, które zawierają zmienne dane. Profilowanie zasobów może być świetne, gdy jest używane z wizualizatorem w czasie rzeczywistym do monitorowania stanu systemu, zmian interesujących zmiennych i tak dalej.
* Śledzenie zadań i danych: gwarantuje programistom możliwość obserwowania zdarzeń w aplikacji systemu operacyjnego w czasie rzeczywistym. W rezultacie można uzyskać wgląd w wydajność, opóźnienia zadań, czasy wykonywania i wiele więcej.
* Śledzenie instrukcji: zapewnia sposobność rejestrowania każdej instrukcji przeprowadzanej na procesorze. Może to służyć do zrozumienia pokrycia kodu podczas testowania, debugowania problemów z kompilatorem i nie tylko.
Opanowanie wszystkich tych technik i wiedza, kiedy ich użyć może znacznie ograniczyć zakres pracy przeznaczanej na debugowanie, gdy błąd dostanie się do systemu.
Podsumowanie
Można poświęcić dużo czasu na debugowanie oprogramowania wbudowanego. I czasami nie da się tego uniknąć, jednak programiści mogą obecnie czynić to częściej niż to konieczne. Przedstawiono powyżej w zarysie kilka obszarów, które można dokładniej zbadać, aby skrócić ten period. Jeśli dedykujecie więcej niż 20% swojego czasu pracy na debugowanie, warto wydzielić godzinę tygodniowo, aby określić, jakie zmiany można zacząć natychmiast wprowadzać do procesu tworzenia oprogramowania, by zminimalizować tę wartość.
Źródło: https://www.embedded.com/3-tips-for-decreasing-time-spent-debugging/
Cool? Ranking DIY