Guía de seguimiento dinámico de Solaris

Capítulo 10 Acciones y subrutinas

Puede utilizar las llamadas afunciones de D como trace() o printf() para invocar dos tipos diferentes de servicios proporcionados por DTrace: las acciones que realizan un seguimiento de los datos o modifican el estado externo de DTrace, y las subrutinas, que sólo afectan al estado interno de DTrace. En este capítulo se definen las acciones y las subrutinas, y se describen su sintaxis y su semántica.

Acciones

Las acciones permiten que los programas de DTrace interactúen con el sistema fuera de esta aplicación. Las acciones más comunes registran datos en una memoria intermedia de DTrace. Hay otras acciones disponibles como, por ejemplo, detener el proceso actual, emitir una señal específica en el proceso actual o suspender todo el seguimiento. Algunas de estas acciones son destructivas, en el sentido de que modifican el sistema, aunque de una forma bien definida. Las acciones destructivas sólo pueden utilizarse si se han habilitado explícitamente. De forma predeterminada, las acciones de registro de datos realizan esta función en la memoria intermedia principal. Para obtener más información sobre la memoria intermedia principal y las directivas de memoria intermedia, consulte el Capítulo 11Memorias intermedias y almacenamiento en memoria intermedia.

Acción predeterminada

Una cláusula puede contener una serie de acciones y manipulaciones de variables. Si una cláusula se deja vacía, se lleva a cabo la acción predeterminada. La acción predeterminada consiste en realizar un seguimiento del identificador de sondeo habilitado (EPID) en la memoria intermedia principal. EPID identifica cuándo se ha habilitado un determinado sondeo con un predicado y acciones específicas. Desde EPID, los consumidores de DTrace pueden determinar el sondeo que ha inducido la acción. De hecho, cuando se realiza un seguimiento de cualquier dato, éste debe ir acompañado de EPID para que el consumidor pueda reconocer los datos. Por lo tanto, la acción predetermina consiste únicamente en realizar un seguimiento de EPID.

El uso de la acción predeterminada facilita la utilización de dtrace(1M). Por ejemplo, el siguiente comando de ejemplo habilita todos los sondeos en el módulo de programación de timeshare TS con la acción predeterminada:


# dtrace -m TS

Es posible que el comando anterior genere una salida similar a la siguiente:


# dtrace -m TS
dtrace: description 'TS' matched 80 probes
CPU     ID                    FUNCTION:NAME
  0  12077                 ts_trapret:entry 
  0  12078                ts_trapret:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12081                  ts_wakeup:entry 
  0  12082                 ts_wakeup:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12023                  ts_update:entry 
  0  12079             ts_update_list:entry 
  0  12080            ts_update_list:return 
  0  12079             ts_update_list:entry 
...

Acciones de registro de datos

Las acciones de registro de datos son las acciones principales de DTrace. Cada una de estas acciones registra datos de forma predeterminada en la memoria intermedia principal, aunque también es posible que se utilicen para registrar datos en memorias intermedias especulativas. Consulte el Capítulo 11Memorias intermedias y almacenamiento en memoria intermedia para obtener más información sobre la memoria intermedia principal. Consulte el Capítulo 13Seguimiento especulativo para obtener más información sobre las memorias intermedias especulativas. Las descripciones incluidas en esta sección sólo hacen referencia a la memoria intermedia dirigida, lo que indica que los datos se registran en la memoria intermedia principal o en una memoria intermedia especulativa si la acción sigue una especulación().

trace()

void trace(expression)

La acción más básica es trace(), que utiliza una expresión del lenguaje D como argumento y realiza un seguimiento del resultado en la memoria intermedia dirigida. Las siguientes instrucciones son ejemplos de acciones trace():

