Sonntag, 24. August 2008

Dtrace im Kampfeinsatz: ZSH debuggen

Ich bin ja vor kurzem auf die wunderbare ZSH umgestiegen und leider Gottes habe ich den ein oder anderen Bug gefunden der ziemlich nervig ist. Es wurde mir verweigert selbst geschriebene Widgets an die menuselect Keymap zu binden. Naja, was macht man da? Ganz einfach, Sourcecode besorgen und dtrace durchstarten lassen. Nach kurzer Zeit hatte ich das Problem eingekreist und konnte mich ohne Probleme auf den relevanten Teil konzentrieren ohne erst großartig den Code zu lesen. In einem Fenster hatte ich die zu debuggende Shell in der anderen lief dtrace und hat mir angezeigt was passiert während ich tippe.

Hier sind ein paar Skripte die ich verwendet habe:

Was passiert gerade im Z-Lineeditor:

#!/usr/sbin/dtrace -s

#pragma D option flowindent

pid$target:zle.so::entry,
pid$target:zle.so::return
{
}

Der Userstack Backtrace sobald selectkeymap aufgerufen wird, inklusive der Information um welche Keymap es sich handelt:

#!/usr/sbin/dtrace -s

pid$target:zle.so:selectkeymap:entry
{
ustack();
printf("%s",copyinstr(arg0));
}

Und das ganze nochmal für selectlocalmap:

#!/usr/sbin/dtrace -s

pid$target:zle.so:selectlocalmap:entry
/arg0 != 0/
{
ustack();
printf("%p",arg0);
}


Das ganze lief am Ende darauf hinaus das im aktuellen Source der Version 4.3.6 einfach die Zeile 2315 in der Datei complist.c von selectlocalmap(mskeymap) in selectkeymap("menuselect",1) geändert werden musste. Da das ganze ein ziemlicher Quickhack ist, übernehme ich aber für nichts die Garantie ;).

Auf jeden Fall kann ich jetzt selbst geschriebene Widgets an die menuselect Keymap binden und $KEYMAP wird beim Wechsel auch richtig gesetzt, alles ohne viel Aufwand oder einer Debugversion von ZSH :D. Mal sehen was Upstream dazu sagt.

Ich sage dazu nur: DTRACE ROCKT!

Samstag, 23. August 2008

ZSH: vi-mode reloaded

Hmmmmm, vi Kommandos in der Shell. Sowas macht doch Spaß. Allerdings hab ich es gerne wenn mir der aktuelle Mode angezeigt und der Shellprompt ausgeblendet wird sobald ich im Kommando-Modus bin. Dafür hab ich meine .zshrc etwas erweitert. Allerdings gibt es ein kleines Problem: Der RPROMPT wird nur angepasst wenn die Shell mindestens ein Kommando ausgeführt hat. Mal sehen was die ZSH-Mailinglist dazu sagt.


zle-line-init zle-keymap-select()
{
RPROMPT=${${KEYMAP/vicmd/-- COMMAND --}/main/}
PROMPT=${${KEYMAP/main/$SAVEDPROMPT}/vicmd/}
zle reset-prompt
}

zle -N zle-line-init
zle -N zle-keymap-select

setopt TRANSIENTRPROMPT

bindkey -M vicmd "^M" down-line-or-history

Donnerstag, 21. August 2008

ZSH: Fit for fun

Heute hab ich mich von der guten alten BASH verabschiedet und mein System auf ZSH umgestellt. Und da so eine mächtige Shell auch hübsch aussehen soll habe ich ihr auch gleich einen netten Look verpasst.



Für alle die es interessiert, hier die .zshrc:

# Lines configured by zsh-newuser-install
HISTSIZE=1000
SAVEHIST=1000
# End of lines configured by zsh-newuser-install
PATH="/usr/bin:\
/usr/X11/bin:\
/usr/sbin:/sbin:\
/opt/SunStudioExpress/bin:\
/opt/netbeans-6.0/bin:\
/opt/DTT/Bin:\
/usr/sfw/bin:\
/usr/gnu/bin"
MANPATH="/usr/share/man:\
/usr/X11/share/man:\
/opt/SunStudioExpress/man:\
/opt/DTT/Man:\
/usr/sfw/share/man:\
/usr/gnu/share/man"

