Capítulo 28. DTrace

This translation may be out of date. To help with the translations please access the FreeBSD translations instance.

28.1. Sinopsis

DTrace, también conocido como Dynamic Tracing, fue desarrollado por Sun™ como una herramienta para localizar cuellos de botella de rendimiento en sistemas en producción y en preproducción. Además de diagnosticar problemas de rendimiento, DTrace puede utilizarse para ayudar a investigar y depurar comportamientos inesperados tanto en el kernel de FreeBSD como en programas del espacio de usuario.

DTrace es una herramienta de perfilado extraordinaria, con un impresionante conjunto de funcionalidades para diagnosticar problemas del sistema. También puede utilizarse para ejecutar scripts ya escritos y así aprovechar sus capacidades. Los usuarios pueden crear sus propias utilidades mediante el lenguaje D de DTrace, lo que les permite adaptar el perfilado a sus necesidades específicas.

La implementación de FreeBSD ofrece soporte completo para DTrace en el kernel y soporte experimental para DTrace en el espacio de usuario. DTrace en el espacio de usuario permite a los usuarios trazar los límites de las funciones de programas del espacio de usuario mediante el proveedor pid, así como insertar sondas estáticas en programas del espacio de usuario para trazarlos posteriormente. Algunos ports, como databases/postgresql12-server y lang/php74, tienen una opción de DTrace para activar las sondas estáticas.

La guía oficial de DTrace la mantiene el proyecto illumos en illumos Dynamic Tracing Guide.

Después de leer este capítulo sabrá:

  • Qué es DTrace y qué funcionalidades ofrece.

  • Las diferencias entre la implementación de DTrace de Solaris™ y la que ofrece FreeBSD.

  • Cómo activar y utilizar DTrace en FreeBSD.

Antes de leer este capítulo debería:

28.2. Diferencias en la implementación

Aunque DTrace en FreeBSD es similar al que se encuentra en Solaris™, existen diferencias. La principal diferencia es que en FreeBSD DTrace está implementado como un conjunto de módulos del kernel, y no puede utilizarse hasta que dichos módulos estén cargados. Para cargar todos los módulos necesarios:

# kldload dtraceall

A partir de FreeBSD 10.0-RELEASE los módulos se cargan automáticamente al ejecutar dtrace(1).

FreeBSD utiliza la opción del kernel DDB_CTF para activar el soporte de carga de datos ctf(5) desde los módulos del kernel y desde el propio kernel. CTF es el Compact C Type Format de Solaris™, que encapsula una forma reducida de información de depuración similar a DWARF y a los veteranos stabs. Los datos CTF se añaden a los binarios mediante las herramientas de compilación ctfconvert(1) y ctfmerge(1). La utilidad ctfconvert analiza las secciones de depuración DWARF ELF creadas por el compilador, y ctfmerge fusiona las secciones CTF ELF de los ficheros objeto en ejecutables o en bibliotecas compartidas.

FreeBSD dispone de algunos proveedores distintos de los de Solaris™. El más destacable es el proveedor dtmalloc, que permite trazar malloc(9) por tipo en el kernel de FreeBSD. Algunos de los proveedores que se encuentran en Solaris™, como cpc, no están presentes en FreeBSD. Es posible que aparezcan en futuras versiones de FreeBSD. Además, algunos de los proveedores disponibles en ambos sistemas operativos no son compatibles, en el sentido de que sus sondas tienen tipos de argumentos diferentes. Por lo tanto, los scripts D escritos en Solaris™ pueden funcionar o no sin modificaciones en FreeBSD, y viceversa.

Debido a diferencias en materia de seguridad, en FreeBSD sólo root puede utilizar DTrace. Solaris™ tiene algunas comprobaciones de seguridad de bajo nivel que todavía no existen en FreeBSD. Por ello, el acceso a /dev/dtrace/dtrace está estrictamente limitado a root.

DTrace está sujeto a la licencia Common Development and Distribution License (CDDL). Para ver esta licencia en FreeBSD consulte /usr/src/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE o véala en línea en https://opensource.org/licenses/CDDL-1.0. Aunque un kernel de FreeBSD con soporte para DTrace tiene licencia BSD, se aplica la CDDL cuando los módulos se distribuyen en formato binario o cuando se cargan los binarios.

28.3. Activar el soporte de DTrace

En FreeBSD 9.2 y 10.0 el soporte de DTrace está incluido en el kernel GENERIC. Los usuarios de versiones anteriores de FreeBSD, o quienes prefieran compilar estáticamente el soporte de DTrace, deben añadir las siguientes líneas a un fichero de configuración del kernel personalizado y recompilar el kernel siguiendo las instrucciones de Configuración del kernel de FreeBSD:

options         KDTRACE_HOOKS
options         DDB_CTF
makeoptions	DEBUG=-g
makeoptions	WITH_CTF=1

Los usuarios de la arquitectura AMD64 deben añadir también esta línea:

options         KDTRACE_FRAME

Esta opción proporciona soporte para dtrace_fbt(4). Aunque DTrace funcionará sin esta opción, el soporte para trazar los límites de las funciones será limitado.

Una vez que el sistema FreeBSD haya arrancado con el nuevo kernel, o que se hayan cargado los módulos del kernel de DTrace mediante kldload dtraceall, instale la versión actual de DTrace Toolkit (sysutils/dtrace-toolkit), una colección de scripts listos para usar que recopilan información del sistema. Hay scripts para comprobar los ficheros abiertos, la memoria, el uso de la CPU y mucho más. FreeBSD también incluye algunos scripts en el sistema base; consulte /usr/share/dtrace.

Los scripts que se encuentran en /usr/share/dtrace han sido adaptados específicamente a FreeBSD. No todos los scripts de DTrace Toolkit funcionarán tal cual en FreeBSD, y algunos pueden requerir cierto esfuerzo para que funcionen en FreeBSD.