trace(execname);
trace(curlwpsinfo->pr_pri);
trace(timestamp / 1000);
trace(`lbolt);
trace("somehow managed to get here");

tracemem()

void tracemem(address, size_t nbytes)

La acción tracemem() utiliza una expresión del lenguaje D como primer argumento, address, y una constante como segundo argumento, nbytes. tracemem() copia la memoria de la dirección especificada por addr en la memoria intermedia dirigida para la longitud especificada por nbytes.

printf()

void printf(string format, ...) 

Al igual que trace(), la acción printf() realiza un seguimiento de las expresiones del lenguaje D. Sin embargo, printf() acepta un formato del estilo printf(3C) más elaborado. Al igual que printf(3C), los parámetros están formados por una cadena format seguida de un número variable de argumentos. De forma predeterminada, se realiza un seguimiento de los argumentos en la memoria intermedia dirigida. Más adelante, se da formato a los argumentos para la salida mediante dtrace(1M) en función de la cadena de formato especificada. Por ejemplo, los dos primeros ejemplos de trace() de trace() pueden combinarse para formar una única acción printf():

printf("execname is %s; priority is %d", execname, curlwpsinfo->pr_pri);

Para obtener más información sobre printf(), consulte el Capítulo 12Formato de salida.

printa()

void printa(aggregation)
void printa(string format, aggregation)

La acción printa() permite mostrar y dar formato a las adiciones. Consulte el Capítulo 9Adiciones para obtener más información sobre las adiciones. Si no se especifica ninguna cadenaformat, printa() sólo realiza un seguimiento de una directiva en el consumidor de DTrace que indica que la adición especificada debería procesarse y mostrarse utilizando el formato predeterminado. Si, por el contrario, se especifica una cadena format, se le dará formato a la adición de la forma en la que se especifique. Consulte el Capítulo 12Formato de salida para obtener una descripción más detallada de la cadena de formato printa().

printa() sólo realiza el seguimiento a una directiva que indica que el consumidor de DTrace debe procesar la adición. No procesa la adición en el núcleo. Por lo tanto, el tiempo entre el seguimiento de la directiva printa() y el procesamiento real de la directiva depende de los factores que afecten al procesamiento de la memoria intermedia. Ente estos factores, se incluyen la velocidad de adición y la directiva de almacenamiento en la memoria intermedia, y si esta directiva es switching, la velocidad a la que se conmutan las memorias intermedias. Consulte el Capítulo 9Adiciones y el Capítulo 11Memorias intermedias y almacenamiento en memoria intermedia para obtener descripciones detalladas de estos factores.

stack()

void stack(int nframes)
void stack(void)

La acción stack() registra el seguimiento de la pila del núcleo en la memoria intermedia dirigida. La pila del núcleo tendrá una profundidad de nframes. Si no se especifica el valor de nframes, el número de marcos de pila registrados será el número especificado por la opción stackframes. Por ejemplo:


# dtrace -n uiomove:entry'{stack()}'
  CPU     ID                    FUNCTION:NAME
    0   9153                    uiomove:entry 
                genunix`fop_write+0x1b
                namefs`nm_write+0x1d
                genunix`fop_write+0x1b
                genunix`write+0x1f7

    0   9153                    uiomove:entry 
                genunix`fop_read+0x1b
                genunix`read+0x1d4

    0   9153                    uiomove:entry 
                genunix`strread+0x394
                specfs`spec_read+0x65
                genunix`fop_read+0x1b
                genunix`read+0x1d4
   ...

La acción stack() es un poco diferente de las otras acciones, ya que puede utilizarse como clave en una adición:


# dtrace -n kmem_alloc:entry'{@[stack()] = count()}'
dtrace: description 'kmem_alloc:entry' matched 1 probe
^C

                rpcmod`endpnt_get+0x47c
                rpcmod`clnt_clts_kcallit_addr+0x26f
                rpcmod`clnt_clts_kcallit+0x22
                nfs`rfscall+0x350
                nfs`rfs2call+0x60
                nfs`nfs_getattr_otw+0x9e
                nfs`nfsgetattr+0x26
                nfs`nfs_getattr+0xb8
                genunix`fop_getattr+0x18
                genunix`cstat64+0x30
                genunix`cstatat64+0x4a
                genunix`lstat64+0x1c
                  1

                genunix`vfs_rlock_wait+0xc
                genunix`lookuppnvp+0x19d
                genunix`lookuppnat+0xe7
                genunix`lookupnameat+0x87
                genunix`lookupname+0x19
                genunix`chdir+0x18
                  1

                rpcmod`endpnt_get+0x6b1
                rpcmod`clnt_clts_kcallit_addr+0x26f
                rpcmod`clnt_clts_kcallit+0x22
                nfs`rfscall+0x350
                nfs`rfs2call+0x60
                nfs`nfs_getattr_otw+0x9e
                nfs`nfsgetattr+0x26
                nfs`nfs_getattr+0xb8
                genunix`fop_getattr+0x18
                genunix`cstat64+0x30
                genunix`cstatat64+0x4a
                genunix`lstat64+0x1c
                  1
    ...

ustack()

void ustack(int nframes, int strsize)
void ustack(int nframes)
void ustack(void)

La acción ustack() registra un seguimiento de la pila del usuario en la memoria intermedia dirigida. La pila del usuario tendrá una profundidad de nframes. Si no se especifica el valor de nframes, el número de marcos de pila registrados será el número especificado por la opción ustackframes. Aunque ustack() puede determinar la dirección de los marcos de llamada cuando se activa el sondeo, los marcos de pila no se convertirán en símbolos hasta que el consumidor de DTrace no procese la acción ustack() a nivel de usuario. Si se especifican strsize y un valor diferente a cero, ustack() asignará la cantidad especificada de espacio de cadena y la utilizará para convertir las direcciones en símbolos directamente desde el núcleo. Esta traducción directa de símbolos de usuario está sólo disponible actualmente para la versión 1.5 y superior de la Máquina virtual de Java. La función de traducción de direcciones en símbolos de Java anota las pilas de usuarios que contienen marcos de Java con la clase de Java y el nombre del método. Si no es posible traducir esos marcos, aparecerán sólo como direcciones hexadecimales.

El siguiente ejemplo realiza un seguimiento de una pila sin espacio de cadena y, por lo tanto, sin traducción de direcciones en símbolos de Java:


# dtrace -n syscall::write:entry'/pid == $target/{ustack(50, 0); 
    exit(0)}' -c "java -version"
dtrace: description 'syscall::write:entry' matched 1 probe
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b58)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b58, mixed mode)
dtrace: pid 5312 has exited
CPU     ID                    FUNCTION:NAME
  0     35                      write:entry 
              libc.so.1`_write+0x15
              libjvm.so`__1cDhpiFwrite6FipkvI_I_+0xa8
              libjvm.so`JVM_Write+0x2f
              d0c5c946
              libjava.so`Java_java_io_FileOutputStream_writeBytes+0x2c
              cb007fcd
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb000152
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_
                          pnGThread__v_+0x187
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_
                          pnGThread__v2468_v_+0x14
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_
                          pnRJavaCallArguments_pnGThread __v_+0x28
              libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_
                          pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_
                          ArgumentPusher_pnGThread__v_+0x180
              libjvm.so`jni_CallStaticVoidMethod+0x10f
              java`main+0x53d

Tenga en cuenta que los marcos de pila de C y C++ de la Máquina virtual de Java se presentan de forma simbólica mediante nombres de símbolos "corruptos" de C++ y los marcos de pila de Java se presentan únicamente como direcciones hexadecimales. En el siguiente ejemplo, se muestra una llamada a ustack() con un espacio de cadena diferente a cero.


# dtrace -n syscall::write:entry'/pid == $target/{ustack(50, 500); exit(0)}'
      -c "java -version"
dtrace: description 'syscall::write:entry' matched 1 probe
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b58)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b58, mixed mode)
dtrace: pid 5308 has exited
CPU     ID                    FUNCTION:NAME
  0     35                      write:entry 
              libc.so.1`_write+0x15
              libjvm.so`__1cDhpiFwrite6FipkvI_I_+0xa8
              libjvm.so`JVM_Write+0x2f
              d0c5c946
              libjava.so`Java_java_io_FileOutputStream_writeBytes+0x2c
              java/io/FileOutputStream.writeBytes
              java/io/FileOutputStream.write
              java/io/BufferedOutputStream.flushBuffer
              java/io/BufferedOutputStream.flush
              java/io/PrintStream.write
              sun/nio/cs/StreamEncoder$CharsetSE.writeBytes
              sun/nio/cs/StreamEncoder$CharsetSE.implFlushBuffer
              sun/nio/cs/StreamEncoder.flushBuffer
              java/io/OutputStreamWriter.flushBuffer
              java/io/PrintStream.write
              java/io/PrintStream.print
              java/io/PrintStream.println
              sun/misc/Version.print
              sun/misc/Version.print
              StubRoutines (1)
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_pnGThread
                          __v_+0x187
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_pnGThread
                          __v2468_v_+0x14
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle
                          _pnRJavaCallArguments_pnGThread__v_+0x28
              libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI
                          _jobject_nLJNICallType_pnK_jmethodID_pnSJNI
                          _ArgumentPusher_pnGThread__v_+0x180
              libjvm.so`jni_CallStaticVoidMethod+0x10f
              java`main+0x53d
              8051b9a

La salida del ejemplo anterior muestra la información de marcos de pila simbólicos para los marcos de pila de Java. Aún hay varios marcos hexadecimales en esta salida, ya que algunas funciones son estáticas y no tienen entradas en la tabla de símbolos de la aplicación. Por lo tanto, no se puede realizar la traducción para estos marcos.

La traducción en símbolos de ustack() para los marcos que no son de Java se produce después de registrar los datos de la pila. Por lo tanto, es posible que se cierre el proceso de usuario antes de que se realice la traducción en símbolos, por lo que la traducción de los marcos de pila no se podría realizar. Si el proceso de usuario se cierra antes de que se realice la traducción en símbolos, dtrace emitirá un mensaje de advertencia, seguido de los marcos de pila hexadecimales, como se muestra en el siguiente ejemplo:


  dtrace: failed to grab process 100941: no such process
                c7b834d4
                c7bca85d
                c7bca1a4
                c7bd4374
                c7bc2628
                8047efc

Las técnicas para solucionar este problema se describen en el Capítulo 33Seguimiento de procesos de usuario.

Por último, dado que los comandos del depurador de DTrace postmortem no pueden realizar la traducción de los marcos, el uso de ustack() con una directiva de memoria intermedia ring siempre generará datos de ustack() sin formato.

El siguiente programa D muestra un ejemplo de ustack() en el que se deja sin especificar strsize:

syscall::brk:entry
/execname == $$1/
{
	@[ustack(40)] = count();
}

Para ejecutar este ejemplo en el explorador Web de Netscape, .netscape.bin en las instalaciones predeterminadas de Solaris, utilice el siguiente comando:


# dtrace -s brk.d .netscape.bin
dtrace: description 'syscall::brk:entry' matched 1 probe
^C
                libc.so.1`_brk_unlocked+0xc
                88143f6
                88146cd
                .netscape.bin`unlocked_malloc+0x3e
                .netscape.bin`unlocked_calloc+0x22
                .netscape.bin`calloc+0x26
                .netscape.bin`_IMGCB_NewPixmap+0x149
                .netscape.bin`il_size+0x2f7
                .netscape.bin`il_jpeg_write+0xde
                8440c19
                .netscape.bin`il_first_write+0x16b
                8394670
                83928e5
                .netscape.bin`NET_ProcessHTTP+0xa6
                .netscape.bin`NET_ProcessNet+0x49a
                827b323
                libXt.so.4`XtAppProcessEvent+0x38f
                .netscape.bin`fe_EventLoop+0x190
                .netscape.bin`main+0x1875
                   1

                libc.so.1`_brk_unlocked+0xc
                libc.so.1`sbrk+0x29
                88143df
                88146cd
                .netscape.bin`unlocked_malloc+0x3e
                .netscape.bin`unlocked_calloc+0x22
                .netscape.bin`calloc+0x26
                .netscape.bin`_IMGCB_NewPixmap+0x149
                .netscape.bin`il_size+0x2f7
                .netscape.bin`il_jpeg_write+0xde
                8440c19
                .netscape.bin`il_first_write+0x16b
                8394670
                83928e5
                .netscape.bin`NET_ProcessHTTP+0xa6
                .netscape.bin`NET_ProcessNet+0x49a
                827b323
                libXt.so.4`XtAppProcessEvent+0x38f
                .netscape.bin`fe_EventLoop+0x190
                .netscape.bin`main+0x1875
                  1
    ...

jstack()

void jstack(int nframes, int strsize)
void jstack(int nframes)
void jstack(void)

jstack() es un alias de ustack() que utiliza la opción jstackframes para el valor de número de marcos de pila especificado y para el valor de tamaño de espacio de cadena especificado por la opción jstackstrsize. jstacksize se establece de forma predeterminada en un valor diferente a cero. Como resultado, el uso de jstack() generará una pila con una traducción de marcos de Java.

Acciones destructivas

Algunas acciones de DTrace son destructivas, en el sentido de que modifican el estado del sistema de una forma bien definida. Las acciones destructivas no pueden utilizarse a menos que se habiliten explícitamente. Al utilizar dtrace(1M), puede habilitar las acciones destructivas mediante la opción -w. Si se intentan utilizar las acciones destructivas en dtrace(1M) sin habilitarlas explícitamente, dtrace fallará y generará un mensaje similar al siguiente:


dtrace: failed to enable 'syscall': destructive actions not allowed

Acciones destructivas de procesos

Algunas acciones son sólo destructivas para un proceso determinado. Estas acciones están disponibles para los usuarios con los privilegios dtrace_proc o dtrace_user. Consulte el Capítulo 35Seguridad para obtener más información sobre los privilegios de seguridad de DTrace.

stop()

void stop(void)

La acción stop() fuerza la detención del proceso que activa el sondeo habilitado cuando se abandona el núcleo a continuación, como si lo hubiera detenido la acción proc(4). La utilidad prun(1) puede usarse para reanudar un proceso detenido por la acción stop(). La acción stop() puede utilizarse para detener un proceso en cualquier punto del sondeo de DTrace. Esta acción puede utilizarse para capturar un programa en un estado específico que sería difícil de conseguir con un simple punto de interrupción y, a continuación, adjuntar al proceso un depurador tradicional como, por ejemplo, mdb(1). También puede usar la utilidad gcore(1) para guardar el estado de un proceso detenido en un archivo del núcleo central para su posterior análisis.

raise()

void raise(int signal)

La acción raise() envía la señal especificada al proceso que se está ejecutando en ese momento. Esta acción es similar al uso del comando kill(1) para enviar una señal a un proceso. La acción raise() puede utilizarse para enviar una señal en un momento específico de la ejecución de un proceso.

copyout()

void copyout(void *buf, uintptr_t addr, size_t nbytes)

La acción copyout() copia nbytes de la memoria intermedia especificada por buf en la dirección especificada por addr en el espacio de direccionamiento del proceso asociado al subproceso actual. Si la dirección de espacio de usuario no se corresponde con una página fallida válida en el espacio de direccionamiento actual, se generará un error.

copyoutstr()

void copyoutstr(string str, uintptr_t addr, size_t maxlen)

La acción copyoutstr() copia la cadena especificada por str en la dirección especificada por addr en el espacio de nombre del proceso asociado al subproceso actual. Si la dirección de espacio de usuario no se corresponde con una página fallida válida en el espacio de direccionamiento actual, se generará un error. La longitud de la cadena está limitada por el valor definido por la opción strsize. Consulte el Capítulo 16Opciones y optimizables para obtener más información.

system()

void system(string program, ...) 

La acción system() provoca que se ejecute el programa especificado por program como si se hubiera especificado en la shell como entrada. La cadena program puede contener cualquiera de las conversiones de formato de printf()/printa. () Deben especificar los argumentos que coincidan con las conversiones de formato. Consulte el Capítulo 12Formato de salida para obtener información sobre las conversiones de formato válidas.

En el ejemplo siguiente, se ejecuta el comando date(1) una vez por segundo:


# dtrace -wqn tick-1sec'{system("date")}'
 Tue Jul 20 11:56:26 CDT 2004
 Tue Jul 20 11:56:27 CDT 2004
 Tue Jul 20 11:56:28 CDT 2004
 Tue Jul 20 11:56:29 CDT 2004
 Tue Jul 20 11:56:30 CDT 2004

En el siguiente ejemplo, se muestra un uso más elaborado de la acción, utilizando las conversiones de printf() en la cadena program junto con las herramientas de filtrado tradicionales como, por ejemplo, las conducciones:

#pragma D option destructive
#pragma D option quiet

proc:::signal-send
/args[2] == SIGINT/
{
	printf("SIGINT sent to %s by ", args[1]->pr_fname);
	system("getent passwd %d | cut -d: -f5", uid);
}

La ejecución de la secuencia de comandos anterior devuelve una salida similar al siguiente ejemplo:


# ./whosend.d
SIGINT sent to MozillaFirebird- by Bryan Cantrill
SIGINT sent to run-mozilla.sh by Bryan Cantrill
^C
SIGINT sent to dtrace by Bryan Cantrill

La ejecución de los comandos especificados no se produce en el contexto de la activación del sondeo, sino cuando la memoria intermedia que contiene los detalles de la acción system() se procesa a nivel de usuario. El momento y la ubicación en que tiene lugar este procesamiento dependen de la directiva de almacenamiento en memoria intermedia, descrita en el Capítulo 11Memorias intermedias y almacenamiento en memoria intermedia. Con la directiva de almacenamiento en memoria intermedia, la velocidad de procesamiento se especifica mediante la opción switchrate. Puede observar el retraso inherente a system() si ajusta de forma explícita switchrate con un valor superior al valor predeterminado de un segundo, como se muestra en el siguiente ejemplo:

#pragma D option quiet
#pragma D option destructive
#pragma D option switchrate=5sec

tick-1sec
/n++ < 5/
{
	printf("walltime  : %Y\n", walltimestamp);
	printf("date      : ");
	system("date");
	printf("\n");
}

tick-1sec
/n == 5/
{
	exit(0);
}

La ejecución de la secuencia de comandos anterior devuelve una salida similar al siguiente ejemplo:


# dtrace -s ./time.d
 walltime  : 2004 Jul 20 13:26:30
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:31
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:32
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:33
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:34
date      : Tue Jul 20 13:26:35 CDT 2004

Tenga en cuenta que el valor de walltime varía, aunque los valores de date sean idénticos. Este resultado refleja el hecho de que la ejecución del comando date(1) sólo se ha producido cuando se ha procesado la memoria intermedia y no cuando se ha registrado la acción system().

Acciones destructivas del núcleo

Algunas acciones son destructivas para todo el sistema. Evidentemente, estas acciones deben utilizarse con mucha precaución, ya que afectarán a todos los procesos del sistema y a cualquier otro sistema que dependa implícita o explícitamente de los servicios de red del sistema afectado.

breakpoint()

void breakpoint(void)

La acción breakpoint() induce un punto de interrupción del núcleo, provocando que el sistema se detenga y transfiera el control al depurador del núcleo, que emitirá una cadena que indicará que el sondeo de DTrace ha desencadenado la acción. Por ejemplo, si se va a realizar lo siguiente:


# dtrace -w -n clock:entry'{breakpoint()}'
dtrace: allowing destructive actions
dtrace: description 'clock:entry' matched 1 probe

En el sistema Solaris que se ejecuta en SPARC, es posible que aparezca el siguiente mensaje en la consola:


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb 30002765700)
Type  'go' to resume
ok

