Je vais donc commencer par vous présenter l'historique de DTrace, je poursuivrai avec son principe de fonctionnement, et enfin je terminerai par un cas d'utilisation dans le cadre d'un développement en PHP.

DTrace est un outil qui a été créé en 2003 par Sun Microsystems, dans le cadre du développement de son système d'exploitation Solaris, dans lequel il a été intégré en 2005.

Suite à l'ouverture du code source de ce dernier via le projet OpenSolaris, il est devenu un outil libre, régit par la licence CDDL.

Son objectif est de permettre de tracer l’exécution d'un programme, de manière transparente et sans aucune perte de performance, afin qu'il puisse être mis en œuvre dans des environnements de production.

Le principe de DTrace est basé sur celui du patron de conception observateur/observable.

L'observable déclenche lors de son exécution des sondes, ce qui propage un évènement vers les observateurs.

Ces évènements sont interceptés, ou non, par les observateurs, qui sont des scripts en langage D, capables d'effectuer différentes actions en fonction des besoins.

Cette architecture a plusieurs avantages majeurs :

  • Elle est pratique, car il est possible de l'activer à la demande sur un programme en cours d’exécution.
  • Elle est efficace, car elle permet de ne tracer que ce qui est nécessaire.
  • Elle est souple, car elle permet de tracer ce que l'on veut, comme par exemple un temps d’exécution, une pile d'appel ou l'utilisation de la mémoire.
  • Elle ne dégrade pas les performances du programme observé, car tout se passe au niveau du noyau du système d'exploitation.
  • Elle est capable de tracer plusieurs processus simultanément, ou encore plusieurs niveaux différents de l'application en parallèle.
  • Elle permet de tracer simultanément l’exécution au niveau de l'environnement de l'utilisateur et au niveau du noyau du système d'exploitation.

DTrace est donc particulièrement adapté pour diagnostiquer des problèmes réels en temps réel sur des systèmes en production pris dans leur ensemble.

Le principe de DTrace est donc très différent de celui de gdb.

En effet, de part son principe de fonctionnement, ce dernier ne peut pas faire la même chose que DTrace.

Il est en effet adapté pour résoudre un problème sur un exécutable spécifique, alors que DTrace peut surveiller plusieurs exécutable simultanément, voir même le système d'exploitation, puisque c'est sa vocation première dans le cadre de Solaris.

De plus, gdb doit être activé au démarrage du programme à observer, alors que DTrace peut être déclenché à tout moment.

Il n'est donc pas du tout conçu pour travailler sur du code en production.

En contrepartie, gdb peut influencer sur le déroulement du programme, au contraire de DTrace qui ne permet que la collecte d'informations.

Au final, ce ne sont donc pas des outils concurrents, mais bien complémentaires, car ils permettent respectivement de répondre à une problématique différente :

  • DTrace permet d'identifier les problèmes sur une application de manière globale.
  • Une fois ces problèmes identifiés, gdb permet de les résoudre de manière locale.

La version du trunk de PHP dispose depuis quelques temps maintenant des sondes suivantes :

  • request-startup, déclenchée lorsqu'un script PHP est exécuté,
  • request-shutdown, déclenchée lorsque l’exécution d'un script PHP est terminée,
  • compile-file-entry, déclenchée lorsque la compilation d'un script PHP commence,
  • compile-file-return, déclenchée lorsque la compilation d'un script PHP est terminée,
  • execute-entry, déclenchée lors de l’exécution d'un opcode PHP,
  • execute-return, déclenchée lorsque l’exécution d'un opcode PHP est terminée,
  • function-entry, déclenchée lors de l’exécution d'une fonction ou d'une méthode PHP commence,
  • function-return, déclenchée lorsque l’exécution d'une fonction ou d'une méthode PHP est terminée,
  • exception-thrown, déclenchée lorsqu'une exception est lancée,
  • exception-caught, déclenchée lorsqu'une exception est attrapée,
  • error, déclenchée lorsqu'une erreur survient au cours de l’exécution.

Comme nous l'avons vu, pour les utiliser, il faut passer par un script écrit en langage D.

Attention, contrairement à ce que j'ai affirmé lors de ma conférence, il ne s'agit aucunement du langage D de Walter Bright, mais bien d'un langage spécifique à DTrace dérivé du langage awk.

Un script en D minimal dispose de la structure suivante :

sonde
/expression/
{
actions
}

Évidement, il peut y avoir plusieurs bloc de ce type dans un script, afin d'interroger autant de sondes différentes.

sondepermet de décrire la sonde qui doit déclencher l'action qui suit, à l'aide de la conventionfournisseur:module:fonction:emplacement, ou :

  • fournisseur correspond à l'émetteur de la sonde, dans le cas de PHP, il s'agit de php, mais de base, DTrace fourni un grand nombre de fournisseurs, comme profile qui permet de déclencher une sonde à intervalle fixe.
  • module indique le sous-ensemble auquel est rattaché la sonde, par exemple libc.
  • fonction défini le nom de la fonction qui doit déclencher la sonde, par exemple fstat.
  • emplacement permet de dire si la sonde doit se déclencher à la fin ou bien au début de la fonction, à l'aide par exemple des mots-clefs entry ou return.

L'attribut expression permet de spécifier une condition qui autorisera l’exécution de l'action, uniquement si elle est vérifiée.

Il est ainsi par exemple possible de cibler un processus spécifique, soit par son nom, soit par son numéro.

Le bloc entre accolade défini quant à lui les actions qui doivent être effectuées lorsque la sonde est déclenchée.

DTrace propose de base un grand nombre d'action, comme par exemple :

  • printf qui permet d'écrire sur la sortie standard.
  • trace qui permet d'obtenir la valeur d'une variable.
  • ustack qui permet d'obtenir la pile d’exécution.

En combinant tout cela, il est possible, par exemple, de surveiller les temps d’exécution respectifs de PHP, de httpd, mysql et firefox :

#!/usr/sbin/dtrace -qs
BEGIN
{
total=mysqlcnt=httpcnt=phpcnt=ffxcnt=0;
printf("%10s %10s %10s %10s\n","% MYSQL","% APACHE","% FIREFOX","% PHP");
}

php*:::request-startup
{
inphp[pid,tid]=1;
}

php*:::request-shutdown
{
inphp[pid,tid]=0;
}

profile-1001
{
total++;
(execname=="mysqld")?mysqlcnt++:\
(execname=="httpd")?(inphp[pid,tid]==1?phpcnt++:httpcnt++):\
(execname=="firefox-bin")?ffxcnt++;
}

tick-30s
{
printf("%10s %10s %10s %10s %10s %10s\n","% MYSQL","% APACHE","% FIREFOX","% PHP");
}

tick-2s
{
printf("%10d %10d %10d %10d %10d %10d\n",mysqlcnt*100/total,httpcnt*100/total,ffxcnt*100/total,phpcnt*100/total);
total=mysqlcnt=httpcnt=phpcnt=ffxcnt=javacnt=othercnt=0;
}

Une fois les droits nécessaires donnés au script, il ne reste plus qu'à le lancer en ligne de commande et à admirer le résultat.

Je vous laisse imaginer la puissance de ce script pour définir les goulots d'étranglement dans un système complexe, d'autant qu'il est possible de grandement l'améliorer...

En contrepartie, pour pouvoir être utilisé, ce qui nécessite un système d'exploitation qui le supporte, ce qui est uniquement le cas pour le moment de Solaris, Mac OS X et FreeBSD.