Solaris: Introducción a DTrace

Kernel, OpenSolaris, Tuning, dtrace Dejar un comentario

Hace tiempo que estoy detrás de intentar escribir una pequeña entrada en el blog sobre DTrace, pienso que ha sido una de las herramientas más interesante que han aparecido para el estudio del rendimiento de los sistemas y no solo para eso, tambien puede ayudar a todos aquellos que deseen profundizar en la compresión de cómo funciona el Kernel de Solaris. Creo que cualquier persona que bien vaya a desarrollar, bien administre sistemas Solaris, debería conocer DTrace, sino en profundidad, al menos conocer las posibilidades que nos ofrece. En este artículo veremos una breve introducción a DTrace, de todas formas es imprescindible, para todos aquellos que deseen profundizar más, la lectura de Solaris Dynamic Tracing Guide.

  • ¿Qués es DTrace?
  • Lenguaje D
  • Agregaciones
  • dtrace(1M)
  • Providers
  • Integrar DTrace en las aplicaciones
  • DTrace GUI – chime

¿Qué es DTrace?

Es una herramienta de instrumentación desarrollada por Sun en el 2005 y disponible en Solaris 10. No consiste en una simple herramienta de consulta de estadísticas, al estilo kstat, donde todos los datos son generados y posteriormente recogidos. DTrace explota el concepto de Instumentación, tal y como se conoce en el mundo de la Ingeniería.