En el sistema Solaris que se ejecuta en x86, es posible que aparezca el siguiente mensaje en la consola:


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb d2b97060)
stopped at      int20+0xb:      ret
kmdb[0]:

La dirección que aparece después de la descripción del sondeo es la dirección del bloque de control de habilitación (ECB) de DTrace. Puede utilizar esta dirección para determinar detalles adicionales acerca de la habilitación del sondeo que ha provocado la acción de punto de interrupción.

Si se comete un error con la acción breakpoint(), es posible que se llame a esta acción con mayor frecuencia de la prevista. Este comportamiento podría a su vez impedir que finalice el consumidor de DTrace que está desencadenando las acciones de punto de interrupción. En esta situación, establezca la variable de entero del núcleo dtrace_destructive_disallow en 1. Esta configuración prohibirá el uso de todas las acciones destructivas en el equipo. Aplique esta configuración sólo en esta situación específica.

El método exacto para establecer dtrace_destructive_disallow dependerá del depurador del núcleo que utilice. Si utiliza OpenBoot PROM en un sistema SPARC, use w!:


ok 1 dtrace_destructive_disallow w!
ok

Confirme que la variable se haya establecido mediante w?:


ok dtrace_destructive_disallow w?
1
ok

A continuación, escriba go:


ok go

Si utiliza kmdb(1) en sistemas x86 o SPARC, use el modificador de escritura de 4 bytes (W) con el formato / dcmd:


kmdb[0]: dtrace_destructive_disallow/W 1
dtrace_destructive_disallow:    0x0             =       0x1
kmdb[0]:

A continuación, utilice :c:


kadb[0]: :c

Para volver a habilitar las acciones destructivas después de continuar con el proceso, deberá restablecer explícitamente dtrace_destructive_disallow a 0 utilizando mdb(1):


# echo "dtrace_destructive_disallow/W 0" | mdb -kw
dtrace_destructive_disallow:    0x1             =       0x0
#

panic()

void panic(void)

La acción panic() genera un aviso grave del núcleo cuando se desencadena. Esta acción debe utilizarse para forzar un volcado por fallo del sistema en el momento pertinente. Puede utilizar esta acción con el almacenamiento en memoria intermedia circular y el análisis postmortem para comprender un problema. Para obtener más información, consulte el Capítulo 11Memorias intermedias y almacenamiento en memoria intermedia y el Capítulo 37Seguimiento postmortem. Cuando se utiliza una acción de pánico, aparece un mensaje grave que indica el sondeo que está provocando la situación. Por ejemplo:


  panic[cpu0]/thread=30001830b80: dtrace: panic action at probe
  syscall::mmap:entry (ecb 300000acfc8)

  000002a10050b840 dtrace:dtrace_probe+518 (fffe, 0, 1830f88, 1830f88,
    30002fb8040, 300000acfc8)
    %l0-3: 0000000000000000 00000300030e4d80 0000030003418000 00000300018c0800
    %l4-7: 000002a10050b980 0000000000000500 0000000000000000 0000000000000502
  000002a10050ba30 genunix:dtrace_systrace_syscall32+44 (0, 2000, 5,
    80000002, 3, 1898400)
    %l0-3: 00000300030de730 0000000002200008 00000000000000e0 000000000184d928
    %l4-7: 00000300030de000 0000000000000730 0000000000000073 0000000000000010

  syncing file systems... 2 done
  dumping to /dev/dsk/c0t0d0s1, offset 214827008, content: kernel
  100% done: 11837 pages dumped, compression ratio 4.66, dump
  succeeded
  rebooting...

syslogd(1M) también emitirá un mensaje al reiniciar:


  Jun 10 16:56:31 machine1 savecore: [ID 570001 auth.error] reboot after panic:
  dtrace: panic action at probe syscall::mmap:entry (ecb 300000acfc8)

La memoria intermedia de mensajes del volcado por fallo del sistema también contiene el sondeo y el ECB responsable de la acción panic().

chill()

void chill(int nanoseconds)

La acción chill() provoca que DTrace gire durante el número especificado de nanosegundos. chill() es útil, sobre todo, para examinar problemas que puedan estar relacionados con el control del tiempo. Por ejemplo, puede utilizar esta acción para abrir las ventanas de condiciones de competencia, o sincronizar y desincronizar eventos periódicos unos con otros. Como las interrupciones están deshabilitadas cuando se encuentra el contexto de sondeo de DTrace, el uso de chill() inducirá la latencia de interrupción, programación y distribución. Por lo tanto, chill() puede provocar efectos sistémicos inesperados, y no debería utilizarse indiscriminadamente. Como las actividades del sistema utilizan la administración de interrupciones periódicas, DTrace no ejecutará la acción chill() durante más de 500 milisegundos para cada intervalo de un segundo en cualquier CPU especificada. Si se supera el intervalo máximo de chill(), DTrace informará de un error de operación no válida, como se muestra en el siguiente ejemplo:


