10 narzędzi programistycznych

narzędzia do tworzenia oprogramowania

Narzędzia programistyczne są dla Ciebie ważne, w przeciwnym razie nie trafiłbyś do tego artykułu. Wydajne narzędzie może znacznie zwiększyć produktywność zespołu. Nie chodzi tylko o narzędzia programistyczne, ale także o komunikację i zarządzanie zasobami.

Wdrożenie nowego narzędzia programistycznego, przyzwyczajenie się do niego i zrozumienie go wymaga czasu. Możliwości wyboru platformy programistycznej jest wiele. Ten artykuł zawiera listę najczęściej używanych narzędzi używanych w działach rozwoju.

1. Tekst sublime

Sublime Text to narzędzie programistyczne do edycji kodu źródłowego. Istnieje wiele skrótów klawiaturowych, które umożliwiają łatwe otwieranie plików i przełączanie między wierszami. Możesz edytować wiele wierszy jednocześnie dzięki opcji wielokrotnego wyboru. Możesz także łatwo przełączać się między projektami. Interfejs API wtyczki jest również silny.

2. Narzędzie programistyczne Intel

Intellij to zintegrowane środowisko programistyczne. IDE integruje wszystkie potrzebne narzędzia. Ma więc edytor kodu, kompilator, debugger, itp. Intellij działa automatycznie i jest naprawdę przyjazny dla użytkownika i łatwy w użyciu. Pomaga również w szybkim poruszaniu się po kodzie i analizowaniu błędów. Znacznie zwiększa produktywność.

3. Doker

Docker oferuje platformę konteneryzacji oprogramowania, która umożliwia umieszczenie aplikacji lub oprogramowania w kontenerze plików. Ten pojemnik można umieścić i wyeksportować w dowolnym miejscu. Znajdziesz tu wszystko, czego potrzebujesz do pracy: kod, biblioteki systemowe itp. Oznacza to, że oprogramowanie będzie działać wszędzie tak samo i nie jest zależne od środowiska.

4. Git

Git to system kontroli wersji typu open source dla projektów oprogramowania. Deweloper musi często wprowadzać zmiany w kodzie, zanim najnowsza wersja będzie gotowa.

System kontroli wersji przechowuje każdą dokonaną zmianę, aby zespół mógł współpracować. Przechowywana jest kopia kodu każdego programisty. Dzięki Git praca zespołowa może być zsynchronizowana, kod jest aktualizowany w dowolnym momencie.

5. GitHub

GitHub to repozytorium Git w Internecie. To jak dropbox dla projektów oprogramowania, w którym można znaleźć kod. Przesyłając projekt, możesz ustawić go jako publiczny lub prywatny. To świetne miejsce do nawiązywania kontaktów, poznawania ludzi o podobnych poglądach i udostępniania projektów. Społeczność jest ogromna, a liczba projektów jeszcze większa.

6. Jira

Jira jest przeznaczona dla zespołów zwinnych do planowania, śledzenia i wydawania oprogramowania. Oczywiście, podczas gdy zespoły Agile używają Jira. Obsługuje zarządzanie projektami. Jira jest wysoce konfigurowalna i ma zaawansowane funkcje na każdym etapie rozwoju. Możesz zarządzać postępem, wizualizować bieżącą pracę i generować raporty w jednym narzędziu.

7. Jenkins

Jenkins to serwer automatyzacji typu open source, a dokładniej: Serwer ciągłej integracji? Możesz sobie wyobrazić to narzędzie do tworzenia oprogramowania jako pośrednik między twoim kodem a serwerem kompilacji. Jenkins regularnie szuka zmian na twoim serwerze i wysyła je do serwera kompilacji. Narzędzie ma świetne wtyczki ułatwiające życie.

8. Slack

Slack to aplikacja, której można używać do komunikacji zespołowej. Jest świetny, ponieważ można go używać wszędzie. Użycie znacznie ogranicza wewnętrzne wykorzystanie poczty elektronicznej. Slack jest bardzo praktyczny: masz „kanały”, które tworzysz dla projektów, zespołów, tematów itp. Możesz łatwo nawigować między kanałami. Masz również możliwość wysyłania bezpośrednich wiadomości prywatnych do członków swojego zespołu. Możesz udostępniać i komentować wszelkiego rodzaju pliki lub oznaczać ludzi. Możesz także przeszukać całą historię.

9. Przepełnienie stosu

Stack Overflow nie jest tak naprawdę narzędziem do tworzenia oprogramowania. Jest to witryna z pytaniami i odpowiedziami z największą społecznością programistów. W tej bibliotece znajdziesz wszystkie odpowiedzi na swoje pytania. Od zmiany koloru tekstu do zmiany jądra Linuksa. To świetne miejsce do nauki i dzielenia się wiedzą.

10. YouTube

Dzięki YouTube możesz przeglądać samouczki i porady innych specjalistów IT. Ale także seminaria internetowe, które przegapiliśmy.

Inne narzędzia programistyczne

Ta lista nie jest jedynym narzędziem programistycznym, którego możesz użyć. Istnieje wiele innych dobrych narzędzi do znalezienia. To zależy tylko od tego, co wolisz. Jeśli chcesz podzielić się z nami innymi sugestiami dotyczącymi narzędzi do tworzenia oprogramowania, możesz odpowiedzieć w grupach LinkedIn.

