GNU/Linux >> Tutoriales Linux >  >> Linux

Perfilado de aplicaciones de Linux

Idealmente, necesito una aplicación que se adjunte a un proceso y registre instantáneas periódicas de:

  • uso de memoria
  • número de subprocesos
  • Uso de la CPU

Bueno, para recopilar este tipo de información sobre su proceso, en realidad no necesita un generador de perfiles en Linux.

  1. Puedes usar top en modo por lotes. Se ejecuta en el modo por lotes hasta que se elimina o hasta que se realizan N iteraciones:

    top -b -p `pidof a.out`
    

    o

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

    y obtendrás esto:

    $ 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. Puedes usar ps (por ejemplo, en un script de shell)

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

    Necesito algún medio para registrar el rendimiento de una aplicación en una máquina Linux

    Para hacer esto necesitas usar perf si su kernel de Linux es mayor que 2.6.32 u OProfile si es anterior. Ambos programas no requieren que usted instrumente su programa (como lo requiere Gprof). Sin embargo, para obtener el gráfico de llamadas correctamente en perf necesita construir su programa con -fno-omit-frame-pointer. Por ejemplo:g++ -fno-omit-frame-pointer -O2 main.cpp .

En cuanto a Linux perf :

  1. Para registrar datos de rendimiento:

    perf record -p `pidof a.out`
    

    o para grabar durante 10 segundos:

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

    o para grabar con un gráfico de llamadas ()

    perf record -g -p `pidof a.out`
    
  2. Para analizar los datos registrados

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

    En RHEL 6.3, se permite leer /boot/System.map-2.6.32-279.el6.x86_64, por lo que generalmente agrego --kallsyms=/boot/System.map-2.6.32-279.el6.x86_64 cuando haciendo un informe de rendimiento:

    perf report --stdio -g --kallsyms=/boot/System.map-2.6.32-279.el6.x86_64
    
    Aquí escribí más información sobre el uso de Linux `perf`:

    En primer lugar, este es un tutorial sobre la creación de perfiles de Linux con perf

    Puedes usar perf si su kernel de Linux es mayor que 2.6.32 u OProfile si es más antiguo. Ambos programas no requieren que usted instrumente su programa (como lo requiere Gprof). Sin embargo, para obtener el gráfico de llamadas correctamente en perf necesitas construir tu programa con -fno-omit-frame-pointer . Por ejemplo:g++ -fno-omit-frame-pointer -O2 main.cpp .

    Puede ver un análisis "en vivo" de su aplicación con perf top :

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

O puede registrar los datos de rendimiento de una aplicación en ejecución y analizarlos después:

  1. Para registrar datos de rendimiento:

    perf record -p `pidof a.out`
    

    o para grabar durante 10 segundos:

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

    o para grabar con un gráfico de llamadas ()

    perf record -g -p `pidof a.out`
    
  2. Para analizar los datos registrados

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

O puede registrar los datos de rendimiento de una aplicación y analizarlos después simplemente iniciando la aplicación de esta manera y esperando a que se cierre:

perf record ./a.out

Este es un ejemplo de creación de perfiles de un programa de prueba.

El programa de prueba está en el archivo main.cpp (principal.cpp está al final de la respuesta):

Lo compilo de esta manera:

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

Yo uso libmalloc_minimial.so ya que está compilado con -fno-omit-frame-pointer mientras que libc malloc parece estar compilado sin esta opción. Luego ejecuto mi programa de prueba:

./my_test 100000000

Luego registro los datos de rendimiento de un proceso en ejecución:

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

Luego analizo la carga por módulo:

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]

Luego se analiza la carga por función:

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

Luego se analizan las cadenas de llamadas:

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

Entonces, en este punto, ya sabe dónde pasa el tiempo su programa.

Y este es el main.cpp archivo para la prueba:

#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 al propio Linus Torvalds:

No use gprof. Eres mucho es mejor usar la nueva herramienta 'perf' de Linux.

Y luego...

Casi puedo garantizar que una vez que comience a usarlo, nunca volverá a usar gprof u oprofile.

Ver Re:[PATCH] grep:no hacer grep externo en entradas de skip-worktree (2010-01-04)


Linux
  1. Linux:¿comando para medir fallas de Tlb en Linux?

  2. Linux:¿qué aplicación usar para un calendario?

  3. Linux:¿no se pudo instalar Perf en Slackware 13.1?

  4. Limitación del acceso al sistema para una aplicación de Linux

  5. ¿Cómo firmar una aplicación Mac OS X en Linux?

Cómo enviar registros de aplicaciones de Linux a AWS CloudWatch

Terminal Velocity:una aplicación CLI para tomar notas para Linux

Cómo habilitar la opción "Editar" en la aplicación Shutter en Linux

Disfruta de Twitch en Linux con la aplicación GNOME Twitch

Comando de rendimiento de Linux

Cómo instalar y configurar Perf en distribuciones de Linux