# dtrace -w -n syscall::open:entry'{chill(500000001)}'
dtrace: allowing destructive actions
dtrace: description 'syscall::open:entry' matched 1 probe
dtrace: 57 errors
CPU     ID                    FUNCTION:NAME
dtrace: error on enabled probe ID 1 (ID 14: syscall::open:entry): \
  illegal operation in action #1

Se aplicará obligatoriamente este límite, aunque el tiempo se divida en varias llamadas a chill() o en varios consumidores de DTrace de un único sondeo. Por ejemplo, el siguiente comando generaría el mismo error:


# dtrace -w -n syscall::open:entry'{chill(250000000); chill(250000001);}'

Acciones especiales

En esta sección, se describen las acciones que no son de registro de datos ni destructivas.

Acciones especulativas

Las acciones asociadas al seguimiento especulativo son speculate(), commit() y discard(). Estas acciones se describen en el Capítulo 13Seguimiento especulativo.

exit()

void exit(int status)

La acción exit() se utiliza para detener de forma inmediata el seguimiento e informar al consumidor de DTrace de que debería dejar de realizar el seguimiento, realizar el procesamiento final y llamar a exit(3C) con el estado especificado. Dado que exit() devuelve un estado a nivel de usuario, se trata de una acción de registro de datos. Sin embargo, a diferencia de otras acciones de almacenamiento de datos, no se puede realizar un seguimiento especulativo de exit(). exit() provocará que el consumidor de DTrace detenga el proceso independientemente de la directiva de memoria intermedia. Como exit() es una acción de registro de datos, puede descartarse.

