GNU/Linux >> Linux Esercitazione >  >> Linux

Profilazione delle applicazioni Linux

Idealmente, ho bisogno di un'applicazione che si colleghi a un processo e registri istantanee periodiche di:

  • utilizzo della memoria
  • numero di thread
  • Utilizzo della CPU

Bene, per raccogliere questo tipo di informazioni sul tuo processo, in realtà non hai bisogno di un profiler su Linux.

  1. Puoi usare top in modalità batch. Funziona in modalità batch finché non viene ucciso o finché non vengono eseguite N iterazioni:

    top -b -p `pidof a.out`
    

    o

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

    e otterrai questo:

    $ 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. Puoi usare ps (ad esempio in uno script di shell)

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

    Ho bisogno di un mezzo per registrare le prestazioni di un'applicazione su una macchina Linux

    Per fare questo devi usare perf se il tuo kernel Linux è superiore a 2.6.32 o OProfile se è più vecchio. Entrambi i programmi non richiedono da te di strumentare il tuo programma (come richiede Gprof). Tuttavia, per ottenere correttamente il grafico delle chiamate in perf devi costruire il tuo programma con -fno-omit-frame-pointer. Ad esempio:g++ -fno-omit-frame-pointer -O2 main.cpp .

Per quanto riguarda Linux perf :

  1. Per registrare i dati sulle prestazioni:

    perf record -p `pidof a.out`
    

    o per registrare per 10 secondi:

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

    o per registrare con un grafico delle chiamate ()

    perf record -g -p `pidof a.out`
    
  2. Analizzare i dati registrati

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

    Su RHEL 6.3 è consentito leggere /boot/System.map-2.6.32-279.el6.x86_64, quindi di solito aggiungo --kallsyms=/boot/System.map-2.6.32-279.el6.x86_64 quando facendo un rapporto sulle prestazioni:

    perf report --stdio -g --kallsyms=/boot/System.map-2.6.32-279.el6.x86_64
    
    Qui ho scritto alcune informazioni in più sull'uso di Linux `perf`:

    Prima di tutto, questo è un tutorial sulla profilazione di Linux con perf

    Puoi usare perf se il tuo kernel Linux è superiore a 2.6.32 o OProfile se è più vecchio. Entrambi i programmi non richiedono da te di strumentare il tuo programma (come richiede Gprof). Tuttavia, per ottenere correttamente il grafico delle chiamate in perf devi costruire il tuo programma con -fno-omit-frame-pointer . Ad esempio:g++ -fno-omit-frame-pointer -O2 main.cpp .

    Puoi vedere un'analisi "dal vivo" della tua applicazione con perf top :

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

Oppure puoi registrare i dati sulle prestazioni di un'applicazione in esecuzione e analizzarli successivamente:

  1. Per registrare i dati sulle prestazioni:

    perf record -p `pidof a.out`
    

    o per registrare per 10 secondi:

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

    o per registrare con un grafico delle chiamate ()

    perf record -g -p `pidof a.out`
    
  2. Analizzare i dati registrati

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

Oppure puoi registrare i dati sulle prestazioni di un'applicazione e analizzarli successivamente semplicemente avviando l'applicazione in questo modo e aspettando che esca:

perf record ./a.out

Questo è un esempio di profilazione di un programma di test.

Il programma di test si trova nel file main.cpp (main.cpp è in fondo alla risposta):

Lo compilo in questo modo:

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

Uso libmalloc_minimial.so poiché è compilato con -fno-omit-frame-pointer mentre libc malloc sembra essere compilato senza questa opzione. Quindi eseguo il mio programma di test:

./my_test 100000000

Quindi registro i dati sulle prestazioni di un processo in esecuzione:

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

Quindi analizzo il carico per modulo:

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]

Quindi viene analizzato il carico per funzione:

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 ...

Quindi vengono analizzate le catene di chiamate:

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 ...

Quindi a questo punto sai dove trascorre il tempo il tuo programma.

E questo è main.cpp file per il 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;
}

Citando lo stesso Linus Torvalds:

Non usare gprof. Sei molto meglio usare il nuovo strumento 'perf' di Linux.

E dopo...

Posso praticamente garantire che una volta che inizi a usarlo, non utilizzerai mai più gprof o oprofile.

Vedi Re:[PATCH] grep:non eseguire grep esterno sulle voci skip-worktree (2010-01-04)


Linux
  1. Linux:comando per misurare i mancati tlb su Linux?

  2. Linux:quale applicazione utilizzare per un calendario?

  3. Linux – Impossibile installare Perf su Slackware 13.1?

  4. Eseguire un'applicazione Java come servizio su Linux

  5. Ottenere l'elenco delle applicazioni di avvio in Linux

Come inviare i log delle applicazioni Linux ad AWS CloudWatch

Terminal Velocity:un'applicazione CLI per prendere appunti per Linux

Come abilitare l'opzione "Modifica" nell'applicazione Shutter in Linux

Divertiti con Twitch su Linux con l'applicazione GNOME Twitch

Comando Linux Perf

Come installare e configurare Perf nelle distribuzioni Linux