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.

Donnerstag, 14. August 2008

Dtrace Workshop - Teil 1: Einführung

Nachdem ich ja immer so viel von dtrace schwärme möchte ich den Menschen auch gerne mal dieses extrem praktische Tool nahe bringen. Das dieses Thema extrem umfangreich ist werde ich es in verschiedene Sektionen aufteilen und von Zeit zu Zeit immer mal wieder etwas dazu posten.

Was ist dtrace überhaupt? Der beste Vergleich den ich kenne ist wie folgt:
Stell dir vor du hast Probleme mit deinem Auto. Es macht komische Geräusche wenn du schnell fährst. Und nun stell dir vor du fährst auf die Autobahn, beschleunigst auf 200 Sachen und steigst gemütlich aus, öffnest die Motorhaube und baust die Kiste auseinander ohne das diese aufhört zu arbeiten. Du kannst dir jede Schraube jeden Schlauch und jedes noch so komplexe Teil ansehen und herausfinden wo es Probleme gibt. Genau das macht Dtrace mit deinem Betriebssystem und alle Programmen die darauf laufen.

Jetzt kann man sicherlich sagen. Naja in meine Programme konnte ich aber schon immer mit einem Debugger reingucken. Klar konnte man das, aber nur in einzelne Prozesse. Dtrace ermöglicht systemweites Debuggen ohne das man das System in ein großes Performanceloch schmeißt wie das mit Tools wie strace und truss z.B. der Fall ist.

Wie funktioniert das Ganze? Dtrace verfügt über sogenannte Provider, welche in der Lage sind das System zu "instrumentalisieren". Das heißt: Sie kennen Punkte im System an dem sie Messungen durchführen können.

Listen wir erst mal unsere Messpunkte auf:

raichoo@itzkoatl:~# dtrace -l
ID PROVIDER MODULE FUNCTION NAME
1 dtrace BEGIN
2 dtrace END
3 dtrace ERROR
4 fbt fcsm fcsm_job_cache_destructor entry
5 fbt fcsm fcsm_job_cache_destructor return
6 fbt fcsm fcsm_job_cache_constructor entry
7 fbt fcsm fcsm_job_cache_constructor return
8 fbt fcsm fcsm_display entry
9 fbt fcsm fcsm_display return
...
69649 lockstat genunix lock_clear_splx spin-release
69650 lockstat genunix CLOCK_UNLOCK spin-release
69651 lockstat genunix rw_enter rw-acquire
69652 lockstat genunix rw_enter rw-block
69653 lockstat genunix rw_exit rw-release
69654 lockstat genunix rw_tryenter rw-acquire
69655 lockstat genunix rw_tryupgrade rw-upgrade
69656 lockstat genunix rw_downgrade rw-downgrade
69657 lockstat genunix thread_lock thread-spin
69658 lockstat genunix thread_lock_high thread-spin

Ok, hier wurden wir gerade mit verdammt vielen Messpunkten oder auch Probes erschlagen. Wie man sieht ist jede Probe einem Provider, einem Modul, einer Funktion und einem Namen zugeordnet. Mit diesen Tupeln sind wir in der Lage jeden Messpunkt zu spezifizieren und damit anzusprechen. Gucken wir einfach mal wie viele Probes uns unser System so zur Verfügung stellt.

raichoo@itzkoatl:~# uname -a
SunOS itzkoatl 5.11 snv_95 i86pc i386 i86pc
raichoo@itzkoatl:~# dtrace -l | wc -l
69659

Alleine auf meinem System stehen mir fast 70.000 Probes zur Verfügung. Und das sind noch lange nicht alle, es gibt noch ein paar spezielle Provider (z.B. den PID-Provider und auch Provider für so ziemlich jedes Skriptsprache z.B. Python, Ruby, PHP, JavaScript und und und) die einem noch mehr Transparenz geben.

