Guía de seguimiento dinámico de Solaris

Capítulo 13 Seguimiento especulativo

En este capítulo, se describe la utilidad de DTrace para el seguimiento especulativo, la capacidad de realizar un seguimiento provisional de los datos y, a continuación, decidir si se deben confirmar los datos en una memoria intermedia de seguimiento o descartarlos. En DTrace, el mecanismo principal para filtrar los eventos sin interés es el mecanismo de predicado, que se describe en el Capítulo 4Estructura del programa D. Los predicados resultan de utilidad cuando sabe en el momento en que se activa un sondeo si el evento de sondeo es o no de su interés. Por ejemplo, si sólo le interesan las actividades asociadas a un determinado proceso o descriptor de archivo, sabrá cuando se active el sondeo si está asociado al proceso o descriptor de archivo de su interés. Sin embargo, es posible que en otras situaciones no sepa si un determinado evento de sondeo le interesa hasta que haya transcurrido un tiempo desde la activación del sondeo.

Por ejemplo, si el sistema falla ocasionalmente con un código de error común (por ejemplo, EIO o EINVAL), es posible que desee examinar la ruta de código que ha provocado la condición de error. Para capturar la ruta de código, podría habilitar cada uno de los sondeos, pero sólo si la llamada fallida puede aislarse de tal manera que pueda crearse un predicado significativo. Si los fallos son esporádicos y no determinantes, deberá realizar obligatoriamente un seguimiento de todos los eventos que puedan ser interesantes y un procesamiento posterior de los datos para filtrar aquéllos que no estén asociados a la ruta de código fallida. En ese caso, aunque el número de eventos interesantes pueda ser razonablemente pequeño, el número de eventos de los que debe realizarse un seguimiento es muy elevado, dificultando el procesamiento posterior.

Puede utilizar la utilidad de seguimiento especulativo en estas situaciones para realizar un seguimiento provisional de los datos en una o varias ubicaciones de sondeo y, a continuación, decidir si confirma los datos en la memoria intermedia principal situada en otra ubicación de sondeo. Como resultado, los datos del seguimiento contienen sólo los resultados relevantes, por lo que no es necesario ningún procesamiento posterior, y se reduce al mínimo la carga adicional indirecta de DTrace.

Interfaces de especulación

La siguiente tabla describe las funciones de especulación de DTrace:

Tabla 13–1 Funciones de especulación de DTrace

Nombre de la función 

Arg. 

Descripción 

speculation

Ninguna 

Devuelve un identificador para una nueva memoria intermedia especulativa. 

speculate

Id. 

Indica que debería realizarse un seguimiento del resto de la cláusula en la memoria intermedia especulativa especificada por el Id. 

commit

Id. 

Confirma la memoria intermedia especulativa asociada al Id. 

discard

Id. 

Descarta la memoria intermedia especulativa asociada al Id. 

Creación de una especulación

La función speculation() asigna una memoria intermedia especulativa y devuelve un identificador de especulación. El identificador de especulación debería utilizarse en llamadas posteriores a la función speculate. () Las memorias intermedias especulativas son un recurso finito: si no hay disponible ninguna memoria intermedia especulativa al llamar a speculation(), se devuelve un Id. con el valor 0 y se aumenta el contador de errores de DTrace correspondiente. Un Id. con el valor cero no es nunca válido, aunque puede transferirse a speculate(), commit() o discard(). Si falla una llamada a speculation(), se genera un mensaje de dtrace parecido al siguiente:


dtrace: 2 failed speculations (no speculative buffer space available)

El número de memorias intermedias especulativas se establece de forma predeterminada en 1, pero puede ajustarse opcionalmente a un valor superior. Consulte Opciones y ajustes de especulación para obtener más información.

Uso de una especulación

Para utilizar una especulación, el identificador devuelto por speculation() debe transferirse a la función speculate() en una cláusula antes de llevar a cabo cualquier acción de registro de datos. Se realizará un seguimiento especulativo de todas las acciones de registro de datos siguientes de la cláusula que contenga una función speculate. () El compilador del lenguaje D generará un error de tiempo de compilación si la llamada a speculate() es posterior a las acciones de registro de datos en una cláusula de sondeo de D. Por lo tanto, las cláusulas pueden contener solicitudes de seguimiento especulativo o no especulativo, pero no ambas.

