web-dev-qa-db-fra.com

linux perf: comment interpréter et trouver des hotspots

J'ai essayé l'utilitaire linux ' perf aujourd'hui et j'ai du mal à interpréter ses résultats. Je suis habitué au callgrind de valgrind qui est bien sûr une approche totalement différente de la méthode de perf basée sur l'échantillonnage.

Ce que j'ai fait:

perf record -g -p $(pidof someapp)
perf report -g -n

Maintenant, je vois quelque chose comme ça:

 + 16,92% kdevelop libsqlite3.so.0.8.6 [.] 0x3fe57 ↑ 
 + 10,61% kdevelop libQtGui.so.4.7.3 [.] 0x81e344 ▮ 
 + 7,09% kdevelop libc-2.14.so [.] 0x85804 ▒ 
 + 4,96% kdevelop libQtGui.so.4.7.3 [.] 0x265b69 ▒ 
 + 3,50% kdevelop libQtCore.so.4.7.3 [. ] 0x18608d ▒ 
 + 2,68% kdevelop libc-2.14.so [.] memcpy ▒ 
 + 1,15% kdevelop [kernel.kallsymes] [k] copy_user_generic_string ▒ 
 + 0,90% kdevelop libQtGui.so.4.7.3 [.] QTransform :: translate (double, double) ▒ 
 + 0,88% kdevelop libc-2.14.so [.] __libc_malloc ▒ 
 + 0,85% kdevelop libc-2.14.so [.] memcpy 
 ... 

Ok, ces fonctions peuvent être lentes, mais comment savoir d'où elles sont appelées? Comme tous ces hotspots se trouvent dans des bibliothèques externes, je ne vois aucun moyen d'optimiser mon code.

Fondamentalement, je recherche une sorte de graphique d'appel annoté avec le coût cumulé, où mes fonctions ont un coût d'échantillonnage inclus plus élevé que les fonctions de bibliothèque que j'appelle.

Est-ce possible avec perf? Si c'est le cas, comment?

