Go to main content

Oracle® Solaris 11.4 DTrace (Dynamic Tracing) Guide

Exit Print View

Updated: September 2020
 
 

DTrace Handler Interfaces

A DTrace consumer might encounter certain errors while running a D program and consuming the data it generates. For example, the DTrace framework might drop data, or terminate an instrumented process. The libdtrace library has a default behavior to handle such cases, but this behavior might not be appropriate for a particular consumer. You can specify alternate behavior through the handler interface provided by the libdtrace library.

Drop Handler

Because DTrace uses fixed-size buffers in the kernel and the mechanism for transferring data from these buffers is driven by the consumer, the system is designed to drop data when there is no space in the buffers. The DTrace framework has a mechanism to inform the consumer about how much data is dropped and why the data is being dropped.

By default, thelibdtrace library aborts the process when the consumer is informed about data drop. You can see this behavior if you run a simple consumer by using the following D program:

#pragma D option dynvarsize=1k

syscall:::entry
{   
       self->ts = timestamp;
}
syscall:::return
/self->ts/
{
      @c[probefunc] = avg(timestamp - self->ts);
      self->ts = 0;
}

By setting the dynvarsize tunable to a small value, you can guarantee that the program runs out of dynamic variable space while the program is executing. The following consumer does not implement a drop handler:

# ./consumer-no-drophandler
processing aborted: Abort due to drop
#

The dtrace_handle_drop() interface provided by the libdtrace library specifies a drop handler.

    The arguments to the dtrace_handle_drop() function are:

  • DTrace handle

  • Pointer to a function to handle the drop

  • Pointer to a private argument to be passed to the drop handler

The dtrace_handle_drop() function takes two arguments: a pointer to the dtrace_dropdata data structure and a pointer to the private argument originally passed to the dtrace_handle_drop() function. It returns one of two values: DTRACE_HANDLE_ABORT or DTRACE_HANDLE_OK.

The dtrace_dropdata data structure contains information about the data drop since the last time the handler was called.

typedef struct dtrace_dropdata {
        dtrace_hdl_t *dtdda_handle;             /* handle to DTrace library */
        processorid_t dtdda_cpu;                /* CPU, if any */
        dtrace_dropkind_t dtdda_kind;           /* kind of drop */
        uint64_t dtdda_drops;                   /* number of drops */
        uint64_t dtdda_total;                   /* total drops */
        const char *dtdda_msg;                  /* preconstructed message */
} dtrace_dropdata_t;

    The drop handler has access to the following information:

  • DTrace handle

  • CPU on which the drops occur

  • Number and kind of drop for the call, including the total number

  • Predefined message

A simple drop handler returns DTRACE_HANDLE_OK to ignore all data drops. However, the following example alerts the person running the D program about the data drop by printing a predefined message.

static int
drophandler(const dtrace_dropdata_t *dropdata, void *arg)
{
        fprintf(stderr, "%s", dropdata->dtdda_msg);
        return (DTRACE_HANDLE_OK);
}

The output of example consumer would appear similar to the following when using this drop handler:

# ./consumer-drophandler
255 dynamic variable drops with non-empty dirty list
417 dynamic variable drops with non-empty dirty list
385 dynamic variable drops with non-empty dirty list
407 dynamic variable drops with non-empty dirty list
428 dynamic variable drops with non-empty dirty list
417 dynamic variable drops with non-empty dirty list
2 dynamic variable drops
447 dynamic variable drops with non-empty dirty list
431 dynamic variable drops with non-empty dirty list
440 dynamic variable drops with non-empty dirty list
29 dynamic variable drops
497 dynamic variable drops with non-empty dirty list
273 dynamic variable drops with non-empty dirty list
  getpid                                                 1633
  sysconfig                                              1651
  p_online                                               1977
  gtime                                                  2279
  pset                                                   2924
  clock_gettime                                          3046
[ ... ]

    A more sophisticated drop handler might choose to terminate processing under certain conditions. A consumer might choose to terminate under the following conditions:

  • Number of drops exceeds a threshold within a certain period.

  • Total number of drops exceeds a certain threshold, only upon seeing the drops from a particular CPU, or only upon seeing certain types of drops.

For example, a process aborts on seeing the aggregation drops but not on seeing the dynamic variable drops.

Error Handler

The libdtrace library enables you to install an error handler similar to the drop handler. Errors can be events such as a process attempting to access an invalid pointer or attempting to divide by zero. The default behavior for errors is to terminate the process, which is the same behavior as for drops. You can see this behavior if you run the Example 35, Embedding DTrace in a Consumer with the following D program:

BEGIN
{
     trace(strlen(0));
}
syscall:::entry
{
     @c[probefunc] = count();
}

When you run this program, the following output is displayed:

# ./consumer-no-errhandler
processing aborted: Abort due to error
#

    The dtrace_handle_err() interface provided by the libdtrace library specifies an error handler. The arguments to the dtrace_handle_err() function are:

  • DTrace handle

  • Pointer to a function to handle the error

  • Pointer to a private argument to be passed to the error handler

The error handler function takes two arguments: a pointer to the dtrace_errdata data structure and a pointer to the private argument originally passed to the dtrace_handle_err() function. It returns one of the two values, DTRACE_HANDLE_ABORT or DTRACE_HANDLE_OK.

The dtrace_errdata data structure contains information about the error that occurred.

typedef struct dtrace_errdata {
       dtrace_hdl_t *dteda_handle;                 /* handle to DTrace library */
       dtrace_eprobedesc_t *dteda_edesc;           /* enabled probe inducing err */
       dtrace_probedesc_t *dteda_pdesc;            /* probe inducing error */
       processorid_t dteda_cpu;                    /* CPU of error */
       int dteda_action;                           /* action inducing error */
       int dteda_offset;                           /* offset in DIFO of error */
       int dteda_fault;                            /* specific fault */
       uint64_t dteda_addr;                        /* address of fault, if any */
       const char *dteda_msg;                      /* preconstructed message */
} dtrace_errdata_t;

Aside from the self-evident members of this structure, the dteda_pdesc probe description contains the names of the provider, module, function, and name, and predefined error message. The simplest error handler returns DTRACE_HANDLE_OK to ignore any errors. A more sophisticated error handler also prints the predefined error message.

static int
errhandler(const dtrace_errdata_t *data, void *arg)
{ 
        fprintf(stderr, "%s", data->dteda_msg);
        return(DTRACE_HANDLE_OK);
}

The error handler used in Example 35, Embedding DTrace in a Consumer displays output similar to the following example:

# ./consumer-errhandler
error on enabled probe ID 1 (ID 1: dtrace:::BEGIN): invalid address (0x0) in
action #1 at DIF offset 28
  fstat64                              1
  mmap                                 1 
  schedctl                             1
  sendto                               1
  pset                                 3
  sysconfig                            3
  brk                                  6  
  gtime                                6
  write                               11
  lwp_park                            13
  setitimer                           16
  read                                18
  p_online                           256
  pollsys                            315
  lwp_sigmask                        570
  ioctl                             1410
#

    A more sophisticated error handler might choose to terminate processing under certain conditions. A consumer might choose to terminate in the following conditions:

  • Number of errors exceeds a threshold within a certain period.

  • Total number of errors exceeds a certain threshold, only upon seeing the errors from a particular CPU, or only upon seeing certain types of errors.

For example, a process might terminate on a division by zero but not on accessing an invalid address.

Process Handler

DTrace provides the ability to insert user space probes in processes. DTrace can inform the consumer when certain events occur with respect to a process. You can install a process handler that runs when such events occur. For more information, about the APIs that provide the ability to insert user space probes in processes, see Process Control Interface in DTrace.

A process handler is invoked when a traced process terminates, or if DTrace cannot open or reopen a process (the process is untraceable), or if a shared library is loaded but an error occurred when instrumenting the library it invokes the process handler. For more information about controlling the process interface, see Example 37, Using the Process Control Interface.

setopt Handler

    Options affecting the behavior of DTrace can be set in one of the following ways:

  • Directly by the consumer

  • Indirectly by using a pragma in the D program

  • From within a D program with the setopt() action.

In the first two cases, the option is set prior to running the D program. In the third case, the option is set at runtime.


Note -  Certain options, such as buffer sizes, might only be set prior to executing a D program.

The consumer must be informed when the values of certain options change during the execution of a D program. The setopt handler informs the consumer about any change in the options during runtime. In the absence of the setopt handler, the processing continues when options are changed. This behavior differs from the behavior for errors and drops, but setting options is not considered to be an abnormal occurrence.

    The dtrace_handle_setopt() interface provided by the libdtrace library specifies a setopt handler. The arguments to the dtrace_handle_setopt() function are:

  • DTrace handle

  • Pointer to a function to handle the setopt

  • Pointer to a private argument to be passed to the setopt handler

The setopt handler is a function that takes two arguments: a pointer to the dtrace_setoptdata data structure and a pointer to the private argument originally passed to the dtrace_handle_setopt() function. It returns one of two values: DTRACE_HANDLE_ABORT or DTRACE_HANDLE_OK.

The dtrace_setoptdata data structure contains information about the option that was set, including which option was set and the old and new values for that option.

typedef struct dtrace_setoptdata {
        dtrace_hdl_t *dtsda_handle;                 /* handle to DTrace library */
        const dtrace_probedata_t *dtsda_probe;      /* probe data */
        const char *dtsda_option;                   /* option that was set */
        dtrace_optval_t dtsda_oldval;               /* old value */
        dtrace_optval_t dtsda_newval;               /* new value */
} dtrace_setoptdata_t;

Because the changing of values is not an abnormal occurrence, there is no predefined message is included in this data structure. So there is no setopt handler to parallel the drop and error handlers where the prepared message is displayed to the user. You can ignore a setopt either by installing a handler that only returns DTRACE_HANDLE_OK or by relying on the default behavior.

There might be cases in which the consumer might alter its behavior based on the value of certain options. In the following example, the setopt handler for dtrace checks whether the value for the quiet or flowindent option has been set.

static int
setopthandler(const dtrace_setoptdata_t *data, void *arg)
{
       if (strcmp(data->dtsda_option, "quiet") == 0)
               g_quiet = data->dtsda_newval != DTRACEOPT_UNSET;
 
       if (strcmp(data->dtsda_option, "flowindent") == 0)
               g_flowindent = data->dtsda_newval != DTRACEOPT_UNSET;

       return (DTRACE_HANDLE_OK);
}

The dtrace utility predicates certain behavior on those options. The chew() function for DTrace prints extra information, such as the probe ID and the CPU on which a probe fired, when the quiet option is not set. The chew() function also implements the formatting for the flowindent processing.