web-dev-qa-db-fra.com

Profilage d'application Linux

J'ai besoin d'un moyen d'enregistrer les performances d'une application sur une machine Linux. Je n'aurai pas d'IDE.

Idéalement, j'ai besoin d'une application qui s'attachera à un processus et enregistrera des instantanés périodiques de: utilisation de la mémoire nombre de threads utilisation du processeur

Des idées?

36
MalcomTucker

Si vous cherchez des choses à faire pour accélérer éventuellement le programme, vous avez besoin de stackshots . Une façon simple de le faire est d'utiliser l'utilitaire pstack , ou lsstack si vous pouvez l'obtenir.

Vous pouvez faire mieux que gprof . Si vous souhaitez utiliser un outil de profilage officiel, vous voulez quelque chose qui échantillonne la pile d'appels sur l'heure de l'horloge murale et présente le coût au niveau de la ligne, comme Oprofile ou RotateRight/Zoom.

7
Mike Dunlavey

Idéalement, j'ai besoin d'une application qui s'attachera à un processus et enregistrera des instantanés périodiques de: utilisation de la mémoire nombre de threads utilisation du processeur

Eh bien, pour collecter ce type d'informations sur votre processus, vous n'avez pas besoin d'un profileur sous Linux.
1) Vous pouvez utiliser top en mode batch. Il s'exécute en mode batch jusqu'à ce qu'il soit tué ou jusqu'à ce que N itérations soient effectuées:

top -b -p `pidof a.out`

ou

top -b -p `pidof a.out` -n 100

et vous obtiendrez ceci:

$ top -b -p `pidof a.out`
top - 10:31:50 up 12 days, 19:08,  5 users,  load average: 0.02, 0.01, 0.02
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  16330584k total,  2335024k used, 13995560k free,   241348k buffers
Swap:  4194296k total,        0k used,  4194296k free,  1631880k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
24402 SK        20   0 98.7m 1056  860 S 43.9  0.0   0:11.87 a.out


top - 10:31:53 up 12 days, 19:08,  5 users,  load average: 0.02, 0.01, 0.02
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.9%us,  3.7%sy,  0.0%ni, 95.5%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  16330584k total,  2335148k used, 13995436k free,   241348k buffers
Swap:  4194296k total,        0k used,  4194296k free,  1631880k cached

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
24402 SK      20   0 98.7m 1072  860 S 19.0  0.0   0:12.44 a.out

2) Vous pouvez utiliser ps (par exemple dans un script Shell)

ps --format pid,pcpu,cputime,etime,size,vsz,cmd -p `pidof a.out`

J'ai besoin d'un moyen d'enregistrer les performances d'une application sur une machine Linux

Pour ce faire, vous devez utiliser perf si votre Linux Kernal est supérieur à 2.6.32 ou Oprofile s'il est plus ancien. Les deux programmes ne nécessitent pas de vous pour instruire votre programme (comme gporf requiert). Cependant, pour pouvoir appeler correctement le graphique dans perf, vous devez créer votre programme avec -fno-omit-frame-pointer. Par exemple: g++ -fno-omit-frame-pointer -O2 main.cpp.

Comme pour Linux perf:

1) Pour enregistrer les données de performances:

perf record -p `pidof a.out`

ou pour enregistrer pendant 10 secondes:

perf record -p `pidof a.out` sleep 10

ou pour enregistrer avec le graphique d'appel ()

perf record -g -p `pidof a.out` 

2) Analyser les données enregistrées

perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g

Sur RHEL 6.3, il est autorisé de lire /boot/System.map-2.6.32-279.el6.x86_64 donc j'ajoute généralement --kallsymes =/boot/System.map-2.6.32-279.el6.x86_64 quand faire rapport de performance:

perf report --stdio -g --kallsyms=/boot/System.map-2.6.32-279.el6.x86_64

Tout d'abord - c'est tutoriel sur le profilage Linux avec perf

Vous pouvez utiliser perf si votre noyau Linux est supérieur à 2.6.32 ou oprofile s'il est plus ancien. Les deux programmes n'ont pas besoin de vous pour instrumenter votre programme (comme le requiert gprof). Cependant, pour obtenir correctement le graphique des appels dans perf, vous devez créer votre programme avec -fno-omit-frame-pointer. Par exemple: g++ -fno-omit-frame-pointer -O2 main.cpp. Vous pouvez voir l'analyse "en direct" de votre application avec perf top:

Sudo perf top -p `pidof a.out` -K

Ou vous pouvez enregistrer les données de performances d'une application en cours d'exécution et les analyser ensuite: 1) Pour enregistrer les données de performances:

perf record -p `pidof a.out`

ou pour enregistrer pendant 10 secondes:

perf record -p `pidof a.out` sleep 10

ou pour enregistrer avec le graphique d'appel ()

perf record -g -p `pidof a.out` 

2) Analyser les données enregistrées

perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g

Ou vous pouvez enregistrer les données de performance d'une application et les analyser après cela simplement en lançant l'application de cette manière et en attendant qu'elle se termine:

perf record ./a.out

Voici un exemple de profilage d'un programme de test Le programme de test se trouve dans le fichier main.cpp (je mettrai main.cpp en bas du message): je le compile de cette façon:

g++ -m64 -fno-omit-frame-pointer -g main.cpp -L.  -ltcmalloc_minimal -o my_test

J'utilise libmalloc_minimial.so car il est compilé avec -fno-omit-frame-pointer tandis que libc malloc semble être compilé sans cette option. Ensuite, je lance mon programme de test

./my_test 100000000 

Ensuite, j'enregistre les données de performance d'un processus en cours d'exécution:

perf record -g  -p `pidof my_test` -o ./my_test.perf.data sleep 30

J'analyse ensuite la charge par module:

perf report --stdio -g none --sort comm,dso -i ./my_test.perf.data

# Overhead  Command                 Shared Object
# ........  .......  ............................
#
    70.06%  my_test  my_test
    28.33%  my_test  libtcmalloc_minimal.so.0.1.0
     1.61%  my_test  [kernel.kallsyms]

Ensuite, la charge par fonction est analysée:

perf report --stdio -g none -i ./my_test.perf.data | c++filt

# Overhead  Command                 Shared Object                       Symbol
# ........  .......  ............................  ...........................
#
    29.30%  my_test  my_test                       [.] f2(long)
    29.14%  my_test  my_test                       [.] f1(long)
    15.17%  my_test  libtcmalloc_minimal.so.0.1.0  [.] operator new(unsigned long)
    13.16%  my_test  libtcmalloc_minimal.so.0.1.0  [.] operator delete(void*)
     9.44%  my_test  my_test                       [.] process_request(long)
     1.01%  my_test  my_test                       [.] operator delete(void*)@plt
     0.97%  my_test  my_test                       [.] operator new(unsigned long)@plt
     0.20%  my_test  my_test                       [.] main
     0.19%  my_test  [kernel.kallsyms]             [k] apic_timer_interrupt
     0.16%  my_test  [kernel.kallsyms]             [k] _spin_lock
     0.13%  my_test  [kernel.kallsyms]             [k] native_write_msr_safe

     and so on ...

Ensuite, les chaînes d'appel sont analysées:

perf report --stdio -g graph -i ./my_test.perf.data | c++filt