Remarque: J'ai découvert que "E" déballe le callgraph et donne un peu plus d'informations. Mais le callgraph n'est souvent pas assez profond et/ou se termine de manière aléatoire sans donner d'informations sur le montant des informations dépensées. Exemple:

 - 10,26% kate libkatepartinterfaces.so.4.6.0 [.] Kate :: TextLoader :: readLine (int & ... 
 Kate :: TextLoader :: readLine (int &, int &) 
 Kate :: TextBuffer :: load (QString const &, bool &, bool &) 
 KateBuffer :: openFile (QString const &) 
 KateDocument :: openFile () 
 0x7fe37a81121c 

Serait-ce un problème que j'exécute sur 64 bits? Voir aussi: http://lists.fedoraproject.org/pipermail/devel/2010-November/144952.html (je n'utilise pas Fedora mais semble s'appliquer à tous les systèmes 64 bits).

43
milianw

Vous devriez essayer le hotspot: https://www.kdab.com/hotspot-gui-linux-perf-profiler/

Il est disponible sur github: https://github.com/KDAB/hotspot

Il est par exemple capable de générer des graphiques de flammes pour vous.

flamegraph

15
Martin Gerhardy

Avec Linux 3.7 perf est enfin capable d'utiliser les informations DWARF pour générer le callgraph:

perf record --call-graph dwarf -- yourapp
perf report -g graph --no-children

Bien, mais l'interface graphique des malédictions est horrible par rapport à VTune, KCacheGrind ou similaire ... Je recommande d'essayer FlameGraphs à la place, ce qui est une visualisation assez soignée: http://www.brendangregg.com/FlameGraphs/cpuflamegraphs .html

Remarque: à l'étape du rapport, -g graph simplifie la sortie des résultats pour comprendre les pourcentages "par rapport au total", plutôt que les nombres "par rapport au parent". --no-children affichera uniquement le coût personnel, plutôt que le coût inclusif - une fonctionnalité que je trouve également inestimable.

Si vous avez un nouveau processeur perf et Intel, essayez également le dérouleur LBR, qui a de bien meilleures performances et produit des fichiers de résultats beaucoup plus petits:

perf record --call-graph lbr -- yourapp

L'inconvénient ici est que la profondeur de la pile d'appels est plus limitée par rapport à la configuration de dérouleur DWARF par défaut.

39
milianw

Ok, ces fonctions peuvent être lentes, mais comment savoir d'où elles sont appelées? Comme tous ces hotspots se trouvent dans des bibliothèques externes, je ne vois aucun moyen d'optimiser mon code.

Êtes-vous sûr que votre application someapp est construite avec l'option gcc -fno-omit-frame-pointer (et éventuellement ses bibliothèques dépendantes)? Quelque chose comme ça:

g++ -m64 -fno-omit-frame-pointer -g main.cpp
18
Sergei Kurenkov

Vous pouvez obtenir un rapport au niveau source très détaillé avec perf annotate, voir Analyse au niveau source avec annotation de perf . Cela ressemblera à ceci (volé sans vergogne sur le site Web):

------------------------------------------------
 Percent |   Source code & Disassembly of noploop
------------------------------------------------
         :
         :
         :
         :   Disassembly of section .text:
         :
         :   08048484 <main>:
         :   #include <string.h>
         :   #include <unistd.h>
         :   #include <sys/time.h>
         :
         :   int main(int argc, char **argv)
         :   {
    0.00 :    8048484:       55                      Push   %ebp
    0.00 :    8048485:       89 e5                   mov    %esp,%ebp
[...]
    0.00 :    8048530:       eb 0b                   jmp    804853d <main+0xb9>
         :                           count++;
   14.22 :    8048532:       8b 44 24 2c             mov    0x2c(%esp),%eax
    0.00 :    8048536:       83 c0 01                add    $0x1,%eax
   14.78 :    8048539:       89 44 24 2c             mov    %eax,0x2c(%esp)
         :           memcpy(&tv_end, &tv_now, sizeof(tv_now));
         :           tv_end.tv_sec += strtol(argv[1], NULL, 10);
         :           while (tv_now.tv_sec < tv_end.tv_sec ||
         :                  tv_now.tv_usec < tv_end.tv_usec) {
         :                   count = 0;
         :                   while (count < 100000000UL)
   14.78 :    804853d:       8b 44 24 2c             mov    0x2c(%esp),%eax
   56.23 :    8048541:       3d ff e0 f5 05          cmp    $0x5f5e0ff,%eax
    0.00 :    8048546:       76 ea                   jbe    8048532 <main+0xae>
[...]

N'oubliez pas de passer le -fno-omit-frame-pointer et le -ggdb drapeaux lorsque vous compilez votre code.

11
Ali

À moins que votre programme n'ait très peu de fonctions et n'appelle presque jamais une fonction système ou des E/S, les profileurs qui échantillonnent le compteur de programme ne vous en diront pas beaucoup, comme vous le découvrez. En fait, le profileur bien connu gprof a été créé spécifiquement pour essayer de remédier à l'inutilité du profilage auto-temporel (pas qu'il ait réussi).

Ce qui fonctionne réellement est quelque chose qui échantillonne la pile d'appels (découvrant ainsi d'où viennent les appels), à horloge murale heure (incluant ainsi I/O time), et indiquez par ligne ou par instruction (identifiant ainsi les appels de fonction que vous devez étudier, pas seulement les fonctions dans lesquelles ils vivent).

De plus, la statistique que vous devez rechercher est pour cent du temps sur la pile, pas le nombre d'appels, pas le temps de fonction inclusif moyen. Surtout pas "self time". Si une instruction d'appel (ou une instruction non-appel) est sur la pile 38% du temps, alors si vous pouviez vous en débarrasser, combien vous sauvegardez? 38%! Assez simple, non?

Un exemple d'un tel profileur est Zoom .

Il y a plus de problèmes à comprendre à ce sujet.

Ajouté: @caf m'a fait rechercher les informations sur perf, et puisque vous avez inclus l'argument de ligne de commande -g il collecte des échantillons de pile. Ensuite, vous pouvez obtenir un rapport call-tree . Ensuite, si vous vous assurez d'échantillonner sur l'heure de l'horloge murale (vous obtenez donc le temps d'attente ainsi que le temps processeur), vous avez presque ce dont vous avez besoin.

5
Mike Dunlavey