Las acciones de adición, las acciones destructivas y la acción exit nunca pueden ser especulativas. Cualquier intento de realizar una de estas acciones en una cláusula que contenga una función speculate() provocará un error de tiempo de compilación. La función speculate() no puede ir detrás de otra función speculate(): sólo se permite una especulación por cláusula. Una cláusula que contenga sólo una función speculate() realizará un seguimiento especulativo de la acción predeterminada, que se define para realizar un seguimiento sólo del Id. de sondeo habilitado. Consulte le Capítulo 10Acciones y subrutinas, para obtener una descripción de la acción predeterminada.

Normalmente, debe asignar el resultado de una función speculation() a una variable de subproceso local y, a continuación, utilizar esa variable como predicado subsiguiente de otros sondeos, así como un argumento para speculate(). Por ejemplo:

syscall::open:entry
{
	self->spec = speculation();
}

syscall:::
/self->spec/
{
	speculate(self->spec);
	printf("this is speculative");
}

Confirmación de una especulación

Puede confirmar especulaciones mediante la función commit. () Al confirmar una memoria intermedia especulativa, sus datos se copian en la memoria intermedia principal. Si hay más datos en la memoria intermedia especulativa que espacio disponible en la memoria intermedia principal, no se copia ningún dato y se aumenta el recuento de anulaciones de la memoria intermedia. Si se ha realizado un seguimiento especulativo de la memoria intermedia en más de una CPU, los datos especulativos de la CPU de confirmación se copian inmediatamente, mientras que los datos de las otras CPU se copian una vez transcurrido un periodo de tiempo desde que se efectuó la acción commit(). Por lo tanto, es posible que pase algún tiempo entre el inicio de la acción commit() en una CPU y la copia de datos de las memorias intermedias especulativas a las memorias intermedias principales en todas las CPU. Se garantiza que este periodo de tiempo no sea superior al tiempo indicado por la tasa de limpieza. Consulte Opciones y ajustes de especulación para obtener más información.

No estará disponible una memoria intermedia especulativa de confirmación para las siguientes llamadas a speculation() hasta que cada memoria intermedia especulativa por CPU se haya copiado por completo en la memoria intermedia principal por CPU correspondiente. Del mismo modo, se descartarán las posteriores llamadas a la función speculate() para la memoria intermedia silenciosa y fallarán de forma silenciosa las siguientes llamadas a la función commit() o discard. () Por último, una cláusula que incluya una función commit() no puede contener ninguna acción de registro de datos, aunque una cláusula puede contener varias llamadas a commit() para confirmar memorias intermedias independientes.

Descartar una especulación

Puede descartar especulaciones mediante la función discard. () Al descartar una memoria intermedia especulativa, se elimina su contenido. Si la especulación sólo ha estado activa en la CPU que llama a discard(), la memoria intermedia estará disponible de forma inmediata para las siguientes llamadas a la función speculation (). Si la especulación ha estado activa en varias CPU, la memoria intermedia descartada estará disponible para la siguiente función speculation() una vez transcurrido un tiempo después de efectuar la llamada a discard(). Se garantiza que el tiempo transcurrido entra la acción discard() en una CPU y la disponibilidad de la memoria intermedia para las siguientes especulaciones no sea superior al tiempo indicado por la tasa de limpieza. Si, al llamar a la funciónspeculation(), no hay disponible ninguna memoria intermedia porque se han confirmado o descargado todas las memorias intermedias especulativas, se generará un mensaje de dtrace parecido al siguiente:


dtrace: 905 failed speculations (available buffer(s) still busy)

La probabilidad de que no haya ninguna memoria intermedia disponible puede reducirse ajustando el número de memorias intermedias especulativas o la tasa de limpieza. Consulte Opciones y ajustes de especulación para obtener más información.

Ejemplo de especulación

Un posible uso de las especulaciones consiste en resaltar una determinada ruta de código. En el siguiente ejemplo, se muestra la ruta de código completa debajo de la llamada del sistema open(2) cuando falla la función open():


Ejemplo 13–1 specopen.d: flujo de código para una operación fallida open(2)

#!/usr/sbin/dtrace -Fs