if ((EUID != 0));
then
PROMPT="[%{$(echo -n '\e[1;32m')%}%n\
%{$(echo -n '\e[0;00m')%}@\
%{$(echo -n '\e[0;33m')%}%m\
%{$(echo -n '\e[0;00m')%}:\
%{$(echo -n '\e[0;34m')%}%1~\
%{$(echo -n '\e[0;00m')%}]\
%{$(echo -n '\e[0;34m')%}> \
%{$(echo -n '\e[0;00m')%}"
HISTFILE=~/.histfile
else
PROMPT="[%{$(echo -n '\e[0;31m')%}%n\
%{$(echo -n '\e[0;00m')%}@\
%{$(echo -n '\e[0;33m')%}%m\
%{$(echo -n '\e[0;00m')%}:\
%{$(echo -n '\e[0;34m')%}%~\
%{$(echo -n '\e[0;00m')%}]\
%{$(echo -n '\e[0;31m')%}> \
%{$(echo -n '\e[0;00m')%}"
HISTFILE="/root/.histfile"
fi

ls()
{
if [[ ${1[0,2]} != "-v" ]] && [[ (${1[0,2]} != "-V") ]];
then
/bin/dir $argv --color=always
else
/bin/ls $argv
fi
}
setopt HIST_IGNORE_DUPS
setopt VI
unsetopt flowcontrol

bindkey "^[[3~" delete-char

Als kleine Besonderheit habe ich ls so umgestellt das es im Normalfall das GNU dir nimmt (weil das so schön bunt ist :P) und sobald ich die Option -v oder -V verwende das Solaris ls (weil das mit NFSv4 ACLs klarkommt welche ZFS-Standard sind)

Mittwoch, 20. August 2008

Dtrace Workshop - Teil 4: Userspace tracen mit dem PID-Provider

Nachdem wir uns in den letzten drei Teilen eigentlich nur mit Kerneltracing befasst haben, wird es Zeit sich dem Userspace zu widmen. Hierfür liefert dtrace den PID-Provider mit dem wir jede Funktion innerhalb eines Prozesses genauso unter die Lupe nehmen können wie wir es auch schon beim Kernel gelernt haben. Diesen Provider werde ich jetzt anhand eines einfachen C-Programms vorstellen.

#include <stdio.h>

void a(int i)
{
printf("a\n");
return;
}

void b(int i)
{
printf("b\n");
a(3);
}

void c(int i)
{
printf("c\n");
b(2);
}

int main()
{
c(1);
return 0;
}

Dieses Programm werden wir komplett ohne Debugsymbole übersetzen und userspace nennen. Wir wollen jetzt mit dem PID-Provider sehen welche Funktionen innerhalb des Programms ausgeführt werden. Dazu schreiben wir ein einfaches D Skript.

#!/usr/sbin/dtrace -s

#pragma D option flowindent

pid$target:userspace::return,
pid$target:userspace::entry
{
}

Die Variable $target bezieht sich auf das Programm das wir für dtrace als Ziel angeben. Dieses können wir mir der -c Option für ein spezifisches Kommando, oder mit -p für eine bestimmte PID angeben. Normal setzt sich der PID-Provider aus dem Wort pid und einer PID zusammen (z.B. pid2342), mit der Zielvariablen ist unser Skript allerdings sehr viel dynamischer. Als Modul haben wir hier userspace angegeben. Das ist der Name unseres Programms. Wir werden also nur Funktionen tracen die wir innerhalb unseres Programms definiert haben. Mit dtrace -l -p pidPID kann man sich übrigens alle tracebaren Funktionen eines bestimmten Prozesses ausgeben lassen (aber vorsicht, das ist meistens echt viel ;) ).

raichoo@itzkoatl:~/Projects# ./userspacetrace.d -c ./userspace
dtrace: script './userspacetrace.d' matched 13 probes
c
b
a
CPU FUNCTION
0 -> _start
0 -> __fsr
0 <- __fsr
0 -> main
0 -> c
0 -> b
0 -> a
0 <- a
0 <- b
0 <- c
0 <- main
dtrace: pid 697 has exited

Jetzt würden wir aber gerne noch unsere printfs mittracen, dazu erweitern wir unser Skript einfach um die Tupel pid$target:libc.so.1:printf:entry und pid$target:libc.so.1:printf:return um die in libc.so.1 definierte printf Funktion zu tracen (das geht auch für jede andere gelinkte lib). Wir erhalten nun folgende Ausgabe:

dtrace: script './userspacetrace.d' matched 15 probes
c
b
a
CPU FUNCTION
0 -> _start
0 -> __fsr
0 <- __fsr
0 -> main
0 -> c
0 -> printf
0 <- printf
0 -> b
0 -> printf
0 <- printf
0 -> a
0 -> printf
0 <- printf
0 <- a
0 <- b
0 <- c
0 <- main
dtrace: pid 714 has exited

Somit können wir jetzt jede Userspace Funktion tracen. Zum Abschluss will ich noch zeigen wie wir hier auch Probeargumente anwenden können:

#!/usr/sbin/dtrace -s

#pragma D option flowindent

pid$target:userspace::entry
{
printf("Funktionsargument %d",arg0);
}


pid$target:userspace::return,
pid$target:libc.so.1:printf:return
{
}

pid$target:libc.so.1:printf:entry
{
printf("Funktionsargument %s",copyinstr(arg0));
}

Dienstag, 19. August 2008

Dtrace Workshop - Teil 3: Der FBT-Provider und erste dtrace-Skripte

In Teil 1 angekündigt und in Teil 2 aufgeschoben will ich jetzt endlich zum FBT-Provider kommen. Hiermit kann man sich wie gesagt jede Funktion die es im Kernel gibt ausgeben lassen. Als Beispiel werden wir uns hier angucken was im Kernel passiert wenn wir bestimmte Systemcalls ausführen. Da unsere dtrace Anweisungen aber inzwischen immer komplexer werden, wird es langsam Zeit das wir uns damit befassen wie man dtrace Skripte schreibt.

Wie bereits mehrmals gesagt wurde handelt es sich bei D um eine eigene Programmiersprache handelt welche sich an AWK und C anlehnt. D ist eventgesteuert, das heißt das bei bestimmten Ereignissen etwas ausgeführt wird und der Code nicht einfach durchlaufen wird. Als ersten sehen wir und mal ein einfaches dtrace Skript an. Wir greifen dabei auf eine der ersten Lektionen zurück die wir hatten, nämlich wie man sich anzeigen läßt welcher Prozess gerade welchen Systemcall ausführt.

#!/usr/sbin/dtrace -s

syscall:::entry
{
trace(execname);
}

Wir speichern das ganze unter syscall.d und machen es mit chmod +x syscall.d ausführbar. Das Resultat sieht dann wie folgt aus:

raichoo@itzkoatl:~# ./syscall.d
dtrace: script './syscall.d' matched 233 probes
CPU ID FUNCTION:NAME
0 68998 ioctl:entry dtrace
0 68998 ioctl:entry dtrace
0 69134 sysconfig:entry dtrace
0 69134 sysconfig:entry dtrace
0 69266 schedctl:entry dtrace
0 69068 sigaction:entry dtrace
0 69068 sigaction:entry dtrace
0 69068 sigaction:entry dtrace
0 69068 sigaction:entry dtrace
...

Voila, schon haben wir unsere "Mühe" permanent in ein Skript gepackt, immer wieder abrufbar :).

Wollen wir uns jetzt mal ein etwas komplexeres D Skript ansehen. Hier werden wir uns ansehen was beim mmap Syscall alles im Kernel passiert.

#!/usr/sbin/dtrace -s

syscall::mmap:entry
{
self->verfolgen = 1;
}

fbt:::entry,
fbt:::return
/self->verfolgen/
{
}

syscall::mmap:return
{
self->verfolgen = 0;
exit(0);
}

Ok das ist jetzt etwas viel auf einmal. Besonders mysteriös ist hier die Zeile self->verfolgen = 1. Hierbei handelt es sich um eine theadlokale Variable. Ruft ein Thread mmap auf wird verfolgen für diesen Thread auf 1 gesetzt, das wird wichtig wenn wir die Bedingung für unsere FBT Probes ansehen, diese wird nämlich nur ausgeführt wenn self->verfolgen gesetzt ist. Erinnern wir uns: in Teil 1 haben wir festgestellt das D Events wie folgt aufgebaut sind:

probe-tupel
/Bedingung/
{
Funktionskörper
}

Sobald der mmap Systemcall zurückkehrt (mit syscall::mmap:return) wird die Variabel auf 0 gesetzt und das Skript beendet. Gucken wir uns mal die Ausgabe dieses Skripts an:

