Aus Linux-Magazin 04/2014

Kernel- und Treiberprogrammierung mit dem Linux-Kernel – Folge 73

© psdesign1, Fotolia

Das Tracing-Werkzeug Ktap hilft Admins und Kernelentwicklern bei Tuning und Fehlersuche. Diese Kern-Technik zeigt die ersten Schritte mit dem Interpreter im Kernel.

Linux bietet schon seit Kernel 2.6.9 vom Oktober 2004 Kernel-Tracing, also die Möglichkeit, Informationen über Kernel-interne Abläufe nachzuverfolgen. Bereits 2005 stellte die “Kern-Technik” die damals aktuellen Kernel-Interfaces Kprobe und Jprobe vor [1]. Da diese in der Handhabung jedoch kompliziert sind, haben sie nur eine sehr kleine Anwenderbasis für sich gewonnen.

Mit Systemtap und Dtrace ist in den letzten Jahren ein Werkzeugkasten entstanden, mit dem sich das Tracing-Interface programmgesteuert nutzen lässt. Damit können Admins und Entwickler komplexe Fehlersuchen realisieren oder Performance-Bottlenecks aufspüren.

Allerdings stehen diese Werkzeugkästen nicht allen offen. Das hängt zum einen mit lizenzrechtlichen Problemen, zum anderen mit den technischen Lösungen als solchen zusammen. Die Dtrace-Lizenz beispielsweise verhindert eine Aufnahme des Codes in den Standard-Linux-Kernel, auch erlauben die Komplexität und die Fixierung der Lösung auf eine einzelne Plattform keinen Einsatz in den kleineren eingebetteten Systemen. Außerdem kann Dtrace das System “einfrieren” lassen – bei produktiven Systemen lässt der Systemadministrator also lieber die Finger davon.

Mit entsprechender Euphorie haben im Mai 2013 Admins und Kernelentwickler den neuen Ansatz Ktap [2] aufgenommen. Als schlanke Lösung erlaubt Ktap das Tracing auf Embedded-Systemen genauso wie auf Servern. Eine eingebaute Skriptsprache zur Definition der zu messenden Abläufe (Tracing), die in Bytecode übersetzt auf einer virtuellen Maschine (VM) im Kernel läuft, bietet nicht nur Flexibilität, sondern auch weitgehende Unabhängigkeit von der zugrunde liegenden Hardware.

Ob auf einer x86-Plattform oder einem System mit ARM-Prozessor (Tablet, Smartphone): Der Code läuft gleichermaßen und vor allem auch in gemischten, verteilten Umgebungen.

Vorbild Minecraft

Beim Skripting hat sich der Ktap-Entwickler Jovi Zhangwei für Lua entschieden, eine Sprache, die speziell zur einfachen Funktionserweiterung von Programmen geschrieben wurde. Als eingebettete Skriptsprache findet Lua sich beispielsweise im Spiel Minecraft, im Netzwerkanalysator Wireshark, in Nmap und im Mediaplayer VLC. Der Interpreter beziehungsweise die zur Abarbeitung notwendige virtuelle Maschine umfasst nur wenige Hundert KByte, der Bytecode selbst besteht aus gerade mal 38 Befehlen. Die Sprache ist ein Mischmasch aus C und Pascal und nicht typisiert. Der Typ einer Variablen, ob Integer oder String, ergibt sich aus dem Kontext.

Ende 2013 hat Linus Torvalds auf Vorschlag von Greg Kroah-Hartman den Ktap-Kerneltreiber in sein Standard-Linux übernommen. Allerdings hagelte es stante pede kräftig Kritik, die Torvalds zwang den Commit wieder rückgängig zu machen. Die Kernelentwickler kritisierten allerdings nicht Ktap selbst, sondern die Geschwindigkeit, mit der ein so umfangreiches und auch sicherheitskritisches Subsystem ohne ausgiebige kritische Kontrolle übernommen werden sollte. Jetzt ist die Aufnahme des Subsystems für einen der nächsten Kernel, vielleicht 3.15, vorgesehen. Dennoch lässt sich Ktap bereits heute sinnvoll einsetzen.

Interpreter im Kernel

Ktap besteht aus zwei Teilen: zum einen aus der zumeist als Modul realisierten Kernelerweiterung »ktapvm.ko« und zum anderen aus der Applikation »ktap« . Bei der Kernelerweiterung handelt es sich um den Interpreter, die VM und die Anbindung an die Kernelfunktionen, die so genannte Runtime-Library. Die Ktap-Applikation lädt Lua-Programme, übersetzt sie in Bytecode und übergibt diesen dem Kernel. Sie initiiert die Abarbeitung, holt die Ergebnisse ab und stellt sie auf dem Bildschirm dar (Abbildung 1).