Ok, jetzt da wir wissen das wir wirklich viel messen können wollen wir mal loslegen! Als Einführungsbeispiel will ich hier den Syscallprovider vorstellen. Dieser Provider weiß wie man Systemcalls instrumentalisiert. Systemcalls sind Funktionen bei deren Ausführung ein Programm ein Aufgabe an den Kernel abgibt z.B. öffne eine Datei, mappe mir etwas in den virtuellen Speicher, schicke ein Signal an einen anderen Prozess.

Als erstes wollen wir erst mal jeden Syscall-Entry anzeigen lassen.

raichoo@itzkoatl:~# dtrace -n syscall:::entry
...
0 69480 pollsys:entry
0 69166 write:entry
0 69450 lwp_sigmask:entry
0 69450 lwp_sigmask:entry
0 69164 read:entry
0 69480 pollsys:entry
0 69450 lwp_sigmask:entry
0 69450 lwp_sigmask:entry
0 69166 write:entry
0 69480 pollsys:entry
0 69166 write:entry
0 69450 lwp_sigmask:entry
0 69450 lwp_sigmask:entry
0 69164 read:entry
0 69480 pollsys:entry
0 69450 lwp_sigmask:entry
0 69450 lwp_sigmask:entry
0 69166 write:entry

Jetzt werden uns in Echtzeit alle Systemcall-Entrys auf dem System gezeigt ohne das das System ausgebremst wird. Diese Information ist allerdings eher unspannend. Wir wollen ja auch wissen WER das macht, also erweitern wir unser Kommando:

raichoo@itzkoatl:~# dtrace -n 'syscall:::entry{trace(execname);}'
...
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69258 ioctl:entry dtrace
0 69352 mmap:entry dtrace
0 69286 lwp_park:entry dtrace
0 69450 lwp_sigmask:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69164 read:entry sshd
0 69480 pollsys:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69166 write:entry sshd
0 69480 pollsys:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69164 read:entry sshd
0 69480 pollsys:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69166 write:entry sshd
0 69480 pollsys:entry sshd
0 69330 sigpending:entry dtrace
0 69450 lwp_sigmask:entry dtrace
0 69332 setcontext:entry dtrace
0 69166 write:entry dtrace
0 69450 lwp_sigmask:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69164 read:entry sshd
0 69480 pollsys:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69450 lwp_sigmask:entry sshd
0 69166 write:entry sshd
0 69480 pollsys:entry sshd
0 69258 ioctl:entry dtrace

Mit trace(execname) geben wir an das wir uns für das Programm interessieren das den Systemcall tätigt. execname ist eine vordefinierte Variable von der es eine ganze Menge gibt.

Aber was genau sehen wir da jetzt? Einige werden es sicher schon ahnen. Ich bin über SSH mit meinem Solarisrechner verbunden und alles was wir jetzt sehen sind die Systemcalls die das System braucht um all die Informationen für mich sichtbar zu machen. Wir gucken also dem System zu wie es uns sagt das es etwas sagt.

Wollen wir also unsere "Observation" weiter einschränken. Wir können ein Bedingung angeben die uns nur in bestimmeten Fällen einen Messwert liefert. Das geht wie folgt.

raichoo@itzkoatl:~# dtrace -n 'syscall:::entry/execname == "gdm-binary"/{}'
dtrace: description 'syscall:::entry' matched 233 probes
CPU ID FUNCTION:NAME
0 69164 read:entry
0 69164 read:entry
0 69164 read:entry
0 69166 write:entry
0 69412 yield:entry
0 69164 read:entry
0 69164 read:entry
0 69486 c2audit:entry
0 69368 xstat:entry
0 69312 readlink:entry
0 69486 c2audit:entry
0 69450 lwp_sigmask:entry
0 69450 lwp_sigmask:entry
0 69450 lwp_sigmask:entry
0 69450 lwp_sigmask:entry
0 69356 munmap:entry
0 69356 munmap:entry
...


Hier werden uns nur Informationen ausgegeben wenn das ausführende Programm gdm-binary ist.

Unsere Abfrage ist also wie folgt aufgebaut:

dtrace -n probe-tupel
/Bedingung/
{
Funktionskörper
}

Wie man sieht stellt dtrace eine kleine Programmiersprache zur Verfügung. Diese Sprache hat den Namen D und ist stark mit AWK und C verwand.

Das war nur ein extrem kleiner Einblick in das was dtrace kann. Als nächstes werde ich mich mit Probe-Argumenten, dem FBT-Provider (welcher jede Funktion im Kernel tracen kann) und dtrace-Skripten befassen. Wer bis dahin nicht warten kann, kann sich auch den Vortrag von Brian Cantrill über dtrace ansehen (SEHR SEHENSWERT!) oder einfach mal im Dtrace User's Guide schmöckern.

Dtrace kann man auch auf der OpenSolaris LiveCD benutzen. Als Anmerkung gilt zu sagen das man root-Rechte braucht (oder bestimmte RBAC-Rechte auf einem Produktivsystem)

Pimp my Blog

Nachdem es irgendwie recht defaultmässig auf meinem Blog zugegangen ist hab ich mir mal ein paar zusätzliche Einstellungen gegönnt im das gute Stück hier etwas persönlicher und auch übersichtlicher zu gestallten. :)

Montag, 4. August 2008

Der widerspenstigen Zähmung

Da vi/vim unter manchen Systemen die nervige Angewohnheit hat im Insert-Mode etwas eigensinnig mit den Pfeiltasten umzugehen, hier ein kleiner Patch um dem guten Stück Beine zu machen:

Wir schreiben folgendes in unsere .exrc und .vimrc:

set t_ku=[strg-v][pfeil-hoch]
set t_kd=[strg-v][pfeil-runter]
set t_kr=[strg-v][pfeil-rechts]
set t_kl=[strg-v][pfeil-links]


(Anmerkung: Das was in den eckigen Klammern steht nicht abtippen sondern machen ;) )

Um das ganze im Nachhinein noch etwas aufzupeppen kann man auch noch "set showmode" und "syntax on" hinzufügen um den aktuellen Mode anzeigen zu lassen und das Syntaxhighlighting zu aktivieren.

Sonntag, 3. August 2008

Dtrace überall

In den letzten Jahren hat dtrace, das dynamische Tracingtool von OpenSolaris, für eine Menge aufsehen gesorgt und inzwischen findet es auf immer mehr Plattformen ein zuhause. Ports existieren bereits für Mac OS X und QNX. Derzeit werden auch vom FreeBSD (man munkelt auch über einen NetBSD Port) Team Anstrengungen unternommen es zu portieren und vor einigen Monaten wurde der erste Code in den CURRENT Zweig commited.
Unter Linux gab es allerdings Probleme da die CDDL-Lizenz, unter der dtrace steht, nicht kompatibel zur GPL ist. Es wurden zwar mit systemtap Ansätze unternommen dtrace zu kopieren, doch ist es in keiner Weise vergleichbar da es unter anderem nicht in der Lage ist Userspace Prozesse zu tracen und die Stabilität des Systems beeinträchtigt da unter anderem das "Skripten" welches fast C ähnlich ist, völlig über das Ziel hinausschiesst und gefährlichen Code ermöglicht. Inzwischen ist das ftrace-Framework im Gespräch welches aber im Gegensatz zu dtrace und systemtap nicht skriptbar ist und damit extrem an Flexibilität einbüßt. Alles in allem eher spärliche Aussichten. Für ein Licht am Ende des Tunnels sorgt Paul Fox der sich daran gemacht hat dtrace auf Linux zu portieren. Ob es letztendlich möglich sein wird Linux Distributionen mit dtrace auszuliefern kann ich derzeit leider nicht sagen (da mir der ganze Lizenzkram langsam eh zu undurchschaubar wird), aber es bleibt die Hoffnung das es wenigstens ein Patchset geben wird das dieses unglaublich mächtige Tool in die Linuxwelt bringen wird.