raichoo@itzkoatl:~# ./mmap.d
dtrace: script './mmap.d' matched 66380 probes
CPU ID FUNCTION:NAME
0 31880 smmap32:entry
0 23175 smmap_common:entry
0 27816 as_rangelock:entry
0 27817 as_rangelock:return
0 23173 zmap:entry
0 31698 choose_addr:entry
0 21835 map_addr:entry
...
0 29853 as_map:return
0 23174 zmap:return
0 29415 as_rangeunlock:entry
0 29952 cv_signal:entry
0 29953 cv_signal:return
0 29416 as_rangeunlock:return
0 23176 smmap_common:return
0 31881 smmap32:return
0 69093 mmap:return

Ok das ist ein ziemlicher Rattenschwanz (natürlich habe ich die Ausgabe hier gekürzt ;) )und leider auch sehr unübersichtlich, aber das alles passiert innerhalb eines mmap Syscalls im Kernel. Es wäre aber doch viel schöner wenn man die Ausgabe etwas einrücken könnte und damit besser lesbar. Das erreichen wir durch eine zusätzliche Zeile in unserem Skript.

#!/usr/sbin/dtrace -s

#pragma D option flowindent

syscall::mmap:entry
{
self->verfolgen = 1;
}

fbt:::entry,
fbt:::return
/self->verfolgen/
{
}

syscall::mmap:return
{
self->verfolgen = 0;
exit(0);
}

Durch die Anweisung #pragma D option flowindent veranlassen wir dtrace unsere Ausgabe je nach entry oder return einzurücken und somit sehr viel lesbarer zu machen.
Hier nochmal die gleiche Ausagebe mit flowindent:

raichoo@itzkoatl:~# ./mmap.d
dtrace: script './mmap.d' matched 66380 probes
CPU FUNCTION
0 -> smmap32
0 -> smmap_common
0 -> as_rangelock
0 <- as_rangelock
0 -> zmap
0 -> choose_addr
0 -> map_addr
...
0 <- zmap
0 -> as_rangeunlock
0 -> cv_signal
0 <- cv_signal
0 <- as_rangeunlock
0 <- smmap_common
0 <- smmap32
0 <= mmap

Jetzt kann man sicher sagen: "Ok... das sagt mir zwar jetzt welche Funktionen ausgeführt werden, aber was passiert denn jetzt genau". Stimmt, ohne Code sagt uns das jetzt recht wenig. Sun hat allerdings unter http://src.opensolaris.org/source/ den gesamten veröffentlichen Code durchsuchbar gemacht. Wir brauchen uns also nur eine Funktion von Interesse aussuchen, ihren Namen unter Definition eingeben und schon kriegen wir die Datei angegeben in der sie definiert ist und können sie uns auch online ansehen. Zugegeben ist das keine leichte Kost, aber das ist Kernelcode eigentlich fast nie. Hier kann man ihn aber live in Aktion sehen was das Verständnis um einiges erleichtert. :)

Montag, 18. August 2008

Warum OpenSolaris?

Ich muss zugeben das mir diese Frage in den letzten Wochen ziemlich oft gestellt wurde, vor allem da man mich seit 12 Jahren eigentlich als überzeugten Linuxuser kannte. Zugegeben hat mich das ganze selbst ziemlich unerwartet getroffen, da ich OpenSolaris bis auf ZFS eigentlich als ziemlich uninteressant empfunden habe und teilweise sogar belächelt. Aber mit Betriebssystemen ist es manchmal wie mit Menschen: "Wenn man es erst einmal kennengelernt hat, ist es eigentlich richtig cool".

"Aber warum nicht Linux?"

, hört man aus der Linuxecke (bei den BSDler ist es oft ähnlich ^^). Die Frage ist sicherlich nicht ganz unberechtigt, verfügt Linux doch über sehr viel mehr Treiber und je nach Distribution derzeit noch über sehr viel mehr Pakete. Das mit den Treiber ist tatsächlich immer wieder ein Argument welches mir begegnet. Die Sache mit Treiber ist allerdings: Man braucht sie nur wenn man die Hardware hat. Und 99% der unterstützten Hardware besitze ich nun mal nicht und ich habe genauso wenig vor sie zu kaufen, also ist das Treiberargument hinfällig. Außerdem erinnere ich mich noch an eine Zeit als ich unter Linux weder Sound noch einen brauchbaren Browser hatte und trotzdem habe ich es benutzt weil es eben das gemacht hat was ich von ihm verlangt habe.
Und genau das ist es was OpenSolaris jetzt für mich macht, derzeit allerdings noch mehr als Entwicklungsumgebung und noch nicht als mein Alltagsdesktop da es unter anderem noch an einer gewissen Multimediafähigkeit mangelt. Da OpenSolaris in dieser Form allerdings erst seit Mai existiert und es dafür schon eine sehr vollständige Figur macht, kann ich darüber ersteinmal hinwegsehen. Außerdem ist besagtes für den nächsten Release im November in Arbeit.

