Documentation XHProf (Brouillon)

Sommaire
  1. Introduction
  2. Présentation
  3. Installer l’extension XHProf
  4. Profiler avec XHProf
  5. Définir un environnement graphique pour XHProf
  6. Notes sur l’utilisation d’XHProf en production
  7. Mode aléatoire et léger
  8. Fonctionnalités supplémentaires
  9. Dépendences
  10. Remerciements
  1. Introduction

    XHProf est un outil de profilage hiérarchique pour PHP. Il relève les appels au niveau des fonctions et mesure inclusivement et exclusivement des métriques telles que le temps écoulé la charge CPU ou l’usage de la mémoire. Un profil de fonction peut être divisé selon ses appelants, ou ses appelés. Le composant qui extrait les données brutes est écrit en C et implémenté telle une extension PHP Zend. xhprof. XHProf a une interface utilisateur simple en HTML, (écrite en PHP). L’interface permet de visualiser et de partager facilement le résultat des profilages dans un navigateur. Un rendu sous forme de graphique est également disponible.

    Les rapports fournis par XHProf permettent souvent de mieux comprendre la structure du code qui est éxécuté. Le rendu hiérarchique des rapports permet par exemple de déterminer quelle chaîne d’appels mène à une fonction particulière.

    XHProf propose également de comparer deux runs (résultat de profilage) pour analyser les différences ou aggréger les résultat de multiples runs afin d’analyser des données consolidées. Les comparaisons et aggrégations de données permettent surtout de visualiser des données plates

    XHProf est un outil de profilage très léger. Pendant la phase de collecte Il garde une trace du nombre d’appels et des métriques inclusives viualisables en courbes dans le graphe d’appels dynamique d’un programme. Il calcule les métriques exclusives dans la phase de rapport. XHProf supporte les fonctions recursives en détectant les cycles dans la pile d’appels dès la capture des données et en utilisant un nom unique pour l’invocation principale.

    La nature légère d’XHProf, ses performances et ses possibilités de consolidations de données en font un outil taillé pour les environnements de production [Voir les notes sur l’usage en production.]

    Développé à l’origine par Facebook, XHProf est maintenant open source depuis mars 2009.

  2. Présentation

    XHProf offre:

    Terminologie

    1. Temps inclusive (ou temps du sous-ensemble): Inclus le temps passé dans la fonction et celui passé dans les fonctions descendantes (filles).
    2. Temps exclusive (ou temps "propre"): Mesure le temps passé dans la fonction elle-même et n’inclus pas le temps passé dans les fonctions descendantes.
    3. Wall Time: Temps passé ou temps ressenti.
    4. CPU Time: Charge CPU sur les process utilisateur + charge CPU sur les process noyaux

    Convention de nommage pour les fonctions spéciales

    1. main(): Une fonction fictive qui est à la racine de la pile d’appel.

    2. load::<filename> et run_init::<filename>:

      XHProf trace les appels include/require comme des appels de fonction.

      Par exemple, une inclusion include "lib/common.php"; va donner deux entrées pour XHProf :

      • load::lib/common.php - Cela représente le travail fait par l’interpréteur pour charger et compiler le fichier. [Note: Si vous utilisez un cache d’opcode PHP comme APC, alors la compilation intervient uniquement si le cahce est manquant dans APC.]
      • run_init::lib/common.php - Cela répresente le code exécuté au niveau du fichier, soit le résultat de l’inclusion.

    3. foo@<n>: Implique un appel récursif de foo(), ou <n> représente le niveau de récursion. Cette récursion peut être directe comme foo() --> foo()), ou indirecte comme foo() --> goo() --> foo().

    Limitations

    Un vrai profileur hiérarchique trace toute la pile d’appel pour chaque donnée., et est capables de répondre aux questions comme : Quel était le coût du 3e appel de foo(), ou quel était le coût de bar() quand il était appelé par a()->b()->bar()?

    XHProf garde une trace d’un seul niveau dans le contexte de l’appel et est seulement capable de répondre aux questions à propos d’une fonction qui regarde un niveau en dessus ou en dessous. Il appraît que dans la majorité des cas c’est bien suffisant.

    Pour mieux comprendre, regaredez l’exemple suivant :

    Vous avez:
     1 appel de a() --> c()
     1 appel de b() --> c()
     50 appels de c() --> d()
    

    Quand XHProf peut vous dire que d() a été appelé par c() 50 fois, il ne peut pas vous dire combien d’appels dont dus à a() ou b(). [On peut imaginer que c’est peut être 25 pour a() et 25 pour b(), mais ce n’est pas nécéssairement vrai.]

    De toutes façons en pratique ce n’est pas vraiment une limitation.

  3. Installer l’extension XHProf

    L’extension se trouve dans le sous-répertoire "extension/".

    Les étapes suivantes sont prévues pour un environnement Linux/Unix.

    % cd <repertoire_source_xhprof>/extension/
    % phpize
    % ./configure --with-php-config=<chemin vers php-config>
    % make
    % make install
    % make test
    

    php.ini file: Vous pouvez mettre à jour votre fichier php.ini file afin qu’il charge automatiquement votre extension en ajoutant le code suivant :

    [xhprof]
    extension=xhprof.so
    ;
    ; répertoire utilisé par l’implémentation par défaut de l’interface iXHProfRuns
    ; (nommée, XHProfRuns_Default class) pour stocker les runs XHProf.
    ;
    xhprof.output_dir=<repertoire_pour_stocker_les_runs_xhprof>
    
  4. Profiler avec XHProf

    Test de génération de donées brutes avec l’exemple simple d’un programme tel que :

    Lancez ce programme :

    % php -dextension=xhprof.so foo.php
    

    Vous devez avoir un résultat tel que :

    Array
    (
        [foo==>bar] => Array
            (
                [ct] => 2         # 2 appels de bar() depuis foo()
                [wt] => 27        # temps inclusif dans bar() quand il est appelé par foo()
            )
    
        [foo==>strlen] => Array
            (
                [ct] => 2
                [wt] => 2
            )
    
        [bar==>bar@1] => Array    # un appelrécursif à bar()
            (
                [ct] => 1
                [wt] => 2
            )
    
        [main()==>foo] => Array
            (
                [ct] => 1
                [wt] => 74
            )
    
        [main()==>xhprof_disable] => Array
            (
                [ct] => 1
                [wt] => 0
            )
    
        [main()] => Array         # fausse fonction représentant la racine
            (
                [ct] => 1
                [wt] => 83
            )
    
    )
    

    Note: Les données brutes contienent uniquement les métriques inclusives. Par exemple les données brutes du tableau de données temporelles represente les temps inclusifs en microsecondes. Les temps exclusifs sont calculés pour chaque fonction lors de la phase d’analyse et de rapport.

    Note: Par défault suelemnt le nombre d’appel & et le temps passé sont profilés. Vous pouvez aussi profilerle temps CPU et/ou la charge mémoire. Remplacez,

    dans le programme précédent avec, par exemple :

    Vous aurez en sortie :

    Array
    (
        [foo==>bar] => Array
            (
                [ct] => 2        # nombre d’appel à bar() depuis foo()
                [wt] => 37       # tempas passé dans bar() quand appel de foo()
                [cpu] => 0       # temps cpu time dans bar() quand appel de foo()
                [mu] => 2208     # changement dans l’usage de la mémoire par PHP dans bar() quand appel de foo()
                [pmu] => 0       # changement dans l’usage de pic mémoire par PHP pour bar() quand appel de foo()
            )
    
        [foo==>strlen] => Array
            (
                [ct] => 2
                [wt] => 3
                [cpu] => 0
                [mu] => 624
                [pmu] => 0
            )
    
        [bar==>bar@1] => Array
            (
                [ct] => 1
                [wt] => 2
                [cpu] => 0
                [mu] => 856
                [pmu] => 0
            )
    
        [main()==>foo] => Array
            (
                [ct] => 1
                [wt] => 104
                [cpu] => 0
                [mu] => 4168
                [pmu] => 0
            )
    
        [main()==>xhprof_disable] => Array
            (
                [ct] => 1
                [wt] => 1
                [cpu] => 0
                [mu] => 344
                [pmu] => 0
            )
    
        [main()] => Array
            (
                [ct] => 1
                [wt] => 139
                [cpu] => 0
                [mu] => 5936
                [pmu] => 0
            )
    
    )
    

    Éviter les fonctions natives lors du profilage

    Par défault les fonctions natives de PHP (comme strlen) sont profilées. Si vous ne voulez pas les profiler (pour simplifier le résultat et la taille des données brutes générées), Vous pouvez utiliser le drapeau XHPROF_FLAGS_NO_BUILTINS comme dans l’exemple ci-dessous :

    Ignorer des fonctions spécfiques lors du profilage (0.9.2 ou plus récent)

    À partir de la version 0.9.2 d’XHProf, vous pouvez spécifier une liste de fonctions à ignorer pendant le profilage. Cela vous permet de ne pas prendre en compte par exemple des fonctions utilisées pour des appels indirects comme call_user_func et call_user_func_array. Ces fonctions intermédiaires compliquent inutilement la hirarchie des appels et rendent plus ardue l’interprétation des rapports en brouillant les relations parent/enfant.

    Pour spécifier cette liste de fonctions à ignorer durant le profilage, il suffit d’utiliser le second paramètre (optionnel) de xhprof_enable. Par exemple,

      
      // temps passé en profilage; ignore les appels de call_user_func* pendant le profilage
      xhprof_enable(0,
                   array('ignored_functions' =>  array('call_user_func',
                                                       'call_user_func_array')));
      
      or,
      
      // tempas pasé en profilage + profilage mémoire; ignore call_user_func* durant le profilage
      xhprof_enable(XHPROF_FLAGS_MEMORY,
                    array('ignored_functions' =>  array('call_user_func',
                                                        'call_user_func_array')));
      
      
  5. Définir un environnement graphique pour XHProf

    1. Structure de la source PHP

      l’interface graphique d’XHProf est implémentée en PHP. Le code est divisé en deux sous-répertoires, xhprof_html/ and xhprof_lib/.

      Le répertoire xhprof_html contient les 3 pages PHP principales.

      • index.php: Pour visualiser un run ou un différentiel entre deux runs.
      • callgraph.php: Pour visualiser sous la forme de graphique avec un rendu en image.
      • typeahead.php: Utilisé implicitement pour les fonctions de gestion de pile sur un rapport XHProf.

      Le répertoire xhprof_lib contient le code pour l’analyse et l’affichage. (calcul sur les informations de profilage, calcul des différentiels, aggrégation de données, etc.).

    2. Configuration du server web : Vous devez vous assurer que le répertoire xhprof_html/ est accessible depuis le serveur web, et qu’il est configuré pour éxécuter des scripts PHP.

    3. Gérer les runs XHProf

      Les clients web ont une certaine souplesse dans la manière de sauvegarder les données brutes fournies par XHProf. XHProf expose une interface utilisateur nommée iXHProfRuns (voir xhprof_lib/utils/xhprof_runs.php) que les clients peuvent implémenter. Cela permet aux clients de préciser comment afficher les donées des runs.

      L’interface utilisateur d’XHProf fournit une implementation par défaut nommée, "XHProfRuns_Default" (aussi dans xhprof_lib/utils/xhprof_runs.php). L’implementation par d"faut stocke les runs dans le répertoire définit par le paramètre INI : xhprof.output_dir.

      Un run XHProf doit être définit de manière unique par un espace de nom et un identifiant de run.

      a) Sauver les données XHProf de façon persistente :

      Soit si vous utilisez l’interface par défaut, XHProfRuns_Default qui implémente iXHProfRuns, Un run XHProf sauvegardé ressemble au code suivant :

      // début du profilage
      xhprof_enable();
      
      // lancement du programme
      ...
      
      // fin du profilage
      $xhprof_data = xhprof_disable();
      
      //
      // Sauvegarde du run XHProf
      // en utilisant l’implementation par défaut de iXHProfRuns.
      //
      include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";
      include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";
      
      $xhprof_runs = new XHProfRuns_Default();
      
      // sauvegarde du run avec l’espace de nom "xhprof_foo".
      //
      // **NOTE**:
      // par défault save_run() va automatiquement générer un identifiant de run
      // unique. [Vous pouvez surcharger cette donnée en passant l’identifiant en paramètre optionnel 
      // à la méthode save_run().]
      //
      $run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_foo");
      
      echo "---------------\n".
           "En partant du principe que vous avez parametré l’interface utilisateur http \n".
           "XHProf, vous pouvez visualiser les runs avec l’adresse : \n".
           "http://<adresse-interface-utilisateur-xhprof>/index.php?run=$run_id&source=xhprof_foo\n".
           "---------------\n";
      
      

      La suite permet de sauvegarder le run sous forme d’un fichier dans le répertoire spécifié par le paramètre ini xhprof.output_dir. Le nom du fichier doit être de la forme 49bafaa3a3f66.xhprof_foo; Les deux parties du nom sont formées par l’identifiant du run ("49bafaa3a3f66") et l’espace de nom ("xhprof_foo"). [Si vous souhaitez créer un identifiant de run vous-même (comme une sequence de base de données, ou un timestamp), vous pouvez explicitementpasser l’identifiant du run à la méthode save_run.

      b) En utilisant votre propre implementation d’iXHProfRuns

      Si vous décidez de stocker différement les runs XHProf (soit dans un format compressé, dans une base de données, etc.), vous aurez besoin d’implémenter une classe qui implémente l’interface iXHProfRuns().

      Vous devrez également modifier les 3 pages PHP d’entrée (index.php, callgraph.php, typeahead.php) dans le répertoire "xhprof_html/" pour utiliser la nouvelle interface au lieu de celle par défaut (XHProfRuns_Default), changez cette ligne dans les 3 fichier.

      $xhprof_runs_impl = new XHProfRuns_Default();
      

      Vous aurez aussi besoin d’inclure le fichier qui implémente votre classe dans les fichiers cités.

    4. Acceéder aux runs depuis l’interface utilisateur

      a) Voir un rapport simple

      Pour voir un rapport avec l’identifiant <run_id> et l’espace de nom <namespace> utilisez une url de la forme :

      http://<adresse-interface-utilisateur-xhprof>/index.php?run=<run_id>&source=<namespace>

      Par example,

      http://<adresse-interface-utilisateur-xhprof>/index.php?run=49bafaa3a3f66&source=xhprof_foo

      b) Voir un rapport différentiel

      Pour voir un rapport avec les identifiants <run_id1> et <run_id2> et l’espace de nom <namespace> utilisez une url de la forme :

      http://<adresse-interface-utilisateur-xhprof>/index.php?run1=<run_id1>&run2=<run_id2>&source=<namespace>

      c) Voir un rapport d’aggrégation

      Vous pouvez aussi spécifier un ensemble de runspour lesquels vous souhaitez un rapport d’aggrégation.

      Si vous avez trois runs XHProf avec les identifiants 1, 2 & 3 pour l’espace de noms "benchmark". Pour voir l’aggrégation de ces trois runs :

        http://<adresse-interface-utilisateur-xhprof>/index.php?run=1,2,3&source=benchmark

      Aggrégations pondérées: En supposant que les trois runs correspondent à trois types de programmes p1.php, p2.php and p3.php qui occupent chacun respectivement 20%, 30% et 50%. Pour voir un rapport d’aggrégation pondéré par les poids des runs :

        http://<adresse-interface-utilisateur-xhprof>/index.php?run=1,2,3&wts=20,30,50&source=benchmark

  6. Notes sur l’utilisation d’XHProf en production

    Quelques observations qui peuvent faire varier votre expérience :

  7. Mode d’échantillonage léger

    L’extension XHProf propose aussi un mode très léger d’échantillonage. L’intervalle est de 0,1 seconde. Les échantillons enregistrent l’ensemble des données. Ce mode peut être très utile pour avoir un impact le plus négligeable possible, et permettre Le mode sample peut être utile si vous désirez un moyen avec peu de dépassement de faire de la surveillance de performances et des diagnostics.

    Les très pertinentes fonctions utilisées par l’extension pour utiliser le mode d’échantillonage sont xhprof_sample_enable() et xhprof_sample_disable().

    [TBD: Documentation plus détaillée pour le mode d’échantillonage.]

  8. Fonctionnalités supplémentaires

  9. Le fichier XHProf_lib/utils/xhprof_lib.php contient des librairies de fonctions additionellesqui peuvent être utilisées pour manipuler et aggréger les runs XHProf.

    Par exemple:

  10. Dependances

  11. Remerciements

    Le rendu HTML et l’interface de navigation pour consulter les résultat du profilage sont inspirés par un outil similaire qui existe pour les procédures stockées PL/SQL d’Oracle. Mais c’est là que la comparaison s’arrête; Le fonctionnement interne du profileur étant assez différent [NDT : Merci à Rudy Rigot (@rudyrigot) pour sa relecture attentive ]