# Overhead  Command                 Shared Object                       Symbol
# ........  .......  ............................  ...........................
#
    29.30%  my_test  my_test                       [.] f2(long)
            |
            --- f2(long)
               |
                --29.01%-- process_request(long)
                          main
                          __libc_start_main

    29.14%  my_test  my_test                       [.] f1(long)
            |
            --- f1(long)
               |
               |--15.05%-- process_request(long)
               |          main
               |          __libc_start_main
               |
                --13.79%-- f2(long)
                          process_request(long)
                          main
                          __libc_start_main

    15.17%  my_test  libtcmalloc_minimal.so.0.1.0  [.] operator new(unsigned long)
            |
            --- operator new(unsigned long)
               |
               |--11.44%-- f1(long)
               |          |
               |          |--5.75%-- process_request(long)
               |          |          main
               |          |          __libc_start_main
               |          |
               |           --5.69%-- f2(long)
               |                     process_request(long)
               |                     main
               |                     __libc_start_main
               |
                --3.01%-- process_request(long)
                          main
                          __libc_start_main

    13.16%  my_test  libtcmalloc_minimal.so.0.1.0  [.] operator delete(void*)
            |
            --- operator delete(void*)
               |
               |--9.13%-- f1(long)
               |          |
               |          |--4.63%-- f2(long)
               |          |          process_request(long)
               |          |          main
               |          |          __libc_start_main
               |          |
               |           --4.51%-- process_request(long)
               |                     main
               |                     __libc_start_main
               |
               |--3.05%-- process_request(long)
               |          main
               |          __libc_start_main
               |
                --0.80%-- f2(long)
                          process_request(long)
                          main
                          __libc_start_main

     9.44%  my_test  my_test                       [.] process_request(long)
            |
            --- process_request(long)
               |
                --9.39%-- main
                          __libc_start_main

     1.01%  my_test  my_test                       [.] operator delete(void*)@plt
            |
            --- operator delete(void*)@plt

     0.97%  my_test  my_test                       [.] operator new(unsigned long)@plt
            |
            --- operator new(unsigned long)@plt

     0.20%  my_test  my_test                       [.] main
     0.19%  my_test  [kernel.kallsyms]             [k] apic_timer_interrupt
     0.16%  my_test  [kernel.kallsyms]             [k] _spin_lock
     and so on ...

Donc, à ce stade, vous savez où votre programme passe du temps. Et c'est main.cpp pour le test:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

time_t f1(time_t time_value)
{
  for (int j =0; j < 10; ++j) {
    ++time_value;
    if (j%5 == 0) {
      double *p = new double;
      delete p;
    }
  }
  return time_value;
}

time_t f2(time_t time_value)
{
  for (int j =0; j < 40; ++j) {
    ++time_value;
  }
  time_value=f1(time_value);
  return time_value;
}

time_t process_request(time_t time_value)
{

  for (int j =0; j < 10; ++j) {
    int *p = new int;
    delete p;
    for (int m =0; m < 10; ++m) {
      ++time_value;
    }
  }
  for (int i =0; i < 10; ++i) {
    time_value=f1(time_value);
    time_value=f2(time_value);
  }
  return time_value;
}

int main(int argc, char* argv2[])
{
  int number_loops = argc > 1 ? atoi(argv2[1]) : 1;
  time_t time_value = time(0);
  printf("number loops %d\n", number_loops);
  printf("time_value: %d\n", time_value );

  for (int i =0; i < number_loops; ++i) {
    time_value = process_request(time_value);
  }
  printf("time_value: %ld\n", time_value );
  return 0;
}
48
user184968

Citant Linus Torvalds lui-même:

"Don't use gprof. You're _much_ better off using the newish Linux 'perf' tool."

et ensuite ...

"I can pretty much guarantee that once you start using it, you'll never use gprof or oprofile again."

Voir: http://marc.info/?l=git&m=126262088816902&w=2

Bonne chance!

22
holygeek

Vous pouvez utiliser valgrind . Il enregistre les données dans un fichier que vous pouvez analyser plus tard en utilisant une interface graphique appropriée comme KCacheGrind

Un exemple d'utilisation serait:

valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes your_program

Cela va générer un fichier appelé callgrind.out.xxx où xxx est le pid du programme

edit : contrairement à gprof valgrind fonctionne avec de nombreux langages différents dont Java avec quelques limitations .

3
f4.

Avez-vous étudié le gprof? Vous devez compiler le code avec l'option -pg, qui instrumente le code. Après cela, vous pouvez exécuter le prorgam et utiliser gprof pour voir les résultats.

2
Richard Pennington

Vous pouvez également essayer cpuprofiler.com . Il obtient les informations que vous obtiendriez normalement à partir de la commande supérieure, et les données d'utilisation du processeur peuvent même être consultées à distance à partir d'un navigateur Web.

1
rosewater