Zalecenia dotyczące narzędzi ITpedia Development Propozycja oprogramowania do tworzenia aplikacji Kluczowe funkcje, których należy szukać w oprogramowaniu do tworzenia aplikacji: Podejmując decyzję, które oprogramowanie do tworzenia aplikacji najlepiej pomoże Ci w tworzeniu dostosowanych aplikacji, zwróć dużą uwagę na cztery kluczowe obszary: funkcje tworzenia treści, dystrybucję, zaangażowanie klientów i pomoc oraz zaplecze pomocnicze. Tworzenie treści Dystrybucja i zgodność z systemem operacyjnym Funkcje pomocy i wsparcia Customer Engagement

Zaległości w pracy Oprogramowanie do zarządzania projektami online dla programistów | Backlog Backlog to kompleksowe oprogramowanie do zarządzania projektami dla całego zespołu. Śledzenie problemów, hosting Git i kontrola wersji oraz Wiki. Odpowiednie narzędzia, które pomogą Twojemu zespołowi programistów szybciej dostarczać wysokiej jakości projekty. Zacznij od darmowego konta!

Caylent Rozwiązania DevOps dla wszystkich Caylent dostarcza niestandardowe rozwiązania DevOps dla firm na każdym etapie, dając Twojemu zespołowi swobodę skupienia się na funkcjach generujących przychody, a nie na infrastrukturze. Umożliwić zespołom programistycznym automatyzację wdrożeń kontenerów bez żadnych kłopotów lub zarządzanie infrastrukturą chmurową lub utrzymywanie rurociągów CI i CD. Powoduje to bezproblemową współpracę między zespołami programistycznymi i operacyjnymi, co pozwala im uprościć najbardziej wymagające przepływy pracy.

Ul Układy projektu. Organizuj projekty na wykresie Gantta, tablicy Kanban, tabeli lub kalendarzu i łatwo przełączaj się między poszczególnymi układami. Aktualizacje są odzwierciedlane we wszystkich widokach projektu, więc cały zespół jest informowany niezależnie od używanej opcji. Podsumowanie widoków. Połącz kilka projektów i zobacz pełny obraz swojej firmy lub działu. Projekty można sortować według aktualnego stanu, członka zespołu lub przypisanych etykiet. Szablony akcji. Łatwo planuj i powtarzaj zadania za pomocą szablonów akcji. Ułóż wszystkie wymagane kroki w szablonie działań wielokrotnego użytku, aby przydzielać zadania odpowiednim osobom we właściwym czasie.

Zaproponuj oprogramowanie do zarządzania projektami IT Najlepsze rozwiązania do zarządzania projektami IT dla zespołów DevOps

DV Zarządzanie zwinne to zestaw zasad, które pomagają zarządzać projektami i zespołami. Chociaż jest to często błędnie interpretowane, jako że wielu menedżerów magicznych sztuczek śledzi bez zrozumienia ich prawdziwej wartości, to, co naprawdę oferuje Agile, to lista podstawowych wartości i wytycznych, które, jak udowodniono, zwiększają zarówno wydajność zespołu, jak i odpowiedzialność.

Usługi tworzenia stron internetowych Najlepsi twórcy stron internetowych, jak: Wix, Bizness Apps, Weebly i Web Site Builder.

Porozmawiaj z nami LinkedIn.

5 narzędzi używanych w inżynierii systemów wbudowanych, których będziesz musiał się nauczyć

Jednym ze schematów, za którym podążają osoby chcące rozpocząć swoją karierę w świecie systemów wbudowanych, jest przygotowanie do pracy oparte na kilku podstawach. Są to m.in. lektura popularnych książek poruszających tematykę programowania w języku C mikrokontrolerów z rodziny AVR lub STM32, znajomość podstaw elektroniki cyfrowej i analogowej, wykształcenie techniczne skorelowane z tematyką embedded oraz wykorzystanie tej wiedzy w praktyce poprzez wykonanie, chociaż kilku działających układów opartych o wymienione wcześniej platformy. Jest to niewątpliwie słuszne podejście. Warto jednak móc dodatkowo wyróżnić się czymś więcej na tle innych kandydatów – czymś, czym można zaskoczyć swojego przyszłego pracodawcę podczas rozmowy rekrutacyjnej.

Michał Łokcewicz. Programista systemów wbudowanych i Lider zespołu programistów w Comarch. Zajmuje się analizą wymagań, implementacją i nadzorem nad architekturą oprogramowania dla urządzeń telemedycznych (przede wszystkim rejestratorów EKG). Pracuje głównie z językami C i C++. Prywatnie gitarzysta w zespole nic ważnego.

