Process Control Interface in DTrace

One of the strengths of DTrace is its ability to insert probes in user space and in the kernel. The libdtrace library provides the following functions to consumers to manipulate processes:

  • struct ps_prochandle *dtrace_proc_create(dtrace_hdl_t *dtp, const char *file, char *const *argv);
  • struct ps_prochandle *dtrace_proc_grab(dtrace_hdl_t *dtp, pid_t pid, int flags);
  • void dtrace_proc_release(dtrace_hdl_t *dtp, struct ps_prochandle *P);
  • void dtrace_proc_continue(dtrace_hdl_t *dtp, struct ps_prochandle *P);
  • dtrace_proc_create(): Creates a process to run the specified program, file, with the specified arguments, argv. When the process is created it is in a stop state so that DTrace can instrument the process before it runs.

    Note:

    Either the dtrace_proc_create() function or the dtrace_proc_grab() function must be called before a D program is compiled by the consumer. Certain variables, such as $target, are set based on values set by these functions.
  • dtrace_proc_grab(): Attaches to an existing process specified by PID. By default, the process is left in a stop state so that DTrace can instrument the process before it continues to run. You can modify this process by specifying the PGRAB_NOSTOP flag in the flags argument. The flags argument is passed to the pgrab() function from the libproc library. For more information, see the libproc.h man page.

  • dtrace_proc_release(): Releases a process, which is then grabbed or created by using the dtrace_proc_create() or the dtrace_proc_grab() functions. A process that is created by the consumer terminates after being released.

  • dtrace_proc_continue(): Starts the stopped grabbed or created process.

Example 20-3 Using the Process Control Interface

You can perform simple profiling on an application by using the following D program:

pid$target:::entry
{
       @c[probefunc] = count();
}

You can modify the consumer example to perform simple profiling on an application. First, you must either create or grab the process you want to profile. If you want to create a process, add the following code before the calling the dtrace_program_strcompile() function. Because the consumer example is simple, it takes no arguments and you can pass the remaining arguments to the dtrace_proc_create() function.

if ((g_pr = dtrace_proc_create(g_dtp, argv[1], &argv[1])) == NULL)
        fatal("cannot create process");

If you want to grab a existing process, call the dtrace_proc_grab() function:

if ((g_pr = dtrace_proc_grab(g_dtp, (pid_t)atoi(argv[1]), 0)) == NULL)
        fatal("cannot grab process");

In each case, you must also call the dtrace_proc_continue() function after calling the dtrace_go() function, which starts the process again.

The original consumer loops a specified number of times before printing the gathered data and exiting. If you have grabbed a process, and want to grab a sample profile without waiting for the process to exit, you can use the same loop. However, if you want to follow either a grabbed or created process to its completion, you must modify the loop as follows:

do {
      dtrace_sleep(g_dtp);

      if (g_done) {
              if (dtrace_stop(g_dtp) == -1)
                      fatal("dtrace_stop()");    
       }
      
       switch (dtrace_work(g_dtp, stdout, chew, chewrec, NULL)) {
       case DTRACE_WORKSTATUS_DONE:
               g_done = 1;     
               break;
       case DTRACE_WORKSTATUS_OKAY:
               break;
       default:
               fatal("processing aborted");
       }  
    } while (!g_done);

This loop does not terminate by itself when the process exits. The consumer requires a process handler to catch the process termination:

static void
prochandler(struct ps_prochandle *P, const char *msg, void *arg)
{
        if (Pstate(P) == PS_UNDEAD)
                g_done = 1;
        return;
}

You can install this handler as follows:

if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1)
        fatal("failed to install proc handler");

When you run this consumer example, you can see the following output:

# ./consumer-proc-create /bin/ls /
bin      devices     home      media      opt        root     system     var
boot     etc         kernel    mnt        platform   rpool    tmp
dev      export      lib       net        proc       sbin     usr
  ___lwp_private                                                     1
  __close                                                            1
  __fcntl_syscall                                                    1
[ ... ] 
   mutex_lock                                                       35
   mutex_lock_impl                                                  35
   mutex_unlock                                                     35
   ferror                                                           44
   strlen                                                           45
   _ti_bind_clear                                                   54
   set_cancel_pending_flag                                          56
   rt_bind_clear                                                    58
   strcol                                                           67
   __time                                                           70
   time                                                             70
   __strcoll_C                                                      78
   compar                                                           78
   strcoll                                                          78
   sigon                                                            97
   _flsbuf                                                         109
   strcmp                                                          113
   _realbufend                                                     134
   getxfdat                                                        135