Al llamar a exit(), sólo se completarán las acciones de DTrace en curso en otras CPU. No tendrá lugar ninguna nueva acción en ninguna CPU. La única excepción a esta regla es el procesamiento del sondeo END, al que se llamará una vez que el consumidor de DTrace haya procesado la acción exit() e indicado que debe detenerse el seguimiento.

Subrutinas

Las subrutinas se diferencias de las acciones en que, normalmente, sólo afectan al estado interno de DTrace. Por lo tanto, no hay subrutinas destructivas y, además, éstas nunca realizarán un seguimiento de los datos en las memorias intermedias. Muchas subrutinas tienen elementos análogos en las interfaces de la Sección 9F o la Sección 3C. Consulte Intro(9F) e Intro(3) para obtener más información sobre las subrutinas correspondientes.

alloca()

void *alloca(size_t size)

alloca() asigna el número de bytes especificado en size fuera del espacio temporal y devuelve un puntero a la memoria asignada. Se garantiza que el puntero devuelto presente una alineación de 8 bytes. El espacio temporal sólo es válido durante la duración de una cláusula. La memoria asignada con alloca() se desasignará cuando se complete la cláusula. Si no hay suficiente espacio temporal disponible, no se asignará ninguna memoria y se generará un error.

basename()

string basename(char *str)

basename() es un elemento análogo del lenguaje D para basename(1). Esta subrutina crea una cadena formada por una copia de la cadena especificada, aunque sin un prefijo terminado en /. La cadena devuelta se asigna fuera de la memoria temporal y, por lo tanto, sólo es válida durante la duración de la cláusula. Si no hay suficiente espacio temporal disponible, basename no se ejecuta y se genera un error.

bcopy()

void bcopy(void *src, void *dest, size_t size)

bcopy() copia el número de bytes especificado en size de la memoria a la que señala src a la memoria a la que señala dest. Toda la memoria de origen debe encontrarse fuera de la memoria temporal, y toda la memoria de destino debe encontrarse dentro. Si no se cumplen estas condiciones, no se realizará la copia y se generará un error.

cleanpath()

string cleanpath(char *str)

cleanpath() crea una cadena formada por una copia de la ruta que indica str, aunque con determinados elementos redundantes eliminados. En concreto, se eliminan los elementos "/./" de la ruta y se contraen los elementos "/../". Los elementos /../ de la ruta se contraen independientemente de los vínculos simbólicos. Por lo tanto, es posible que cleanpath() pueda utilizar una ruta válida y devolver una ruta no válida más corta.

Por ejemplo, si str es " /foo/../bar" y /foo es un vínculo simbólico a /net/foo/export, cleanpath() devolverá "/bar", aunque bar sólo puede estar en /net/foo y no en /. Esta limitación se debe al hecho de que la llamada a cleanpath() se realiza en el contexto de una activación del sondeo, en el que no se puede realizar la resolución completa de vínculos simbólicos ni utilizar nombres arbitrarios. La cadena devuelta se asigna fuera de la memoria temporal y, por lo tanto, sólo es válida durante la duración de la cláusula. Si no hay suficiente espacio temporal disponible, cleanpath no se ejecuta y se genera un error.

copyin()

void *copyin(uintptr_t addr, size_t size)

copyin() copia el tamaño especificado en bytes de la dirección de usuario especificada en la memoria intermedia temporal de DTrace y devuelve la dirección de esta memoria intermedia. La dirección de usuario se interpreta como una dirección en el espacio del proceso asociado al subproceso actual. Se garantiza que el puntero de memoria intermedia resultante presente una alineación de 8 bytes. La dirección en cuestión debe corresponderse con la página fallida en el proceso actual. Si la dirección no se corresponde con la página fallida o si no hay suficiente espacio temporal disponible, se devuelve el valor NULL y se genera un error. Consulte el Capítulo 33Seguimiento de procesos de usuario para conocer las técnicas que permiten reducir las probabilidades de que se produzcan errores de copyin.

copyinstr()

string copyinstr(uintptr_t addr)

copyinstr() copia la cadena de C finalizada con un valor "null" de la dirección de usuario especificada en la memoria intermedia temporal de DTrace y devuelve la dirección de esta memoria intermedia. La dirección de usuario se interpreta como una dirección en el espacio del proceso asociado al subproceso actual. La longitud de la cadena está limitada por el valor definido por la opción strsize; consulte el Capítulo 16Opciones y optimizables para obtener información. Al igual que en copyin, la dirección especificada debe corresponderse con una página fallida en el proceso actual. Si la dirección no se corresponde con la página fallida o si no hay suficiente espacio temporal disponible, se devuelve el valor NULL y se genera un error. Consulte el Capítulo 33Seguimiento de procesos de usuario para conocer las técnicas que permiten reducir las probabilidades de que se produzcan errores de copyinstr.