Poza umiejętnościami i narzędziami, które stanowią rdzeń pracy programisty systemów wbudowanych, jak np. znajomość C/C++, znajomość architektury i układów peryferyjnych stosowanego mikrokontrolera), w profesjonalnym wytwarzaniu oprogramowania wykorzystywane jest dodatkowo wiele narzędzi, mających usprawnić lub zautomatyzować proces. Do takich rozwiązań zaliczyć możemy recenzję kodu (ang. code review), testowanie, kompilację projektu lub analizę statyczną kodu – czyli wszystkie te procesy, którymi nie zajmuje się hobbysta chcący po prostu tworzyć ciekawe projekty. Wiele z narzędzi jest powszechnych w produkcji oprogramowania niezależnie od jego specyfiki – np. narzędzia kontroli wersji takie jak Git lub systemy zarządzania rozwojem projektu, np. Jira, ale często używane są też narzędzia dedykowane i przystosowane do specyfiki inżynierii systemów wbudowanych. Prędzej czy później zetkniesz się z nimi podczas swojej kariery. Opanowanie ich wcześniej przyniesie Ci wiele pożytku, nie tylko podczas rozmowy rekrutacyjnej, ale też podczas wdrażania się do nowego projektu.

CMake

Jak skompilować kod? W najprostszym przypadku ograniczać się to będzie do skorzystania z przycisku w oknie środowiska programistycznego (IDE). W analogiczny sposób możemy załadować program do pamięci i nie przejmować się całą tą złożoną sekwencją zdarzeń, w wyniku której otrzymujemy działający na mikrokontrolerze program. W praktyce jednak zachodzi potrzeba uniezależnienia się od środowiska programistycznego oraz dostosowania procesu budowania projektu do własnych potrzeb. Ponadto proces budowania projektu przez IDE, choć wygodny dla programistów, zwykle nie jest łatwy do automatyzacji. Odpowiedzmy sobie zatem na pytanie – co tak naprawdę trzeba zrobić, aby skompilować kod źródłowy do pliku binarnego? W pierwszym podejściu, budowanie projektu ogranicza się do długiej listy wywołań szeregu aplikacji toolchaina z odpowiednimi parametrami – trzeba jedynie wskazać gdzie znajdują się pliki źródłowe, gdzie znajdują się pliki nagłówkowe, jak ma wyglądać plik wynikowy (czy to w postaci binarnej, Intel hex, czy bardziej uniwersalnej postaci pliku wskazać opcje kompilacji takie jak optymalizacja, wybrać standard języka, flagi manipulujące listą wyświetlanych przez kompilator ostrzeżeń i sprawdzanych błędów… no właśnie. O ile taka metoda bezpośredniego wywołania kompilatora i ręczne wpisywanie parametrów za każdym razem może się sprawdzić w przypadku kompilacji kilku plików źródłowych (chociaż i to jest mocno naciągane – nikt tak nie robi), o tyle w przypadku odrobinę poważniejszych projektów, plików i wariantów tworzenia pliku wynikowego przybywa tyle, że trudno jest zapanować nad prostą czynnością, jaką jest kompilacja kodu.

Pierwszym krokiem, aby uporządkować i uprościć tę czynność jest tworzenie plików Makefile – w dużym skrócie jest to plik zawierający przepis na plik wynikowy. Zawiera dane niezbędne dla kompilatora takie jak ścieżki do plików źródłowych i nagłówkowych.

Właśnie w ten sposób popularne w środowiskach embedded IDE kompilują projekty. Każdy nowo utworzony plik uwzględniany jest w tworzonych na bieżąco przez IDE plikach Makefile. Aby jednak nie było potrzeby polegania na konkretnym środowisku oraz aby nasz projekt zawierał wszystkie niezbędne do kompilacji informacje zapisane w postaci kodu (zgodnie z IaC, Infrastructure as Code), należy uzyskać możliwość samodzielnego tworzenia plików Makefile. Opanowanie składni plików Makefile, chociaż w podstawowym zakresie, jest bardzo przydatne w zrozumieniu procesu tworzenia pliku wynikowego.

Problem jednak w tym, że analogicznie jak przy poprzedniej metodzie, przy większych projektach i złożonych wariantach kompilacji, tworzenie i utrzymywanie plików Makefile staje się czasochłonne i obarczone ryzykiem błędów. Rozwiązaniem takich problemów jest narzędzie CMake.

CMake to narzędzie pozwalające w prosty sposób tworzyć przepis na plik z regułami dla kompilacji (np. Makefile) – analogicznie do tego, że Makefile jest przepisem na plik binarny). Składnia CMake’a jest prostsza i bardziej przejrzysta niż składnia plików Makefile. Dzięki temu aktualizacja plików CMake’a podczas rozwoju projektu, uwzględnianie nowych wersji platformy docelowej, praca z różnymi wersjami kompilatorów oraz jakakolwiek parametryzacja procesu budowania staje się dużo szybsza i łatwiejsza. Jednak najważniejszą zaletą użycia CMake względem Makefile jest przenośność – projekty budowane przy pomocy CMake można zbudować na szeregu systemów operacyjnych i szeregu systemów budowania, niekoniecznie z użyciem Makefile.

Najprostszy plik CMake pozwalający zbudować aplikację na podstawie pliku źródłowego oraz katalogu z plikami nagłówkowymi przedstawiono poniżej:

cmake_minimum_required(VERSION 3.10) # set the project name project(simple_project) # include headers include_directories(inc) # add the executable add_executable(simple_project 1 2 3 4 5 6 7 8 9 10 cmake_minimum_required ( VERSION 3.10 ) # set the project name project ( simple_project ) # include headers include_directories ( inc ) # add the executable add_executable ( simple_project main . c )

Skrypty CMake stanowią bardzo często integralną część projektu, a potrzeba ich modyfikacji może występować stosunkowo często. Warto więc zaznajomić się z ideą tworzenia plików CMake, ich składnią oraz procesem kompilacji.