"Aber was macht OpenSolaris denn jetzt besser als Linux?"

Erstmal: Es handelt sich hier um persönliche Präferenzen. Ich kann hier noch soviel von Features schwärmen, aber letztendlich muss man diese Features auch nutzen. Hier einfach mal ein kleiner Einblick:

ZFS

In den letzten Monaten ist ZFS schon fast zum Buzzword geworden. Irgendwie redet jeder davon aber selten wird wirklich mal erklärt was ZFS eigentlich kann. Für alle Interessenten kann ist jedem Mal den ZFS Podcast vom Chaosradio ans Herz legen. Ein besonders tolles Feature bei ZFS ist die Möglichkeit Snapshots von seinen Filesystemen anzulegen und damit sein System ähnlich wie bei modernen Versionskontrollsystemen zu verzweigen. Hier ein Beispiel von meinem System.

raichoo@itzkoatl:~$ beadm list

BE Active Active on Mountpoint Space
Name reboot Used
---- ------ --------- ---------- -----
opensolaris no no - 14.58M
opensolaris_snv95 yes yes / 6.40G

Es verfügt über 2 sogenannte Bootumgebungen. opensolaris und opensolaris_snv95. Auf der ersten liegt das Ursystem, mit anderen Worten die Version von OpenSolaris mit der ich installiert habe. Mit der Zeit kamen allerdings mehrere Updates und wie heißt es so schön: "Never change a winning Team". Also habe ich eine zusätzliche Bootumgebung mit dem Namen opensolaris_snv95 angelegt. Nachdem man diese erzeugt hat, besitzt man eine exakte Kopie seines Systems welche man dann separat updaten kann. Das ganze funktioniert so:

raichoo@itzkoatl:~# beadm create opensolaris_snv95
raichoo@itzkoatl:~# mkdir /tmp/mein_neues_system
raichoo@itzkoatl:~# beadm mount opensolaris_snv95 /tmp/mein_neues_system
raichoo@itzkoatl:~# pkg -R /tmp/mein_neues_system image-update
...
raichoo@itzkoatl:~# beadm unmount opensolaris_snv95
raichoo@itzkoatl:~# beadm activate opensolaris_snv95

Mit pkg habe ich die Kopie meines Systems auf den neusten Stand gebracht und mit activate habe ich dafür gesorgt das sie beim nächsten booten als Standard ausgewählt wird. Besonders praktisch ist hier das beadm auch schon grub eingerichtet hat und ich jetzt für jede Version einen Eintrag hat. Dazu kommt noch das die Kopie meines Systems nur den Unterschied zwischen beiden Systemen an Plattenplatz belegt. So kann man immer ohne Risiko an seinem System herumschrauben und mit einem Handgriff zurück. Aber ZFS kann noch mehr, unter anderem auch unglaublich mächtige Backupfunktionen, sehr komfortables NFS und Samba-Management, Raidfunktionen die weit über die Möglichkeit von traditionellem Hardwareraid hinausgehen, transparente Kompression und bald auch transparente Verschlüsselung, Selbstheilung und alles läßt sich mit zwei Befehlen einfach und intuitiv aus der Konsole steuern. Wers nicht glaubt, hier die manpages zu zpool und zfs.

Dtrace und MDB

Ich bin jemand der gerne versteht was in seinem System vorgeht. Das schließt Userspace Programme genauso ein wie Kernelspace Aktivitäten. Aus diesem Grund sammel ich auch Bücher die sich mit den jeweiligen Kerneln befassen. Zu Linux besitze ich das Buch "Understanding the Linux Kernel", und auch wenn es in vielerlei Hinsicht nicht uninteressant ist hat es einen gravierenden Nachteil: Ich kann so gut wie nichts davon live beobachten und mir bringt das Wissen um den Kernel und seine Funktionsweise relativ wenig, da nachdem er übersetzt ist er sich für mich in eine Blackbox verwandelt. Mit dtrace und dem Modular Debugger mdb kann ich jederzeit in den Kernel hineinsehen und nachvollziehen was passiert. Ich kann mein Wissen über den Kernel einsetzen um an die Informationen heranzukommen die ich brauche. Ein extremer Vorteil wenn man Probleme sucht, oder einfach nur verstehen will was eigentlich vor sich geht. Zu dtrace haben ich, wie einige vielleicht schon bemerkt haben, einen Workshop auf meinem Blog, eventuell werde ich das gleiche auch noch für mdb machen.