copyinto()

void copyinto(uintptr_t addr, size_t size, void *dest)

copyinto() copia el tamaño especificado en bytes de la dirección de usuario especificada en la memoria intermedia temporal de DTrace indicado por dest. La dirección de usuario se interpreta como una dirección en el espacio del proceso asociado al subproceso actual. La dirección en cuestión debe corresponderse con la página fallida en el proceso actual. Si la dirección no se corresponde con una página fallida o si la memoria de destino se encuentra fuera del espacio temporal, no se realiza la copia y se genera un error. Consulte el Capítulo 33Seguimiento de procesos de usuario para conocer las técnicas que permiten reducir las probabilidades de que se produzcan errores de copyinto.

dirname()

string dirname(char *str)

dirname() es un elemento análogo del lenguaje D para dirname(1). Esta subrutina crea una cadena formada por todos los niveles del nombre de ruta especificado por str, excepto el último. La cadena devuelta se asigna fuera de la memoria temporal y, por lo tanto, sólo es válida durante la duración de la cláusula. Si no hay suficiente espacio temporal disponible, dirname no se ejecuta y se genera un error.

msgdsize()

size_t msgdsize(mblk_t *mp)

msgdsize() devuelve el número de bytes del mensaje de datos al que señala mp. Consulte msgdsize(9F) para obtener más información. msgdsize() sólo incluye bloques de datos del tipo M_DATA en el recuento.

msgsize()

size_t msgsize(mblk_t *mp)

msgsize() devuelve el número de bytes del mensaje al que señala mp. A diferencia de msgdsize(), que devuelve sólo el número de bytes de datos, msgsize () devuelve el número total de bytes del mensaje.

mutex_owned()

int mutex_owned(kmutex_t *mutex)

mutex_owned() es una implementación de mutex_owned(9F). mutex_owned() devuelve un valor distinto a cero si el subproceso de llamada contiene actualmente la exclusión mutua del núcleo especificada o el valor cero si la exclusión mutua adaptable especificada no tiene actualmente ningún propietario.

mutex_owner()

kthread_t *mutex_owner(kmutex_t *mutex)

mutex_owner() el puntero de subproceso del propietario de la exclusión mutua del núcleo adaptable especificada. mutex_owner() devuelve el valor NULL si la exclusión mutua adaptable especificada no tiene actualmente ningún propietario o si la exclusión mutua especificada es circular. Consulte mutex_owned(9F).

mutex_type_adaptive()

int mutex_type_adaptive(kmutex_t *mutex)

mutex_type_adaptive() devuelve un valor distinto a cero si la exclusión mutua del núcleo especificada es del tipo MUTEX_ADAPTIVE o el valor cero si no es de ese tipo. Las exclusiones mutuas son adaptables si cumplen una o varias de las siguientes condiciones:

Consulte mutex_init(9F) para obtener más información sobre las exclusiones mutuas. La mayoría de las exclusiones mutuas del núcleo de Solaris son adaptables.

progenyof()

int progenyof(pid_t pid)

progenyof() devuelve un valor distinto a cero si el proceso de llamada (el proceso asociado al subproceso que está activando actualmente el sondeo coincidente) se encuentra entre la progenie del Id. de proceso especificado.

rand()

int rand(void)

rand() devuelve un entero seudoaleatorio. El número devuelto es un número débil pseudoaleatorio que no debe utilizarse para ninguna aplicación criptográfica.

rw_iswriter()

int rw_iswriter(krwlock_t *rwlock)

rw_iswriter() devuelve un valor distinto a cero si un escritor mantiene o desea realizar un bloqueo de lector-escritor. Si sólo los lectores mantienen el bloqueo y no hay ningún escritor bloqueado, o si no se realiza ningún bloqueo, rw_iswriter() devuelve el valor cero. Consulte rw_init(9F).

rw_write_held()

int rw_write_held(krwlock_t *rwlock)

rw_write_held() devuelve un valor distinto a cero si un escritor mantiene actualmente un bloqueo de escritor-lector. Si sólo los lectores mantienen el bloqueo o si no se realiza ningún bloqueo, rw_write_held() devuelve el valor cero. Consulte rw_init(9F).

speculation()

int speculation(void)

speculation() reserva una memoria intermedia de seguimiento especulativo para su uso con speculate() y devuelve un identificador para esta memoria intermedia. Consulte el Capítulo 13Seguimiento especulativo para obtener más información.

strjoin()

string strjoin(char *str1, char *str2)

strjoin() crea una cadena formada por str1 en combinación con str2. La cadena devuelta se asigna fuera de la memoria temporal y, por lo tanto, sólo es válida durante la duración de la cláusula. Si no hay suficiente espacio temporal disponible, strjoin no se ejecuta y se genera un error.

strlen()

size_t strlen(string str)

strlen() devuelve la longitud de la cadena especificada en bytes, a excepción del byte nulo final.