syscall::open:entry,
syscall::open64:entry
{
	/*
	 * The call to speculation() creates a new speculation.  If this fails,
	 * dtrace(1M) will generate an error message indicating the reason for
	 * the failed speculation(), but subsequent speculative tracing will be
	 * silently discarded.
	 */
	self->spec = speculation();
	speculate(self->spec);

	/*
	 * Because this printf() follows the speculate(), it is being 
	 * speculatively traced; it will only appear in the data buffer if the
	 * speculation is subsequently commited.
	 */
	printf("%s", stringof(copyinstr(arg0)));
}

fbt:::
/self->spec/
{
	/*
	 * A speculate() with no other actions speculates the default action:
	 * tracing the EPID.
	 */
	speculate(self->spec);
}

syscall::open:return,
syscall::open64:return
/self->spec/
{
	/*
	 * To balance the output with the -F option, we want to be sure that
	 * every entry has a matching return.  Because we speculated the
	 * open entry above, we want to also speculate the open return.
	 * This is also a convenient time to trace the errno value.
	 */
	speculate(self->spec);
	trace(errno);
}

syscall::open:return,
syscall::open64:return
/self->spec && errno != 0/
{
	/*
	 * If errno is non-zero, we want to commit the speculation.
	 */
	commit(self->spec);
	self->spec = 0;
}

syscall::open:return,
syscall::open64:return
/self->spec && errno == 0/
{
	/*
	 * If errno is not set, we discard the speculation.
	 */
	discard(self->spec);
	self->spec = 0;
}

Si se ejecuta la secuencia de comandos anterior, la salida es parecida a la siguiente:


# ./specopen.d
dtrace: script './specopen.d' matched 24282 probes
CPU FUNCTION                                 
  1  => open                                  /var/ld/ld.config
  1    -> open                                
  1      -> copen                             
  1        -> falloc                          
  1          -> ufalloc                       
  1            -> fd_find                     
  1              -> mutex_owned               
  1              <- mutex_owned               
  1            <- fd_find                     
  1            -> fd_reserve                  
  1              -> mutex_owned               
  1              <- mutex_owned               
  1              -> mutex_owned               
  1              <- mutex_owned               
  1            <- fd_reserve                  
  1          <- ufalloc                       
  1          -> kmem_cache_alloc              
  1            -> kmem_cache_alloc_debug      
  1              -> verify_and_copy_pattern   
  1              <- verify_and_copy_pattern   
  1              -> file_cache_constructor    
  1                -> mutex_init              
  1                <- mutex_init              
  1              <- file_cache_constructor    
  1              -> tsc_gethrtime             
  1              <- tsc_gethrtime             
  1              -> getpcstack                
  1              <- getpcstack                
  1              -> kmem_log_enter            
  1              <- kmem_log_enter            
  1            <- kmem_cache_alloc_debug      
  1          <- kmem_cache_alloc              
  1          -> crhold                        
  1          <- crhold                        
  1        <- falloc                          
  1        -> vn_openat                       
  1          -> lookupnameat                  
  1            -> copyinstr                   
  1            <- copyinstr                   
  1            -> lookuppnat                  
  1              -> lookuppnvp                
  1                -> pn_fixslash             
  1                <- pn_fixslash             
  1                -> pn_getcomponent         
  1                <- pn_getcomponent         
  1                -> ufs_lookup              
  1                  -> dnlc_lookup           
  1                    -> bcmp                
  1                    <- bcmp                
  1                  <- dnlc_lookup           
  1                  -> ufs_iaccess           
  1                    -> crgetuid            
  1                    <- crgetuid            
  1                    -> groupmember         
  1                      -> supgroupmember    
  1                      <- supgroupmember
  1                    <- groupmember         
  1                  <- ufs_iaccess           
  1                <- ufs_lookup              
  1                -> vn_rele                 
  1                <- vn_rele                 
  1                -> pn_getcomponent         
  1                <- pn_getcomponent         
  1                -> ufs_lookup              
  1                  -> dnlc_lookup           
  1                    -> bcmp                
  1                    <- bcmp                
  1                  <- dnlc_lookup           
  1                  -> ufs_iaccess           
  1                    -> crgetuid            
  1                    <- crgetuid            
  1                  <- ufs_iaccess           
  1                <- ufs_lookup              
  1                -> vn_rele                 
  1                <- vn_rele                 
  1                -> pn_getcomponent         
  1                <- pn_getcomponent         
  1                -> ufs_lookup              
  1                  -> dnlc_lookup           
  1                    -> bcmp                
  1                    <- bcmp                
  1                  <- dnlc_lookup           
  1                  -> ufs_iaccess           
  1                    -> crgetuid            
  1                    <- crgetuid            
  1                  <- ufs_iaccess           
  1                  -> vn_rele               
  1                  <- vn_rele               
  1                <- ufs_lookup              
  1                -> vn_rele                 
  1                <- vn_rele                 
  1              <- lookuppnvp                
  1            <- lookuppnat                  
  1          <- lookupnameat                  
  1        <- vn_openat                       
  1        -> setf                            
  1          -> fd_reserve                    
  1            -> mutex_owned                 
  1            <- mutex_owned                 
  1            -> mutex_owned                 
  1            <- mutex_owned                 
  1          <- fd_reserve                    
  1          -> cv_broadcast                  
  1          <- cv_broadcast                  
  1        <- setf                            
  1        -> unfalloc                        
  1          -> mutex_owned                   
  1          <- mutex_owned                   
  1          -> crfree                        
  1          <- crfree                        
  1          -> kmem_cache_free               
  1            -> kmem_cache_free_debug       
  1              -> kmem_log_enter            
  1              <- kmem_log_enter            
  1              -> tsc_gethrtime             
  1              <- tsc_gethrtime             
  1              -> getpcstack                
  1              <- getpcstack                
  1              -> kmem_log_enter            
  1              <- kmem_log_enter
  1              -> file_cache_destructor     
  1                -> mutex_destroy           
  1                <- mutex_destroy           
  1              <- file_cache_destructor     
  1              -> copy_pattern              
  1              <- copy_pattern              
  1            <- kmem_cache_free_debug       
  1          <- kmem_cache_free               
  1        <- unfalloc                        
  1        -> set_errno                       
  1        <- set_errno                       
  1      <- copen                             
  1    <- open                                
  1  <= open                                          2