Unity

Jednym z etapów weryfikacji oprogramowania są testy jednostkowe, tzn. testy obejmujące jeden konkretny moduł oprogramowania w oderwaniu od całości kodu aplikacji. Testy jednostkowe tworzone są przy użyciu odpowiedniego środowiska, które wymusza określoną strukturę ułatwiającą wykonywanie testów. Zbiór testów jednostkowych weryfikuje funkcjonalność testowanego modułu według zadanego scenariusza (zbioru przypadków testowych). Przypadki testowe sprawdzają funkcjonalności modułu, dostarczając zestawu danych wejściowych i weryfikując uzyskane dane wyjściowe.

Jednym z najpopularniejszych narzędzi umożliwiających wygodne tworzenie scenariuszy testowych projektów embedded tworzonych w języku C jest Unity. Framework ten zawiera zestaw makr oraz funkcji pozwalających w prosty sposób tworzyć pliki z definicją przypadków testowych, uruchamiać testy i tworzyć raport z wynikami w popularnym formacie Junit.

W najprostszym przypadku, do przeprowadzenia testu potrzebujemy:

● plików modułu testowanego,

● plików źródłowych (lub obiektowych) frameworku do testów,

● pliku z przypadkami testowymi,

● test runnera, czyli aplikacji wykonującej poszczególne przypadki testowe i generującej raport z wynikami.

Wyobraź sobie, że utworzyłeś moduł network_utils, którego jednym z zadań jest przeliczanie siły sygnału odbieranego przez modem na wartość w dBm. Scenariusz testowy dla takiego modułu mógłby wyglądać tak:

#include ##include #include void test_convert_csq_to_dbm(void) { TEST_ASSERT_EQUAL_INT(-87, csq_to_dbm(13)); TEST_ASSERT_EQUAL_INT(-83, csq_to_dbm(15)); TEST_ASSERT_EQUAL_INT(-75, csq_to_dbm(19)); TEST_ASSERT_EQUAL_INT(-61, csq_to_dbm(26)); } void test_convert_dbm_to_csq(void) { TEST_ASSERT_EQUAL_INT(13, dbm_to_csq(-87)); TEST_ASSERT_EQUAL_INT(15, dbm_to_csq(-83)); TEST_ASSERT_EQUAL_INT(19, dbm_to_csq(-75)); TEST_ASSERT_EQUAL_INT(26, dbm_to_csq(-61)); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_convert_csq_to_dbm); RUN_TEST(test_convert_dbm_to_csq); ret 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include ##include #include void test_convert_csq_to_dbm ( void ) { TEST_ASSERT_EQUAL_INT ( - 87 , csq_to_dbm ( 13 ) ) ; TEST_ASSERT_EQUAL_INT ( - 83 , csq_to_dbm ( 15 ) ) ; TEST_ASSERT_EQUAL_INT ( - 75 , csq_to_dbm ( 19 ) ) ; TEST_ASSERT_EQUAL_INT ( - 61 , csq_to_dbm ( 26 ) ) ; } void test_convert_dbm_to_csq ( void ) { TEST_ASSERT_EQUAL_INT ( 13 , dbm_to_csq ( - 87 ) ) ; TEST_ASSERT_EQUAL_INT ( 15 , dbm_to_csq ( - 83 ) ) ; TEST_ASSERT_EQUAL_INT ( 19 , dbm_to_csq ( - 75 ) ) ; TEST_ASSERT_EQUAL_INT ( 26 , dbm_to_csq ( - 61 ) ) ; } int main ( void ) { UNITY_BEGIN ( ) ; RUN_TEST ( test_convert_csq_to_dbm ) ; RUN_TEST ( test_convert_dbm_to_csq ) ; ret

Po skompilowaniu, co ważne, nie na platformie docelowej, a w środowisku developerskim i uruchomieniu powyższego kodu uzyskamy raport z wynikami testów:

Expected -75 Was 20 ----------------------- 2 Tests 1 Failures 0 Ignored FAIL 1 2 3 4 5 6 . . / src / test_simple_project . c : 17 : test_convert_csq_to_dbm : FAIL : Expected - 75 Was 20 . . / src / test_simple_project . c : 34 : test_convert_dbm_to_csq : PASS -- -- -- -- -- -- -- -- -- -- -- - 2 Tests 1 Failures Ignored FAIL

Warto zaznaczyć jeszcze jeden, bardzo ważny element procesu testowania jednostkowego. Żeby umożliwić kompilację oddzielonego modułu należy stworzyć puste lub częściowe implementacje wszystkich zależności danego modułu. W najprostszym przypadku, będzie to jedynie zdefiniowanie pustej funkcji. Często jednak zachodzi potrzeba nie tylko odwzorowania działania funkcji, ale również kontrola wartości zwracanych przez funkcję, zliczanie jej wywołań lub odczytanie wartości argumentów, z jakimi została wywołana. Taka na nowo zaimplementowana funkcja zastępująca funkcję oryginalną, w zależności od poziomu złożoności nosi nazwę mock, fake lub stub. Narzędziem do wygodnego tworzenia i kontroli wywołań takich funkcji są narzędzia, takie jak np. CMock lub Fake Function Framework. Wykorzystuje się je podczas tworzenia scenariuszy testowych – np. definiując jako zestaw danych wejściowych do testu konkretne wartości zwracane przez imitowaną funkcję, z której korzysta moduł testowany.