Abbildung 1: Ktap besteht aus einem Kernelmodul und einer Applikation.

Abbildung 1: Ktap besteht aus einem Kernelmodul und einer Applikation.

Die Installation von Ktap gestaltet sich unter Ubuntu 12.04 LTS erfreulich problemlos. Der mitgelieferte Betriebssystemkern ist bereits mit den benötigten Optionen »CONFIG_EVENT_TRACING« , »CONFIG_PERF_EVENTS« , »CONFIG_DEBUG_FS« und »CONFIG_FTRACE_SYSCALLS« kompiliert. Damit braucht man nur noch den Ktap-Quellcode per Git auf den Rechner zu holen und per »make« zu übersetzen. Es empfiehlt sich, zuvor noch die Pakete »elfutils« und »libelf-dev« zu installieren. Mit Hilfe dieser Werkzeuge beziehungsweise Bibliotheken verarbeitet Ktap ELF-Dateien (Executable and Linkable Format) sowie die in Programmdateien hinterlegten Meta-Informationen und löst Adressen auf. Damit ist es Ktap auch möglich, Applikationen zu überwachen. Listing 1 zeigt die Kommandos zum Installieren im Detail [3].

Listing 1

Ktap installieren

01 $ sudo apt-get install elfutils libelf-dev
02 $ git clone http://github.com/ktap/ktap.git
03 $ cd ktap
04 $ make
05 $ sudo make load

Nach dem erfolgreichen »make load« aus dem Quellcode-Verzeichnis heraus ist Ktap einsatzbereit. Optional kann der Admin noch »sudo make install« sowie danach »depmod« aufrufen. Damit installiert er Kernelmodul und Applikation für den systemweiten Zugriff. Allerdings wird das Kernelmodul beim nächsten Update des Linux-Kernels nicht automatisch neu generiert. Für alle Freunde des Editors Vim installiert »make install« gleich Syntaxdateien mit, die die Schlüsselwörter im Editor farblich hervorheben.

Einzeiler

Ein erster Test mit Ktap ist schnell durchgeführt. Dazu gibt der Anwender im Ktap-Quellcodeverzeichnis (hier hat »make« den Applikationsteil abgelegt) auf der Kommandozeile den Einzeiler

sudo ./ktap -e 'printf("Hello World\n")'

ein. Verständlicherweise benötigt Ktap bei der Ausführung Rootrechte, sodass man es mit vorangestelltem »sudo« aufruft. Auf die Eingabe des Passworts reagiert die Anwendung mit dem bekannten Erstlingswerk “Hello World”.

Die Option »-e« erlaubt es, ein Kommando direkt anzugeben, ohne sie erwartet Ktap eine Skriptdatei. Innerhalb der Hochkommata folgen die auszuführenden Befehle in der Programmiersprache Lua. Im obigen Fall ist das nur ein »printf()« . Das in der Programmiersprache C übliche Semikolon kann, muss aber nicht folgen. Eine »main()« -Funktion wie in C-Programmen gibt es nicht.

Die so genannte Runtime-Library erweitert Lua unter anderem um Funktionen zum Kernel-Tracing. Wesentlich ist »trace« , gefolgt von der Angabe eines Funktionsbereichs und – durch Doppelpunkt getrennt – eines Funktionsnamens, durchaus auch in Form eines regulären Ausdrucks. Außerdem folgt noch eine durch geschweifte Klammern eingefasste Codesequenz. Immer wenn die hinter »trace« definierte Kernelfunktion ausgeführt wird, arbeitet der Lua-Interpreter die Codesequenz ab (Abbildung 2).

Abbildung 2: Per »trace« setzt der Anwender Überwachungspunkte.

Abbildung 2: Per »trace« setzt der Anwender Überwachungspunkte.

Prozesse beobachten

Listing 2 zeigt einen Ktap-Befehl samt Ergebnis, der die gerade aktiven Rechenprozesse ausgibt, und zwar mit deren PID, Namen und der CPU, auf der der Job läuft. Die in der Befehlszeile verwendeten Funktionsaufrufe »cpu()« , »pid()« und »execname()« sind fest in Ktap eingebaut. Weitere Standardfunktionen listet Tabelle 1 auf.

Tabelle 1

Funktionen der Ktap-Runtime-Library [3]

Funktionsname

Bedeutung

print()

Schnelle, unformatierte Ausgabe von Daten

printf(fmt, […])

Formatierte Ausgabe von Daten

pairs(t)

Funktion, um über alle Schlüssel-Wert-Paare einer Tabelle zu iterieren

