GNU/Linux >> Tutoriels Linux >  >> Linux

Profilage d'applications Linux

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 fils
  • Utilisation du processeur

Eh bien, pour collecter ce type d'informations sur votre processus, vous n'avez pas réellement 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 noyau Linux est supérieur à 2.6.32 ou OProfile s'il est plus ancien. Les deux programmes ne vous demandent pas d'instrumenter votre programme (comme Gprof l'exige). Cependant, afin d'obtenir correctement le graphe d'appels dans perf vous devez construire 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 un graphe d'appel ()

    perf record -g -p `pidof a.out`
    
  2. Pour 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 permis de lire /boot/System.map-2.6.32-279.el6.x86_64, donc j'ajoute généralement --kallsyms=/boot/System.map-2.6.32-279.el6.x86_64 quand faire un rapport de performance :

    perf report --stdio -g --kallsyms=/boot/System.map-2.6.32-279.el6.x86_64
    
    Ici, j'ai écrit quelques informations supplémentaires sur l'utilisation de Linux `perf` :

    Tout d'abord - c'est un 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 ne vous demandent pas d'instrumenter votre programme (comme Gprof l'exige). Cependant, afin d'obtenir correctement le graphique des appels dans perf vous devez construire votre programme avec -fno-omit-frame-pointer . Par exemple :g++ -fno-omit-frame-pointer -O2 main.cpp .

    Vous pouvez voir une analyse "live" 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 un graphe d'appel ()

    perf record -g -p `pidof a.out`
    
  2. Pour 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 ensuite simplement en lançant l'application de cette manière et en attendant qu'elle se ferme :

perf record ./a.out

Ceci est un exemple de profilage d'un programme de test.

Le programme de test est dans le fichier main.cpp (main.cpp est au bas de la réponse):

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 alors 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 :

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

Ensuite j'analyse 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'appels 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 voici le main.cpp fichier 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;
}

Citant Linus Torvalds lui-même :

N'utilisez pas gprof. Vous êtes beaucoup mieux vaut utiliser le nouvel outil Linux "perf".

Et plus tard...

Je peux à peu près garantir qu'une fois que vous aurez commencé à l'utiliser, vous n'utiliserez plus jamais gprof ou oprofile.

Voir Re :[PATCH] grep :ne pas faire de grep externe sur les entrées skip-worktree (2010-01-04)


Linux
  1. Linux - Commande pour mesurer les échecs Tlb sous Linux ?

  2. Limiter l'accès aux appels système pour une application Linux

  3. Comment signer une application Mac OS X sous Linux ?

  4. Obtenir la liste des applications de démarrage sous Linux

  5. Éviter le démontage des applications Linux en manque de mémoire

Comment envoyer des journaux d'application Linux à AWS CloudWatch

Terminal Velocity - Une application de prise de notes CLI pour Linux

Comment activer l'option "Modifier" dans l'application Shutter sous Linux

Profitez de Twitch sur Linux avec l'application GNOME Twitch

Commande Linux Perf

Comment installer et configurer Perf dans les distributions Linux