Warto w tym momencie nadmienić, że możliwość podziału kodu na moduły i testowania ich niezależnie w dużej mierze zależy od stylu pisania kodu. Nie każdy kod da się efektywnie przetestować testami jednostkowymi i jest to zjawisko niepożądane. Kod taki zwie się wówczas „nietestowalnym”. Metoda odpowiedniego pisania kodu tak, by dało się go łatwo testować, jest sztuką samą w sobie.

Dlaczego warto nauczyć się pisania testów jednostkowych? Duża część zadań implementacyjnych podczas pracy programisty, kończy się utworzeniem bądź rozwinięciem istniejących scenariuszy testowych. Dopiero po ukończeniu testów wynikiem pozytywnym można mówić o ukończeniu pracy nad zagadnieniem. Aby tworzyć poprawne testy, niezbędne jest zrozumienie idei testów jednostkowych oraz narzędzi pozwalających na ich przeprowadzanie.

Jenkins oraz testy HIL

Wymienione powyżej czynności, takie jak budowanie projektu oraz wykonywanie testów jednostkowych podczas rozwoju oprogramowania wykonuje się nie tylko lokalnie (w celu weryfikacji programu i sprawdzenia wyników testów), ale również na serwerze, który uruchamia budowanie projektu przy każdym wypchnięciu zmian na zdalne repozytorium. Podejście to jest częścią procesu tzw. Ciągłej Integracji (ang. Continuous Integration) opierającym się, w skrócie, na częstym integrowaniu i testowaniu zmian wprowadzonych w oprogramowaniu. Pojęcie Continuous Integration jest bardzo szerokie i obejmuje wiele etapów, m.in. kompilację, testy jednostkowe, statyczną analizę kodu i wiele innych. Proces CI odbywa się w predefiniowanym środowisku uruchomieniowym na serwerze. Dzięki temu, możemy mieć stałą kontrolę nad stanem oprogramowania. Dobrze zbudowany proces CI, obejmujący analizy statyczne, testy jednostkowe, testy funkcjonalne, testy HIL oraz testy integracyjne, pozwala na zidentyfikowanie większości regresji odpowiednio wcześnie, dzięki raportowi wygenerowanemu przez odpowiednie narzędzie.

Jednym z najpopularniejszych tego typu narzędzi jest stosowany powszechnie Jenkins. Jest to darmowy system, pozwalający na automatyzację budowania kodu poprzez tworzenie tzw. Zadań (ang. Job). Zadanie to zbiór instrukcji, które ma wykonać Jenkins w następstwie zdefiniowanego wcześniej zdarzenia (np. wypchnięcie zmian na repozytorium Git). Zadania można definiować z poziomu serwera, jak również (zgodnie ze wspomnianą już ideą IaC), z poziomu struktury projektu, dzięki plikom z instrukcjami dla Jenkinsa (Jenkinsfile).

Przykładowy plik Jenkinsfile definiujący tzw. pipeline (jeden z rodzajów Zadań) obejmujący etapy budowania projektu za pomocą CMake’a oraz testów jednostkowych przedstawiono poniżej:

pipeline { agent any options { skipStagesAfterUnstable() } stages { stage('Build') { steps { sh 'make' } } stage('Test'){ steps { sh 'make check' junit 'reports/**/*.xml' } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 pipeline { agent any options { skipStagesAfterUnstable ( ) } stages { stage ( 'Build' ) { steps { sh 'make' } } stage ( 'Test' ) { steps { sh 'make check' junit 'reports/**/*.xml' } } } }

Jedną z możliwości, jakie daje Jenkins w obszarze systemów wbudowanych, jest wykorzystanie go do przeprowadzania automatycznych testów aplikacji na platformie docelowej (ang. Hardware-in-the-loop). Wymaga to dodania kolejnego etapu (stage) do pliku Jenkinsfile oraz umożliwienie komunikacji pomiędzy urządzeniem docelowym, a węzłem Jenkinsa (np. poprzez SSH lub port szeregowy). Do węzła takiego podłącza się również programator, umożliwiając tym samym automatyczne ładowanie programu do pamięci mikrokontrolera przy każdej modyfikacji kodu.

Pliki Jenkinsfile, podobnie jak pliki CMake traktowane są jako część projektu. Tak samo jak kod, są rozwijane i utrzymywane. Warto więc zapoznać się zarówno z samą ideą CI, funkcjami Jenkinsa, jak i składni plików Jenkinsfile.

Valgrind

Duża grupę błędów w oprogramowaniu stanowią błędy w zarządzaniu pamięcią. Może być to m.in. brak zwolnienia zaalokowanej wcześniej pamięci (wyciek) lub nieuprawniony dostęp do pamięci. Problemy tego typu zyskują na znaczeniu w systemach wbudowanych, gdzie często ilość dostępnej pamięci RAM jest bardzo ograniczona, a skutki błędu zarządzania pamięcią mogą nieść ryzyko utraty funkcjonalności całego urządzenia. Dodatkowo, nie wszystkie mikrokontrolery posiadają MPU (Memory Protection Unit, sprzętowa ochrona pamięci) a jeśli je mają, funkcjonalność MPU jest często nieprawidłowo skonfigurowana lub ograniczająca rozwój oprogramowania na tyle, że programiści celowo nie korzystają z niej wcale. To zdecydowanie ogranicza możliwość wczesnej detekcji w dostępie czy zapisie pamięci.

Aby wykrywać tego rodzaju błędy już na etapie rozwoju oprogramowania, stosuje się narzędzia do kontroli przydziału pamięci dynamicznej. Jednym z najpopularniejszych jest Valgrind – aplikacja analizująca dynamicznie proces zarządzania pamięcią. W systemach wbudowanych narzędzie to bardzo często wykorzystuje się podczas tworzenia testów jednostkowych (co pozwala używać Valgrinda nie na platformie docelowej, lecz na serwerze CI).

Przykładowy program z błędem wycieku pamięci przedstawiono poniżej

#include void foo(void) { int *x = malloc(10 * sizeof(int)); x[1] = 0; } int main(void) { f(); return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 #include void foo ( void ) { int * x = malloc ( 10 * sizeof ( int ) ) ; x [ 1 ] = ; } int main ( void ) { f ( ) ; return ; }

Po uruchomienia analizy poleceniem:

valgrind --leak-check=yes myprog 1 valgrind -- leak - check = yes myprog

Uzyskamy m.in. takie informacje:

==19182== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==19182== at 0x1B8FF5CD: malloc ==19182== by 0x8048385: foo ==19182== by 0x80483AB: main 1 2 3 4 == 19182 == 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 == 19182 == at 0x1B8FF5CD : malloc ( vg_replace_malloc . c : 130 ) == 19182 == by 0x8048385 : foo ( a . c : 5 ) == 19182 == by 0x80483AB : main ( a . c : 11 )

Valgrind wskazał dokładnie co się stało i w którym miejscu tkwi błąd – pamięć zaalokowana w funkcji foo, jest tracona bezpowrotnie. W większych projektach, w których błędy nie są tak oczywiste, pozwala to zaoszczędzić wiele czasu spędzonego na poszukiwaniu błędu oraz znacznie zmniejszyć ryzyko braku stabilności aplikacji.

Clang-tidy

Clang to front-end dla kompilatora LLVM zaprojektowany m.in. z myślą o językach C i C++. Jest to projekt open-source, jedna z alternatyw wobec kompilatora GCC. W pakiecie Clang poza samym kompilatorem, znajdziemy narzędzie do analizy statycznej – Clang-tidy.

Analiza statyczna to proces przeglądu struktury kodu źródłowego pod kątem podatności na błędy, przestrzegania dobrych praktyk programistycznych, duplikacji lub bezpieczeństwa kodu. Celem jest usunięcie wszelkich błędów możliwych do zidentyfikowania jeszcze przed uruchomieniem aplikacji.

Clang tidy pozwala na skanowanie plików źródłowych w celu znalezienia fragmentów niezgodnych ze zdefiniowanymi regułami. Reguły podzielone są na wiele grup, m.in. związane ze stylem konkretnych projektów (Boost, C++ Core Guidlines, Google, Linux Kernel), a także dotyczące wydajności, czytelności lub przestarzałych konstrukcji językowych. Błędy wynikające z łamania wielu z reguł możemy naprawić automatycznie za pomocą odpowiedniej flagi.

Pora na przykład – poniższa funkcja ma za zadanie przypisać do globalnego wskaźnika p, adres łańcucha znakowego (tablicy znaków):

char const *p; void test(void) { char str[] = "string"; p = str; } 1 2 3 4 5 6 7 char const * p ; void test ( void ) { char str [ ] = "string" ; p = str ; }

Kompilacja nie wykazała żadnych błędów. Wynik analizy powyższej funkcji przedstawiono poniżej:

warning: Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference [clang-analyzer-core.StackAddrEscapeBase] 1 C : Projects Test test . c : 16 : 1 : warning : Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller . This will be a dangling reference [ clang - analyzer - core . StackAddrEscapeBase ]

Wygenerowane zostało ostrzeżenie – funkcja zwraca adres obiektu przechowywanego na stosie. Oznacza to, że po wyjściu z funkcji nie ma gwarancji, że pod adresem przechowywanym we wskaźniku p, dalej znajduje się utworzony wcześniej łańcuch znaków. Dane, na które wskazuje p, będą się zmieniać w zależności od danych umieszczanych na stosie podczas dalszego działania programu. Ostateczny efekt w działaniu aplikacji jest na tyle nieprzewidywalny, że skutecznie utrudnia poszukiwanie błędu – warto zaoszczędzić sobie wiele czasu, wykrywając go już na etapie analizy statycznej.

Podobnie jak wszystkie poprzednio omawiane narzędzia, Clang-tidy można zintegrować je z procesem CI (np. z Jenkinsem).

Jak widać, droga od ukończenia pracy nad samym kodem, do momentu faktycznego zakończenia zadania jest długa i skomplikowana. Jest to jednak niezbędne, aby zapewnić maksymalną niezawodność tworzonego oprogramowania oraz uczynić proces możliwie płynnym i zautomatyzowanym – a co za tym idzie – szybszym. Wszystkie wymienione narzędzia, z pewnością przydadzą Ci się w pracy zawodowej – nawet sama świadomość ich istnienia oraz zrozumienie powodów, dla których się je stosuje – są dobrym wstępem do poznania praktyk stosowanych o inżynierii oprogramowania wbudowanego. Jednak żadna teoria, nie zastąpi przygotowania praktycznego. Dlatego dobrym pomysłem na rozpoczęcie kariery jest staż o profilu embedded, organizowany co roku przez firmę Comarch, a którego sam byłem uczestnikiem. Pod okiem specjalistów z wieloletnim doświadczeniem studenci mają możliwość uczenia się i realizowania projektów, co jest bardzo ważne, w praktyce. Dzięki temu mogą skonfrontować swoje oczekiwania już na początku kariery, zobaczyć, jak wygląda praca programistów w praktyce oraz „doszlifować” kompetencje miękkie. Jeżeli chcesz rozwijać się w obszarze elektroniki i hardware oraz tworzyć, integrować i weryfikować oprogramowanie, jest to idealna ścieżka, by rozpocząć przygodę z IT! Zachęcamy do zgłaszania się tu:

Oferty pracy z

Zdjęcie główne artykułu pochodzi z

5 narzędzi używanych w inżynierii systemów wbudowanych, które musisz znać

Jednym ze schematów, za którym podążają osoby chcące rozpocząć swoją karierę w świecie systemów wbudowanych, jest przygotowanie do pracy oparte na kilku podstawach. Są to m.in. lektura popularnych książek poruszających tematykę programowania w języku C mikrokontrolerów z rodziny AVR lub STM32, znajomość podstaw elektroniki cyfrowej i analogowej, wykształcenie techniczne skorelowane z tematyką embedded oraz wykorzystanie tej wiedzy w praktyce poprzez wykonanie, chociaż kilku działających układów opartych o wymienione wcześniej platformy. Jest to niewątpliwie słuszne podejście. Warto jednak móc dodatkowo wyróżnić się czymś więcej na tle innych kandydatów – czymś, czym można zaskoczyć swojego przyszłego pracodawcę podczas rozmowy rekrutacyjnej.

Poza umiejętnościami i narzędziami, które stanowią rdzeń pracy programisty systemów wbudowanych, jak np. znajomość C/C++, znajomość architektury i układów peryferyjnych stosowanego mikrokontrolera), w profesjonalnym wytwarzaniu oprogramowania wykorzystywane jest dodatkowo wiele narzędzi, mających usprawnić lub zautomatyzować proces. Do takich rozwiązań zaliczyć możemy recenzję kodu (ang. code review), testowanie, kompilację projektu lub analizę statyczną kodu – czyli wszystkie te procesy, którymi nie zajmuje się hobbysta chcący po prostu tworzyć ciekawe projekty. Wiele z narzędzi jest powszechnych w produkcji oprogramowania niezależnie od jego specyfiki – np. narzędzia kontroli wersji takie jak Git lub systemy zarządzania rozwojem projektu, np. Jira, ale często używane są też narzędzia dedykowane i przystosowane do specyfiki inżynierii systemów wbudowanych. Prędzej czy później zetkniesz się z nimi podczas swojej kariery. Opanowanie ich wcześniej przyniesie Ci wiele pożytku, nie tylko podczas rozmowy rekrutacyjnej, ale też podczas wdrażania się do nowego projektu.

CMake

Jak skompilować kod? W najprostszym przypadku ograniczać się to będzie do skorzystania z przycisku w oknie środowiska programistycznego (IDE). W analogiczny sposób możemy załadować program do pamięci i nie przejmować się całą tą złożoną sekwencją zdarzeń, w wyniku której otrzymujemy działający na mikrokontrolerze program. W praktyce jednak zachodzi potrzeba uniezależnienia się od środowiska programistycznego oraz dostosowania procesu budowania projektu do własnych potrzeb. Ponadto proces budowania projektu przez IDE, choć wygodny dla programistów, zwykle nie jest łatwy do automatyzacji. Odpowiedzmy sobie zatem na pytanie – co tak naprawdę trzeba zrobić, aby skompilować kod źródłowy do pliku binarnego? W pierwszym podejściu, budowanie projektu ogranicza się do długiej listy wywołań szeregu aplikacji toolchaina z odpowiednimi parametrami – trzeba jedynie wskazać gdzie znajdują się pliki źródłowe, gdzie znajdują się pliki nagłówkowe, jak ma wyglądać plik wynikowy (czy to w postaci binarnej, Intel hex, czy bardziej uniwersalnej postaci pliku wskazać opcje kompilacji takie jak optymalizacja, wybrać standard języka, flagi manipulujące listą wyświetlanych przez kompilator ostrzeżeń i sprawdzanych błędów… no właśnie. O ile taka metoda bezpośredniego wywołania kompilatora i ręczne wpisywanie parametrów za każdym razem może się sprawdzić w przypadku kompilacji kilku plików źródłowych (chociaż i to jest mocno naciągane – nikt tak nie robi), o tyle w przypadku odrobinę poważniejszych projektów, plików i wariantów tworzenia pliku wynikowego przybywa tyle, że trudno jest zapanować nad prostą czynnością, jaką jest kompilacja kodu.

Pierwszym krokiem, aby uporządkować i uprościć tę czynność jest tworzenie plików Makefile – w dużym skrócie jest to plik zawierający przepis na plik wynikowy. Zawiera dane niezbędne dla kompilatora takie jak ścieżki do plików źródłowych i nagłówkowych.

Właśnie w ten sposób popularne w środowiskach embedded IDE kompilują projekty. Każdy nowo utworzony plik uwzględniany jest w tworzonych na bieżąco przez IDE plikach Makefile. Aby jednak nie było potrzeby polegania na konkretnym środowisku oraz aby nasz projekt zawierał wszystkie niezbędne do kompilacji informacje zapisane w postaci kodu (zgodnie z IaC, Infrastructure as Code), należy uzyskać możliwość samodzielnego tworzenia plików Makefile. Opanowanie składni plików Makefile, chociaż w podstawowym zakresie, jest bardzo przydatne w zrozumieniu procesu tworzenia pliku wynikowego.

Problem jednak w tym, że analogicznie jak przy poprzedniej metodzie, przy większych projektach i złożonych wariantach kompilacji, tworzenie i utrzymywanie plików Makefile staje się czasochłonne i obarczone ryzykiem błędów. Rozwiązaniem takich problemów jest narzędzie CMake.

CMake to narzędzie pozwalające w prosty sposób tworzyć przepis na plik z regułami dla kompilacji (np. Makefile) – analogicznie do tego, że Makefile jest przepisem na plik binarny). Składnia CMake’a jest prostsza i bardziej przejrzysta niż składnia plików Makefile. Dzięki temu aktualizacja plików CMake’a podczas rozwoju projektu, uwzględnianie nowych wersji platformy docelowej, praca z różnymi wersjami kompilatorów oraz jakakolwiek parametryzacja procesu budowania staje się dużo szybsza i łatwiejsza. Jednak najważniejszą zaletą użycia CMake względem Makefile jest przenośność – projekty budowane przy pomocy CMake można zbudować na szeregu systemów operacyjnych i szeregu systemów budowania, niekoniecznie z użyciem Makefile.

Valgrind

Duża grupę błędów w oprogramowaniu stanowią błędy w zarządzaniu pamięcią. Może być to m.in. brak zwolnienia zaalokowanej wcześniej pamięci (wyciek) lub nieuprawniony dostęp do pamięci. Problemy tego typu zyskują na znaczeniu w systemach wbudowanych, gdzie często ilość dostępnej pamięci RAM jest bardzo ograniczona, a skutki błędu zarządzania pamięcią mogą nieść ryzyko utraty funkcjonalności całego urządzenia. Dodatkowo, nie wszystkie mikrokontrolery posiadają MPU (Memory Protection Unit, sprzętowa ochrona pamięci) a jeśli je mają, funkcjonalność MPU jest często nieprawidłowo skonfigurowana lub ograniczająca rozwój oprogramowania na tyle, że programiści celowo nie korzystają z niej wcale. To zdecydowanie ogranicza możliwość wczesnej detekcji w dostępie czy zapisie pamięci.

Aby wykrywać tego rodzaju błędy już na etapie rozwoju oprogramowania, stosuje się narzędzia do kontroli przydziału pamięci dynamicznej. Jednym z najpopularniejszych jest Valgrind – aplikacja analizująca dynamicznie proces zarządzania pamięcią. W systemach wbudowanych narzędzie to bardzo często wykorzystuje się podczas tworzenia testów jednostkowych (co pozwala używać Valgrinda nie na platformie docelowej, lecz na serwerze CI).

Jak widać, droga od ukończenia pracy nad samym kodem, do momentu faktycznego zakończenia zadania jest długa i skomplikowana. Jest to jednak niezbędne, aby zapewnić maksymalną niezawodność tworzonego oprogramowania oraz uczynić proces możliwie płynnym i zautomatyzowanym – a co za tym idzie – szybszym. Wszystkie wymienione narzędzia, z pewnością przydadzą Ci się w pracy zawodowej – nawet sama świadomość ich istnienia oraz zrozumienie powodów, dla których się je stosuje – są dobrym wstępem do poznania praktyk stosowanych o inżynierii oprogramowania wbudowanego. Jednak żadna teoria, nie zastąpi przygotowania praktycznego. Dlatego dobrym pomysłem na rozpoczęcie kariery jest staż o profilu embedded, organizowany co roku przez firmę Comarch, a którego sam byłem uczestnikiem. Pod okiem specjalistów z wieloletnim doświadczeniem studenci mają możliwość uczenia się i realizowania projektów, co jest bardzo ważne, w praktyce. Dzięki temu mogą skonfrontować swoje oczekiwania już na początku kariery, zobaczyć, jak wygląda praca programistów w praktyce oraz „doszlifować” kompetencje miękkie. Jeżeli chcesz rozwijać się w obszarze elektroniki i hardware oraz tworzyć, integrować i weryfikować oprogramowanie, jest to idealna ścieżka, by rozpocząć przygodę z IT!

Opublikowany fragment pochodzi z artykułu "5 narzędzi używanych w inżynierii systemów wbudowanych, których będziesz musiał się nauczyć" autorstwa Michała Łokcewicza. Pełen artykuł można przeczytać na portalu Geek Just Join IT.

Jarosław Kułak
Jarosław Kułak

Leave a Comment