len(s) len(t)

Falls das Argument ein String ist, wird die Länge des Strings zurückgegeben; ist das Argument eine Tabelle, ist das Ergebnis die Anzahl der Tabellenelemente

in_interrupt()

Falls sich das System gerade im Interrupt-Kontext befindet, gibt die Funktion »true« zurück

exit()

Beendet Ktap

pid()

Gibt die PID des gerade aktiven Jobs zurück

tid()

Gibt die Thread-ID (TID) des aktuell ausgeführten Jobs zurück

uid()

Liefert die UID des aktuell ausgeführten Jobs

execname()

Gibt den Namen des aktuell ausgeführten Jobs zurück

cpu()

Gibt die ID der CPU an, die für die Abarbeitung zuständig ist

arch()

Gibt in Kurzform die Architektur der Maschine zurück (»x86« , »arm« , …)

kernel_v()

Liefert die Kernelversion

user_string(addr)

Liefert den zu einer Adresse aus dem Userspace gehörenden String

histogram(t)

Stellt die als Parameter übergebene Tabelle als Histogramm dar

curr_task_info(offset, fetch_bytes)

Liefert einzelne 4 oder 8 Byte breite Werte aus dem Task-Control-Block des gerade aktiven Jobs zurück

print_backtrace()

Gibt den Stack-Trace des aktuell ausgeführten Jobs aus

Listing 2

Aktive Rechenprozesse ausgeben

01 # ktap -e 'trace sched:sched_process_exec
   { print("cpu pid name: ", cpu(), pid(), execname()); }'
02 Tracing... Hit Ctrl-C to end.
03 cpu pid name:   2       20990   sh
04 cpu pid name:   1       20991   ps
05 cpu pid name:   3       20992   grep
06 cpu pid name:   3       20993   sh
07 cpu pid name:   2       20994   sh
08 cpu pid name:   3       20995   w
09 cpu pid name:   1       20996   grep
10 cpu pid name:   2       20998   grep

Auf das Kommando »./ktap -le« gibt Ktap die Namen der Funktionen aus, die man mit dem Befehl »trace« verwenden und überwachen kann. Bei Kernel 3.5.0 sind das immerhin 1254. Es gibt noch viele weitere Routinen, auf die Ktap über das Ftrace-Subsystem zugreift. Der Befehl »ktap -lf Programm« listet Funktionen auf, die man zusätzlich in der Anwendung »Programm« tracen kann.

Wie sich damit ein in C geschriebenes “Hello World”-Programm überwachen lässt, zeigt Abbildung 3. Dort ist ebenfalls zu sehen, wie unter der Kontrolle von Ktap die mit »– Programm« angehängte zu tracende Applikation startet. Bereits laufende Programme lassen sich übrigens unter Angabe ihrer Prozess-ID mit »-p PID« überwachen.

Abbildung 3: Ktap überwacht auch Applikationen.

Abbildung 3: Ktap überwacht auch Applikationen.

Systemcalls

Ein weiteres Beispiel zeigt Listing 3. Hier listet Ktap fortlaufend jene Programme auf, die den Systemcall »open()« aufrufen. Dabei gibt es neben dem Programmnamen auch den Name der Dateien aus, auf die die Anwendungen zugreifen.

Listing 3

Ktap überwacht das Öffnen von Dateien

01 sudo ./ktap -e 'trace syscalls:sys_enter_open
 { print( execname(), user_string(arg2)) }'
02
03 Tracing... Hit Ctrl-C to end.
04 ktap    /sys/kernel/debug/ktap/trace_pipe_6976
05 sh      /etc/ld.so.cache
06 sh      /lib/x86_64-linux-gnu/libc.so.6
07 sh      /tmp/numLogin.txt
08 sh      /etc/ld.so.cache
09 sh      /lib/x86_64-linux-gnu/libc.so.6
10 w       /etc/ld.so.cache
11 w       /lib/libproc-3.2.8.so
12 w       /lib/x86_64-linux-gnu/libc.so.6
13 grep    /etc/ld.so.cache
14 grep    /lib/x86_64-linux-gnu/libdl.so.2
15 grep    /lib/x86_64-linux-gnu/libc.so.6
16 w       /proc/version
17 w
18 w       /proc/1/stat
19 [...]

Besonders interessant ist die Fähigkeit der Programmiersprache Lua, mit Tabellen in Kombination mit Histogrammen umzugehen. Tabellen dürfen in ihren Zeilen beliebig viele Elemente enthalten, die sogar noch unterschiedlichen Datentyps sind. Sie lassen sich in Lua wie Felder über Indizes, aber auch in Form so genannter assoziativer Felder ansprechen. Ein solches Feld speichert einen Schlüssel-Wert-Verbund (Key-Value). Der Index des Feldes ist ein Name (String). Damit lässt sich etwa zählen, wie oft einzelne Systemcalls aufgerufen werden.