Instrumentación industrial: es el grupo de elementos que sirven para medir, convertir, transmitir, controlar o registrar variables de un proceso con el fin de optimizar los recursos utilizados en éste. (http://es.wikipedia.org)‏

DTrace está formado por una serie de elementos, el uso de los cuales nos permiten medir, controlar, registrar, etc. variables del sistema. Cuando utilicemos DTrace debes pensar (la misma nomenclatura de DTrace nos lleva a ello) que estamos poniendo sondas en el sistema que están recogiendo datos para nosotros.

Está orientada tanto para desarrolladores, a los cuales puede ayudar en las distintas fases de desarrollo, midiendo variables del sistema, de la misma forma que ocurriría en un sistema en producción. También es una herramienta fundamental para los administradores, DTrace va mucho más allá del imprescindible truss, ahora un administrador con unos conocimientos básicos de DTrace puede, por ejemplo, conocer cuanto tiempo tardan las escrituras en disco de un proceso determinado o la veces que se llama a una syscall determinada.

Lo mejor de DTrace es que podemos realizar todas estas tareas en sistemas en producción, sin coste alguno para el rendimiento del sistemas, ya que es una herramienta no intrusiva.

Arquitectura de DTrace

dtrace1.JPG

En la figura anterior podemos ver un esquema de la arquitectura de DTrace, básicamente está formada por:

  • Consumers, son todos aquellos elementos que de una forma u otra utilizan DTrace, en este punto debemos aclarar que DTrace es el sistema de instrumentación y que existe un comando <b>dtrace(1M)</b> que es uno de los <b>consumers</b> de DTrace, pero no el único.
  • Providers, son todos aquellos elementos que pueden generar información sobre el sistema, normalmente consiste en un módulo del Kernel.
  • Probe, es la sonda que definimos para poder extraer información del sistema.
  • Programas en D, son programas desarrollados en lenguaje D, los cuales serán compilados por el comando dtrace(1M) y pasados al Kernel para extraer la información que deseemos. Estos programas definen las sondas y su comportamiento.

Lenguaje D

Es una mezcla de C y awk, la característica mas notable es que este lenguaje no dispone de instrucciones para el control de fujo, no tenemos ni if … then, ni while, for, etc.

Los programas son compilados con el comando dtrace(1M), de una forma parecida a como se hace en Java, una vez probado que no contiene errores es enviado a Kernel para que lo ejecute DTrace.

La estructura básica de un programa en D es:


Descripción de la sonda
/ predicado /
{
Acciones;
}

Descripción de la sonda

La descripción de la sonda, está formada por una o más cadenas de la forma:

provider:module:funcition:name

  • Provider, es el módulo de Dtrace que publica una sonda.
  • Module, las distintas sondas de un provider pueden estar organizadas en módulos.
  • Function, es la función sobre la que actuará la sonda.
  • Name, es una descripción de qué hace la sonda.

Predicado

La única forma de controlar el flujo de un programa en D es mediante los predicados.
Son expresiones encerradas en / / y que se evaluan como verdaderas o falsas.

Ejemplo:

 / pid == 78 /     El PID es igual a 78
/ execname == "bash"/   El nombre del programa sea bash
/ x <= 1 /     El valor de la variable x sea menor o igual a 1

Acciones

La lógica de la sonda que estamos definiendo, se describe mediante una serie de acciones, las cuales estarán encerradas entre {}. Estas acciones nos permitiran medir, imprimir, contar, modificar, los datos que la sonda obtenga.

Disponemos de una serie de funciones, que podemos utilizar para definir el comportamiento de nuestra sonda. Alguna de las cuales son:

  • trace, toma una exprecion y la pasa directamente al buffer.
  • printf, nos permite formatear una salida.
  • stack, almacena el stack.
  • stop, para el proceso.
  • system, ejecuta un programa.
  • panic, genera un kernel panic.
  • exit, termina la ejecución de la sonda.

NOTADebemos leer la guía de DTrace antes de utilizar las funciones disponibles, ya que algunas de estas funciones son destructivas y podrían generarnos algunos problemas.

Existen una serie de variables definidas, la cual contienen cierta información, como el nombre del ejecutable, el pid, el tiempo desde el arranque del sistemas:

  • pid, ID del proceso.
  • tid, ID del thread.
  • timestamp, el tiempo en nanosegundos desde el arranque de la máquina.
  • execname, nombre del proceso.
  • arg0-argN, argumento de las funciones.
  • probefunc, es el 3 campo del nombre de la sonda, normalmente el nombre de la función.
  • probename, es el 4 campo de la definición de la sonda.
  • $1..$N, contienen los argumentos de la llamada al comando dtrace(1M).

Ejemplo 1

Este primer ejemplo muestra el sencillo “Hola mundo” en lenguaje D.

dtrace:::BEGIN{
trace(“Hola mundo”);
} 

dtrace:::END
{
trace(“Adios!!”);
} 

bash-3.2# dtrace -s ./hola.d
dtrace: script './hola.d' matched 2 probes
CPU     ID                    FUNCTION:NAME
0      1                           :BEGIN   Hola mundo
^C
0      2                             :END   Adios!!

Ejemplo 2

Este ejemplo muestra una versión en lenguaje D del comando truss.

syscall:::entry/ pid == $1 /
{
printf("%s (%d, 0x%x, %4d)n",probefunc,arg0,arg1,arg2);
} 

bash-3.2# dtrace -q -s ./ejemplo2.d 1046
pollsys (134509504, 0x1,  134509672)‏
gtime (1203163434, 0x4fa7e748,  1336403752)‏
write (3, 0x80b36e8,     8)‏
read (3, 0x8047600,    32)‏
pollsys (134509568, 0x1,     0)‏
read (3, 0x8047600,    32)‏
gtime (1203163434, 0x4fa7e748,  1336403752)‏
gtime (1203163434, 0x4fa7e748,  1336403752)‏
read (3, 0x80af6e0,    32)‏
^C

Ejemplo 3

En este ejemplo vamos a crear un programa en D que nos devuelva el tiempo que se ha tardado en ejecutar cada syscall, para obtener el tiempo utilizaremos la variable timestamp, la cual da los nanosegundos desde que se inicio el sistema.

syscall:::entry
/ pid == $1 /
{
array_sc[probefunc] = timestamp;
} 

syscall:::return
/ pid == $1 /
{
printf("time %d t %s (%d, 0x%x,  %4d) n",timestamp-array_sc[probefunc], probefunc, arg0, arg1, arg2);
} 

bash-3.2# dtrace -q -s ./ejemplo3.d 1046 

time 10687       pollsys (0, 0x0,     0)
time 2847        gtime (1203190270, 0x47b739fe,     0)
time 18121       write (8, 0x8,     0)
time 5630        read (-1, 0xffffffffffffffff,  4294967295)
time 59836       pollsys (1, 0x1,     0)
time 2045        ioctl (0, 0x0,     0)
time 1173        ioctl (0, 0x0,     0)
^C

Agregaciones

El lenguaje D, dispone de una serie de funciones de agregación, que nos ayudarán a analizar los datos obtenidos tras el lanzamiento de una sonda.

  • count(), cuenta el número de veces.
  • avg(), realiza una media de los datos.
  • min(), obtiene el valor mínimo.
  • max(), obtiene el valor máximo.
  • lquantize(), distribución de frecuencia lineal.
  • quantize(), distribución de frecuencia de potencia de 2.

Ejemplo 4

En este ejemplo hemos construido un programa en D, el cual cuenta con 3 probes:

  • El primer probe almacena en el array @array_sc_count la veces que se inicia cualquier syscall del proceso pid == $1, también se almacena en array_sc[probefunc] el timestamp de la entrada en la llamada.
  • El segundo probe, solo aplica en la entrada de la syscall read y almacena en el array @array_time_lq[probefunc] el valor devuelto por la función lquantize(arg0,…) que como primer parámetro hemos puesto el primer argumento de la llamada read que es el file descriptor.
  • El tercer probe, solo aplica a la salida de la llamada read, cuyo pid == $1, almacena en el array @array_time_q[probefunc] el valor de la función de agregación quantize(timestamp-array_sc[probefunc]).
    syscall:::entry
    / pid == $1 /
    {
    @array_sc_count[probefunc] = count();
    array_sc[probefunc] = timestamp;
    } 
    
    syscall::read:entry
    / pid == $1 /
    {
    @array_time_lq[probefunc]=lquantize(arg0,0,100,1);
    } 
    
    syscall::read:return
    / pid == $1 /
    {
    @array_time_q[probefunc]=quantize(timestamp-array_sc[probefunc]);
    } 
    
    bash-3.2# dtrace -q -s ./ejemplo4.d 576
    lwp_sigmask                                                      53
    setcontext                                                       53
    setitimer                                                       107
    writev                                                          131
    pollsys                                                         295
    read                                                            371
    clock_gettime                                                   521 
    
    read
    value  ------------- Distribution ------------- count
    11 |                                         				0
    12 |                                         				2
    13 |@@@@                                     			37
    14 |                                         				0
    41 |@@@@@@@@                                 			74
    42 |                                        				 0
    43 |@@@@@@@@@@@@@@@@@@@@@@@@@@@         252
    44 |                                         				0 
    
    read
    value  ------------- Distribution ------------- count
    512 |                                         				0
    1024 |@@                                       			19
    2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@          241
    4096 |@@@@@@@@@                                			86
    8192 |@@                                       			23
    16384 |                                         				1
    32768 |                                         				0

    dtrace(1M)

    Es el comando que nos permite compilar nuestros programas en D. Dispone de una serie deparámetros entre los que podemos destacar:

    • -l listado de todos los probes disponibles.
    • -s compila un programa en D.
    • -P lista los probes de un provider específico.
    • -32/64 compila los programas D en 32 o 64 bits.
    • -G genera un fichero ELF que podemos usar para linkar con otros programas.
    • -n especifica el nombre de un probe.
    bash-3.2# dtrace -l 
    
    ID   PROVIDER            MODULE                          FUNCTION NAME
    1     dtrace                                                     BEGIN
    2     dtrace                                                     END
    3     dtrace                                                     ERROR
    4 Xserver576              Xorg                   CloseDownClient client-disconnect
    5 Xserver576              Xorg                          Dispatch request-done
    6 Xserver576              Xorg                          Dispatch request-start
    7 Xserver576              Xorg                       AddResource resource-alloc
    8 Xserver576              Xorg               FreeClientResources resource-free
    9 Xserver576              Xorg    FreeClientNeverRetainResources resource-free
    10 Xserver576              Xorg                FreeResourceByType resource-free
    ...

    Providers

    Las sondas (probes) son generadas por unos módulos del kernel que se denominan providers. Con el comando dtrace(1M) podemos ver la lista de providers que tiene nuestro sistema, de esta lista podemos destacar:

    • fbt (Function Boundary Tracing) nos permiten tracear la mayoría de las funciones del Kernel. Con el comando dtrace -l -P fbt podemos ver la lista de los probes disponibles.
    • lockstat, con este provider podemos tracear posibles problemas de contención en locks.
    • mib, nos da acceso a los contadores MIBs de Solaris.
    • proc, obtenemos información sobre el estado de los procesos y los threads.
    • syscall, nos permite tracear todas las llamadas a systema.
    • sysinfo, obtenemos información sobre el sistema.
    • vminfo, obtenemos información sobre el VM.

    Integrar DTrace en las aplicaciones

    Una de las cosas que hace interesante a Dtrace es que podemos crear nuestros propios providers, para que generen probes en cualquiera de nuestras aplicaciones.

    • Es una herramienta muy potente para las fases de desarrollo de SW, ya que permite de forma sencilla hacer un análisis del rendimiento de la aplicación.
    • El nuevo provider no generará nada mientras no se cree un probe que lo consulte, por lo que las aplicaciones no reducirán su rendimiento.
    • Para los administradores, es una forma sencilla de controlar el estado y rendimiento de las aplicaciones, sobre todo de aquellas de las que dispongamos el fuente.


    Un ejemplo:

    En cualquier SMTP de código abierto, podríamos modificar el fuente, para que nos devuelva información de cuanto tarda en procesar un mensaje o el tiempo medio que se utiliza para almacenar un mensaje en disco.

    Ejemplo 5: Cómo crear nuestro propio provider

    Vamos a ver como en 4 sencillos pasos podemos, crear nuestro propio provider, definir una serie de probes y hacer que cualquiera de nuestras aplicaciones (siempre que tengamos el código fuente) puedan utilizar DTrace.

    1.- Crear un fichero con la definición del provider.

    Creamos un fichero en el cual definimos el nuevo provider y los probes que tendrá, para nuestro ejemplo, creamos el fichero myprovider.d y definimos el provider myprovider.

    bash-3.2# cat myprovider.d 
    
    provider myprovider
    {
    probe op_entry(string);
    probe op_return();
    };

    Nuestro provider tendrá dos probes:

    • op_entry, el cual acepta una parámetro como entrada.
    • op_return, este probe no acepta parámetros.

    2.- Modificamos nuestra aplicación para que utilice los probes de nuestro provider.

    Para modificar nuestra aplicación debemos disponer del código fuente.

    bash-3.2# cat app_main.c 
    
    #include < stdio.h >
    #include < sys >
    #include < unistd.h > 
    
    main()
    {
    int sp;
    for(;;)
           {
           DTRACE_PROBE1(myprovider,op_entry,"Entrada en op");
           sp=rand()%5;
           printf("Inicio Op: sleep (%d) n",sp);
           sleep(sp);
           printf("Fin Opn");
           DTRACE_PROBE(myprovider,op_return);
           }
    }

    3.- Compilamos nuestra aplicación junto con nuestro programa en D.

    bash-3.2# gcc -c app_main.c
    bash-3.2#
    bash-3.2# dtrace -G -s myprovider.d app_main.o
    bash-3.2#
    bash-3.2# gcc -o app1 myprovider.o app_main.o
    bash-3.2#
    bash-3.2# ./app1 
    
    Inicio Op: sleep (3)
    Fin Op
    Inicio Op: sleep (3)
    Fin Op
    Inicio Op: sleep (4)
    Fin Op
    Inicio Op: sleep (3)
    Fin Op
    Inicio Op: sleep (2)

    4.- Creamos un script en D para poder chequear los nuevos probes en nuestra aplicación.

    bash-3.2# cat app1_probe.d 
    
    myprovider$1:::op_entry
    {
    ts = timestamp;
    } 
    
    myprovider$1:::op_return
    {
    printf("n probename: %s t time: %d ns",probename, (timestamp - ts));
    } 
    
    bash-3.2#

    En la siguiente imagen podemos ver un ejemplo de la salida del programa que acabamos de crear app1 y la ejecución del programa app1_probe.d que hemos lanzado con el comando dtrace(1M).

    desktop1.png

    DTrace GUI – chime

    Existe un GUI para Dtrace que nos permite de forma gráfica realizar un análisis de nuestro sistema. Por defecto vienen algunas sondas preparadas, pero con chime podemos visualizar de una forma rápida nuestro propios programas D. La imagen siguiente es un ejemplo de lo que podemos hacer con chime.

    chime

    NotaSi tiene alguna pregunta o sugerencia sobre este artículo, por favor, no dude en ponerse en contacto conmigo mediante email en jjmora AT arrakis DOT es

Technorati Tag(s) :

Una Respuesta en “Solaris: Introducción a DTrace”

  1. Jose Manuel Dijo:

    Excelente artículo

My name is null, /dev/null
Algunos derechos reservados. Licencia Creative Commons