DTrace Toolkit incluye muchos scripts escritos en el lenguaje especial de DTrace. Este lenguaje se denomina lenguaje D y es muy similar a C++. Un análisis en profundidad del lenguaje queda fuera del alcance de este documento. Consulte la página de manual d(7) de FreeBSD para obtener una visión general del lenguaje D en FreeBSD. El lenguaje D también se trata extensamente en la illumos Dynamic Tracing Guide.

28.4. Activar DTrace en módulos externos al kernel

Para añadir soporte de DTrace a un módulo externo al kernel, algo útil para el desarrollo y la depuración, incluya la siguiente línea en el Makefile del módulo:

CFLAGS+= -DKDTRACE_HOOKS

Este parámetro activa los hooks de DTrace durante la compilación, lo que permite una depuración y una monitorización avanzadas del módulo. Asegúrese de recompilar el módulo después de esta modificación para activar la funcionalidad de DTrace.

28.5. Uso de DTrace

Los scripts de DTrace consisten en una lista de una o más sondas, o puntos de instrumentación, cada una de las cuales está asociada a una acción. Cada vez que se cumple la condición de una sonda se ejecuta la acción asociada. Por ejemplo, una acción puede producirse cuando se abre un fichero, cuando se inicia un proceso o cuando se ejecuta una línea de código. La acción puede consistir en registrar cierta información o en modificar variables de contexto. La lectura y escritura de variables de contexto permite a las sondas compartir información y analizar de forma conjunta la correlación entre distintos eventos.

Para ver todas las sondas, el administrador puede ejecutar la siguiente orden:

# dtrace -l | more

Cada sonda tiene un ID, un PROVIDER (por ejemplo, dtrace o fbt), un MODULE y un FUNCTION NAME. Consulte dtrace(1) para obtener más información sobre esta orden.

Los ejemplos de esta sección ofrecen una visión general de cómo utilizar dos de los scripts de DTrace Toolkit que están totalmente soportados: los scripts hotkernel y procsystime.

El script hotkernel está diseñado para identificar qué función consume más tiempo del kernel. Producirá una salida similar a la siguiente:

# cd /usr/local/share/dtrace-toolkit
# ./hotkernel
Sampling... Hit Ctrl-C to end.

Tal y como se indica, utilice la combinación de teclas Ctrl+C para detener el proceso. Al terminar, el script mostrará una lista de funciones del kernel e información de tiempos, ordenando la salida de menor a mayor tiempo:

kernel`_thread_lock_flags                                   2   0.0%
0xc1097063                                                  2   0.0%
kernel`sched_userret                                        2   0.0%
kernel`kern_select                                          2   0.0%
kernel`generic_copyin                                       3   0.0%
kernel`_mtx_assert                                          3   0.0%
kernel`vm_fault                                             3   0.0%
kernel`sopoll_generic                                       3   0.0%
kernel`fixup_filename                                       4   0.0%
kernel`_isitmyx                                             4   0.0%
kernel`find_instance                                        4   0.0%
kernel`_mtx_unlock_flags                                    5   0.0%
kernel`syscall                                              5   0.0%
kernel`DELAY                                                5   0.0%
0xc108a253                                                  6   0.0%
kernel`witness_lock                                         7   0.0%
kernel`read_aux_data_no_wait                                7   0.0%
kernel`Xint0x80_syscall                                     7   0.0%
kernel`witness_checkorder                                   7   0.0%
kernel`sse2_pagezero                                        8   0.0%
kernel`strncmp                                              9   0.0%
kernel`spinlock_exit                                       10   0.0%
kernel`_mtx_lock_flags                                     11   0.0%
kernel`witness_unlock                                      15   0.0%
kernel`sched_idletd                                       137   0.3%
0xc10981a5                                              42139  99.3%

Este script también funciona con módulos del kernel. Para utilizar esta funcionalidad, ejecute el script con -m:

# ./hotkernel -m
Sampling... Hit Ctrl-C to end.
^C
MODULE                                                  COUNT   PCNT
0xc107882e                                                  1   0.0%
0xc10e6aa4                                                  1   0.0%
0xc1076983                                                  1   0.0%
0xc109708a                                                  1   0.0%
0xc1075a5d                                                  1   0.0%
0xc1077325                                                  1   0.0%
0xc108a245                                                  1   0.0%
0xc107730d                                                  1   0.0%
0xc1097063                                                  2   0.0%
0xc108a253                                                 73   0.0%
kernel                                                    874   0.4%
0xc10981a5                                             213781  99.6%

El script procsystime captura y muestra el tiempo consumido en llamadas al sistema por un proceso determinado, indicado mediante su ID de proceso (PID) o su nombre. En el siguiente ejemplo se lanzó una nueva instancia de /bin/csh. A continuación se ejecutó procsystime, que permaneció a la espera mientras se tecleaban algunas órdenes en la otra instancia de csh. Estos son los resultados de la prueba:

# ./procsystime -n csh
Tracing... Hit Ctrl-C to end...
^C

Elapsed Times for processes csh,

         SYSCALL          TIME (ns)
          getpid               6131
       sigreturn               8121
           close              19127
           fcntl              19959
             dup              26955
         setpgid              28070
            stat              31899
       setitimer              40938
           wait4              62717
       sigaction              67372
     sigprocmask             119091
    gettimeofday             183710
           write             263242
          execve             492547
           ioctl             770073
           vfork            3258923
      sigsuspend            6985124
            read         3988049784

Como puede verse, la llamada al sistema read(2) es la que más tiempo consumió en nanosegundos, mientras que la llamada al sistema getpid(2) es la que menos tiempo consumió.