Installation

XDebug est un module PHP qui s'installe très facilement. Sous Windows, copiez simplement la DLL téléchargées dans un répertoire (c:/php/modules par exemple) puis ajoutez cette ligne dans votre php.ini :

zend_extension_ts="c:/php/modules/php_xdebug-2.0.4-5.2.8.dll"

Redémarrez ensuite votre serveur web et le tour est joué. Attention toutefois à la version téléchargée. Elle doit correspondre à votre version de PHP. Si vous avez PHP 5.2, téléchargez la version 5.2 de XDebug.

Pour vous assurer que tout se soit bien passer, et avant de continuer à lire, vérifiez en affichant un phpinfo() que le module a été correctement chargé.

Profiling

XDebug est un outils puissant et étendu mais nous nous intéresseront uniquement à deux de ses fonctions pour l'instant. La première est le profiler. Cette fonctionnalité va nous permettre d'analyser le temps d'exécution de notre code.

Une première bonne surprise, il n'est pas nécessaire de modifier son code PHP pour utiliser le profiler. Il suffit simplement de l'activer. Pour ce faire, si vous avez des droits de configuration par l'intermédiaire d'un .htaccess il vous suffit d'ajouter ces lignes :

php_flag    xdebug.profiler_enable    On
php_value    xdebug.profiler_output_dir    "c:/php/modules/xdebug"

La première ligne active le profiler. La seconde ligne spécifie un répertoire dans lequel les différentes analyses seront placées. Si vous affichez maintenant à nouveau votre site, vous verrez un fichier apparaître dans le répertoire spécifier.

Vous pouvez également connaître le nom du fichier généré par le chargement de la page en utilisant la fonction xdebug_get_profiler_filename( )

Les fichiers générés par XDebug ne sont pas très parlant en mode texte. Heureusement, des programmes existent pour les lire et mieux comprendre ce qu'il se passe. Sous Windows (il existe des version Linux et Mac également), vous pouvez télécharger WinCacheGrind. Installez le puis ouvrez le fichier généré.

Vous aurez devant vous l'analyse dans un mode de lecture plus "humain". Sur votre gauche, l'arbre des appels réalisés par le chargement de votre page. Sur la droite, le détail des chargements du niveau sélectionné. Vous pouvez dupliquer sur un niveau pour ouvrir son enfant.

wcg_left.jpg

L'onglet "Line by Line", comme son nom l'indique, vous présente ligne par ligne les appels effectués. L'onglet "Overall" vous permet d'afficher de manière synthétisée la totalité des appels afin d'analyser de manière plus globale l'exécution de votre code.

wcg_line.jpg

Les colonnes "Self" indique le temps de chargement de l'occurrence seule, sans ses enfants. La colonne "Cum." affiche le temps d'exécution cumulé jusqu'à l'occurrence.

wcg_overall.jpg

Dans l'onglet "Overall", la colonne "Avg. Self" affiche le temps moyen d'exécution de l'occurrence et "Total Self" la somme du temps d'exécution de tous les appels à l'occurrence. La dernière colonne "Calls" affiche le nombre d'appels à l'occurrence.

Function Traces

Le profiler nous a permis d'identifier les goulets d'étranglement dans l'arbre d'exécution. Il est maintenant possible de dire quel partie de notre code prend plus de temps. Par contre, un autre problème rencontré lors de l'utilisation du framework Zend est sa consommation mémoire. Pour analyser cet autre donnée, nous allons utiliser la fonctionnalité Function Traces de XDebug.

Pour activer cette fonctionnalité, nous allons à nouveau modifier notre fichier .htaccess et ajouter les deux lignes suivantes :

php_flag    xdebug.auto_trace    On
php_flag    xdebug.show_mem_delta    On
php_value    xdebug.trace_output_dir     "c:/php/modules/xdebug"

La première ligne permet d'activer le log des appels avant l'exécution du script donc comprenant votre fichier prepend si vous en utilisez un. La seconde ligne permet l'affichage des deltas de la mémoire utilisée par chaque appel de fonction. La troisième ligne spécifie le répertoire dans lequel nous retrouveront notre log.

Toutefois il est possible de tracer qu'une petite partie de votre code en utilisant les fonctions xdebug_start_trace() et xdebug_stop_trace(). Vous pouvez par exemple insérer ces fonctions juste avant et après l'appelle à Zend_Controller_Front::dispatch().

Si nous retournons dans notre répertoire où sont créés les fichiers, nous découvrons un fichier .xt. Il est assez facile de l'ouvrir dans OpenOffice Calc par exemple pour avoir une meilleur visibilité et pouvoir ordonnancer les données.

Voici un exemple de trace réalisée avec XDebug :

TRACE START [2009-03-12 21:28:23]
    0.0010      74648   +74648   -> {main}() E:\Projects\zend_skeleton\www\index.php:0
    0.0010      76032    +1384     -> define() E:\Projects\zend_skeleton\www\index.php:15
    0.0011      76048      +16     -> define() E:\Projects\zend_skeleton\www\index.php:16
    0.0011      76064      +16     -> dirname() E:\Projects\zend_skeleton\www\index.php:19
    0.0011      76064       +0     -> dirname() E:\Projects\zend_skeleton\www\index.php:19
    0.0011      76064       +0     -> define() E:\Projects\zend_skeleton\www\index.php:19
    0.0012      76064       +0     -> define() E:\Projects\zend_skeleton\www\index.php:20
    0.0012      76088      +24     -> define() E:\Projects\zend_skeleton\www\index.php:23
    0.0012      76376     +288     -> get_include_path() E:\Projects\zend_skeleton\www\index.php:29
    0.0012      76480     +104     -> set_include_path() E:\Projects\zend_skeleton\www\index.php:30
    0.0019     130032   +53552     -> require_once(E:\Projects\zend_skeleton\library\Zend\Loader.php) E:\Projects\zend_skeleton\www\index.php:33
    0.0019     129640     -392     -> Zend_Loader::registerAutoload() E:\Projects\zend_skeleton\www\index.php:34
    0.0019     130280     +640       -> function_exists() E:\Projects\zend_skeleton\library\Zend\Loader.php:204
    0.0020     130360      +80       -> Zend_Loader::loadClass() E:\Projects\zend_skeleton\library\Zend\Loader.php:209
    0.0020     131816    +1456         -> class_exists() E:\Projects\zend_skeleton\library\Zend\Loader.php:54
    0.0020     131840      +24         -> interface_exists() E:\Projects\zend_skeleton\library\Zend\Loader.php:54
    0.0020     131840       +0         -> str_replace() E:\Projects\zend_skeleton\library\Zend\Loader.php:64
    0.0021     132008     +168         -> Zend_Loader::_securityCheck() E:\Projects\zend_skeleton\library\Zend\Loader.php:82
    0.0021     132224     +216           -> preg_match() E:\Projects\zend_skeleton\library\Zend\Loader.php:235

La première colonne est le temps d'exécution. La seconde est la mémoire utilisée cumulée et la troisième colonne représente le delta de mémoire utilisée.

Résultats

Après avoir analysé un code simple de Zend Framework, j'ai pu aisément constater où se trouve les goulets d'étranglement. Cette analyse fera toutefois l'objet d'un prochain article.