DTrace


A cheat-sheet on DTrace

In macOS, run csrutil enable --without dtrace in recovery mode (Cmd+R during boot).

Command Line

dtrace -l
dtrace -q

You can also add the following pragma:

#pragma D option quiet
dtrace -C
dtrace -lvn <probe>

One-Liners

dtrace -n 'syscall::read:entry /execname != "dtrace"/ { @reads[execname, fds[arg0].fi_pathname] = count(); }'
dtrace -n 'io:::start { @bytes = quantize(args[0]->b_bcount); }'
dtrace -n 'syscall:::entry { @sc[execname, probefunc] = count(); }'
dtrace -n 'syscall::open:entry { printf("%s %s", execname, copyinstr(arg0)); }'
dtrace -n 'profile-997hz /arg0/ { @[func(arg0)] = count(); }
dtrace -n 'profile-997hz /arg1/ { @[execname, ufunc(arg1)] = count(); }'
dtrace -n 'syscall:::entry { @[pid, execname] = count(); }'
dtrace -n 'vminfo:::as_fault { @mem[execname] = sum(arg0); }'
dtrace -n 'pid$target::malloc:entry { @[arg0] = count(); }' -p <PID>
dtrace -n 'pid$target::malloc:entry { @ = quantize(arg0); }' -p <PID>
dtrace -n 'pid$target::malloc:entry { @[ustack()] = sum(arg0); }' -p <PID>
dtrace -n 'io:::start { @io = count(); } tick-1sec { printa("Disk I/Os per second: %@d \n", @io); trunc(@io); }'

Scripts

#!/usr/sbin/dtrace -qs

fsinfo::: /execname != "dtrace"/ {
  @[execname, probename, args[0]->fi_fs, args[0]->fi_pathname] = count();
}

dtrace:::END {
  trunc(@, 10);
  printf("%-16s %-8s %-8s %-42s %-8s\n", "EXEC", "FUNCTION", "FS TYPE", "PATH", "COUNT");
  printa("%-16s %-8s %-8s %-42s %-@8d\n", @);
}

The D Language

From an inline script:

dtrace -n 'probe /predicate/ { actions }'

From the command line:

dtrace -s script.d

From a script file:

#!/usr/sbin/dtrace -s

probe
/predicate/
{
  actions
}

More than one probe can be defined, separated by comma, if the predicate and actions remain the same.

You can enable all probes of a certain namespace. For example, instead of saying syscall::exit:entry, you can enable probes for the entry points of all system calls as syscall:::entry. DTrace will interpret blank spaces between colons as wildcards. You can also use wildcards. Like syscall::read*:entry.

A string can be checked for emptiness by comparing it with NULL.

Global variables might be accessed by probes firing from different CPUs, so they can become corrupted.

Thread local variables are stored inside the self namespace. For example self->a.

Clause local variables can only be used from a single action group. These are stored inside the this namespace. These variables don’t need to be freed as they are automatically destroyed once the probe is executed.

File Descriptors

Given a file descriptor (i.e. as an argument to the write system call), we can fetch a fileinfo_t structure with information about the file descriptor using the fds object.

The fileinfo_t structure looks like this:

typedef struct fileinfo {
  string fi_name;        // Basename of `fi_pathname`
  string fi_dirname;     // Dirname of `fi_pathname`
  string fi_pathname;    // Full path name
  offset_t fi_offset;    // Offset within the file
  string fi_fs;          // Filesystem
  string fi_mount;       // Mount point of file system
}

For example, we can find what files we’re writing to with write as:

syscall::write:entry {
  @[fds[arg0].fi_pathname] = count();
}

Probes

Probe names are specified like this:

provider:module:function:name

dtrace:::BEGIN

Triggers at the start of the program. Useful to print output headers.

dtrace:::END

Triggers at the end of the program. Useful to print final reports.

profile:::profile-<number><unit>

Fires on every CPU at a rate of <number><unit>. For example profile:::profile-199hz.

This probe has two arguments: arg0 is the program counter if running on the kernel, and arg1 is the program counter when running in user mode. Therefore if arg0 is true, then we’re running in the kernel, and vice-versa.

profile:::tick-<number><unit>

Fires on one CPU only, every <number><unit>. For example: profile:::tick-1s.

Globals

The D language provides the following variables in predicates:

Macros

Aggregates

Variables prefixed by @. For example:

@a = count();

They can be indexed, and the indexes appear as different columns:

@a[uid, probefunc] = count();

If the script only uses one aggregation, then you can just use @ directly (without a name).

The result is automatically sorted in ascending order.

Aggregation Functions

Functions

trace()

Print a variable.

printf()

Print with formatting.

tracemem()

Print a region of memory.

Providers

These are libraries of probes. The most common ones are:

You can see a list of probes from a particular provider as:

dtrace -l -P <provider>

References