Opciones y ajustes de especulación

Si una memoria intermedia especulativa está llena cuando se intenta realizar una acción de seguimiento especulativo, no se almacena ningún dato en la memoria intermedia y se aumenta el recuento de anulaciones. Si se produce esta situación, se generará un mensaje de dtrace parecido al siguiente:


dtrace: 38 speculative drops

Las anulaciones especulativas no impedirán que se copie la memoria intermedia especulativa completa en la memoria intermedia principal al confirmar la memoria intermedia. Del mismo modo, se pueden producir anulaciones especulativas, aunque éstas se experimenten en una memoria intermedia especulativa que se haya descartado en último término. Puede reducir el número de anulaciones especulativas aumentando el tamaño de la memoria intermedia especulativa, que se puede ajustar mediante la opción specsize. La opción specsize puede especificarse con cualquier sufijo de tamaño. La opción bufresize establece la directiva de cambio de tamaño de esta memoria intermedia.

Es posible que las memorias intermedias especulativas no estén disponibles al llamar a la función speculation. () Si hay memorias intermedias que aún no se han confirmado o descartado, se generará un mensaje de dtrace parecido al siguiente:


dtrace: 1 failed speculation (no speculative buffer available)

Puede reducir las probabilidades de que se produzcan especulaciones fallidas de esta naturaleza, aumentando el número de memorias intermedias especulativas con la opción nspec. El valor de nspec se establece de forma predeterminada en 1.

Es posible que speculation() pueda fallar también debido a que todas las memorias intermedias especulativas estén ocupadas. En ese caso, se generará un mensaje de dtrace parecido al siguiente:


dtrace: 1 failed speculation (available buffer(s) still busy)

Este mensaje indica que se ha llamado a la función speculation() después de llamar a commit() para una memoria intermedia especulativa, pero antes de que la memoria intermedia se haya confirmado realmente en todas las CPU. Puede reducir las probabilidades de que se produzcan especulaciones fallidas de esta naturaleza, aumentando la tasa de limpieza de las CPU con la opción cleanrate. El valor de cleanrate se establece de forma predeterminada en 101hz.


Nota –

Los valores de la opción cleanrate se deben especificar en número por segundo. Utilice el sufijo hz.