IPS

Über die Jahre gab es viele Paketsysteme und inzwischen sind die etablierten mehr oder weniger veraltet. Rpm soll überholt werden und apt ist für meinem Geschmack einfach zu kompliziert wenn es ums schnüren von Paketen geht. Ich will meine Software einfach nur übersetzen und dann freigeben können. Genau das macht IPS für mich. Ich muss mich nicht damit befassen Skripte zu schreiben um ein Paket zu erschaffen sondern kann meine Software einfach in mein eigenes Repository hochladen, das ganze ist in der Funktionalität wieder stark an moderne Versionskontrollsysteme angelehnt. Alles wunderbar ist dokumentiert auf der OpenSolaris Seite.

Dokumentation

Womit wir auch gleich schon zum nächsten Punkt kommen. Als Developer hasst man es sie zu schreiben, als Anwender liebt man es sie zu haben. Dokumentation. Was mich bei praktisch allen Linux Distributionen immer gestört hat war der Mangel an aktueller tiefgreifender Dokumentation, die auch zugegebenermaßen bei den vielen Änderungen (ob nötig oder nicht sei mal dahingestellt) oft nicht auf dem neusten Stand ist oder teilweise einfach nur oberflächlich am Problem kratzt. Meistens provoziert ein sogenanntes Howto noch weitere Fragen die wieder dazu führen das man suchen muss. Das ganze artet oft in eine Art Schnitzeljagd im Netz aus, die nicht immer vom Erfolg gekrönt ist und gerade bei komplexen Fragen oft im Nichts endet.
Zu Solaris gibt es allerdings einen zentralen Dokumentationsserver. http://docs.sun.com wartet mit einem Berg an Büchern auf (ja, ganze Bücher keine Howtos)die alle frei zum herunterladen angeboten werden. Alles im handlichen PDF Format, sprengt das Angebot ohne Probleme die 10.000 Seiten Grenze (vermutlich sogar weit darüber hinaus). Alles wird verständlich mit Beispielen erklärt und sollte doch mal eine Frage auftauchen existiert meistens schon ein Link zum Buch das diese Frage klärt.

Stabile ABI

Etwas vor dem sich Linux leider bis heute fürchtet oder ablehnt. Oft sind nach 2 oder 3 Versionen Treiber im Binärformat nicht mehr zu gebrauchen und man muss praktisch alles neu kompilieren. Nutzt man eine vorgefertigte Distribution hat man dieses Problem nicht, aber kommerzielle Anbieter schrecken davor zurück Treiber für Linux herzustellen da die Binärkompatibilität nicht gewährleistet ist. Auch wenn ich prinzipiell gegen sogenannte Blobs bin ist es für viele Anbieter ein Grund keine Treiber für Linux zu veröffentlichen, da der Wartungsaufwand in keinerlei Bezug zum Nutzen wäre. Unter Solaris 10 z.B. ist es allerdings immer noch möglich einen Netzwerktreiber zu laden der für Solaris 8 geschrieben wurde.

Es gibt noch viele weitere Grunde wie das SMF System, Zones, die SunStudio Entwicklungsumgebung, die p*-Tools usw. Die mich dazu gebracht haben auf Solaris zu wechseln, aber auch hier gilt: Ein Feature ist nur dann sinnvoll wenn man es nutzt. Mir macht es auf jeden Fall das Leben ungeahnt leichter :)

Wer jetzt neugierig ist kann sich auch einmal den Podcast von pofacs.de (eine allgemein sehr lohnenswerte Seite) zu OpenSolaris anhören.


OpenSolaris: Innovation Matters

Sonntag, 17. August 2008

Dtrace Workshop - Teil 2: Probeargumente und Aufzählungen

In Teil 1 dieses Workshops haben wir uns mit grundlegenden Funktionen von dtrace befasst. Heute will ich mich mit Probeargumenten und Aufzählungen befassen.

Wie ich letztes mal schon festgestellt habe, arbeitet man bei dtrace mit der Programmiersprache D. Diese Sprache verfügt unter anderem über Datentypen. Ein besonderer Datentyp ist die Aufzählung. Ich will hier mal in einem kleinen Beispiel zeigen wie das funktioniert indem ich dtrace Systemcalls aufzählen lasse.

raichoo@itzkoatl:~# dtrace -n 'syscall:::entry{@[probefunc] = count();}'
dtrace: description 'syscall:::entry' matched 233 probes
^C

fstat64 1
mmap 1
schedctl 1
sigpending 1
nanosleep 2
stat64 2
pset 3
sysconfig 3
c2audit 4
dup 4
fcntl 4
open 4
sigaction 4
brk 6
close 8
gtime 10
clock_gettime 11
writev 12
setcontext 13
lwp_sigmask 15
setitimer 25
p_online 32
portfs 84
write 133
getpid 169
read 171
doorfs 252
lwp_park 320
pollsys 496
ioctl 648


Wollen wir mal der Aufzählung auseinandernehmen:
Mit dem @ zeigen wir an das wir etwas aufzählen wollen. Was das genau ist geben wir in eckigen Klammern an, in diesem Fall ist das die Probefunktion. probefunc ist eine vordefinierte Variable die uns sagt welche Probe "gefeuert" hat. Kommen wir nun zum = count(). Dieser Teil sagt uns WIE wir aufzählen wollen. count() zählt einfach nur wie oft ein Ereignis eingetreten ist. Später werden wir uns genauer unterschiedlichen Aufzählungsarten befassen.
Als Anmerkung bleibt wohl noch zu sagen das dtrace während der Ausführung nichts ausgibt sondern erst wenn wir es mit Strg-C beenden.

Als nächstes wollen wir uns zur Vertiefung noch einen dtrace-Aufruf ansehen der uns anzeigt welche Programme den mmap Systemcall aufrufen und wie oft.

raichoo@itzkoatl:~# dtrace -n 'syscall::mmap:entry{@[execname] = count();}'
dtrace: description 'syscall::mmap:entry' matched 1 probe
^C

dtrace 1
hostname 16
utmp_update 16
bash 33


Hier zählen wir nicht nach probefunc sondern nach execname auf. Außerdem haben wir das Probetupel genauer spezifiziert.

Wir wollen uns jetzt mal genauer angucken was ein Systemcall so macht. Als Beispiel nehme ich hier den open Systemcall. Gucken wir uns die Funktionsdefinition in der dazugehörigen Manpage an:

int open(const char *path, int oflag, /* mode_t mode */);

Diese Informationen werden an den open Syscall übergeben und wenn man ehrlich ist, würde man da eigentlich auch ganz gerne drankommen wenn man dtrace benutzt. Und das geht auch. Wir haben mit execname und probefunc bereits 2 vordefinierte Variablen kennengelernt, jetzt kommen die Variablen arg0 bis arg9 hinzu. Jede gibt uns Zugang zu einem Argument der jeweiligen Probe.
Als Beisiel konstruieren wir hier mal einen dtrace Aufruf der uns anzeigt welche Dateien unser System gerade öffnet.

raichoo@itzkoatl:~# dtrace -n 'syscall::open:entry{printf("%s",copyinstr(arg0));}'
dtrace: description 'syscall::open:entry' matched 1 probe
CPU ID FUNCTION:NAME
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/index.theme
0 69268 open:entry /usr/share/icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/index.theme
0 69268 open:entry /usr/share/icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/index.theme
0 69268 open:entry /usr/share/icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/index.theme
0 69268 open:entry /usr/share/icons/Neutral_Plus_Inv/cursors/xterm
0 69268 open:entry /export/home/raichoo/.icons/Neutral_Plus_Inv/cursors/xterm

Zu printf werde ich hier nicht viel verlieren sondern auf die C Manpage verweisen, da sich die dtrace Funktion genauso verhält. Wir wollen und das erste Argument der Probe ansehen, wie wir oben schon gesehen haben ist das erste Argument const char *path der Pfad der zur öffnenden Datei. Um dieses auszulesen benutzen wir das Probeargument arg0. Da wir uns in einem Systemcall befinden müssen wir die Informationen aus dem Kerneladressraum in den Userspaceaddressraum kopieren, dazu ist die copyinstr() Funktion da.

Eigentlich wollte ich noch etwas auf den FBT-Provider eingehen, aber ich glaube das ist erstmal genug Input für heute, also werde ich mir das für später aufbewahren.