Åırodowisko uruchomieniowe dla ruterów z interfejsami 802.11

Transkrypt

Åırodowisko uruchomieniowe dla ruterów z interfejsami 802.11
Politechnika Warszawska
Wydział Elektroniki i Technik
Informacyjnych
Instytut Informatyki
Rok akademicki 2012/2013
Praca dyplomowa inżynierska
Marcin Cieślikowski
Środowisko uruchomieniowe dla
ruterów z interfejsami 802.11
Opiekun pracy:
dr inż. Jacek Wytre˛ bowicz
Ocena . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.....................................
Podpis Przewodniczacego
˛
Komisji Egzaminu Dyplomowego
Specjalność: Informatyka –
Inżynieria oprogramowania
i systemy informacyjne
Data urodzenia: 3 marca 1990 r.
Data rozpoczecia
˛
studiów: 1 października 2009 r.
Życiorys
Urodziłem si˛e 3 marca 1990r. w Warszawie. W latach 2006-2009 ucz˛eszczałem do XI Liceum Ogólnokształcacego
˛
imienia Mikołaja Reja w Warszawie. W roku 2009 rozpoczałem
˛
studia wyższe na wydziale Elektroniki i Technik Informacyjnych Politechniki Warszawskiej na kierunku Informatyka.
.....................................
podpis studenta
Egzamin dyplomowy
Złożył egzamin dyplomowy w dn. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Z wynikiem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ogólny wynik studiów . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Dodatkowe wnioski i uwagi Komisji . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
..................................................................................
Streszczenie
Praca ta prezentuje sposób konfiguracji oraz użycia zbioru narzedzi,
˛
tworzacy
˛
środowisko uruchomieniowe ruterów z interfejsami 802.11. Cze˛ ścia˛
pracy jest aplikacja demonstracyjna, która została stworzona do prezentacji
wykorzystana środowiska uruchomieniowego. W pracy zostały zaprezentowane programy służace
˛ do kompilacji, debugowania, profilowania programów oraz generowania ruchu sieciowego. Zostały poruszone zagadnienia
tworzenia oprogramowania w przestrzeni użytkownika oraz w przestrzeni
jadra.
˛
Słowa kluczowe: kompilator, debugger, profiler, generator ruchu sieciowego,
przestrzeń jadra,
˛
przestrzeń użytkownika.
Abstract
Title: Development tools for routers with 802.11 interfaces.
This thesis presents a way of configuring and using development tools
for routers with 802.11 interface. The part of the thesis is a demonstrative
application, which was created to show usage of development tools. In this
thesis are presented programs used for compiling, debugging, profiling programs and generating network traffic. Also are mentioned issues of creating
software in user space and kernel space.
Key words: compilator, debugger, profiler, network traffic generator, kernel
space, user space.
Spis treści
1. Wprowadzenie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
2. Elementy środowiska uruchomieniowego
2.1. System operacyjny rutera . . . . . . .
2.1.1. Linux . . . . . . . . . . . . . . .
2.1.2. VxWorks . . . . . . . . . . . . .
2.2. Kompilator . . . . . . . . . . . . . . . .
2.2.1. GCC . . . . . . . . . . . . . . . .
2.2.2. Wind River (Diab) Compiler . .
2.3. Debugger . . . . . . . . . . . . . . . . .
2.3.1. GDB . . . . . . . . . . . . . . . .
2.3.2. VxWorks Debugger . . . . . . .
2.3.3. kdb . . . . . . . . . . . . . . . .
2.3.4. kgdb . . . . . . . . . . . . . . . .
2.4. Symulator . . . . . . . . . . . . . . . .
2.4.1. VxSim . . . . . . . . . . . . . . .
2.5. Profiler . . . . . . . . . . . . . . . . . .
2.5.1. gprof . . . . . . . . . . . . . . .
2.5.2. Oprofile . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
4
4
4
4
5
5
5
5
6
6
6
6
6
6
7
3. Aplikacja demonstracyjna . . . . . . . . . . . . . . . . . . . . .
3.1. Sposób użycia . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1. UDPGenerator i UDPReceiver w trybie użytkownika
3.1.2. UDPGenerator i UDPReceiver jako moduł jadra
˛
. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8
8
9
9
4. Kompilacja . . . . . . . . . . . . . . . . . . . . . . . .
4.1. Instalacja środowiska OpenWrt . . . . . . . . .
4.1.1. Wymagania wstepne
˛
. . . . . . . . . . .
4.1.2. Kompilacja oprogramowania dla rutera
4.1.3. Konfiguracja obrazu . . . . . . . . . . .
4.1.4. Konfiguracja kompilacji jadra
˛
. . . . . .
4.1.5. Opcje kompilacji . . . . . . . . . . . . . .
4.2. Kompilacja własnego oprogramowania . . . . .
4.3. Tworzenie pakietu OpenWrt . . . . . . . . . . .
4.4. Kompilacja modułu jadra
˛
. . . . . . . . . . . .
4.5. Instalacja obrazu na RB433AH . . . . . . . . .
4.5.1. Wymagania wstepne
˛
. . . . . . . . . . .
4.5.2. Kompilacja obrazów . . . . . . . . . . . .
4.5.3. Ustawienia systemu macierzystego . . .
4.5.4. Uruchamianie OpenWrt z pamieci
˛ RAM
4.5.5. Uruchamianie OpenWrt z pamieci
˛ flash
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
11
11
12
12
14
14
14
15
17
18
18
18
19
20
21
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5. Debugowanie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.1. Debugowanie programu pracujacego
˛
w trybie użytkownika . . . . . . . 23
5.1.1. Warunki wste˛ pne . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
ii
Spis treści
5.1.2. Korzystanie z gdb . . . . .
5.2. Integracja z Eclipse . . . . . . . .
5.3. Debugowanie jadra
˛
Linux . . . .
5.3.1. Funkcja printk . . . . . . .
5.3.2. KDB i KGDB . . . . . . . .
5.3.3. Korzystanie z KDB . . . .
5.3.4. Korzystanie z KGDB . . .
5.3.5. KGDB i ładowalny moduł
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
24
28
33
33
34
36
37
38
6. Profilowanie . . . . . . . . . . . . . . . . . . . . . .
6.1. gprof . . . . . . . . . . . . . . . . . . . . . . . .
6.1.1. gprof płaskie statystyki . . . . . . . . .
6.1.2. gprof graf wywołań funkcji . . . . . . .
6.2. Oprofile . . . . . . . . . . . . . . . . . . . . . .
6.2.1. Instalacja Oprofile . . . . . . . . . . . .
6.2.2. Uruchomienie Oprofile . . . . . . . . .
6.2.3. Prezentacja wyników Oprofile . . . . .
6.2.4. Profilowanie kodu jadra
˛
. . . . . . . .
6.2.5. Konwersja formatu Oprofile do Gprof
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
39
39
40
41
42
42
43
44
46
47
7. Testowanie . . . . . . . . . .
7.1. Programy testowe . . .
7.1.1. ping . . . . . . .
7.1.2. iperf . . . . . . .
7.1.3. CRUDE i RUDE
7.1.4. nping . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
48
48
48
49
49
52
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8. Podsumowanie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
A. Zawartość płyty CD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
1. Wprowadzenie
Sprawne tworzenie oprogramowania wymaga użycia takich narz˛edzi jak
edytor, debugger, profiler, a w j˛ezykach kompilowanych również kompilator. W klasycznym środowisku powyższe narz˛edzia sa˛ uruchamianie na tym
samym systemie co docelowa aplikacja. W systemach wbudowanych takich
jak, rutery z interfejsem 802.11, sytuacja jest bardziej skomplikowana. Wyróżniamy dwa systemy: docelowy (system wbudowany) i komputer macierzysty. Na komputerze macierzystym tworzone jest oprogramowanie, kompilowane skrośne na system docelowy i uruchamiany jest interfejs debuggera.
Na systemie docelowym uruchamiany jest kod programu, debugger właściwy, który łaczy
˛
si˛e z interfejsem za pomoca˛ sieci lub portu szeregowego.
Dodatkowo cz˛esta˛ praktyka˛ jest wst˛epne uruchamianie aplikacji na systemie macierzystym, a dopiero w nast˛epnej fazie przeniesienie jej do systemu
docelowego.
Ze wzgl˛edu na szerokie wykorzystanie w ruterach i łatwa˛ dost˛epność
(brak opłat licencyjnych) w niniejszej pracy zostały głównie omówione rozwiazania
˛
darmowe. Jednakże wspomniane zostały również rozwiazania
˛
komercyjne firmy Wind River, które szeroko wyst˛epuja˛ w systemach wbudowanych.
Praca ta powstała z powodu zaobserwowanych trudności jakie napotyka
sie˛ przy projekcie przeznaczonym dla ruterów z interfejsem 802.11. W wielu
projektach czas przeznaczony na skonfigurowanie i zapoznanie si˛e z środowiskiem uruchomieniowym ruterów przewyższa czas pracy nad właściwym projektem. Ponadto wiele projektów nie kończy si˛e sukcesem z powodu braku znajomości środowiska uruchomieniowego. Aby móc przedstawić w najprzyst˛epniejszy sposób środowisko uruchomieniowe praca moja
ma form˛e przewodnika, w którym został opisany sposób użycia programów
składajacy
˛ si˛e na środowisko ruterów. Po lekturze niniejszej pracy czytelnik
powinien posiaść
˛ wiedz˛e wystarczajac
˛ a˛ do przygotowania środowiska uruchomieniowego ruterów. Poznać zasad˛e działania programów środowiska
uruchomieniowego i poznać podstawowe ich funkcje. Bardziej zaawansowane funkcje nie zostały przedstawione z powodu ciagłego
˛
rozwoju oprogramowania oraz żeby nie powielać bogatych dokumentacji tych programów.
Praca ta jest przeznaczona nie tylko dla studentów i absolwentów informatyki, ale również skierowana jest dla studentów kierunków pokrewnych
np. telekomunikacji oraz informatyków amatorów. Dlatego elementy środowiska zostały przedstawione od podstaw i opisane dla osób, które nie znaja˛
tego zagadnienia. Jednakże praca ta nie jest kursem programowania. Czytelnik, który nie posiada umiej˛etności programowania powinien zasi˛egnać
˛
wcześniej do publikacji przybliżajacych
˛
ten temat.
Praca została podzielona na nast˛epujace
˛ rozdziały.
1. Wprowadzenie
2
Rozdział 2 przedstawia programy składajace
˛ si˛e na środowisko uruchomieniowe rutera z interfejsem 802.11. W rozdziale znalazł si˛e opis zadań
stawianych przed elementami środowiska uruchomieniowego oraz krótka
charakterystyka konkretnych implementacji. W rozdziale zostały przedstawione z rozwiazaniami
˛
darmowymi również rozwiazania
˛
komercyjne firmy
Wind River. Dzi˛eki czemu możliwe stało si˛e krótkie porównanie tych dwóch
rozwiaza
˛ ń.
W rozdziale 3 została przedstawiona stworzona przez zemnie aplikacja,
która posłuży jako przykładowa aplikacja w nast˛epnych rozdziałach.
Rozdział 4 opisuj˛e sposób kompilacji i instalacji oprogramowania na ruterze. Rozdział jest pierwszym z serii trzech rozdziałów w formie przewodnika. Kolejność tych rozdziałów jest zgodna z kolejnościa˛ pracy nad projektem przeznaczonym dla ruterów.
Rozdział 5 zawiera opis wykrywania bł˛edów programów pracujacy
˛ w trybie użytkownika oraz kodu jadra
˛
systemu. W skomplikowanych projektach
informatycznych wykrywanie bł˛edów w oprogramowaniu zajmuj˛e wielokrotnie wi˛ecej czasu niż pisanie kodu oprogramowania. Dlatego warto korzystać
z debuggerów, które potrafia˛ znaczaco
˛ skrócić ten czas.
Rozdział 6 przedstawia sposoby znajdowania cz˛eści kodu wartego optymalizacji. Optymalizacja kodu jest szczególnie ważna przy tworzeniu oprogramowania dla ruterów, które maja˛ niewielkie zasoby sprz˛etowe. W rozdziale zostały przedstawione programy przeznaczone poznania, które cz˛eści
kodu sa˛ najcz˛eściej i najdłużej wykonywane.
W Rozdziale 7 został zawarty opis oprogramowania, które pozwala generować ruch sieciowy. Takie oprogramowanie może posłużyć do testowanie
wydajności i funkcjonalności oprogramowania.
2. Elementy środowiska
uruchomieniowego
2.1. System operacyjny rutera
Decyzja o wyborze systemu operacyjnego dla systemu wbudowanego jest
kluczowa˛ przy tworzeniu środowiska uruchomieniowego. Implikuje ona lub
znaczaco
˛ ogranicza wybór kolejnych narz˛edzi.
Systemy operacyjne można dzielić według licznych kryteriów. Poniżej
przedstawiłem podstawowe podziały, które powinny pomóc w wyborze odpowiedniego systemu.
Podział ze wzgl˛edu na architekture˛ :
Architektura monolityczna W systemach tych każdy moduł sytemu działa
na tych samych pełnych prawach. Systemy te sa˛ uznawane za mniej
stabilne i bezpieczne. Wynika to z faktu, że każdy bład
˛ w module może
spowodować bład
˛ w całym systemie. Pomimo tej wady wi˛eksze systemy
sa˛ oparte o ta˛ architekture˛ . Wynika to z faktu, że wcześniej zostały
poznane i sa˛ prostsze w architekturze.
Architektura mikrojadra
˛
Systemy z podziałem na mikrojadro
˛
i usługi komunikujace
˛
si˛e mi˛edzy soba˛ poprzez mikrojadro.
˛
Bład
˛ w pojedynczej
usłudze nie powinien powodować bł˛edu w całym systemie. Z powodu
narzutu na komunikacje mi˛edzy usługami obsługa funkcji systemowych
może zajać
˛ wi˛ecej czasu. Systemy te spotykamy w urzadzeniach,
˛
gdzie
ważniejsza od ilości funkcji jest stabilność.
Podział systemów ze wzgl˛edu na model rozwoju:
Systemy komercyjne - Zaleta˛ tych systemów jest możliwość uzyskania
wsparcia od producentów. Wada˛ jest oczywiście konieczność wykupienia
licencji, która cz˛esto wprowadza wiele ograniczeń.
Systemy opensource - Wada˛ tych systemów jest brak wsparcia. Powoduje
to, że wiele problemów trzeba rozwiazać
˛
samodzielnie lub zapłacić firmie
trzeciej, która nam takiego wsparcia udzieli. Zaleta˛ tych systemów jest,
że mamy dost˛ep do źródeł i możliwość ich edycji. Również nie musimy
płacić za licencje.
Przy wyborze systemu operacyjnego należy upewnić si˛e, że wyst˛epuje
on na nasza˛ platform˛e sprz˛etowa˛ oraz, że zawiera wszystkie sterowniki do
urzadze
˛
ń jakie wyst˛epuja˛ w ruterze. Własna implementacja sterowników
może kosztować sporo pieni˛edzy i czasu. Jeżeli jest nieunikniona należy ja˛
uwzgl˛ednić w założeniach projektu.
2.2. Kompilator
4
2.1.1. Linux
Linux jest systemem uniksopodobnym uniwersalnego przeznaczenia. Dzi˛eki
modelowi rozwoju opartemu na otwartych źródłach system ten został dostosowany do wielu platform od superkomputerów po systemy wbudowane
takie jak telefony, tunery satelitarne i rutery. Ta własność umożliwia tworzenie i testowanie oprogramowania na tym samym komputerze, a dopiero
w nast˛epnej fazie przenoszenie go na system docelowy. Linux wykonany jest
w architekturze monolitycznej.
2.1.2. VxWorks
System czasu rzeczywistego stworzony przez firm˛e Wind River Systems.
W przeciwieństwie do Linuksa VxWorks jest specjalizowany do systemów
wbudowanych. Całe środowisko programistyczne jest przeznaczone dla systemu macierzystego i jest dostarczane dla systemów Unix i Windows. Oparty
jest o mikrojadro.
˛
W porównaniu z Linuksem ma mniejsze wymagania na
pami˛eć nieulotna˛ i RAM. Jego małe zapotrzebowanie na zasoby okupione
jest mała˛ funkcjonalnościa.
˛ Dlatego przy wyborze VxWorksa należy mieć
świadomość, że może zaistnieć potrzeba implementacji funkcji, które w innych systemach działaja˛ i sa˛ dobrze przetestowane.
2.2. Kompilator
Kompilator jest programem tłumaczacym
˛
kod wysokiego poziomu np. C
na kod maszynowy procesora. W systemach wbudowanych kompilacja odbywa si˛e na komputerze macierzystym, a produkowany kod maszynowy jest
zgodny z procesorem systemu wbudowanego. Taka˛ kompilacja˛ nazywamy
skrośna.
˛ Przy wyborze kompilatora należy uwzgl˛ednić nast˛epujace
˛ fakty:
— Czy produkuje kod dla naszego procesora w systemie wbudowanym?
— Czy działa na naszym systemie macierzystym?
— Czy kompilator zgodny jest z j˛ezykiem wysokiego poziomu naszego wyboru?
— Jakość wynikowego kodu kompilatora.
— Integracje z środowiskiem programistycznym.
Konsolidator Proces łaczenia
˛
kodu wynikowego ze soba˛ oraz z bibliotekami
nazywamy konsolidacja.
˛ Konsolidator najcz˛eściej dostarczany jest razem z
kompilatorem. Przy używaniu konsolidatora należy pami˛etać, że wszystkie
elementy podlegajace
˛ konsolidacji musza˛ być skompilowane na ta˛ sama˛
platform˛e.
2.2.1. GCC
Kompilator poczatkowo
˛
stworzony dla systemu GNU. W tej chwili GCC potrafi kompilować kod dla wielu systemów operacyjnych w tym mi˛edzy innymi Linux i VxWorks oraz dla wielu procesorów np. ARM i MIPS. GCC
również kompiluje wiele j˛ezyków wysokiego poziomu takich jak: C, C++,
2.3. Debugger
5
Objective-C, Fortran, Java, Ada, i Go. Wraz z GCC dostarczane sa˛ standardowe biblioteki dla tych j˛ezyków. Dzi˛eki swojej uniwersalności GCC stał
sie˛ jednym z najpopularniejszych kompilatorów.
2.2.2. Wind River (Diab) Compiler
Komercyjny kompilator tworzony przez firm˛e Wind River. Diab został stworzony specjalnie dla systemów wbudowanych z systemem VxWorks. Producent podkreśla, że jego kompilator tworzy jednocześnie mocno zoptymalizowany kod ze wzgl˛edu na szybkość i wielkość. Kompilator firmy Wind River
w przeciwieństwie do GCC obsługuje jedynie C i C++. Potrafi produkować
kod na praktycznie wszystkie procesory wyst˛epujace
˛ w systemach wbudowanych w tym oczywiście dla ARM i MIPS.
2.3. Debugger
Jednym z najtrudniejszych etapów tworzenia oprogramowania jest znajdowanie bł˛edów w tymże oprogramowaniu. Aby wspomóc znajdowanie źródła
błedów
˛
stosuje si˛e programy typu debugger. Programy te umożliwiaja˛ zatrzymanie programu w wyznaczonym miejscu, wykonywanie instrukcji po
instrukcji, a także informuja˛ o wartościach zmiennych w programie. W przypadku wystapienia
˛
bł˛edu w programie, który uniemożliwił dalsze wykonywanie si˛e programów np. odwołanie si˛e programu do nieprzydzielonej pamieci.
˛ System Linux zrzuca zawartość pami˛eci programu do pliku. Debugger
potrafi zanalizować zrzucony obraz pami˛eci przez system i podać informacje
takie jak: która instrukcja programu spowodowała bład
˛ i jakie były aktualne
wartości bł˛edów.
2.3.1. GDB
Debugger stworzony w ramach projektu GNU. Głównie umożliwia debugowanie programów napisanych w C i C++. Jednakże posiada cz˛eściowe lub
pełne wsparcie dla innych j˛ezyków programowania. Działa na platformach
Linux, VxWorks i innych. GDB umożliwia prac˛e w dwóch trybach: lokalnym i zdalnym. Tryb lokalny polega na uruchomieniu debuggera na tym
samym systemie na którym jest debuggowany program. W trybie tym debugger jest uruchomiony na innym komputerze niż program debugowany.
W trybie zdalnym połaczenie
˛
mi˛edzy komputerami nast˛epuje poprzez sieć
lub port szeregowy. W systemach wbudowanych używamy trybu zdalnego.
GDB udost˛epnia interfejs tekstowy. Interfejs tekstowy nie jest zbyt wygodny dlatego powstały liczne nakładki graficzne np. DDD oraz liczne
wtyczki do środowisk programistycznych.
2.3.2. VxWorks Debugger
Debugger udost˛epniony przez Wind River przeznaczony dla ich systemu
VxWorks. Debugger dobrze si˛e integruje ze środowiskiem programistycznym
dla VxWorks.
2.4. Symulator
6
2.3.3. kdb
Wbudowany debugger w jadro
˛
Linuksa. Kdb pozwala badać pami˛eć jadra
˛
i jej struktury podczas działajacego
˛
systemu. Kdb umożliwia pełna˛ kontrole nad wykonywaniem si˛e jadra.
˛
Umożliwia wykonywanie instrukcji po
instrukcji, ustawianie pułapek na wybranych instrukcjach i inne. Aby wykorzystać kdb należy połaczyć
˛
si˛e z maszyna˛ docelowa˛ za pomoca˛ konsoli
szeregowej.
2.3.4. kgdb
Kolejny debugger w jadrze
˛
Linuksa. W przeciwieństwie do kdb nie udost˛epnia własnego interfejsu użytkownika, ale posiada interfejs dla gdb. Dlatego można używać znanego programistom gdb. GDB należy ustawić w tryb
zdalny i połaczyć
˛
z kgdb za pomoca˛ portu szeregowego. W ostatnich wersjach jadra
˛
kdb i kgdb zostały ze soba˛ połaczone.
˛
Teraz należy przełaczać
˛
sie˛ mi˛edzy tymi trybami za pomoca˛ specjalnych poleceń.
2.4. Symulator
Symulatory sa˛ to specjalne programy, które potrafia˛ stworzyć środowisko
wykonania zgodne z innym system operacyjnym. Nie należy mylić ich z emulatorami, które pozwalaja˛ uruchomić program na innej platformie sprz˛etowej niż został skompilowany. W przypadku symulatorów kod programu
należy skompilować dla procesora na którym jest uruchomiony symulator.
Symulator umożliwia tworzenie oprogramowania bez dost˛epu do systemu
docelowego.
2.4.1. VxSim
VxSim symulator systemu VxWorks. Umożliwia tworzenie wi˛ekszość rodzajów oprogramowania dla VxWorks. Jedynie nie można stworzyć sterowników, które wymagaja˛ bezpośredniego dost˛epu do sprz˛etu.
2.5. Profiler
Profilery sa˛ to programy, które służa˛ do zbierania statystyk. W statystykach
zbierane sa˛ dane, ile razy funkcje programu sa˛ wywoływane i ile procesor
spedza
˛
w nich czasu. Dzi˛eki tym informacjom wiemy, która˛ funkcje warto
optymalizować. Te dane moga˛ również posłużyć do wykrywania bł˛edów w
programie.
2.5.1. gprof
Gprof jest profilerem tworzonym w ramach projektu GNU. Aby z niego skorzystać należy najpierw skompilować program z opcja˛ profilowania. GCC należy przekazać parametr ’-pg’. Tak skompilowany program b˛edzie zapisywał
statystyki wykonania do pliku gmon.out. Statystyki te można przegladać
˛
i
analizować za pomoca˛ programu gprof.
2.5. Profiler
7
2.5.2. Oprofile
Mechanizm do profilowania znajdujacy
˛ si˛e w jadrze.
˛
Aby można było z niego
korzystać należy wkompilować go w jadro.
˛
Umożliwia profilowanie programów pracujacych
˛
w trybie użytkownika i modułów jadra.
˛
Oprócz modułu
znajdujacego
˛
si˛e w jadrze
˛
należy użyć programu opcontrol, który b˛edzie
zbierał statystyki od modułu.
3. Aplikacja demonstracyjna
Aplikacja demonstracyjna powstała w celu zaprezentowania obsługi nast˛epujacych
˛
programów: kompilatorów, debuggerów, profilerów. Aplikacja została napisana, tak aby kod był przejrzysty dla czytelnika i jednocześnie
umożliwiał prezentacje wi˛ekszości funkcji powyższych narz˛edzi. W poniższej pracy omówiłem narz˛edzia przeznaczone zarówno dla kodu wykonuja˛
cego si˛e w trybie jadra
˛
oraz w przestrzeni użytkownika. W celu prezentacji
narzedzi
˛
dla obydwu rodzajów kodu aplikacja została stworzona w dwóch
wersjach. Jedna wersja działa w trybie użytkownika. Druga wersja jest uruchamiania jako moduł jadra
˛
systemu Linux.
Stworzona aplikacja składa si˛e z dwóch cz˛eści z generatora pakietów UDP
nazwanego UDPGenerator oraz aplikacji odbierajacej
˛
pakiety, a nast˛epnie
odsyłajacej
˛
wartość crc32 obliczona˛ dla odebranych pakietów. Aplikacja odbierajaca
˛ pakiety nazwałem UDPReceiver. UDPGenerator posiada nast˛epujace
˛ cechy:
— Użytkownik może ustalić rozmiar oraz cz˛estotliwość wysyłanych pakietów. Jeżeli użytkownik nie ustawi tych parametrów wykorzystywane sa˛
wartości domyślne.
— Podczas uruchamiania programu użytkownik definiuje port oraz adres
docelowy wysyłanych pakietów.
— Generator odbiera odpowiedzi zawierajace
˛ wartość crc32 i porównuje je
z zapami˛etanymi wartościami dla wysłanych pakietów. Użytkownik jest
informowany o sytuacji braku zgodności sum kontrolnych.
— Zawartość wysyłanych pakietów jest generowana pseudolosowo lub wypełniana samymi zerami.
— Do każdego wysłanego pakietu dołaczany
˛
jest szesnasto-bitowy numer
sekwencyjny.
UDPReceiver charakteryzuje si˛e nast˛epujacymi
˛
cechami:
— Aplikacja domyślnie nasłuchuje na porcie 5000. Parametr ten można
zmienić podczas uruchamiania aplikacji.
— Możliwe jest ustawienie czasu zwłoki po jakim aplikacja odeśle odpowiedź zawierajac
˛ a˛ sum˛e kontrolna˛ crc32 otrzymanego pakietu. Aplikacja
odbiera nast˛epny pakiet dopiero po odesłaniu odpowiedzi.
3.1. Sposób użycia
Wersje aplikacji UDPGenerator i UDPReceiver pracujace
˛ w trybie użytkownika i jadra
˛
sa˛ ze soba˛ kompatybilne. To znaczy UDPReceiver pracujacy
˛
w trybie jadra
˛
może odbierać pakiety wygenerowane przez UDPGenerator
pracujacy
˛ w trybie użytkownika. Sytuacja odwrotna też b˛edzie działać. Z powodu różnicy w przekazywaniu parametrów do aplikacji pracujacej
˛
w trybie
3.1. Sposób użycia
9
użytkownika i modułu jadra
˛
interfejsy użytkownika tych aplikacji znacznie
sie˛ różnia.
˛
3.1.1. UDPGenerator i UDPReceiver w trybie użytkownika
W celu zmiany portu nasłuchiwania i czas odpowiedzi na odebrany pakiet
należy użyć odpowiednio parametru -p i -t podajac
˛ po nim żadan
˛
a˛ wartość.
Dodatkowo parametr -6 pozwala wymusić użycie protokołu ipv6.
$ ./UDPReceiver -p 2000 -t 5 -6
received packet: 0
received packet: 1
received packet: 2
Po odebraniu pakietu aplikacja wypisuje na standardowe wyjście informacje
o tym fakcje. Dodatkowo w pakiecie zawarta jest informacja o jego numerze
sekwencyjnym.
UDPGenerator wymaga podania parametru -h po którym należy podać
nazw˛e hosta, do którego UDPGenerator b˛edzie wysyłał pakiety. Za pomoca˛
parametru -p można zmienić domyślny port docelowy na inny. Parametr -t
zmienia czas pomi˛edzy kolejnymi wysłanymi pakietami. Parametr ten przyjmuje czas w milisekundach. Opcja -s zmienia domyślny rozmiar pakietu.
Opcja ta przyjmuje wartość podana˛ w bajtach. Domyślnie UDPGenerator
wysyła pakiety składajace
˛ si˛e z samych zer. W celu wysyłania pakietów zawierajacych
˛
liczby pseudolosowe należy użyć parametru -r.
$ ./UDPGenerator -h ::1 -r -p 2000 -t 500
packet sent 0
packet received 0
crc32 checksum correct -470045914
packet sent 1
packet received 1
crc32 checksum correct 503868782
3.1.2. UDPGenerator i UDPReceiver jako moduł jadra
˛
Moduł UDPReceiver obsługuj˛e nast˛epujace
˛ parametry podczas ładowania:
— port - port, na którym nasłuchuj˛e na przychodzace
˛ pakiety.
— ipv6 - Tryb ipv6 jest domyślnie właczony,
˛
aby go wyłaczyć
˛
parametr ipv6
ustawiamy wartość 0.
— time - Czas (w milisekundach) opóźnienia wysłania pakietu z odpowiedzia˛ po odebraniu pakietu.
Przykład załadowania modułu UDPReceiver ze wszystkimi parametrami.
# insmod UDPReceiver.ko time=500 ipv6=0 port=2000
W celu odczytania komunikatów modułu należy odczytać bufor komunikatów jadra
˛
za pomoca˛ komendy dmesg.
#dmesg
...
[10887.959540] received packet 0 with correct checksum 1926736969
[10888.962807] received packet 1 with correct checksum 1577853168
[10889.965723] received packet 2 with correct checksum -13885884
3.1. Sposób użycia
10
...
[10913.058702] UDPGeerator module removed.
Moduł UDPGenerator wymaga parametru address, który powinien przyjać
˛ adres ipv4 lub ipv6 komputera docelowego. Moduł nie obsługuje adresów
w formie nazw domenowych. Nast˛epujace
˛ parametry sa˛ opcjonalne.
— port - port, na którym nasłuchuje komputer docelowy.
— send_size - wielkość pakietów wysyłanych (w bajtach).
— random - wartość 0 powoduj˛e wysyłanie samych zer w danych pakietów.
— time - Czas (w milisekundach) pomi˛edzy wysyłanymi pakietami.
Przykład załadowania modułu
insmod UDPGenerator.ko address=127.0.0.1\
port=2000 random=0 time=500 send_size=400
Przykładowe komunikaty modułu UDPReceiver.
[11046.098414]
[11086.459580]
[11088.460528]
[11090.462910]
[11092.464480]
[11106.980779]
UDPReceived module loaded.
received packet number 0
received packet number 1
received packet number 2
received packet number 3
UDPReceived module removed.
4. Kompilacja
4.1. Instalacja środowiska OpenWrt
Zalecanymi systemami operacyjnymi do instalacji środowiska deweloperskiego OpenWrt sa˛ systemy z rodziny GNU/Linux. Instalacja jest również
możliwa na systemach z rodziny BSD oraz Mac OS. W niniejszej pracy został wykorzystany system operacyjny Debian w wersji Wheezy. Podstawowa˛
różnica˛ miedzy systemami b˛edzie sposób instalacji programów i pakietów
wymaganych przez środowisko OpenWrt.
4.1.1. Wymagania wstepne
˛
Aby zaczać
˛ prac˛e z środowiskiem OpenWrt należy zainstalować wymagane
programy i biblioteki. Instalacje pakietów należy wykonywać z konta superużytkownika. Wszystkie inne czynności należy wykonywać z konta zwykłego
użytkownika. Pod Debianem, aby zainstalować wymagane pakiety. Należy
wykonać poniższe polecenie.
apt-get install asciidoc bash binutils bzip2\
fastjar flex git-core g++ gcc util-linux gawk\
libgtk2.0-dev intltool zlib1g-dev make\
libncurses5-dev libssl-dev patch perl-modules\
python2.6-dev rsync ruby sdcc unzip wget gettext\
xsltproc libxml-parser-perl
Po instalacji pakietów należy zalogować si˛e na konto zwykłego użytkownika.
Środowisko deweloperskie OpenWrt wymaga, aby kompilacja była przeprowadzona z konta zwykłego użytkownika. Przy próbuje kompilacji na koncie
superużytkownika środowisko OpenWrt zaprotestuje i nie pozwoli na dalsza˛ prac˛e. Nast˛epnym etapem jest pobranie źródeł środowiska za pomoca˛
protokołu subversion.
mkdir ~/openwrt
cd ~/openwrt
svn co svn://svn.openwrt.org/openwrt/trunk/
cd trunk
Nastepny
˛
krok polega na pobraniu dodatkowych pakietów. Krok ten nie jest
wymagany, ale jest zalecany.
./scripts/feeds update -a
./scripts/feeds install -a
4.1. Instalacja środowiska OpenWrt
12
Należy użyć poniższej komendy, aby sprawdzić czy wszystkie pakiety potrzebne do zbudowania OpenWrt wyst˛epuja˛ w systemie. Brakujace
˛
programy lub biblioteki należy doinstalować.
make prereq
Jeżeli wszystkie zależności sa˛ spełnione można przejść do nast˛epnego etapu,
kompilacji oprogramowania dla rutera.
4.1.2. Kompilacja oprogramowania dla rutera
Źródła OpenWrt zmieniaja˛ si˛e stosunkowo cz˛esto dlatego przed każda˛ kompilacja˛ warto je zaktualizować. Aktualizacje należy wykonać za pomoca˛ svn.
svn update
4.1.3. Konfiguracja obrazu
Podczas kompilacji środowisko OpenWrt tworzy plik, który reprezentuje
zawartość pami˛eci rutera. Taki plik b˛edziemy nazywali obrazem pami˛eci.
Do konfiguracji obrazu służy tekstowy interfejs stworzony za pomoca˛ biblioteki ncurses. Interfejs ten jest bardzo podobny do interfejsu konfiguracji
jadra
˛
Linux. Uruchamia sie˛ go za pomoca˛ polecenia:
make menuconfig
Rysunek 4.1. Konfiguracja OpenWrt
Podczas konfiguracji obrazu pami˛eci rutera ustala si˛e jakie oprogramowanie ma zostać zawarte w obrazie. Dodatkowo oprogramowanie może zostać
skompilowane i udost˛epnione w postaci pakietu. Decyzj˛e podejmujemy zaznaczajac
˛ odpowiednia˛ opcje znajdujac
˛ a˛ si˛e przy nazwie oprogramowania.
— y - Oprogramowanie zostanie skompilowane i umieszczone w obrazie
oprogramowania rutera.
4.1. Instalacja środowiska OpenWrt
13
— m - Oprogramowanie zostanie skompilowane i zostanie z niego utworzony pakiet. Pakiet b˛edzie można doinstalować na ruterze za pomoca˛
polecenia opkg.
— n - Oprogramowanie nie zostanie skompilowane.
Do wi˛ekszości oprogramowania istnieje pomoc, w której można przeczytać
do czego ono służy. Po naciśni˛eciu znaku zapytania na klawiaturze powinna si˛e ona pojawić dla aktualnie zaznaczonej pozycji. Istnieje również
możliwość wyszukiwania po naciśni˛eciu znaku ’/’ na klawiaturze pojaw si˛e
okienko służace
˛ do wyszukiwania.
Podstawowa˛ rzecza˛ po uruchomieniu konfiguracji jest ustawienie systemu docelowego. Dla każdego rutera opcje te b˛eda˛ si˛e różnić. Opcje zależne
od modelu rutera:
Target System
Subtarget
Target Profile
Target Images
Jeżeli zamierzamy debugować programy, to musimy wraz z narz˛edziami
kompilacji(ang. toolchain) skompilować debugger. Wraz z OpenWrt dostarczane jest gdb. W tym celu należy zaznaczyć poniższa˛ opcje.
Advanced configuration options (for developers) -->
Toolchain Options -->
Build gdb
W celu skompilowania kompilatora c++ wybieramy opcje:
Advanced configuration options (for developers) -->
Toolchain Options -->
Build/install c++ compiler and libstdc++
Aby móc debugować jadro
˛
Linux należy skompilować je z symbolami dla
debuggera.Opcja˛ za to odpowiedzialna˛ jest:
Global build settings --->
Compile the kernel with debug information
W tym samym menu możemy właczyć
˛
moduł Oprofile w jadrze.
˛
Global build settings --->
Compile the kernel with profiling enabled
Po skonfigurowaniu wychodzimy z menuconfig i zapisujemy konfiguracje. Teraz wystarczy wydać polecenie:
make
OpenWrt na poczatku
˛
sprawdzi czy sa˛ zainstalowane wszystkie pakiety w
systemie potrzebne do kompilacji. Jeżeli któregoś brakowało wypisze bład.
˛
W takim przypadku należy pakiet doinstalować i wykonać polecenie ponownie.
4.2. Kompilacja własnego oprogramowania
14
4.1.4. Konfiguracja kompilacji jadra
˛
OpenWrt na podstawie swojej konfiguracji tworzy plik konfiguracyjny jadra.
˛
Wiekszość
˛
opcji zostaje ustawionych poprawnie i nie należy ich zmieniać.
Jednakże może zaistnieć potrzeba dodatkowej konfiguracji jadra.
˛
W takim
przypadku po wykonaniu polecenia:
make kernel_menuconfig
Bedzie
˛
możliwość konfiguracji jadra.
˛
Opcje, które pokrywaja˛ si˛e w konfiguracji jadra
˛
Linux i środowiska OpenWrt b˛eda˛ przyjmowane z konfiguracji
OpenWrt.
4.1.5. Opcje kompilacji
Kompilacja na wielu procesorach
OpenWrt wspiera kompilacje na wielu procesorach, poprzez wykonywanie
wielu prac jednocześnie. Te˛ funkcj˛e włacza
˛
opcja -j.
make -j N
N oznacza liczb˛e wykonywanych prac jednocześnie. Zaleca si˛e żeby była
równa powi˛ekszonej o jeden liczbie procesorów w systemie macierzystym.
Opcja ta może powodować bł˛edy w kompilacji. W przypadku bł˛edu kompilacji należy spróbować kompilacji bez tej opcji.
Kompilacja z dodatkowymi komunikatami
Jeżeli podczas kompilacji pojawiaja˛ si˛e bł˛edy i uzyskane komunikaty sa˛ niewystarczajace
˛ do zlokalizowania przyczyn bł˛edów. Możliwe jest zwi˛ekszenie
ilości komunikatów od OpenWrt. W tym celu należy do polecenia make dodać opcje V=99.
make V=99
4.2. Kompilacja własnego oprogramowania
Kompilacja skrośna przebiega podobnie do zwykłej kompilacji. Najważniejsza˛ różnica˛ jest użycie specjalnego kompilatora skrośnego, a nie dostarczonego z systemem. Całe narz˛edzia kompilacyjne powinny zostać skompilowane wraz z kompilacja obrazu. Na poczatku
˛
należy zlokalizować skompilowane narz˛edzia kompilacji dla naszej platformy. Powinny znajdować si˛e w
katalogu
~openwrt/trunk/staging_dir/\
toolchain-architecture_gcc-compilerver_uClibc-libcver/
Gdzie /openwrt/trunk oznacza miejsce instalacji OpenWrt, architecture
oznacza nazw˛e architektury systemu wbudowanego, compilerver wersj˛e
kompilatora i libcver wersje biblioteki C. Nast˛epnie należy odpowiednio ustawić zmienne środowiskowe. Zmiennej staging_dir przypisujemy
ścieżk˛e narz˛edzi kompilacji.
4.3. Tworzenie pakietu OpenWrt
15
export STAGING_DIR=~/openwrt/trunk/staging_dir\
/toolchain-architecture_gcc-compilerver_uClibc-libcver/
Dodajemy katalog z plikami wykonywalnymi do zmiennej PATH.
export PATH=$PATH:$STAGING_DIR/bin
Teraz korzystamy z kompilatora skrośnego jak ze zwykłego jedyna˛ różnica˛
jest, że produkuje on kod dla maszyny wbudowanej. Przykładowo, aby
skompilować plik main.c należy wydać poniższe polecenie.
~/openwrt/trunk/staging_dir\
/toolchain-architecture_gcc-compilerver_uClibc-libcver\
/bin/architecture-openwrt-linux-uclibc-gcc main.c -o main
Teraz program main można przegrać na ruter i uruchomić. Oczywiście,
jeżeli b˛edziemy chcieli debugować program należy dodać opcje -g. Programy
napisane w j˛ezyku C++ kompilujemy odpowiednim kompilatorem. Ścieżka
kompilatora C++ jest taka sama. Jedynie końcówka˛ nazwy si˛e różni g++
zamiast gcc.
W przypadku używania przez nas GNU make należy nadpisać zmienne
CC i LD. Poniżej przykład wywołania polecenia make.
make CC=architecture-openwrt-linux-uclibc-gcc \
LD=architecture-openwrt-linux-uclibc-ld
4.3. Tworzenie pakietu OpenWrt
Wygodniejszym sposobem od re˛ cznej kompilacji oprogramowania jest stworzenie pakietu OpenWrt. Dzi˛eki temu b˛edziemy mogli tworzyć obraz systemu
razem z tym pakietem oraz dystrybuować pakiet w formie pliku opkg. Pakiety rozwiazuj
˛ a˛ również problem z zależnościami. Do pakietu dopisujemy
inne pakiety, które należy zainstalować przed instalacja˛ danego pakietu.
Dzieki
˛ takiemu rozwiazaniu
˛
nie musimy si˛e martwić o dostarczenie wraz z
programem bibliotek i programów, które wykorzystuje nasze oprogramowanie.
Środowisko OpenWrt potrafi ściagn
˛ ać
˛ kod źródłowy pakietu bezpośrednio ze strony www. Dzi˛eki takiemu rozwiazaniu
˛
ściagane
˛
jest oprogramowanie tylko przez nas wykorzystane. W niniejszym przykładzie zostało założone, że dopiero oprogramowanie jest tworzone i nie jest udost˛epnione w
internecie. W takim przypadku kod źródłowy oprogramowania musi znajdować si˛e w katalogu pakietu.
Aby rozpoczać
˛ prac˛e nad pakietem należy utworzyć katalog pakietu w
folderze package środowiska OpenWrt.
mkdir ~/openwrt/trunk/package/nazwa_pakietu
Po utworzeniu katalogu pakietu tworzymy plik Makefile opisujacy
˛ pakiet.
W tym pliku zawarte sa˛ wszystkie informacje o pakiecie np.: nazwa pakietu,
wersja, kategoria, opiekun, sposób instalacji, sposób kompilacji, zależności
i inne.
Poniżej został przedstawiony prosty przykład pliku Makefile pakietu.
4.3. Tworzenie pakietu OpenWrt
16
include $(TOPDIR)/rules.mk
PKG_NAME:=przyklad
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/przyklad
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Przykladowy pakiet
MAINTAINER:=Marcin Cieslikowksi
endef
define Package/przyklad/description
To jest opis przykladowego pakietu.
Opis może zawierać kilka linii.
endef
define Build/Prepare
$(INSTALL_DIR) $(PKG_BUILD_DIR)
$(INSTALL_DATA) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/przyklad/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/przyklad $(1)/usr/bin/
endef
$(eval $(call BuildPackage,przyklad))
Poniżej przedstawiłem elementy pliku Makefile przytoczonego powyżej.
Obie linie zaczynajace
˛
si˛e od include sa˛ wymagane przez środowisko OpenWrt i należy o nich pami˛etać. Zmienna PKG_NAME oznacza nazwe˛ pakietu, PKG_RELEASE wersja pakietu. W sekcji definiujacej
˛
pakiet
define Package/przyklad znajduja˛ si˛e nast˛epujace
˛
zmienne opisujace
˛
pakiet:
SECTION Typ pakietu. Zmienna ta na razie jest nieużywana przez środowisko OpenWrt.
CATEGORY W którym pod menu wystapi
˛ pakiet po uruchomieniu make
menuconfig.
TITLE Krótki opis pakietu, który pojawi si˛e w menu menuconfig.
MAINTAINER Opiekun pakietu. Jest to osoba odpowiedzialna za pakiet.
Nastepna
˛
sekcja Package/przyklad/description zawiera pełny opis,
który pojawi w menu menuconfig. Sekcja Build/Prepare jest specyficzna dla przykładowego pakietu, który nie ściaga
˛
kodu źródłowego.
Zmienna $(INSTALL_DIR) jest tak naprawd˛e komenda,
˛ która tworzy katalog. Ścieżk˛e katalogu opisuje nast˛epny argument komendy.
W tym przypadku argumentem jest zmienna $(PKG_BUILD_DIR),
4.4. Kompilacja modułu jadra
˛
17
która opisuje katalog kompilacji oprogramowania. Nast˛epna zmienna
$(INSTALL_DATA) zawiera komend˛e kopiujac
˛ a.
˛ W tym przypadku
kopiowane sa˛ wszystkie pliki z katalogu src pakietu do folderu kompilacji. Sekcja Package/przyklad/install opisuje sposób instalacji
pakietu. Linia $(INSTALL_DIR) $(1)/usr/bin tworzy katalog /usr/bin
w którym znajdzie si˛e plik wykonywalny oprogramowania. Nastepna
˛
linia
$(INSTALL_BIN) $(PKG_BUILD_DIR)/przyklad $(1)/usr/bin/
kopiuje plik wykonywalny do katalogu /usr/bin. Ostania linia
$(eval $(call BuildPackage,przyklad)) wywołuje makro, które
dodaje pakiet do środowiska OpenWrt. Przyjmuje jeden argument nazw˛e
pakietu, reszt˛e informacji pobiera ze wcześniej zdefiniowanych sekcji.
Powyższy przykład zakłada, że oprogramowanie zawiera plik Makefile,
który opisuje sposób kompilacji. Jeżeli oprogramowanie nie posiada tego
pliku możemy recznie
˛
zdefiniować sposób kompilacji. Wystarczy do pliku
Makefile pakietu dodać sekcje, która to określi.
define Build/Compile
$(TARGET_CC) $(TARGET_CFLAGS) -Wall \
-o $(PKG_BUILD_DIR)/przyklad $(PKG_BUILD_DIR)/przyklad.c
endef
Powyższym przykładzie wykorzystujemy skrośny kompilator $(TARGET_CC)
ustawiamy odpowiednie flagi $(TARGET_CFLAGS), aby skompilować plik
przyklad.c do pliku wykonywalnego przyklad.
Tak przygotowany pakiet powinien pojawić si˛e w menu po wydaniu polecenia make menuconfig. Po zaznaczeniu pakietu w menu możemy go skompilować z całym oprogramowaniem dla rutera poleceniem
make
lub można skompilować pojedynczy pakiet:
make package/<nazwa_pakietu>/{clean,compile,install}
4.4. Kompilacja modułu jadra
˛
Rozwijanie kodu źródłowego jadra
˛
systemu operacyjnego jest czynnościa˛
trudniejsza˛ od tworzenia aplikacji pracujacej
˛
w przestrzeni użytkownika.
Również kompilacja modułu wymaga wi˛ekszego nakładu pracy niż kompilacja przeci˛etnej aplikacji. W celu kompilacji modułu jadra
˛
potrzebne sa˛
nagłówki kodu źródłowego jadra
˛
lub pełny kod źródłowy (pełny kod zawiera
również nagłówki). W przypadku używania środowiska OpenWrt kod źródłowy znajduje si˛e w katalogu build_dir.
Najprostszym przypadkiem kompilacji modułu jest kompilacja niezależna od kompilacji jadra
˛
i OpenWrt. W takim przypadku kod modułu powinien znajdować si˛e w oddzielnym katalogu. W katalogu modułu powinien
znaleźć si˛e plik Makefile. Najprostszy Makefile modułu b˛edzie zawierać tylko
jedna˛ nast˛epujac
˛ a˛ linie.
obj-m += nazwa_modulu.o
4.5. Instalacja obrazu na RB433AH
18
Moduł powinien mieć taka˛ sama˛ nazw˛e jak jego główny plik źródłowy. Aby
mieć możliwość użycia kompilatora i konsolidatora skrośnego należy ustawić zmienne środowiskowe PATH i STAGING_DIR analogicznie do kompilacji
programu pracujacego
˛
w przestrzeni użytkownika. Po stworzeniu pliku Makefile i ustawieniu zmiennych środowiskowych można przejść do kompilacji
modułu. W tym celu należy wydać poniższe polecenie.
make ARCH=architecture CC=architecture-openwrt-linux-uclibc\
-gcc LD=architecture-openwrt-linux-uclibc-ld \
-C ~/openwrt/trunk/build_dir/\
target-mips_r2_uClibc-version/linux-ar71xx_nand/linux-\
version/ M=$PWD modules
Tak jak poprzednio słowo architecture oznacza architekture˛ procesora na jaki kompilujemy oprogramowanie. Po fladze -C znajduje si˛e
ścieżka do źródła jadra
˛
Linuksa. Po wykonaniu powyższego polecenia, jeżeli kod modułu nie zawierał żadnych bł˛edów, powinien pojawić si˛e plik
nazwa_modulu.ko. Ten plik nast˛epnie należy przegrać na ruter i załadować
za pomoca˛ polecenia insmod nazwa_modulu.ko.
W celu wyczyszczenia katalogu, zawierajacego
˛
źródło modułu, z pozostałości po kompilacji należy wykonać nast˛epujace
˛ polecenie.
make -C /home/marcin/openwrt/trunk/build_dir/\
target-mips_r2_uClibc-0.9.33.2/linux-ar71xx_nand/\
linux-3.6.11/ M=$PWD clean
4.5. Instalacja obrazu na RB433AH
Sposób instalacji oprogramowania jest różny dla różnych ruterów. W tym
rozdziale został przedstawiony opis dla Mikrotika RM433AH. Sposoby instalacji na innych ruterach sa˛ przedstawione na stronie OpenWrt.
4.5.1. Wymagania wstepne
˛
Do instalacji OpenWrt b˛eda˛ potrzebne nast˛epujace
˛ elementy:
— Kabel ethernetowy
— Kabel null-modem
— Port szeregowy w komputerze lub przejściówk˛e USB -> port szeregowy
Dodatkowo na komputerze b˛edzie potrzebne nast˛epujace
˛ oprogramowanie:
— Serwer DHCP
— Serwer TFTP
— Emulator terminala
4.5.2. Kompilacja obrazów
Instalacja OpenWrt na Mikrotika RB433AH odbywa si˛e w dwóch etapach.
W pierwszym etapie bootloader pobiera obraz z serwera tftp i umieszcza go
w pami˛eci RAM. W tym momencie cały system jest uruchamiany z pami˛eci
RAM. W nast˛epny etapie spod uruchomionego systemu należy pobrać właściwy obraz systemu z serwera http i zainstalować w pami˛eci flash. Dlatego
4.5. Instalacja obrazu na RB433AH
19
należy stworzyć dwa obrazy jeden służacy
˛ do instalacji i drugi właściwy. Oba
obrazy dziela˛ wspólne ustawienia:
Target System: Atheros AR71xx/AR7240/AR913x/AR934x
Subtarget: Devices with NAND flash (mostly Mikrotik)
Różnia˛ si˛e typem obrazu. Obraz umieszczany w pami˛eci RAM ma nast˛epujace
˛ ustawienia:
Target Images: ramdisk
Obraz umieszczany w pami˛eci flash b˛edzie miał nast˛epujace:
˛
Target Images:
[*] tar.gz
[ ] jffs2
[*] squashfs
4.5.3. Ustawienia systemu macierzystego
W przykładzie zostało założone, że ruter jest podłaczony
˛
z komputerem
poprzez interfejs sieciowy eth0 oraz poprzez przejściówk˛e port szeregowy ->
USB widziana˛ w systemie linuksowym jako /dev/ttyUSB0. Do komunikacji
z ruterem została użyta prywatna podsieć 192.168.6.0/24. Jako serwer
DHCP i TFTP został użyty program DNSmasq. Aby go zainstalować należy
wydać polecenie:
apt-get install dnsmasq
Po zainstalowaniu dnsmasq program należy skonfigurować. Plik konfiguracyjny znajduje si˛e w podanej ścieżce /etc/dnsmasq.conf Poniżej przykładowy plik konfiguracyjny:
interface=eth0
dhcp-range=192.168.6.50,192.168.6.150,12h
dhcp-host=00:0C:42:3C:FF:45,192.168.6.2
dhcp-boot=/var/tftp/boot.elf,192.168.6.1
enable-tftp
tftp-root=/var/tftp
Po skonfigurowaniu serwer dnsmasq należy go przeładować poleceniem:
/etc/init.d/dnsmasq restart
Po skonfigurowaniu dnsmasq należy skonfigurować interfejs sieciowy.
ifconfig eth0 192.168.6.1
Wymagany jest jeszcze serwer HTTP. W przykładzie został użyty serwer
nginx.
apt-get install nginx.
W konfiguracji nginx /etc/nginx/sites-enabled/default należy ustawić położenie plików serwowanych przez serwer.
4.5. Instalacja obrazu na RB433AH
20
server {
listen
80;
root /home/user/openwrt/trunk/bin/ar71xx/;
index index.html index.htm;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
try_files $uri $uri/ /index.html;
}
}
Ścieżka /home/user/ prowadzi do katalogu domowego użytkownika, w którym jest zainstalowany OpenWrt. Dodatkowo należy si˛e upewnić, że nginx
bedzie
˛
miał uprawnienia odczytu z tego katalogu. Po skonfigurowaniu serwera należy go zrestartować.
/etc/init.d/nginx restart
4.5.4. Uruchamianie OpenWrt z pamieci
˛ RAM
Aby ruter mógł pobrać obraz systemu z serwerem tftp należy go przenieść
do katalogu udost˛epnianego przez serwer tftp.
cp ~/openwrt/trunk/bin/ar71xx/\
openwrt-ar71xx-nand-vmlinux.elf\
/var/tftp/boot.elf
Teraz należy skonfigurować bootloader na RB433AH tak, aby obraz systemu pobrał z serwera tftp i go uruchomił. W tym celu należy podłaczyć
˛
ruter z komputerem kablem null-modem. Po podłaczeniu,
˛
należy uruchomić emulator terminala minicom. Po parametrze -D zostaje podana nazwa
portu szeregowego.
minicom -D /dev/ttyUSB0
Powyższe polecenie należy wykonać jako superużytkownik lub udost˛epnić
użytkownikowi port szeregowy. Po uruchomieniu minicom należy właczyć
˛
ruter. Minicom powinien zaczać
˛ wypisywać dane przychodzace
˛ z rutera. Po
pojawieniu si˛e nast˛epujacego
˛
napisu należy nacisnać
˛ dowolny klawisz na
klawiaturze.
RouterBOOT booter 2.39
RouterBoard 433AH
CPU frequency: 680 MHz
Memory size: 128 MB
Press any key within 2 seconds to enter setup..
W tym momencie powinniśmy być w trybie konfiguracji rutera.
4.5. Instalacja obrazu na RB433AH
21
RouterBOOT-2.39
What do you want to configure?
d - boot delay
k - boot key
s - serial console
n - silent boot
o - boot device
u - cpu mode
f - cpu frequency
r - reset booter configuration
e - format nand
g - upgrade firmware
i - board info
p - boot protocol
b - booter options
t - do memory testing
x - exit setup
your choice:
Opcja o umożliwia wybór urzadzenia
˛
z oprogramowaniem do startu. Po wybraniu opcji o możliwe jest wybranie startu systemu za pomoca˛ sieci ethernet wybierajac
˛ opcje e. Opcja x umożliwia wyjście z konfiguracji bootloadera. Po wybraniu powyższych opcji ruter powinien pobrać obraz i go uruchomić. Jeżeli wszystko przebiegło pomyślnie powinien pojawić si˛e dost˛ep
do konsoli OpenWrt. Cz˛estym bł˛edem jest komunikat “kernel out of range”.
Oznacza on, że obraz, który został stworzony jest zbyt duży. W takiej sytuacji należy usunać
˛ nie potrzebne elementy z obrazu i powtórnie wykonać
kompilacje. W takiej sytuacji przydatnymi opcjami moga˛ być:
Global build settings --->
Strip unnecessary exports from the kernel image
Global build settings --->
Strip unnecessary functions from libraries
Wybranie tych opcji spowoduje zredukowanie rozmiaru jadra
˛
Linuksa i
bibliotek. Jednakże moduły i programy później dograne moga˛ nie działać.
To nie powinno być problemem dla systemu służacego tylko do instalacji.
4.5.5. Uruchamianie OpenWrt z pamieci
˛ flash
Aby zainstalować OpenWrt w pami˛eci flash najpierw należy uruchomić
OpenWrt z pami˛eci RAM, pobierajac
˛ obraz przez ethernet. Po uruchomieniu
OpenWrt należy ustawić adres ip jeżeli nie został ustawiony poprawnie w
obrazie.
ifconfig eth0 192.168.6.2
Cała instalacja odbywa si˛e poprzez wydanie jednego polecenia:
wget2nand http://192.168.6.1
4.5. Instalacja obrazu na RB433AH
22
Po wykonaniu si˛e tego polecenia wystarczy zrestartować ruter, przejść do
konfiguracji bootloadera i ustawić bootowanie z pami˛eci nand. Jeżeli instalacja nie powiodła si˛e i otrzymujemy komunikaty “kernel out of memory”
oznacza, że obraz nie zmieścił si˛e w pami˛eci ram. W takiej sytuacji zaleca
sie˛ zmniejszyć rozmiar sytemu instalacyjnego. Możliwe jest także mocniejsze
skompresowanie obrazu np. za pomoca˛ programu bzip2. Domyślnie skompresowany jest za pomoca˛ programu gzip. Po zmianie kompresji należy też
zmodyfikować skrypt wget2nand, aby używał odpowiedniego narz˛edzia do
dekompresji.
5. Debugowanie
5.1. Debugowanie programu pracujacego
˛
w trybie
użytkownika
5.1.1. Warunki wstepne
˛
Aby móc debugować program w systemie wbudowanym potrzebne sa˛ nast˛epujace
˛ elementy:
— gdbserver zainstalowany na systemie wbudowanym.
— gdb skrośne zainstalowane na systemie macierzystym.
— Program skompilowany z symbolami dla debuggera.
— Połaczenie
˛
sieciowe lub połaczenie
˛
szeregowe mi˛edzy systemem macierzystym i wbudowanym
Instalacja gdbserver
W celu kompilacji gdbserver wraz z oprogramowaniem należy zaznaczyć
poniższa˛ opcje.
Utilities → gdbserver
Instalacja skrośnego gdb
Skrośne gdb zostanie skompilowane wraz z narz˛edziami kompilacji, jeżeli
została zaznaczona opcja
Advanced configuration options (for developers) -->
Toolchain Options -->
Build gdb
Jeżeli opcja była wcześniej nie zaznaczona może zaistnieć potrzeba rekompilacji narz˛edzi kompilacji. W celu rekompilacji należy narz˛edzia kompilacji
usunać,
˛ można to wykonać komenda:
˛
make dirclean
Komenda ta usunie również skompilowane obrazy OpenWrt. Pliki konfiguracyjne zostana˛ nietkni˛ete.
Kompilacja programu z symbolami
Tablica symboli dla debuggera przyporzadkowuje
˛
instrukcj˛e w kodzie wykonywalnym do odpowiadajacej
˛
im linii w kodzie źródłowym. Dodatkowo
znajduja˛ si˛e informacje do jakich zmiennych i funkcji odwołuja˛ instrukcje
programu. Aby skompilować program z symbolami dla debuggera należy
przekazać kompilatorowi flag˛e -g.
5.1. Debugowanie programu pracujacego
˛
w trybie użytkownika
24
zdalne debugowanie
Aby zdalnie debugować należy najpierw uruchomić gdbserver i podać mu
ścieżk˛e do debugowanego programu i port na którym ma nasłuchiwać.
gdbserver :9000 /ścieżka/do/programu
Teraz wystarczy uruchomić skrośnego gdb, wczytać do niego symbole potrzebne do debugowania i połaczyć
˛
si˛e z gdbserver. Można ta˛ czynność
recznie
˛
wykonać lub wykorzystać skrypt przygotowany przez developerów
OpenWrt. Poniżej sposób uruchomienia.
./scripts/remote-gdb 192.168.6.2:9000 /śćieżka/do/programu
Po wykonaniu powyższej komendy gdb połaczy
˛
si˛e z ruterem o adresie
192.168.6.2. W tym momencie b˛edziemy w stanie debugować program na
ruterze. Program ten b˛edzie uruchomiony i uśpiony w pierwszej instrukcji.
Aby rozpoczał
˛ swoje działania należy wydać polecenie continue.
Odłaczenie
˛
od gdb
Za pomoca˛ gdb można si˛e odłaczyć
˛
od gdbserver na dwa sposoby. Pierwszy
sposób powoduje, że program na systemie wbudowanym b˛edzie wykonywany. Ten sposób uzyskujemy za pomoca˛ komendy detach. Drugi sposób
spowoduje, że program b˛edzie w uśpieniu oczekiwał na podłaczenie
˛
kolejnego debuggera. W tym celu należy wykonać komend˛e disconnect.
5.1.2. Korzystanie z gdb
Dla wielu osób rozpoczynajacych
˛
prac˛e z gdb program ten wydaj˛e si˛e skomplikowany do użycia. Taka˛ opinie zawdzi˛ecza domyślnemu tekstowemu interfejsowi i dużej ilości funkcji. W zwiazku
˛
z duża˛ ilościa˛ funkcji programu
dokumentacja do gdb jest obszerna. Dlatego przedstawie podstawy użytkowania gdb. Oczywiście b˛edzie to tylko niewielki wycinek możliwości gdb.
Podczas debugowania przydatny jest dost˛ep do kodu źródłowego programu. Można uzyskać go na dwa sposoby. Otwierajac
˛ go w oddzielnym edytorze lub każac
˛ gdb go wypisać. Zaleta˛ wypisania kodu źródłowego przez gdb
jest możliwość w prosty sposób wybrania konkretnego fragmentu. Możemy
wypisać kod wokół aktualnie wykonywanej instrukcji, konkretnej funkcji
wykorzystujac
˛ do tego jedno polecenie. Poleceniem gdb służacym
˛
do wypisywania kodu źródłowego jest list lub w skrócie l [1]. Komenda list
bez argumentów wypisuje 10 linii po poprzednio wypisanych. Jako jeden
parametr możemy przekazać numer linii wokół, której komenda ma wypisywać kod źródłowy, adres instrukcji lub nazw˛e funkcji. W przypadku przekazywania adresu instrukcji należy poprzedzić go znakiem gwiazdki. W celu
wypisania kodu źródłowego wokół instrukcji należy użyć adresu z rejestru
$pc(Standardowy rejestr gdb) zawierajacego
˛
adres nast˛epnej instrukcji do
wykonania. Poniżej wynik wykonania komendy, która wypisuje 10 linii wokół nast˛epnej instrukcji.
0x400c04 is in main (UDPGenerator.c:31).
26 int getNetSocket(char * address, char * port);
27
28 int main(int argc, char *argv[])
5.1. Debugowanie programu pracujacego
˛
w trybie użytkownika
29 {
30
31
32
33
34
35
25
int random=0;
unsigned short int counter=0;
int sfd, datafd;
char *buf;
char *bufferrecv
Komenda list może również przyjmować dwa argumenty oddzielone przecinkiem. Za pomoca˛ tych argumentów można przekazać linie poczatkow
˛
a˛ i
końcowa˛ do wypisania. Przykład poniżej.
(gdb) l 33,36
33
int sfd, datafd;
34
char *buf;
35
char *bufferrecv;
36
size_t size=1024;
Aby przerwać wykonywanie programu na odpowiedniej instrukcji, ustawia
sie˛ pułapki (ang. breakpoint). Do stworzenia pułapki w gdb służy komenda
break lub w skrócie b [1]. Komenda ta przyjmuje jeden argument, który
może być numerem linii kodu, adresem, lub nazwa˛ funkcji. Argument ten
ma taka˛ sama˛ postać jak w komendzie list. Komenda break może również
nie przyjmować żadnego argumentu w takim przypadku ustawi pułapk˛e
na aktualnie wykonywanej instrukcji. Poniżej przykład dodania pułapki na
poczatku
˛
funkcji receiving.
(gdb) break getNetSocket
Breakpoint 2 at 0x4011b4: file UDPGenerator.c, line 131.
Przy pracy z pułapkami przydatne jest wypisanie aktualnie dodanych pułapek wraz z podstawowymi informacjami takimi jak ile razy zostały napotkane przez program. W tym celu należy wydać komend˛e info break.
Poniżej wynik komendy info break.
(gdb) info break
Num
Type
Disp Enb Address
What
1
breakpoint
keep y
0x00400c04 in main at UDPGenerator.c:31
breakpoint already hit 1 time
2
breakpoint
keep y
0x004011b4 in getNetSocket
at UDPGenerator.c:131
Do usuwanie pułapek służa˛ polecenia clear i delete. Komenda clear
kasuje pułapk˛e w nast˛epnej wykonywanej instrukcji (wygodny sposób kasowania pułapki, która właśnie wystapiła).
˛
Po podaniu jednego argumentu,
który może być nazwa˛ funkcji lub numerem linii kodu kasuje pułapki znajdujace
˛ si˛e w tych miejscach. Za pomoca˛ komendy delete możemy wykasować wszystkie pułapki nie podajac
˛ żadnego argumentu lub konkretna˛ pułapke˛ podajac
˛ jej numer. Numer pułapki można odczytać za pomoca˛ wspomnianej już komendy info break.
W celu przywrócenia wykonywania si˛e programu należ wydać polecenie
continue, które posiada skrót c. Program również można wykonywać linia
5.1. Debugowanie programu pracujacego
˛
w trybie użytkownika
26
po linii kodu źródłowego za pomoca˛ komendy next. Komenda ta nie wchodzi w ciała funkcji. Aby wejść do środka funkcji należy użyć polecenia step.
Debugger gdb umożliwia również wykonywanie programu instrukcja po instrukcji procesora. W tym celu należy użyć polecenia stepi. Możliwe jest
również rozpocz˛ecie wykonywania programu od dowolnego miejsca w programie. Polecenie jump przyjmuje jeden argument numer linii, nazw˛e funkcji lub adres i kontynuuje wykonywanie programu od tego miejsca.
W celu podejrzenia wartości wartości zmiennych programu można użyć
polecenia print zmienna.
(gdb) print s
$5 = 0
Jeżeli zachodzi potrzeba obserwacji wartości zmiennych podczas każdego
zatrzymania programu można użyć polecenia display zmienna. Poniżej
przykład.
(gdb) display hints.ai_protocol
1: hints.ai_protocol = 17
(gdb) n
148
for (rp = result; rp != NULL; rp = rp->ai_next) {
1: hints.ai_protocol = 17
(gdb) n
149
sfd = socket(rp->ai_family, rp->ai_socktype,
1: hints.ai_protocol = 17
Kiedy już nie zachodzi potrzeba informacji o zawartości zmiennej można
zrezygnować z jej wyświetlania. Służy do tego komenda undisplay numer.
(gdb) undisplay 1
(gdb) n
150
rp->ai_protocol);
Możliwości gdb nie ograniczaja˛ si˛e tylko do możliwości podgladu
˛ zmiennych,
ale również można je edytować. Komenda set zmienna=wartość służy do
ustawiania wartości zmiennych programu.
(gdb) print hints.ai_flags
$9 = 0
(gdb) set hints.ai_flags=1
(gdb) print hints.ai_flags
$10 = 1
W niektórych przypadkach, np. podczas korzystania z j˛ezyka assembler,
może zaistnieć potrzeba podgladu
˛
wartości rejestrów procesora. Debugger gdb posiada komend˛e info registers, która wypisuje zawartość rejestrów. Poniżej wypisane rejestry procesora o architekturze MIPS.
(gdb) info registers
zero
at
v0
v1
a0
a1
a2
a3
00000000 7f8fc974 00001388 0003a178 00001388 00000002 00000006 00000000
t0
t1
t2
t3
t4
t5
t6
t7
00000000 8017ff90 77eb8014 fffffff8 00031c7f ffffffff 77eb7144 00400a24
s0
s1
s2
s3
s4
s5
s6
s7
7f8fcf77 77e47de0 004006c0 7ffc8de1 00000001 0040d354 0040d35c 00420000
5.1. Debugowanie programu pracujacego
˛
w trybie użytkownika
27
t8
t9
k0
k1
gp
sp
s8
ra
0000000d 77e4b178 00000000 00000000 7f8fcf77 7f8fc980 7f8fc980 00400a24
sr
lo
hi
bad
cause
pc
0000f413 00001bbd 00000028 77e4b170 10800024 00400a28
fsr
fir
00000000 00000000
Podczas debugowania po zatrzymaniu si˛e programu na pułapce może
zajść potrzeba określenia ścieżki dojścia do danego miejsca programu.
W tym celu należy przeanalizować stos programu. Program gdb udost˛epnia
komend˛e backtrace, która wypisuje wywoływane kolejno funkcje oraz ich
argumenty.
(gdb) backtrace
#0 getNetSocket (address=0x7fff6f64 "192.168.47.136", port=0x401450 "5000")
at UDPGenerator.c:150
#1 0x00400d58 in main (argc=4, argv=0x7fff6e74) at UDPGenerator.c:67
Flaga full przekazana do polecenia backtrace powoduje wypisanie
również zmiennych lokalnych funkcji.
(gdb) backtrace full
#0 getNetSocket (address=0x7fff6f64 "192.168.47.136", port=0x401450 "5000")
at UDPGenerator.c:150
hints = {ai_flags = 1, ai_family = 0, ai_socktype = 1,
ai_protocol = 17, ai_addrlen = 0, ai_addr = 0x0, ai_canonname = 0x0,
ai_next = 0x0}
result = 0x412008
rp = 0x412008
s = 0
sfd = 6058232
#1 0x00400d58 in main (argc=4, argv=0x7fff6e74) at UDPGenerator.c:67
random = 1
counter = 0
sfd = 2
datafd = 2147446388
buf = 0x7fff6c78 "\177\377m\020"
bufferrecv = 0x7fff6d10 "w\376"
size = 1024
time = 1000
address = 0x7fff6f64 "192.168.47.136"
opt = -1 ’\377’
tv = {tv_sec = 2013261824, tv_usec = 0}
port = 0x401450 "5000"
rfds = {__fds_bits = {2013135952, 2013137232, 0, 0, 2147446610,
2012584523, 1181, 2012967872, 486, 1113, 4456448, 4361804,
2147445960, 2013148208, 2012584523, 4096, 7, 2013134848, 0, 0,
2013261824, 4196832, 2147446610, 2012770704, 4196352, 0, 6058256,
6058232, 2145971392, 2013161448, 2012770704, 4196412}}
crc32table = 0x7fff6c78
W prosty sposób można podejrzeć wartość zmiennych automatycznych i argumentów znajdujacej
˛
si˛e w aktualnej ramce stosu. Komenda
info locals wypisuje zmienne lokalne funkcji z kolei polecenie info args
wypisze argumenty funkcji.
hints = {ai_flags = 1, ai_family = 0, ai_socktype = 1, ai_protocol = 17,
ai_addrlen = 0, ai_addr = 0x0, ai_canonname = 0x0, ai_next = 0x0}
result = 0x412008
rp = 0x412008
s = 0
5.2. Integracja z Eclipse
28
sfd = 6058232
(gdb) info args
address = 0x7fff6f64 "192.168.47.136"
port = 0x401450 "5000"
5.2. Integracja z Eclipse
Dla systemów Unix i uniksopodobnych powstało bardzo wiele rozbudowanych programów z interfejsem tekstowym takich jak gcc, gdb edytory
tekstowe vi i emacs. Podczas pracy pod konsola˛ Linuksa mamy dost˛ep
do takich narz˛edzi jak grep, awk, find i wiele innych. Dla osoby znajace
˛
te programy konsola staje si˛e wygodnym i pot˛eżnym narz˛edziem. Jednak
wielu programistów woli zintegrowane środowiska deweloperskie z interfejsem graficznym. Takim środowiskiem jest Eclipse. Eclipse został stworzony
przez firm˛e IBM. Firma IBM udost˛epniła kod Eclips’a na licencji Eclipse
Public License, która uznawana jest za licencje wolnego oprogramowania.
Program
Eclipse
można
pobrać
ze
strony
internetowej
http://www.eclipse.org/. Należy wybrać wersje ze wsparciem dla
jezyków
˛
C i C++. Aby móc zintegrować Eclipse’a z OpenWrt należy
wcześniej skompilować narz˛edzia kompilacji z debuggerem. Dodatkowo
oprogramowanie rutera musi zawierać serwer ssh np. dropbear, pakiet
openssh-sftp-server umożliwiajace
˛
połaczenie
˛
poprzez sftp oraz program
gdbserver.
Po instalacji Eclipse’a należy doinstalować do niego wtyczki “C/C++ GCC
Cross Compiler Support” oraz “Remote System Explorer End-User Runtime”. W tym celu należy wybrać z menu zakładk˛e “Help”, a nast˛epnie opcje
“Install new Software”. Należy wybrać prac˛e ze wszystkimi dost˛epnymi stronami. Wymienione powyżej wtyczki należy wybrać z sekcji ”Mobile and Device Development“. Po instalacji wtyczek należy zrestartować Eclipse’a.
5.2. Integracja z Eclipse
29
Rysunek 5.1. Instalacja wtyczek
Teraz można stworzyć nowy projekt przeznaczony dla rutera. W tym celu
należy wybrać opcje tworzac
˛ a˛ nowy projekt j˛ezyka C lub C++. Projekt należy nazwać. W okienku Toolchains należy wybrać ”Cross GCC“ zamiast
”Linux GCC“. W zwykłych projektach cz˛esto zatwierdza si˛e stworzenie projektu przyciskiem ”Finish“. W tym przypadku jednak należy ustawić opcje
odnośnie skrośnego środowiska. Dlatego należy przejść do nast˛epnych opcji
klikajac
˛ ”Next“.
Do poprawnej pracy środowiska należy ustawić zmienna˛ środowiskowa˛
”STAGING“. W tym celu należy kliknać
˛ przycisk ”Advanced settings“. Po pojawieniu si˛e okienka konfiguracji, należy przejść do konfiguracji zmiennych
środowiskowych. Zakładka ”Enviroment“ znajduje si˛e w sekcji ”C/C++ Build“ w menu po lewej stronie. Po wybraniu ”Enviroment“ można dodać własna˛ zmienna klikajac
˛ ”Add“. Nazwa zmiennej powinna być ”STAGING“ wartość powinna być identyczna z ustawiana˛ w rozdziale 4.2. Po ustawieniu
zmiennej ”STAGING“ należy zamknać
˛ okienko przyciskiem ”OK“.
5.2. Integracja z Eclipse
30
Rysunek 5.2. Ustawianie zmiennej staging
Po zamkni˛eciu okienka “Advanced settings” należy przejść do ostatniej
karty klikajac
˛ “next”. W ostatniej karcie ustawia si˛e ścieżk˛e do plików binarnych skrośnego środowiska. Pliki te powinny znajdować w katalogu bin,
który znajduje si˛e w katalogu określonym przez zmienna˛ staging. Ścieżk˛e
te˛ należy wpisać w pole “Cross compiler path”. W polu “Cross compiler
prefix” należy wpisać przedrostek, który wyst˛epuje przed nazwami plików
wykonywalnych skrośnego kompilatora. Na przykład skrośne gcc dla architektury mips w środowisku OpenWrt ma nazw˛e mips-openwrt-linux-gcc
przedrostkiem jest mips-openwrt-linux-. Po wypełnieniu powyższych pól
należy zatwierdzić utworzenie projektu klikajac
˛ “Finish”.
Rysunek 5.3. Ustawianie skrośnego kompilatora
W tak utworzonym projekcie operacja “build project” powinna skompilować program przeznaczony dla rutera. W okienku “Console” powinny pojawić si˛e komunikaty kompilacji programu.
Aby móc uruchomić program musimy przegrać go do pami˛eci rutera.
Proces ten możemy zautomatyzować, aby Eclipse sam przegrywał. W tym
celu musimy dodać zdalne połaczenie.
˛
Na poczatek
˛
należy otworzyć okno
“Remote Systems”. Aby je otworzyć należy wybrać opcje z menu “window”
5.2. Integracja z Eclipse
31
-> “show view” -> “others”. Z Okienka, które si˛e otworzy wybrać “Remote
Systems”.
W okienku Remote Systems należy utworzyć nowe połaczenie
˛
klikajac
˛
prawym klawiszem myszki na “Local”, a nast˛epnie wybierajac
˛ “New” ->
“Connection”. Po wybraniu tej opcji powinno pojawić si˛e okienko służace
˛ do
dodawania nowego połaczenia.
˛
Należy wybrać “Linux” i kliknać
˛ “Next”. Nastepnie
˛
należy wpisać adres ip rutera w polu “Host Name” i kliknać
˛ “Next”.
W nast˛epnej zakładce w polu “Configuration” należy zaznaczyć “ssh.files”.
W kolejnych zakładkach po klikni˛eci “Next” w polu “Configuration” należy
zaznaczać nast˛epujace
˛ pola w kolejnych zakładkach“processes.shel.linux”,
“ssh.shells”, “ssh.terminals”. W ostatniej zakładce należy potwierdzić utworzeniu nowego połaczenia
˛
przyciskiem “Finish”.
W tej chwili Eclipse umożliwia dost˛ep do plików na ruterze, zarzadzanie
˛
procesami, a także dost˛ep do konsoli rutera.
Rysunek 5.4. Eclipse zdalne połaczenia
˛
Do pełnej integracji Eclipse’a brakuje profilu zdalnego debugowania.
W celu jego utworzenia należy z menu “Run” wybrać “Debug Configuration”. Aby utworzyć nowy profil zdalnej aplikacji, należy kliknać
˛ prawym
klawiszem na “C/C++ Remote Configuration”. W zakładce main należy wybrać połaczenie,
˛
które zostało utworzone w poprzednim kroku. Dodatkowo
należy ustawić lokalizacje na ruterze gdzie ma zostać przegrana aplikacja
5.2. Integracja z Eclipse
32
(Remote Absolute File Path for C/C++ Aplication). Katalog aplikacji do którego ma zostać przegrana powinien być wcześniej utworzony.
Rysunek 5.5. Eclipse konfiguracja zdalnego debugowania
W zakładce Debugger należy ustawić ścieżk˛e skrośnego gdb. Skrośne gdb
znajduje si˛e w katalogu plików wykonywalnych skrośnego kompilatora.
Przy rozpocz˛eciu debugowania należy pami˛etać o wybieraniu zdalnego
profilu debugowania. W tym momencie wszystkie czynności zwiazane
˛
z przegrywaniem i uruchamianiem aplikacji powinien zapewniać Eclipse.
Eclipse oferuje znaczna˛ liczb˛e funkcji gdb zapewniajac
˛ przy tym wygodny
i przejrzysty interfejs graficzny. Podczas debugowania korzysta si˛e z perspektywy debug Eclipse’a. Eclipse przy rozpocz˛eciu debugowania programu
powinien zapytać si˛e czy ma przejść do tej perspektywy. Perspektywa ta
umożliwia udost˛epnia wspomniane funkcje gdb.
Po lewej stronie kodu wyst˛epuje pionowy pasek, który umożliwia dodawanie i usuwanie pułapek. W prawym górnym rogu wyświetlane jest
okienko, w którym można obserwować i edytować zmienne programu, rejestry procesora i pułapki. W górnej belce menu wyst˛epuja˛ przyciski umożliwiajace
˛ kontynuowanie wykonywanie programu, wykonywanie programu
linii po linii bez wchodzenia do funkcji i z wchodzeniem do nich. Możliwe
jest także zatrzymywanie wykonywanie si˛e programu po każdej instrukcji
procesora. Wyst˛epuje również przycisk zamykajacy
˛ debugowany program.
Eclipse zamyka debugowane programy wysyłajac
˛ bezwzgl˛edny sygnał zamkni˛ecia, nie dajac
˛ szansy na zapisanie wyników pracy i zwolnienie używanych zasobów przez program. Dlatego powinno si˛e korzystać z tej opcji,
tylko gdy nie ma innej możliwości zamkni˛ecia programu.
Klikni˛ecie prawym klawiszem myszy na linie kodu otwiera menu kontekstowe, w którym wyst˛epuja˛ takie funkcje jak przechodzenie do danej linii,
uruchamianie programu od danej linii.
5.3. Debugowanie jadra
˛
Linux
33
Rysunek 5.6. Eclipse debugowanie
5.3. Debugowanie jadra
˛
Linux
Debugowanie kodu jadra
˛
jest trudniejsze niż programu pracujacego
˛
w przestrzeni użytkownika. Bład
˛ w kodzie uniemożliwia dalsza˛ prac˛e z systemem
i zmusza do restartu urzadzenia.
˛
Może także prowadzić do uszkodzenia systemu plików. Dlatego nie zaleca si˛e trzymania ważnych plików w systemie
plików, do którego dost˛ep ma system operacyjny na którym uruchamiamy
testowy kod.
5.3.1. Funkcja printk
Jedna˛ z najprostszych metod debugowania jadra
˛
jest wypisywanie komunikatów na konsole. Służy do tego odpowiednik funkcji printf o nazwie
printk. Przykład użycia funkcji.
printk( KERN_EMERG "Przyklad uzycia %i\n", zmienna);
Funkcja printk jest bardzo podobna do funkcji printf. Jednakże wyst˛epuja˛ mi˛edzy nimi pewne różnic˛e. Jedna˛ z nich jest brak obsługi liczb zmiennopozycyjnych. Kolejna˛ różnica˛ jest możliwość określenia priorytetu wiadomości przekazanej przez printk. Priorytet wiadomości ma wpływ, czy wiadomość zostanie wypisana na konsol˛e czy tylko zostanie zapisana do bufora
dmesg. Priorytet wiadomości określa si˛e poprzez makrodefinicje poprzedzajac
˛ a˛ pierwszy argument funkcji. Należy zauważyć, że mi˛edzy makrodefinicja˛
a pierwszym argumentem nie stawia si˛e przecinka. Funkcja printk może
przyjmować nast˛epujace
˛ priorytety: [5]
KERN_EMERG Najwyższy priorytet. Używany przy bł˛edach uniemożliwiaja˛
cych dalsza˛ prac˛e systemu.
5.3. Debugowanie jadra
˛
Linux
34
KERN_ALERT Sygnalizuje sytuacje wymagajac
˛ a˛ wykonania natychmiastowej akcji.
KERN_CRIT Używany do informowania o poważnych bł˛edach oprogramowania i sprz˛etu.
KERN_ERR Używany w wiadomościach informujacych
˛
o bł˛edach. Najcz˛eściej korzystaja˛ z niego sterowniki informujac
˛ o problemach sprz˛etowych.
KERN_WARNING Ostrzeżenie o problemach, które moga˛ być bł˛edami.
KERN_NOTICE Wiadomość o tym priorytecie informuje o prawidłowym
zdarzeniu.
KERN_INFO Wiadomość informacyjna np. o załadowaniu modułu lub wykryciu urzadze
˛
ń przez sterownik.
KERN_DEBUG Wiadomość przeznaczona do celów debugowania.
Pewnym problemem jest umieszczanie funkcji printk w cz˛esto wywoływanych funkcjach. Cz˛este wypisywanie komunikatów może spowolnić działanie systemu uniemożliwiajac
˛ prac˛e z nim. Ponadto zbyt cz˛este wypisywanie tego samego komunikatu może bardziej utrudniać debugowanie niż ułatwiać. Jednakże przydatna mogła być wiedza czy funkcja jest wywoływana
i jakie wartości maja˛ zmienne. W takim przypadku można ograniczyć wywoływanie funkcji printk. Funkcja printk_ratelimit( ) analizuje cz˛estość
wywołań funkcji printk. Jeżeli cz˛estość wywołań funkcji printk przekroczy
limit funkcja printk_ratelimit( ) zaczyna zwracać zero. Typowe użycie
funkcji printk_ratelimit( ) wyglada
˛ nast˛epujaco.
˛
if(printk_ratelimit())
printk( KERN_DEBUG "przyklad uzycia ratelimit");
5.3.2. KDB i KGDB
W głównej linii Linuksa przez wiele lat nie znajdował si˛e żaden debugger
jadra.
˛
KDB i KGDB wyst˛epowały tylko jako niezależne poprawki na Linuksa.
Odpowiedzialnym za t˛e sytuacj˛e był Linus Torvalds, autor Linuksa, który
stwierdził, że debugger nie jest odpowiednim narz˛edziem do rozwiazywania
˛
problemów programistycznych. Jednakże w wersji 2.6.26 do Linuksa został
właczony
˛
debugger KGDB. Wersja 2.6.35 przyniosła KDB, które zostało
połaczone
˛
z KGDB.
W aktualnych wersjach Linuksa jedyna˛ różnica˛ mi˛edzy KDB i KGDB jest
interfejs komunikacji z użytkownikiem. KDB udost˛epnia tekstowy interfejs
użytkownika. KGDB udost˛epnia interfejs taki sam jak gdbserver. Dlatego
w celu debugowania za pomoca˛ KGDB należy użyć na systemie macierzystym GDB. Dost˛ep do KDB może odbywać si˛e poprzez monitor i klawiature˛
lub przez port szeregowy. KGDB wykorzystuje tylko połaczenie
˛
poprzez port
szeregowy. Przed właczeniem
˛
do głównej linii jadra
˛
istniała wersja KGDB,
która umożliwiała wykorzystanie połaczenia
˛
ethernet. Ta wersja okazała si˛e
nie działać stabilnie.
Aby móc korzystać z KGDB i KDB należy je właczyć
˛
podczas kompilacji
jadra.
˛
W tym celu należy właczyć
˛
nast˛epujace
˛ opcje przed kompilacja.
˛
-> Kernel hacking
-> KGDB: kernel debugger
5.3. Debugowanie jadra
˛
Linux
35
Opcja ta włacza
˛
obsług˛e KGDB. Poniższa opcja włacza
˛
obsług˛e portu szeregowego dla KGDB.
-> Kernel hacking
-> KGDB: kernel debugger
->KGDB: use kgdb over the serial console
Aby mieć dost˛ep również do interfejsu KDB należy właczyć
˛
nast˛epujac
˛ a˛
opcje.
-> Kernel hacking
-> KGDB: kernel debugger
-> KGDB_KDB: include kdb frontend for kgdb
Aby efektywnie debugować należy skompilować jadro,
˛
podobnie jak zwykła˛
aplikacje, razem z symbolami potrzebnymi do debugowania. W tym celu
należy zaznaczyć podczas konfigurowania jadra
˛
poniższa˛ opcje.
-> Kernel hacking
-> Compile the kernel with debug info
W przypadku korzystania z środowiska OpenWrt należy zaznaczyć t˛e opcj˛e
w konfiguracji OpenWrt. Należy zauważyć, że opcja ustawiona w konfiguracji OpenWrt ma wyższy priorytet od opcji ustawionej w konfiguracji ja˛
dra. Opcja umożliwiajaca
˛ dodanie symboli do jadra
˛
w OpenWrt znajduj˛e si˛e
w nast˛epujacym
˛
miejscu.
-> Global build settings
-> Compile the kernel with debug information
Przed użyciem KGDB należy go odpowiednio skonfigurować. KGDB
potrzebuje informacji, który port i z jaka˛ pre˛ dkościa˛ b˛edzie używać. Te informacje można przekazać do jadra
˛
jako parametry
podczas bootowania lub po uruchomieniu systemu poprzez plik
/sys/module/kgdboc/parameters/kgdboc.
Aby używać KGDB za pomoca˛ portu szeregowego /dev/ttyS0
z predkości
˛
a˛ 115200 bps należy przekazać nast˛epujace
˛
parametry do jadra
˛
kgdboc=ttyS0,115200. W przypadku uruchomienia
systemu bez konfiguracji KGDB można skonfigurować KGDB zapisujac
˛ nazw˛e portu np. ttyS0 do wspomnianego wcześniej pliku
/sys/module/kgdboc/parameters/kgdboc.
W tak przygotowanym jadrze
˛
debugger jest gotowy do pracy. Teraz
wystarczy zatrzymać wykonywanie si˛e systemu i uruchomić debugger.
W tym celu należy do jadra
˛
wysłać sygnał sysrq-g. Sygnał ten można wysłać na nast˛epujace
˛ sposoby. Za pomoca˛ klawiatury naciskajac
˛ klawisze
Print Screen+Alt+g jest to nieprzydatna opcja przy współpracy z ruterami, które nie maja˛ klawiatury. Najwygodniejsza˛ opcja˛ przy pracy z ruterem jest użycie konsoli szeregowej. W tym celu należy wysłać przez konsole
szeregowa˛ sygnał break, a nast˛epnie literk˛e g. Ostatnia˛ możliwościa˛ jest
zapisanie literki g do pliku /proc/sysrq-trigger.
Linux dodatkowo przełaczy
˛
si˛e w tryb debuggera, jeżeli napotka na wyja˛
tek lub na poczatku
˛
uruchamiania,jeżeli był przekazany do jadra
˛
parametr
kgdbwait.
5.3. Debugowanie jadra
˛
Linux
36
Jak zostało wspomniane, wygodne jest wysłanie sysrq-g poprzez port
szeregowy. W tym celu należy wysłać sygnał break. Jest to specjalny sygnał
polegajacy
˛ na wysyłaniu samych zer przez dłuższy czas niż okres jednej
ramki. W różnych emulatorach konsoli szeregowej t˛e funkcje różnie si˛e
uruchamia. W programie minicom należy nacisnać
˛ CTRL+A, a nast˛epnie F.
Po tej kombinacji zostanie wysłany sygnał break. Po nast˛epnym naciśni˛eciu
klawisza G powinien zgłosić si˛e debugger.
Jeżeli po przejściu w tryb debuggera ruter po krótkim czasie samoczynnie restartuje si˛e, to w takim przypadku najprawdopodobniej ruter posiada
sprzetowy
˛
watchdog. Układ ten restartuje ruter kiedy nie otrzymuje od niego
sygnału, że nadal działa. Podczas wejścia w tryb debuggera zatrzymuje si˛e
wykonywanie całego systemu na ruterze łacznie
˛
z programem odpowiedzialnym za obsług˛e watchdoga. Aby rozwiazać
˛
ten problem wystarczy wyłaczyć
˛
program odpowiedzialny za obsług˛e watchdoga, który podczas zamykania
wyłaczy
˛
sprz˛etowego watchdoga. W systemie Linux najcz˛eściej programem
odpowiedzialnym za obsług˛e watchdoga jest demon o nazwie watchdog. Wyłaczyć
˛
go można nast˛epujacym
˛
poleceniem.
/etc/init.d/watchdog stop
5.3.3. Korzystanie z KDB
W rozdziale tym przedstawiłem podstawowe elementy obsługi KDB. Poniższe informacje powinny zapewnić możliwość sprawnej pracy z KDB. KDB
oprócz funkcji typowych dla debuggera oferuje funkcje informujace
˛ o stanie
systemu np. list˛e uruchomionych procesów.
Pierwsza˛ komenda˛ z jaka˛ należy si˛e zapoznać jest help wypisuje ona
wszystkie dost˛epne komendy z krótkim opisem. Poniżej komendy warte
zapoznania. [5]
go [adres ] Kontynuuje wykonywanie si˛e systemu. Opcjonalnie można przekazać adres od którego ma rozpoczać
˛ wykonywanie.
dmesg Komenda wypisujaca
˛
bufor z komunikatami wypisanymi przez
funkcje printk.
ps Wypisuje list˛e procesów uruchomionych w systemie.
kill <-signal> <process> Wysyła sygnał do procesu. Należy zauważyć, że
wysyłanie sygnału do procesu, który nie jest w stanie Running może
doprowadzić do zakleszczenia. Poniżej przykład użycia.
kdb> kill -9 810
Signal 9 is sent to process 810.
lsmod Wypisuje list˛e załadowanych modułów do jadra.
˛
reboot Restartuje komputer.
summary Podsumowanie systemu. Znajduje si˛e w nich wersja jadra,
˛
data
kompilacji, nazwa komputera, data, podsumowanie pami˛eci i inne.
kdb> summary
sysname
Linux
release
3.2.9
version
#30 Fri Jun 8 19:56:07 CEST 2012
machine
mips
nodename
OpenWrt
5.3. Debugowanie jadra
˛
Linux
domainname
ccversion
date
uptime
load avg
37
(none)
CCVERSION
1970-01-01 00:01:07 tz_minuteswest 0
00:01
0.37 0.14 0.05
MemTotal:
31577 kB
MemFree:
29788 kB
Buffers:
0 kB
bp adres Dodaje pułapk˛e pod wskazanym adresem.
kdb> bp 0x8018b664
Instruction(i) BP #0 at 0x8018b664
is enabled addr at 000000008018b664, hardtype=0 installed=0
bl Lista dodanych pułapek.
kdb> bl
Instruction(i) BP #0 at 0x8018b664
is enabled addr at 000000008018b664, hardtype=0 installed=0
bc numer Usuwa pułapk˛e.
bd numer Wyłacza
˛
pułapk˛e.
be numer Włacza
˛
pułapk˛e.
bt Wypisuje dane na stosie (wywoływane funkcje).
kgdb Przełacza
˛
w tryb KGDB. Aby powrócić do trybu KDB należy wpisać
$3#33.
rd Wypisuje zawartość rejestrów procesora.
kdb> rd
zero: 00000000 at: 80310000 v0: 00000001 v1: 00000001 a0: 80290000
a1: 00000000 a2: 00000005 a3: 80190f58 t0: 00000047 t1: 8033ad95
t2: 00000018 t3: 53797352 t4: 00000000 t5: 00000000 t6: 00000000
t7: 00480000 s0: 802cbec0 s1: 00000067 s2: 802d0000 s3: 00000000
s4: 802d0000 s5: 00000007 s6: 00000001 s7: 87911000 t8: 00000008
t9: 8018eedc k0: 1000f402 k1: 00000000 gp: 802bc000 sp: 802bdc70
s8: 8028e9ac ra: 8018b664 sr: 1000f402 lo: 19d60000 hi: 00000000
bad: 80095dd8 cause: 10800024 pc: 8018b664 f0: 8018b664 f1: 8018b664
f2: 8018b664 f3: 8018b664 f4: 8018b664 f5: 8018b664 f6: 8018b664
f7: 8018b664 f8: 8018b664 f9: 8018b664 f10: 8018b664 f11: 8018b664
f12: 8018b664 f13: 8018b664 f14: 8018b664 f15: 8018b664 f16: 8018b664
f17: 8018b664 f18: 8018b664 f19: 8018b664 f20: 8018b664 f21: 8018b664
f22: 8018b664 f23: 8018b664 f24: 8018b664 f25: 8018b664 f26: 8018b664
f27: 8018b664 f28: 8018b664 f29: 8018b664 f30: 8018b664 f31: 8018b664
fsr: 8018b664 fir: 8018b664
5.3.4. Korzystanie z KGDB
W celu skorzystania z KGDB należy wysłać sygnał sysrq-g. Jeżeli jadro
˛
obsługuje KDB, należy właczyć
˛
KGDB za pomoca˛ komendy kgdb.
kdb> kgdb
Entering please attach debugger or use $D#44+ or $3#33
Teraz należy podłaczyć
˛
gdb za pomoca˛ portu szeregowego do KGDB. Po uruchomieniu skrośnego gdb i załadowaniu symboli jadra
˛
należy połaczyć
˛
si˛e
z kgdb w nast˛epujacy
˛ sposób.
5.3. Debugowanie jadra
˛
Linux
38
(gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
0x8018b664 in __handle_sysrq (key=103, check_mask=true)
at drivers/tty/sysrq.c:525
525 op_p->handler(key);
(gdb)
Ponieważ używanie gdb podłaczonego
˛
do kgdb jest podobne do debugowania zwykłej aplikacji opisanej w rozdziale5.1.2 pokazałem tylko przykład
dodania pułapki.
(gdb) b sys_sync
Breakpoint 1 at 0x801193f4: file fs/sync.c, line 103.
(gdb) c
Continuing.
[New Thread 754]
[Switching to Thread 754]
Breakpoint 1, sys_sync () at fs/sync.c:103
103 {
(gdb)
5.3.5. KGDB i ładowalny moduł
W celu wygodnego debugowania modułu, który jest ładowalny do jadra
˛
należy załadować symbole potrzebne do debugowania. Podczas ładowania
symboli modułu należy również poinformować gdb, w którym miejscu pamieci
˛ został załadowany kod modułu, zmienne zainicjowane i zmienne niezainicjowane [5]. W tym celu należy odczytać adresy odpowiednich segmentów. Poniżej odczytane informacje dla modułu o nazwie main.
root@OpenWrt:~# cat /sys/module/main/sections/.text
0x86a52000
root@OpenWrt:~# cat /sys/module/main/sections/.data
0x86a52400
root@OpenWrt:~# cat /sys/module/main/sections/.bss
0x86a525a0
Nastepnie
˛
należy dodać symbole w debuggerze gdb za pomoca˛ komendy
add-symbol-file. Komenda ta przyjmuje nast˛epujace
˛ argumenty: ścieżk˛e
do pliku z symbolami, adres segmentu z kodem wykonywalnym(text) dodatkowo za pomoca˛ flagi -s można przekazać adresy pozostałych segmentów.
Przykład załadowania symboli modułu poniżej.
(gdb) add-symbol-file /home/marcin/DevOverIP/main.ko 0x86a52000 \
-s .data 0x86a52400 -s .bss 0x86a525a0
add symbol table from file "/home/marcin/DevOverIP/main.ko" at
.text_addr = 0x86a52000
.data_addr = 0x86a52400
.bss_addr = 0x86a525a0
(y or n) y
Reading symbols from /home/marcin/DevOverIP/main.ko...done.
6. Profilowanie
Jednym z etapów tworzenia oprogramowania jest optymalizacja. Ponieważ
procesory w ruterach maja˛ dość mała˛ moc obliczeniowa˛ oraz mała˛ ilość
dostepnej
˛
pami˛eci RAM oprogramowanie przeznaczone dla nich wymaga
wiekszej
˛
pracy nad optymalizacja.
˛
Skuteczna optymalizacja kodu oprogramowania polega na optymalizacji
kodu najcz˛eściej wykonywanego. Nie opłaca si˛e optymalizacja kodu, który
wykonuje si˛e bardzo rzadko np. obsługa sytuacji wyjatkowych,
˛
kod inicjujacy
˛ zmienne itp. Aby poznać rzeczywiste cz˛estotliwości wykonywania poszczególnych cz˛eści kodu oprogramowania stosuje si˛e programy zwane profilerami.
W niniejszej pracy zostały przedstawione dwa najpopularniejsze profilery
przeznaczone dla systemu Linux.
6.1. gprof
Gprof jest profilerem tworzonym w ramach projektu GNU. Aby profilować
aplikacje za pomoca˛ gprof trzeba spełnić nast˛epujace
˛ warunki. Aplikacja
musi być skompilowana za pomoca˛ kompilatora gdb z właczon
˛
a˛ opcja˛ gprof.
Aplikacja również musi używać Glibc, standardowej biblioteki j˛ezyka C
stworzonej w ramach projektu GNU. Projekt OpenWrt domyślnie korzysta
z biblioteki uClibc. Biblioteka ta została stworzona specjalnie dla systemów
wbudowanych. uClibc jest biblioteka˛ dużo mniejsza˛ od Glibc, ale zostało to
osiagni˛
˛ ete poprzez zmniejszenie funkcjonalności i wydajności. Środowisko
openWrt udost˛epnia nast˛epujace
˛ standardowe biblioteki j˛ezyka C: uClibc,
Eglibc i musl.
Eglibc jest biblioteka,
˛ której kod bazuje na kodzie biblioteki Glibc. Autorzy Eglibc wprowadzaja˛ jedynie poprawki optymalizujace
˛ działanie biblioteki
w systemach wbudowanych zachowujac
˛ pełna˛ zgodność z Glibc. W zwiazku
˛
z tym do korzystania z gprof można użyć biblioteki Eglibc. W celu użycia
biblioteki EGLIBC w OpenWrt należy wybrać ja˛ podczas konfiguracji obrazu
oprogramowania dla rutera. Wybór znajduje si˛e w nast˛epujacym
˛
miejscu.
-> Advanced configuration options (for developers)
-> Toolchain Options
-> C Library implementation
Należy zauważyć, że po zmianie tej opcji zostanie skompilowane od nowa
całe środowisko OpenWrt. W tym oprogramowanie dla rutera i narz˛edzia
kompilacji (ang. toolchain). Pliki nowego środowiska nie zastapi
˛ a˛ bezpośrednio plików starego środowiska lecz zostana˛ umiejscowione w katalogach,
których nazwa b˛edzie zawierała przyrostek wybranej biblioteki. Zwiazku
˛
40
6.1. gprof
z tym zmieni si˛e ścieżka do katalogu staging oraz ścieżka katalogu z obrazem oprogramowania dla rutera.
W celu profilowania programu należy skompilować program za pomoca˛
kompilatora gcc z flaga˛ -pg. Poniżej przykład kompilacji programu w środowisku OpenWrt.
~/openwrt/trunk/staging_dir/\
toolchain-mips_r2_gcc-version-linaro_eglibc-version/\
bin/mips-openwrt-linux-gcc -pg program.c -o proggprof
Po skompilowaniu programu należy go przegrać oraz uruchomić na maszynie docelowej. Statystyki wykonywania si˛e programu zostana˛ zapisane
w pliku gmon.out. Jeżeli program b˛edzie tworzył nowe procesy np. za pomoca˛ funkcji fork, to procesy b˛eda˛ nadpisywały wspólny plik gmon.out. Aby
rozwiazać
˛
ten problem należy np. w procesach potomnych zmieniać ścieżk˛e
katalogu roboczego, w którym nie znajduj˛e si˛e plik gmon.out. Po wykonaniu si˛e programu należy przegrać plik gmon.out na komputer macierzysty
W celu dokonania analizy za pomoca˛ gprof należy wydać nast˛epujace
˛ polecenie.
gprof /sciezka/do/kodu/programu /sciezka/do/gmon.out
Po wydaniu powyższego polecenia statystyki programu gprof zostana˛ wydrukowane na konsoli. gprof przedstawia statystyki w postaci płaskiej i grafu
wywołań funkcji.
6.1.1. gprof płaskie statystyki
Płaskie statystyki przedstawiaja˛ łaczny
˛
czas sp˛edzony przez procesor w danej funkcji. Funkcje, które nie zostały wywołane nie zostana˛ wypisane jeżeli
gprof nie zostanie uruchomione z opcja˛ -z. Funkcje, które nie zostały skompilowane z obsługa˛ gprof np. z bibliotek zewn˛etrznych nie zostana˛ uwzgl˛ednione, jeżeli nie były wykonywane dostatecznie długo. Poniżej przedstawiam
przykładowe statystyki.
Each sample counts as 0.01 seconds.
%
cumulative
self
self
time
seconds
seconds
calls us/call
37.30
0.15
0.15 76956198
0.00
30.87
0.27
0.12
37613
3.20
14.15
0.32
0.06 38440486
0.00
10.29
0.36
0.04 38440486
0.00
5.15
0.38
0.02
37613
0.53
2.57
0.39
0.01
0.00
0.39
0.00
1
0.00
total
us/call
0.00
9.60
0.00
0.00
10.14
0.00
name
xor
crc32
andff
shift8
mainLoop
main
getNetSocket
Poszczególne kolumny maja˛ nast˛epujace
˛ znaczenie.
% time Procent czasu procesora, który sp˛edził w danej funkcji. Nie wlicza
si˛e czasu procesora podczas wywołań innych funkcji.
cumulative seconds Czas wyrażony w sekundach sp˛edzony w danej funkcji łacznie
˛
z wywołaniem innych funkcji.
self seconds Czas wyrażony w sekundach sp˛edzony w danej funkcji z wyłaczeniem
˛
wywołań innych funkcji
calls liczba wywołań danej funkcji.
41
6.1. gprof
self ns/call Średni czas sp˛edzony przez procesor podczas pojedynczego wywołania funkcji wyrażony w nanosekundach.
total ns/call Średni czas sp˛edzony przez procesor podczas pojedynczego
wywołania funkcji.
name Nazwa funkcji.
6.1.2. gprof graf wywołań funkcji
Graf wywołań funkcji przedstawia funkcje, które sa˛ wywoływane przez dana˛
funkcje oraz funkcje, które wywołuja˛ dana˛ funkcje. Dzi˛eki niemu możemy
sprawdzić, które funkcja najdłużej si˛e wykonuje w danej funkcji.
index % time
self
children
called
name
<spontaneous>
0.01
0.38
main [1]
0.02
0.36
37613/37613
mainLoop [2]
0.00
0.00
1/1
getNetSocket [7]
----------------------------------------------0.02
0.36
37613/37613
main [1]
[2]
97.4
0.02
0.36
37613
mainLoop [2]
0.12
0.24
37613/37613
crc32 [3]
----------------------------------------------0.12
0.24
37613/37613
mainLoop [2]
[3]
92.3
0.12
0.24
37613
crc32 [3]
0.15
0.00 76956198/76956198
xor [4]
0.06
0.00 38440486/38440486
andff [5]
0.04
0.00 38440486/38440486
shift8 [6]
----------------------------------------------0.15
0.00 76956198/76956198
crc32 [3]
[4]
37.2
0.15
0.00 76956198
xor [4]
----------------------------------------------0.06
0.00 38440486/38440486
crc32 [3]
[5]
14.1
0.06
0.00 38440486
andff [5]
----------------------------------------------0.04
0.00 38440486/38440486
crc32 [3]
[6]
10.3
0.04
0.00 38440486
shift8 [6]
----------------------------------------------0.00
0.00
1/1
main [1]
[7]
0.0
0.00
0.00
1
getNetSocket [7]
-----------------------------------------------
[1]
100.0
Graf wywołań funkcji jest podzielony na sekcje. Każda sekcja posiada
wyróżniona˛ funkcje. Funkcja ta jest oznaczona w kolumnie index. Poniżej
wyróżnionej funkcji znajduja˛ si˛e funkcje, które sa˛ przez nia˛ wywoływane.
Powyżej funkcji znajduja˛ si˛e funkcje, które ja˛ wołaja.
˛
Znaczenia poszczególnych kolumn w linii funkcji wyróżnionej sa˛ nast˛epujace.
˛
index Numer funkcji wpisany tylko przy funkcji wyróżnionej.
%time Procent czasu, jaki procesor sp˛edził w danej funkcji oraz funkcjach
przez nia˛ wywołanych.
self Czas sp˛edzony przez procesor w danej funkcji bez czasu sp˛edzonego
w funkcjach przez nia˛ wywołanych.
children Czas sp˛edzony przez procesor w funkcjach wywołanych przez
dana˛ funkcje.
called Liczba wywołań danej funkcji.
6.2. Oprofile
42
name Nazwa danej funkcji oraz jej numer.
W przypadku funkcji wywołujacych
˛
funkcje wyróżniona˛ znaczenie kolumn jest nast˛epujace.
˛
self Czas sp˛edzony w funkcji wyróżnionej kiedy była wywołana przez funkcje opisana˛ ta˛ linia.
˛
children Czas sp˛edzony w funkcjach wywołanych przez funkcje wyróżniona,
˛ która została wywołana przez funkcje opisana˛ ta˛ linia.
˛
called Liczba wywołań funkcji wyróżnionej z funkcji opisanej ta˛ linia˛ oraz
suma wszystkich nierekursywnych wywołań funkcji wyróżnionej.
name Nazwa funkcji oraz jej numer.
Funkcje wywołane przez funkcj˛e wyróżniona˛ opisane sa˛ w nast˛epujacy
˛
sposób.
self Czas procesora sp˛edzony w funkcji opisanej dana˛ linia,
˛ kiedy była
wywołana przez funkcje wyróżniona.
˛
children Czas procesora sp˛edzony w funkcjach wywołanych przez funkcje
opisana,
˛ która była wywołana przez funkcje wyróżniona.
˛
called Liczba wywołań funkcji przez funkcje wyróżniona˛ oraz suma wszystkich nierekursywnych wywołań funkcji.
name Nazwa funkcji i jej numer.
W grafie moga˛ wystapić
˛
cykle rekurencji. Wyst˛epuja˛ one jeżeli funkcje
w sposób bezpośredni lub pośredni wywołuja˛ si˛e nawzajem. W powyższym
przykładzie funkcje tree, treel i treer tworza˛ cykl, który jest oznaczony
jako <cycle 1>. Informacja do jakiego cyklu należy funkcja znajduje si˛e
pomi˛edzy nazwa˛ funkcji, a jej numerem w kolumnie name.
6.2. Oprofile
Oprofile można uruchomić tylko pod Linuksem w przeciwieństwie do gprof,
którego można było używać pod każdym systemem, w którym wyst˛epuj˛e
biblioteka Glibc. Ta drobna wada jest rekompensowana przez liczne zalety
w stosunku do Gprof. Oprofile umożliwia profilowanie kodu jadra,
˛
programy
i biblioteki nie musza˛ być specjalnie kompilowane do profilowania. Umożliwia nie tylko poznanie czasu wykonywania si˛e funkcji w ramach pojedynczych programów, ale oferuje możliwość poznania czasów wykonywania si˛e
całych programów w ramach całego systemu. Oprofile również radzi sobie bez wi˛ekszych problemów z profilowaniem aplikacji, które wykorzystuja˛
wiele procesów. Autorzy Oprofile zapewniaja,
˛ że ich produkt ma mały narzut
na wydajność systemu.
Oprofile przerywa prace˛ systemu poprzez przerwanie co pewien stały
czas. Podczas przerwania Oprofile sprawdza jaki kod właśnie był wykonywany i zapisuje jako próbk˛e. Funkcje, które rzadko i szybko si˛e wykonuja˛
moga˛ nigdy nie trafić jako próbki. Dla funkcji cz˛esto i długo si˛e wykonuja˛
cych, a takie nas interesuja˛ podczas profilowania, model ten przy odpowiednio krótkim czasie próbkowania dobrze si˛e sprawdza.
6.2.1. Instalacja Oprofile
Aby mieć możliwość wykorzystania Oprofile należy właczyć
˛
podczas konfigurowania kompilacji jadra
˛
Linuksa wsparcie dla profilowania.
43
6.2. Oprofile
-> General setup
-> Profiling support
Po zaznaczeniu opcji wsparcia profilowania powinna pojawić si˛e opcja kompilacji Oprofile. Oprofile można skompilować bezpośrednio w jadrze
˛
lub jako
oddzielny moduł.
-> General setup
-> OProfile system profiling
Jeżeli korzystamy z środowiska OpenWrt powyższe opcje właczamy
˛
podczas
konfiguracji obrazu oprogramowania do OpenWrt. Należy pami˛etać, że opcje
podczas konfiguracji obrazu OpenWrt maja˛ wyższy priorytet od opcji konfiguracji jadra
˛
Linuksa przez make kernel_menuconfig.
-> Global build settings
-> Compile the kernel with profiling enabled
Powyższa opcja włacza
˛
wsparcie w jadrze
˛
dla profilowania i jest odpowiednikiem opcji Profiling support.
-> Kernel modules
-> Other modules
-> kmod-oprofile
Opcja
kompilacji
modułu
OProfile system profiling.
Oprofile
odpowiednik
opcji
-> Development
-> oprofile
Właczenie
˛
powyższej opcji spowoduje skompilowanie niezb˛ednych programów do obsługi Oprofile. Dodatkowo właczaj
˛
ac
˛ opcje oprofile-utils
można skompilować dodatkowe programy wchodzace
˛
w skład Oprofile,
które nie sa˛ niezb˛edne do działania Oprofile, ale moga˛ okazać si˛e przydatne.
6.2.2. Uruchomienie Oprofile
Jeżeli Oprofile b˛edzie wykorzystywany do profilowania kodu jadra
˛
Linux należy załadować do niego obraz jadra.
˛
Plik vmlinux zawierajacy
˛ pełne nieskompresowane jadro
˛
w środowisku OpenWrt znajdziemy w podkatalogu
folderu build_dir zawierajacym
˛
kod Linuksa. Ten plik należy przegrać do
pami˛eci rutera np. poprzez sftp. Nast˛epnie za pomoca˛ programu opcontrol
należy poinformować Oprofile o położeniu pliku vmlinux w sposób nast˛epujacy.
˛
opcontrol --vmlinux=/ścieżka/do/vmlinux
Po tej operacji można uruchomić usług˛e Oprofile, która b˛edzie gromadzić
statystyki.
opcontrol --start
6.2. Oprofile
44
6.2.3. Prezentacja wyników Oprofile
Do prezentacji wyników w Oprofile służy program opreport. Program ten
bez żadnych parametrów wyświetli ilość zebranych próbek oraz ich procentowy udział dla programów, bibliotek oraz jadra.
˛
Po uruchomieniu
opreport może wystapić
˛
nast˛epujacy
˛ bład.
˛
opreport error: No sample file found: try running opcontrol
--dump or specify a session containing sample files
Oznacza on, że Oprofile nie zda˛żył jeszcze zapisać pliku ze statystykami.
Oprofile zapisuje statystyki stosunkowo rzadko, aby nie obcia˛żać systemu
ciagłym
˛
zapisem do pliku. Aby zmusić Oprofile do zapisu statystyk wystarczy użyć programu opcontrol z opcja˛ --dump. Polecenia te należy wydawać
kiedy też oczekujemy nowych danych np. po uruchomieniu programu, który
jest profilowany.
Poniżej przykład cz˛eść raportu stworzonego przez opreport.
# opreport
CPU: MIPS 24K, speed 678 MHz (estimated)
Counted INSTRUCTIONS events (1-0 Instructions completed)
with a unit mask of 0x00 (No unit mask) count 100000
INSTRUCTIONS:1...|
samples|
%|
-----------------32672 84.0978 vmlinux
5511 14.1853 UDPGenerator
366 0.9421 libuClibc-0.9.33.2.so
169 0.4350 oprofiled
75 0.1931 busybox
55 0.1416 ld-uClibc-0.9.33.2.so
2 0.0051 libgcc_s.so.1
Pierwsza linia informuje o posiadanym procesorze przez maszyn˛e, na której
został uruchomiony Oprofile. W tym przypadku jest to procesor MIPS 24K
taktowany zegarem 678MHz. Nast˛epna linia informuje o zdarzeniu, które
wyzwala pobranie próbki przez Oprofile. Domyślnym zdarzeniem dla tego
procesora jest licznik zliczajacy
˛
do 100000. Licznik jest taktowany tym
samym zegarem co procesor. Jak łatwo policzyć w tej konfiguracji próbka
bedzie
˛
pobierana 6780 razy na sekund˛e.
Raport ma form˛e tabeli. Pierwsza kolumna tabeli oznacza liczb˛e próbek
pobranych dla danego elementu. W drugiej został zapisany udział procentowy. W trzeciej kolumnie znajduj˛e si˛e nazwa pliku. Po przekazaniu do
opreport opcji -f wyświetlona zostanie pełna nazwa pliku. Poniżej jeden
wiersz takiego raportu.
39942
8.1943 /usr/sbin/dropbear
Aby poznać jaki czas procesor sp˛edził w konkretnych funkcjach, a nie
w całych programach czy bibliotekach należy uruchomić opreport z opcja
-l oraz podać ścieżk˛e do programu lub biblioteki. Poniżej przykład dla
programu test.
6.2. Oprofile
45
# opreport -l /root/test
CPU: MIPS 24K, speed 678 MHz (estimated)
Counted INSTRUCTIONS events (1-0 Instructions completed)
with a unit mask of 0x00 (No unit mask) count 100000
samples %
symbol name
2215
40.1923 crc32
1893
34.3495 xor
747
13.5547 andff
616
11.1776 shift8
39
0.7077 mainLoop
1
0.0181 _PROCEDURE_LINKAGE_TABLE_
W powyższym przykładzie został przedstawiony raport z wykonania testowego programu. Funkcja leave wykorzystała aż 66% czasu procesora. Ta
funkcja powinna być poddana najwi˛ekszej optymalizacji. Z kolei funkcja
main wykonywała si˛e tak krótko, że Oprofile jej nie zauważył. Dlatego optymalizacja funkcji main nie wpłynie na odczuwalne przyspieszenie programu.
Oprofile pozwala na wygenerowanie raportu wykorzystania funkcji
z kilku programów lub bibliotek. Wystarczy podać ścieżki do kilku plików.
Poniżej cz˛eść takiego raportu.
# opreport -l /usr/sbin/dropbear /usr/bin/opreport
CPU: MIPS 24K, speed 678 MHz (estimated)
Counted INSTRUCTIONS events (1-0 Instructions completed)
with a unit mask of 0x00 (No unit mask) count 100000
samples %
app name
symbol name
13
22.0339 opreport
op_get_line
11
18.6441 dropbear
md5_compress
4
6.7797 dropbear
rijndael_ecb_encrypt
3
5.0847 dropbear
sha1_compress
3
5.0847 opreport
_PROCEDURE_LINKAGE_TABLE_
Taki raport różni si˛e tylko dodatkowa˛ kolumna˛ app name, która informuje do której aplikacji należy dana funkcja.
Oprofile pozwala również na stworzenie raportu w formie
grafu wywołań funkcji. Jednakże domyślnie wyłaczone
˛
jest zbieranie
danych
o
łańcuchu
wywoływanych
funkcji.
Poleceniem
opcontrol --callgraph=maksymalna_długość włacza
˛
si˛e zapisywanie
łańcucha wywołań funkcji jednocześnie ustalajac
˛ jego długość. W przypadku ustawienia tej opcji podczas uruchomionego profilera, opcja ta
zostanie uwzgl˛edniona dopiero podczas kolejnego uruchomiania Oprofile.
Aby zatrzymać Oprofile można użyć polecenia opcontrol --shutdown
nastepnie
˛
można uruchomić go za pomoca˛ komendy opcontrol --start.
Poniżej przykład raportu w postaci
grafu wywołań funkcji dla długości łańcucha równej 3.
# opreport -c -l test
CPU: MIPS 24K, speed 678 MHz (estimated)
Counted INSTRUCTIONS events (1-0 Instructions completed)
with a unit mask of 0x00 (No unit mask) count 100000
samples %
image name
symbol name
------------------------------------------------------------------------------3621247 53.2865 UDPGenerator
xor
3621247 100.000 UDPGenerator
xor [self]
-------------------------------------------------------------------------------
6.2. Oprofile
46
1543700 22.7155 UDPGenerator
andff
1543700 100.000 UDPGenerator
andff [self]
------------------------------------------------------------------------------1501640 22.0966 UDPGenerator
shift8
1501640 100.000 UDPGenerator
shift8 [self]
------------------------------------------------------------------------------125286
1.8436 UDPGenerator
crc32
125286
100.000 UDPGenerator
crc32 [self]
------------------------------------------------------------------------------3009
100.000 UDPGenerator
main
3492
0.0514 UDPGenerator
mainLoop
3492
99.1482 UDPGenerator
mainLoop [self]
27
0.7666 libuClibc-0.9.33.2.so
select
3
0.0852 libuClibc-0.9.33.2.so
printf
------------------------------------------------------------------------------429
0.0063 UDPGenerator
_PROCEDURE_LINKAGE_TABLE_
429
100.000 UDPGenerator
_PROCEDURE_LINKAGE_TABLE_ [self]
------------------------------------------------------------------------------7
1.0e-04 UDPGenerator
main
3009
99.7679 UDPGenerator
mainLoop
7
0.2321 UDPGenerator
main [self]
------------------------------------------------------------------------------3
100.000 UDPGenerator
mainLoop
0
0 libuClibc-0.9.33.2.so
printf
0
0 libuClibc-0.9.33.2.so
printf [self]
------------------------------------------------------------------------------27
100.000 UDPGenerator
mainLoop
0
0 libuClibc-0.9.33.2.so
select
0
0 libuClibc-0.9.33.2.so
select [self]
-------------------------------------------------------------------------------
Podobnie jak raporcie z profilera Gprof, raport w formie grafu wywołań
funkcji Oprofile jest podzielony na sekcje. W każdej sekcji jest funkcja, która
nie jest wci˛eta i tej funkcji dotyczy ta sekcja.
Powyżej niewci˛etej funkcji znajduja˛ si˛e funkcje, które ja˛ wołaja.
˛ Liczba
próbek oznacza podczas ilu zebranych próbek była wykonywana funkcja.
Nie należy mylić liczby wywołań funkcji z liczba˛ próbek.
Poniżej niewci˛etej funkcji znajduja˛ si˛e funkcje, które sa˛ wołane przez nia.
˛
6.2.4. Profilowanie kodu jadra
˛
Profilowanie kodu jadra
˛
Linuks wiele si˛e nieróżni od profilowania zwykłego
oprogramowania. Przed uruchomieniem Oprofile należy wskazać miejsce
pliku zawierajacy
˛ skompilowany kod jadra
˛
w sposób opisany w rozdziale
6.2.2. Aby wygenerować raport obrazujacy
˛ sp˛edzony czas procesora w poszczególnych funkcjach należy wywołać opreport z opcja˛ -l i podana˛
ścieżka˛ do kodu jadra.
˛
Jeżeli raport zawierać ma funkcje znajdujace
˛ si˛e
w dodatkowo załadowanych modułach należy podać katalogach, w którym
znajduja˛ si˛e moduły. Ścieżk˛e do tego katalogu podajemy po opcji -p. Poniżej
przykład przykładowego raportu.
# opreport -p /lib/modules/3.7.2/ -l /root/vmlinux
CPU: MIPS 24K, speed 678 MHz (estimated)
Counted INSTRUCTIONS events (1-0 Instructions completed)
with a unit mask of 0x00 (No unit mask) count 100000
47
6.2. Oprofile
samples
6468
873
821
672
458
373
327
279
246
242
116
%
49.7232
6.7113
6.3115
5.1661
3.5209
2.8675
2.5138
2.1448
1.8911
1.8604
0.8918
symbol name
__do_softirq
finish_task_switch.constprop.60
run_timer_softirq
spi_async_locked
r4k_wait
__delay
queue_delayed_work_on
worker_thread
process_one_work
ring_buffer_consume
rcu_idle_enter
6.2.5. Konwersja formatu Oprofile do Gprof
W skład oprogramowania Oprofile wchodzi program opgrof, który umożliwia na podstawie zebranych danych przez Oprofile wygenerować plik
gmon.out. W środowisku OpenWrt program ten nie znajduj˛e si˛e w podstawowym pakiecie oprofile tylko w dodatkowym oprofile-utils. W przypadku nie zainstalowania powyższego pakietu należy go doinstalować.
W celu jego wygenerowania należy uruchomić program opgrof podajac
˛ mu
ścieżk˛e do programu, który ma być profilowany.
opgrof /ścieżka/do/programu
Po wydaniu powyższego polecenia zostanie zapisany plik gmon.out w katalogu, w którym została wywołana.
7. Testowanie
7.1. Programy testowe
Sprawne profilowanie wymaga użycia programów testowych, które odpowiednio mocno obcia˛ża˛ profilowany program lub moduł i pozwola˛ przetestować funkcjonalność tworzonego oprogramowania. Przedstawiłem wybrane
przez zemnie programy generujace
˛ ruch.
7.1.1. ping
Ping jest programem testujacym
˛
drożność połaczenia
˛
mi˛edzy dwoma komputerami. Program wysyła pakiet typu ICMP Echo Request a nast˛epnie
oczekuje pakietu ICMP Echo Request, który jest odpowiedzia˛ na wysłany
wcześniej pakiet. Obsługa pakietów ICMP Echo znajduj˛e si˛e w wi˛ekszości systemów z obsługa˛ sieci. Jednakże pomimo drożnego połaczenia
˛
pomiedzy
˛
dwoma komputerami można nie uzyskać odpowiedzi na pakiet
ICMP Echo Request. Najcz˛eściej powodem jest ściana ogniowa znajdujaca
˛
sie˛ na komputerze docelowym lub pomi˛edzy komputerami. Powodem takiej
konfiguracji ściany ogniowej jest atak nazywany ping of death polegajacy
˛ na
wysłaniu pakietu ICMP Echo Request wi˛ekszego niż 65535 bajtów. Tak wysłany pakiet powodował zawieszenie si˛e docelowego systemu. Współczesne
systemy sa˛ odporne na atak tego typu.
Obsługa polecenia ping polega na uruchomieniu polecenia wraz z adresem komputera docelowego. W systemie Linux program ping b˛edzie wysyłał
pakiety do momentu przerwania mu przez użytkownika kombinacja˛ klawiszy CTRL+C.
root@virt-debian:/home/marcin# ping 192.168.47.197
PING 192.168.47.197 (192.168.47.197) 56(84) bytes of data.
64 bytes from 192.168.47.197: icmp_req=1 ttl=64 time=1.10 ms
64 bytes from 192.168.47.197: icmp_req=2 ttl=64 time=0.427 ms
64 bytes from 192.168.47.197: icmp_req=3 ttl=64 time=0.313 ms
^C
--- 192.168.47.197 ping statistics --3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.313/0.614/1.103/0.349 ms
Program ping informuje czy uzyskał odpowiedź na każdy wysłany pakiet.
Dla otrzymanych pakietów podaje czas jaki upłynał
˛ od wysłania pakietu
ICMP Echo Request. Po zakończeniu testowania program ping podaj˛e dane
statystyczne takie jak minimalny czas odpowiedzi, średni czas odpowiedzi,
maksymalny czas odpowiedzi i inne.
7.1. Programy testowe
49
Program ping umożliwia również ustawienie wielkości wysyłanego pakietu. Podczas testowania warto zwi˛ekszyć wielkość pakietu ponieważ zdarza si˛e, że dopiero duże pakiety gina˛ podczas transmisji.
Możliwe też jest zmienienie cz˛estotliwości wysyłania pakietów. Ustawiajac
˛ wielkość pakietu i cz˛estotliwość jego wysyłania można precyzyjnie sterować wielkościa˛ ruchu, który jest generowany przez program ping.
7.1.2. iperf
Program do testowania przepustowości połaczenia.
˛
Aby program znalazł si˛e
w obrazie oprogramowania dla rutera należy zaznaczyć nast˛epujac
˛ a˛ opcje
podczas jego konfigurowania.
-> Network
-> iperf
Program iperf pracuj˛e w dwóch trybach serwera i klienta. Podczas testowania na jednym komputerze iperf zostaje uruchomiony w trybie serwera.
Na drugim komputerze uruchamia si˛e iperf w trybie klienta podajac
˛ jednocześnie adres komputera, na którym został uruchomiony iperf jako serwer.
Podczas testowania iperf pracujacy
˛ w trybie klienta wysyła dane do serwera.
Możliwe jest również testowanie przepustowości jednocześnie w dwóch kierunkach. Służy do tego opcja -d. Opcje ta jest właściwa tylko dla iperfa działajacego
˛
w trybie klienta i należy ja˛ podawać po opcji wyboru trybu klienta.
Program iperf umożliwia też wybór pliku, z którego b˛edzie czytał dane, które
wysyła. Opcja ta jest cz˛esto używana w przypadku połacze
˛ ń, które sa˛ poddawane kompresji. Dzi˛eki niej można używać danych o różnej podatności na
kompresje. Iperf domyślnie korzysta z protokołu transportowego TCP, ale
wspiera również protokół UDP. W przypadku wyboru protokołu UDP należy
ustawić go jednocześnie na serwerze i kliencie.
W poniższym przykładzie została osiagni˛
˛ eta pre˛ dkość transmisji wynoszaca
˛ 94,1Mbit/sec. Jest to maksymalna pre˛ dkość możliwa do osiagni˛
˛ ecia
za pomoca˛ połaczenia
˛
opartego o ethernet 100Mbit. Wynika to z faktu, że
około 6% transmisji jest przeznaczana na nagłówki protokołów ethernet, ip
i tcp.
root@OpenWrt:/# iperf -s
-----------------------------------------------------------Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
-----------------------------------------------------------[ 4] local 192.168.47.197 port 5001 connected with
192.168.47.198 port 47131
[ ID] Interval
Transfer
Bandwidth
[ 4] 0.0-10.1 sec
114 MBytes 94.1 Mbits/sec
7.1.3. CRUDE i RUDE
RUDE jest programem służacym
˛
do generowania ruchu udp. Program
CRUDE odbiera i zapisuje informacje o otrzymanym ruchu. Programy te
sa˛ cz˛eścia˛ wi˛ekszego projektu Faster 2000.
7.1. Programy testowe
50
Instalacja CRUDE i RUDE
Programy te nie wyst˛epuja˛ jako gotowe pakiety do zainstalowania w środowisku OpenWrt. Dlatego należy je re˛ cznie skompilować. Przed rozpocz˛eciem kompilacji należy oprogramowanie te ściagn
˛ ać
˛ ze strony domowej projektu (http://rude.sourceforge.net). Po ściagni˛
˛ eciu oprogramowania należy
je rozpakować, a nast˛epnie uruchomić skrypt configure znajdujacy
˛ si˛e
w głównym katalogu. Skrypt ten utworzy plik Makefile potrzebny do przeprowadzenia kompilacji. Przed przystapieniem
˛
do kompilacji należy ustawić
zmienne środowiskowe staging_dir i PATH. Po ustawieniu tych zmiennych
należy uruchomić program make z ustawionymi flagami CC i LD. Dokładny
opis tych czynności znajduj˛e si˛e w rozdziale 4.2. Po kompilacji pliki wykonywalne znajda˛ si˛e w katalogach rude i crude.
Użycie RUDE
Program RUDE generuje ruch udp na podstawie pliku tekstowego stworzonego przez użytkownika. Dzi˛eki czemu ruch wygenerowany przez RUDE jest
mocno konfigurowalny. Poniżej opisałem format pliku, który służy RUDE do
generowania ruchu.
Plik ten musi posiadać jedno polecenie start. Dodatkowo powinien posiadać przynajmniej jedno polecenie ON oraz odpowiadajace
˛ mu polecenie
OFF. Para tych poleceń definiuje strumień pakietów, których może być zdefiniowanych kilka. Do każdego strumienia pakietów może odnosić si˛e dowolna ilość poleceń modify, które może modyfikować strumień w czasie
jego wysyłania.
Polecenie START czas rozpocz˛ecia wysyłania pakietów udp. Po słowie
znajduj˛e określenie czasu. Może to być słówko kluczowe NOW określajace
˛ start zaraz po uruchomieniu programu RUDE lub godzina w formie
GG:MM:SS. Jest to 24 godzinny format, gdzie GG oznacza godzin˛e MM minute˛ i SS sekund˛e rozpocz˛ecia.
Polecenie ON ma nast˛epujac
˛ a˛ zaczerpni˛eta˛ z dokumentacji postać.
<stime> <id> ON <sport> <dst.add>:<dst.port> <type> [type parameters]
stime Czas wyrażony w milisekundach. Określa po jakim czasie strumień
ma zostać aktywowany. Czas jest liczony od momentu określonego w poleceniu START.
id Identyfikator strumienia pakietów.
sport Port źródłowy wysyłanych pakietów.
dst.addr Adres docelowy pakietów. Może przyjmować postać postać adresu
IP lub nazwy DNS.
dst.port Port docelowy wysyłanych pakietów.
type Wybór sposobu opisu strumienia pakietów. Możliwy jest wybór
CONSTANT oraz TRACE. Dokładny opis tych strumieni znajduj˛e si˛e poniżej.
Strumień CONSTANT określa strumień stały w czasie. Strumień ten posiada dwa parametry rate i psize. Parametr rate określa ile pakietów na
minut˛e ma zostać wysłanych w danym strumieniu. Opcja psize określa rozmiar danych w pakiecie UDP. Należy pami˛etać, że do rozmiaru pakietu UDP
należy doliczyć nagłówki protokołów UDP, IP oraz warstwy łacza
˛
danych np.
Ethernet.
7.1. Programy testowe
51
Strumień typu TRACE generuje ruch pakietów na podstawie pliku tekstowego. Strumień ten przyjmuje jeden parametr, którym jest ścieżka do
pliku tekstowego. W pliku tym każda linia określa jeden pakiet. Każdy pakiet
zdefiniowany jest dwoma liczbami. Pierwsza liczba jest całkowita i oznacza
rozmiar pakietu. Druga liczba jest zapisana jako zmiennopozycyjna. Liczba
ta określa czas w sekundach, który ma upłynać
˛ do rozpocz˛ecia wysyłania
nastepnego
˛
pakietu. Poniżej został przedstawiony przykładowy plik TRACE.
512 0.040
255 1.1
762 0.000001
Polecenie OFF wyłacza
˛
strumień pakietów o podanym id. Polecenie zapisywane jest w sposób naste˛ pujacy.
˛
<otime> <id> OFF
Parametr otime określa czas w milisekundach po którym strumień pakietów ma zostać zakończony. Czas ten liczony w sposób identyczny jak w poleceniu ON tzn. od czasu określonego w poleceniu START. Drugi parametr id
określa identyfikator strumienia.
Polecenie modify modyfikuje typ strumienia pakietów. Postać polecenia
jest nast˛epujaca.
˛
<mtime> <id> MODIFY <type> [type parameters]
Parametr mtime określa czas wykonania polecenia. Parametr ten jest zdefiniowany identycznie jak w OFF i ON. Identyfikator określony przez parametr
id informuje o modyfikowanym strumieniu, Parametr type określa rodzaj
strumieniu pakietu. Konstrukcja jest identyczna jak w przypadku polecenia
ON.
Po wykonaniu pliku konfiguracyjnego dla programu rude należy go uruchomić przekazujac
˛ ścieżk˛e do tego pliku po fladze -s. W sposób nast˛epujacy.
˛
rude -s example.cfg
Użycie CRUDE
Program CRUDE odbiera ruch wygenerowany przez RUDE oraz zbiera o nim
informacje. Uruchomiony program bez żadnych parametrów b˛edzie nasłuchiwał na porcie 10001 oraz dane na temat pakietów wypisywał na standardowe wyjście.
Port, na którym nasłuchuje CRUDE można zmienić za pomoca˛ flagi -p.
Port należy podać po niej.
W przypadku ch˛eci zapisania wyników programu CRUDE autorzy zalecaja˛ użyć flagi -l podajac
˛ po niej ścieżk˛e do docelowego pliku. CRUDE b˛edzie zapisywał dane niezdekodowane. Dzi˛eki czemu zostanie zredukowana
ilość zapisów do pliku. Tak utworzony plik można zdekodować za pomoca˛
crude przekazujac
˛ flag˛e -d oraz podajac
˛ ścieżk˛e do pliku.
Aby zakończyć działanie programu CRUDE należy użyć nast˛epujacej
˛
kombinacji klawiszy CTRL+C. Poniżej przykładowy wynik działania programu CRUDE.
52
7.1. Programy testowe
ID=0 SEQ=0 SRC=192.168.47.194:3001 DST=0.2.169.84:10001
Tx=0.1358929662 Rx=1358 929661.258863 SIZE=100
ID=0 SEQ=1 SRC=192.168.47.194:3001 DST=0.2.178.210:10001
Tx=0.1358929662 Rx=135 8929661.261222 SIZE=100
7.1.4. nping
Nping jest otwarto źródłowym narz˛edziem do: generowania pakietów sieciowych, analizowania odpowiedzi i pomiaru czasu odpowiedzi. Program ten
potrafi generować pakiety TCP, UDP, ICM i ARP oraz ramki Ethernet. Narzedzie
˛
to, ze wzgl˛edu na swoja˛ uniwersalność może służyć do wielu celów mi˛edzy innymi do: sprawdzenia czy jest możliwa komunikacja z drugim komputerem, pomiaru jakości połaczenia,
˛
ataków odmowy usług (ang.
DoS), ataków zatruwania tablicy ARP i wielu innych.
W celu instalacji npinga na systemie Debian należy zainstalować pakiet
nmap. W pakiecie tym znajduj˛e si˛e popularny skaner portów nmap oraz
został dołaczony
˛
do niego nping.
Użycie nping
Program nping może być wykorzystany do wielu celów. Zaprezentowałem
poniżej tylko kilka przykładów użycia nping.
Domyślnie program zachowuj˛e si˛e tak jak program ping. Wysyła pakiety
ICMP Echo i wypisuj˛e pakiety z odpowiedzia˛ (Echo reply). Tak jak w programie ping można ustawić czas pomi˛edzy wysyłanymi pakietami za pomoca˛ opcji --time oraz wielkość pakietu (nie liczac
˛ nagłówków) za pomoca˛
opcji --data-length. Z powodu długich wierszy zwracanych przez program
nping, znakiem \ zaznaczyłem przeniesienie do nowej linii.
# nping google.pl
Starting Nping 0.6.00 ( http://nmap.org/nping ) at
SENT (0.0325s) ICMP 192.168.47.158 > 173.194.70.94
ttl=64 id=9260 iplen=28
RCVD (0.0737s) ICMP 173.194.70.94 > 192.168.47.158
ttl=50 id=0 iplen=28
SENT (1.0335s) ICMP 192.168.47.158 > 173.194.70.94
ttl=64 id=9260 iplen=28
RCVD (1.0746s) ICMP 173.194.70.94 > 192.168.47.158
ttl=50 id=0 iplen=28
SENT (2.0373s) ICMP 192.168.47.158 > 173.194.70.94
ttl=64 id=9260 iplen=28
RCVD (2.0787s) ICMP 173.194.70.94 > 192.168.47.158
ttl=50 id=0 iplen=28
SENT (3.0401s) ICMP 192.168.47.158 > 173.194.70.94
ttl=64 id=9260 iplen=28
RCVD (3.0815s) ICMP 173.194.70.94 > 192.168.47.158
ttl=50 id=0 iplen=28
SENT (4.0422s) ICMP 192.168.47.158 > 173.194.70.94
ttl=64 id=9260 iplen=28
RCVD (4.0825s) ICMP 173.194.70.94 > 192.168.47.158
ttl=50 id=0 iplen=28
2013-04-08 15:26 CEST
Echo request (type=8/code=0)\
Echo reply (type=0/code=0)\
Echo request (type=8/code=0)\
Echo reply (type=0/code=0)\
Echo request (type=8/code=0)\
Echo reply (type=0/code=0)\
Echo request (type=8/code=0)\
Echo reply (type=0/code=0)\
Echo request (type=8/code=0)\
Echo reply (type=0/code=0)\
Max rtt: 41.089ms | Min rtt: 40.097ms | Avg rtt: 40.744ms
Raw packets sent: 5 (140B) | Rcvd: 5 (230B) | Lost: 0 (0.00%)
7.1. Programy testowe
53
Tx time: 4.01135s | Tx bytes/s: 34.90 | Tx pkts/s: 1.25
Rx time: 5.01358s | Rx bytes/s: 45.88 | Rx pkts/s: 1.00
Nping done: 1 IP address pinged in 5.05 seconds
W celu przełaczenia
˛
npinga w tryb obsługi protokołu TCP należy przekazać flag˛e --TCP. Domyślnie nping wysyła pakiet z flaga˛ SYN i oczekuje
na pakiet z flagami SYN+ACK. Po otrzymaniu pakietu wysyła pakiet z flaga˛
RESET, aby zerwać połaczenie.
˛
Za pomoca˛ flagi -p można przekazać port
docelowy.
# nping --tcp -p 80 google.pl
Starting Nping 0.6.00 ( http://nmap.org/nping ) at 2013-04-09 09:44 CEST
SENT (0.0446s) TCP 192.168.47.159:41707 > 173.194.70.94:80 S ttl=64 id=61600\
iplen=40 seq=1845837474 win=1480
RCVD (0.0856s) TCP 173.194.70.94:80 > 192.168.47.159:41707 SA ttl=50 id=7176\
iplen=44 seq=8807656 win=62920 <mss 1430>
SENT (1.0453s) TCP 192.168.47.159:41707 > 173.194.70.94:80 S ttl=64 id=61600\
iplen=40 seq=1845837474 win=1480
RCVD (1.0905s) TCP 173.194.70.94:80 > 192.168.47.159:41707 SA ttl=50 id=7176\
iplen=44 seq=24460518 win=62920 <mss 1430>
SENT (2.0480s) TCP 192.168.47.159:41707 > 173.194.70.94:80 S ttl=64 id=61600\
iplen=40 seq=1845837474 win=1480
RCVD (2.0906s) TCP 173.194.70.94:80 > 192.168.47.159:41707 SA ttl=50 id=7176\
iplen=44 seq=40117081 win=62920 <mss 1430>
SENT (3.0512s) TCP 192.168.47.159:41707 > 173.194.70.94:80 S ttl=64 id=61600\
iplen=40 seq=1845837474 win=1480
RCVD (3.0934s) TCP 173.194.70.94:80 > 192.168.47.159:41707 SA ttl=50 id=7176\
iplen=44 seq=55789600 win=62920 <mss 1430>
SENT (4.0546s) TCP 192.168.47.159:41707 > 173.194.70.94:80 S ttl=64 id=61600\
iplen=40 seq=1845837474 win=1480
RCVD (4.0973s) TCP 173.194.70.94:80 > 192.168.47.159:41707 SA ttl=50 id=7176\
iplen=44 seq=71476261 win=62920 <mss 1430>
Max rtt: 44.399ms | Min rtt: 40.904ms | Avg rtt: 42.182ms
Raw packets sent: 5 (200B) | Rcvd: 5 (230B) | Lost: 0 (0.00%)
Tx time: 4.01206s | Tx bytes/s: 49.85 | Tx pkts/s: 1.25
Rx time: 5.01442s | Rx bytes/s: 45.87 | Rx pkts/s: 1.00
Nping done: 1 IP address pinged in 5.06 seconds
Opcja --flags umożliwia ustawienie z jakimi flagami maja˛ być wysyłane
pakiety TCP. Nping ponadto umożliwia wysyłanie pakietów z bł˛edna˛ suma˛
kontrolna˛ oraz umożliwia ustawienie numeru sekwencyjnego pakietu.
Elastyczność programu nping pozwala na przeprowadzenie różnych eksperymentów w sieci. Jednym z nich jest zatruwanie tablicy ARP. Nping potrafi wysyłać pakiety ARP z ustawionymi przez użytkownika adresami ip
i MAC adresami. Poniżej przykład wysłania zapytania o MAC adres komputera o ip 192.168.47.191 z ustawionymi źródłowymi adresami ip i MAC na
nieistniejace
˛ w sieci.
# nping --arp --arp-type=ARP --arp-sender-mac=FF:FF:FF:FF:FF:FE \
--arp-sender-ip=192.168.47.8 --arp-target-ip=192.168.47.191 192.168.47.255
Starting Nping
SENT (0.0578s)
RCVD (0.0583s)
SENT (1.0582s)
RCVD (1.0593s)
0.6.00 ( http://nmap.org/nping ) at 2013-04-09 10:30 CEST
ARP who has 192.168.47.191? Tell 192.168.47.8
ARP reply 192.168.47.191 is at 00:0A:CD:21:FF:FF
ARP who has 192.168.47.191? Tell 192.168.47.8
ARP reply 192.168.47.191 is at 00:0A:CD:21:FF:FF
54
7.1. Programy testowe
SENT
RCVD
SENT
RCVD
SENT
RCVD
(2.0618s)
(2.0630s)
(3.0651s)
(3.0661s)
(4.0693s)
(4.0704s)
ARP
ARP
ARP
ARP
ARP
ARP
who has 192.168.47.191?
reply 192.168.47.191 is
who has 192.168.47.191?
reply 192.168.47.191 is
who has 192.168.47.191?
reply 192.168.47.191 is
Tell 192.168.47.8
at 00:0A:CD:21:FF:FF
Tell 192.168.47.8
at 00:0A:CD:21:FF:FF
Tell 192.168.47.8
at 00:0A:CD:21:FF:FF
Max rtt: N/A | Min rtt: N/A | Avg rtt: N/A
Raw packets sent: 5 (210B) | Rcvd: 5 (230B) | Lost: 0 (0.00%)
Tx time: 4.01351s | Tx bytes/s: 52.32 | Tx pkts/s: 1.25
Rx time: 5.01517s | Rx bytes/s: 45.86 | Rx pkts/s: 1.00
Nping done: 1 IP address pinged in 5.07 seconds
Po wysłaniu takich pakietów tablica ARP na komputerze 192.168.47.191
zawiera nast˛epujace
˛ informacje:
#arp -a
Interface: 192.168.47.191 --- 0x1a
Internet Address
Physical Address
192.168.47.8
ff-ff-ff-ff-ff-fe
192.168.47.255
ff-ff-ff-ff-ff-ff
255.255.255.255
ff-ff-ff-ff-ff-ff
Type
dynamic
static
static
Powyższych przykładach przedstawiłem tylko ułamek możliwości programu nping. Po dokładniejsze informacje odsyłam do dokumentacji programu.
8. Podsumowanie
Praca ma na celu zapoznać czytelnika ze środowiskiem uruchomieniowym
dla ruterów z interfejsami 802.11. Opisałem cały proces tworzenia oprogramowania dla ruterów. Zaczynajac
˛ od instalacji i konfiguracji środowiska
uruchomieniowego, poprzez kompilacje projektu, a także wykrywanie bł˛edów, kończac
˛ na optymalizacji oprogramowania. Dla ułatwienia korzystania
z pracy zawarłem zrzuty ekranów zawierajace
˛ opisywane oprogramowanie
oraz wyniki jego działania.
W pracy skupiłem si˛e na otwartym środowisku (o otwartym kodzie źródłowym) o nazwie OpenWrt. Środowisko to jest najcz˛eściej wybierane przez
studentów i inżynierów eksperymentujacych
˛
z nowymi algorytmami lub
usługami w lokalnych sieciach radiowych.
Wiele osób tworzacych
˛
oprogramowanie dla ruterów nie korzysta z debuggerów oraz programów do oceny cz˛estości wykorzystania fragmentów
kodu. Uważaja,
˛ że czas poświ˛econy na nauk˛e i konfiguracj˛e tych narz˛edzi
bedzie
˛
dłuższy niż praca bez tych narz˛edzi. W niektórych małych projektach
może okazać si˛e to prawda.
˛ Jednak kod do którego wykorzystano powyższe
narzedzia
˛
prawdopodobnie b˛edzie zawierał mniej bł˛edów i b˛edzie wydajniejszy. Moja praca powinna też zminimalizować czas potrzebny na konfiguracj˛e
i na nauk˛e opisanych narz˛edzi.
A. Zawartość płyty CD
Do pracy została dołaczon
˛
a˛ płyta CD z nast˛epujac
˛ a˛ zawartościa:
˛
— praca.pdf - Treść niniejszej pracy w postaci pliku pdf.
— user-space/UDPGenerator - Katalog z kodem źródłowym programu UDPGenerator.
— user-space/UDPReceiver - Katalog z kodem źródłowym programu
UDPReceiver.
— kernel-space/UDPGenerator - Katalog z kodem źródłowym modułu jadra
˛
UDPGenerator.
— kernel-space/UDPReceiver - Katalog z kodem źródłowym modułu jadra
˛
UDPReceiver.
Bibliografia
[1]
[2]
[3]
[4]
[5]
GDB Documentation. http://www.gnu.org/software/gdb/documentatio.
GNU gprof manual. http://sourceware.org/binutils/docs-2.19/gprof/.
Linux Kernel HTML Documentation. http://kernel.org/doc/htmldocs/.
OpenWrt Documentation.
Greg Kroah-Hartman Jonathan Corbet, Alessandro Rubini. Linux device drivers
third edition. O’Reilly, Sebastopol, 2005.
[6] John Levon. OProfile manual. http://oprofile.sourceforge.net/doc/index.html.