Das Kommando »histogram()« stellt das Ergebnis übersichtlich dar. So zeigt Listing 4, wie Ktap die Häufigkeit von Systemcalls zählt, die an ihrem Namensvorsatz »sys_enter_« zu erkennen sind. Der Name des Systemcalls (»argname« ) dient als Index (Schlüssel) für die Tabelle »s« . Mit jedem Auftreten zählt der zugehörige Tabelleneintrag hoch. Sobald der Anwender Ktap per [Strg]+[C] abbricht, ruft es die Funktion »trace_end()« auf (siehe Abbildung 2). Diese gibt die Tabelle »s« als Histogramm aus.

Listing 4

Histogramm der am meisten verwendeten Systemcalls

01 # ktap -e 's={} trace syscalls:sys_enter_* { s[argname]+=1 } trace_end { histogram(s) }'
02 Tracing... Hit Ctrl-C to end.
03 ^C
04                           value ------------- Distribution ------------- count
05                   sys_enter_read |@@@@@@@@@@                             13677
06                  sys_enter_close |@@@@@                                  7034
07                   sys_enter_open |@@@@@                                  6922
08               sys_enter_recvfrom |@@@                                    4160
09                sys_enter_newstat |@@                                     3279
10                   sys_enter_poll |@                                      2642
11              sys_enter_setitimer |@                                      2499
12                 sys_enter_writev |@                                      2484
13                  sys_enter_ioctl |@                                      2301
14                 sys_enter_select |                                       1271
15                   sys_enter_mmap |                                       1208
16           sys_enter_rt_sigaction |                                       874
17               sys_enter_newfstat |                                       666
18                  sys_enter_alarm |                                       660
19               sys_enter_mprotect |                                       559
20                  sys_enter_fcntl |                                       510
21                 sys_enter_access |                                       372
22                 sys_enter_munmap |                                       287
23                  sys_enter_lseek |                                       260
24 [...]

Ausblick

Auf seiner Homepage [4] nennt der Kernelentwickler Brendan D. Gregg weitere Beispiele, die belegen, welches Potenzial der Interpreter im Kernel hat. Der Overhead hält sich dabei nach Angaben der Entwickler mit knapp 10 Prozent in Grenzen. Sie arbeiten derzeit daran, ihn weiter zu reduzieren.

Allerdings sollte niemand vergessen, dass ein Interpreter im Kernel sicherheitstechnisch betrachtet ein Desaster darstellt, oder anders formuliert: ein Freudenfest für jeden ambitionierten Hacker. Ein Systemadministrator sollte das Ktap-Modul also nur vorübergehend bei Bedarf laden. Dann allerdings kann er selbst komplexe Fragestellungen tracen, zumindest dann, wenn er ausreichend Wissen über den Kernel, Lua und die Ktap-Runtime-Library besitzt.

Weitere Informationen über die Programmiersprache Lua und die Ktap-Library vermittelt die nächste Folge der Kern-Technik. Daneben beschreibt sie das Foreign Function Interface (FFI) von Ktap. Mit ihm lassen sich direkt aus dem Lua-Skript Funktionen des Linux-Kernels aufrufen, beispielsweise »printk()« .

Als Zugabe demonstriert die nächste Folge außerdem, wie man mit Ktap ein einfaches Tetris spielen kann (siehe Abbildung 4). Alles im Betriebssystemkern, versteht sich. (mhu)

Abbildung 4: Tetris im Kernel? Ktap und die nächste Kern-Technik machen's möglich.

Abbildung 4: Tetris im Kernel? Ktap und die nächste Kern-Technik machen’s möglich.

73

Infos

  1. Quade, Kunst, “Kern-Technik, Folge 22 – Debugging mit Kernel-Probes”: Linux-Magazin 08/05, S. 82
  2. Ktap: http://www.ktap.org
  3. Ktap-Tutorial: http://www.ktap.org/doc/tutorial.html
  4. Brendan D. Gregg, “Ktap Examples”: http://www.brendangregg.com/ktap.html

Der Autor

Eva-Katharina Kunst, Journalistin, und Jürgen Quade, Professor an der Hochschule Niederrhein, sind seit den Anfängen von Linux Fans von Open Source. Mit “Embedded Linux lernen mit dem Raspberry Pi” veröffentlicht Jürgen Quade demnächst sein drittes Linux-Buch.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben