Mittwoch, 14. April 2010

Deadlocks jagen mit DTrace

Deadlocks gehören so ziemlich zu den widerlichsten Phänomenen die man sich so beim Programmieren vorstellen kann. Vor allem wenn sie nur sporadisch auftreten. Irgendein Thread weigert sich einfach ein Lock wieder freizugeben und wir hängen. Mit dem gleichen Problem durfte ich mich neulich auch rumschlagen, leider tappten wir total im Dunkeln da nicht lokalisieren war wer das Lock eigentlich in Beschlag genommen hat. Hier musste wieder mal meine Lieblingswunderwaffe ran und das Problem mit einem D Skript eingekreist werden.

Was dieses Skript macht ist nichts anderes als sich zu jedem Lock der geschlossen wird den Stacktrace zu merken und sobald wir an den Punkt kommen an dem das Deadlock auftritt uns zu zeigen wer gerade das Lock hält das uns am weitergehen hindert. Das ganze ist ein unglaublicher Speicherfresser (ich muss dazu sagen das die jeweiligen Stacktraces einfach nur abnorm waren) da ich hier Speculations verwende (siehe letzter Eintrag ;) ) und DTrace bereinigt den Speicher nur in bestimmten Intervallen, was in bei der Menge an Locks einfach zu langsam war. Außerdem hat man nur eine begrenzte Anzahl an Speculations zur Verfügung. Aber DTrace wär nicht das was es ist wenn man das nicht auch "aushebeln" könnte ;)

nspec dient dazu die Anzahl der möglichen Speculations hochzusetzen und cleanrate bestimmt die Frequenz der Garbagecollection, mit -b schrauben wir den Gesamtpuffer auf 128MB hoch.

Also, ohne weitere Umschweife: der Star des gestrigen Tages!


#!/usr/sbin/dtrace -b 128m -x nspec=20 -x cleanrate=101 -s

pid$target::deadlockgoeshere:entry
{
self->traceme = 1;
}

pid$target::deadlockgoeshere:return
/self->traceme/
{
self->traceme = 0;
}

pid$target::pthread_mutex_lock:entry
/spec[arg0] == 0/
{
spec[arg0] = speculation();
speculate(spec[arg0]);
ustack();
}

pid$target::pthread_mutex_lock:entry
/spec[arg0] && self->traceme/
{
commit(spec[arg0]);
spec[arg0] = 0;
}

pid$target::pthread_mutex_lock:entry
/spec[arg0] && self->traceme == 0/
{
discard(spec[arg0]);
spec[arg0] = 0;
}

pid$target::pthread_mutex_unlock:entry
{
discard(spec[arg0]);
spec[arg0] = 0;
}

Keine Kommentare: