[DTrace-devel] [PATCH v2] examples: add a new set of scripts
Eugene Loh
eugene.loh at oracle.com
Fri Oct 17 19:23:32 UTC 2025
Reviewed-by: Eugene Loh <eugene.loh at oracle.com>
On 10/17/25 15:22, eugene.loh at oracle.com wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
>
> This is a set of new example scripts. These are basic programs to
> demonstrate specific functionality. For example to get an overview
> of system calls executed, processes running, I/O statistics, etc.
> There is also an example of a D script embedded in a shell script.
>
> Signed-off-by: Ruud van der Pas <ruud.vanderpas at oracle.com>
> ---
> examples/getting-started/activity.d | 129 ++++++++++++++++++++
> examples/getting-started/activity1.d | 144 ++++++++++++++++++++++
> examples/getting-started/calltrace.d | 95 +++++++++++++++
> examples/getting-started/countcalls.d | 45 +++++++
> examples/getting-started/countprogs.d | 44 +++++++
> examples/getting-started/countsyscalls.d | 38 ++++++
> examples/getting-started/cswpercpu.d | 88 ++++++++++++++
> examples/getting-started/daterun.d | 42 +++++++
> examples/getting-started/diskact.d | 106 +++++++++++++++++
> examples/getting-started/errno.d | 56 +++++++++
> examples/getting-started/errno1.d | 145 +++++++++++++++++++++++
> examples/getting-started/execcalls.d | 51 ++++++++
> examples/getting-started/fsact.sh | 114 ++++++++++++++++++
> examples/getting-started/goodbye.d | 36 ++++++
> examples/getting-started/hello.d | 28 +++++
> examples/getting-started/readsizes.d | 46 +++++++
> examples/getting-started/readtrace.d | 72 +++++++++++
> examples/getting-started/readtrace1.d | 78 ++++++++++++
> examples/getting-started/rwdiskact.d | 112 +++++++++++++++++
> examples/getting-started/syscalls.d | 50 ++++++++
> examples/getting-started/syscalls1.d | 62 ++++++++++
> examples/getting-started/tick.d | 55 +++++++++
> examples/getting-started/tick1.d | 56 +++++++++
> examples/getting-started/wrun.d | 55 +++++++++
> 24 files changed, 1747 insertions(+)
> create mode 100755 examples/getting-started/activity.d
> create mode 100755 examples/getting-started/activity1.d
> create mode 100755 examples/getting-started/calltrace.d
> create mode 100755 examples/getting-started/countcalls.d
> create mode 100755 examples/getting-started/countprogs.d
> create mode 100755 examples/getting-started/countsyscalls.d
> create mode 100755 examples/getting-started/cswpercpu.d
> create mode 100755 examples/getting-started/daterun.d
> create mode 100755 examples/getting-started/diskact.d
> create mode 100755 examples/getting-started/errno.d
> create mode 100755 examples/getting-started/errno1.d
> create mode 100755 examples/getting-started/execcalls.d
> create mode 100755 examples/getting-started/fsact.sh
> create mode 100755 examples/getting-started/goodbye.d
> create mode 100755 examples/getting-started/hello.d
> create mode 100755 examples/getting-started/readsizes.d
> create mode 100755 examples/getting-started/readtrace.d
> create mode 100755 examples/getting-started/readtrace1.d
> create mode 100755 examples/getting-started/rwdiskact.d
> create mode 100755 examples/getting-started/syscalls.d
> create mode 100755 examples/getting-started/syscalls1.d
> create mode 100755 examples/getting-started/tick.d
> create mode 100644 examples/getting-started/tick1.d
> create mode 100755 examples/getting-started/wrun.d
>
> diff --git a/examples/getting-started/activity.d b/examples/getting-started/activity.d
> new file mode 100755
> index 000000000..30f13b6d5
> --- /dev/null
> +++ b/examples/getting-started/activity.d
> @@ -0,0 +1,129 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * activity.d - report on process create, exec and exit
> + *
> + * SYNOPSIS
> + * sudo dtrace -s activity.d
> + *
> + * DESCRIPTION
> + * Show the processes that are created, executed and exited
> + * while the script is running.
> + *
> + * NOTES
> + * - This script uses the proc provider to trace the following process
> + * activities: create, exec, and exit.
> + *
> + * - This script is guaranteed to produce results if you start one or
> + * more commands while the script is running. There are two ways
> + * to do this:
> + * o Execute this script in the background, and type in the command(s).
> + * o Alternatively, run the script in the foreground and type the
> + * command(s) in a separate terminal window on the same system.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - Associative arrays are used to store the information from the
> + * proc provider.
> + *
> + * - The DTrace User Guide documents the proc provider probe
> + * arguments like args[0] and also structures like psinfo_t. It is
> + * strongly recommended to check the documentation for this info.
> + */
> +
> +/*
> + * Fires when a process (or process thread) is created using fork() or
> + * vfork(), which both invoke clone(). The psinfo_t corresponding to
> + * the new child process is pointed to by args[0].
> + */
> +proc:::create
> +{
> +/*
> + * Store the PID of both the parent and child process from the psinfo_t
> + * structure pointed to by args[0]. Use 3 associative arrays to store the
> + * various items of interest.
> + */
> + this->childpid = args[0]->pr_pid;
> + this->parentpid = args[0]->pr_ppid;
> +
> +/*
> + * Store the parent PID of the new child process.
> + */
> + p_pid[this->childpid] = this->parentpid;
> +
> +/*
> + * Parent command name.
> + */
> + p_name[this->childpid] = execname;
> +
> +/*
> + * Child has not yet been exec'ed.
> + */
> + p_exec[this->childpid] = "";
> +}
> +
> +/*
> + * The process starts. In case proc:::create has fired, store the
> + * absolute time and the full name of the child process.
> + */
> +proc:::exec
> +/ p_pid[pid] != 0 /
> +{
> + time[pid] = timestamp;
> + p_exec[pid] = args[0];
> +}
> +
> +/*
> + * The process starts, but in this case, proc:::create has not fired.
> + * In addition to storing the name of the child process, store the
> + * various other items of interest.
> + */
> +proc:::exec
> +/ p_pid[pid] == 0 /
> +{
> + time[pid] = timestamp;
> + p_exec[pid] = args[0];
> + p_pid[pid] = ppid;
> + p_name[pid] = execname;
> +}
> +
> +/*
> + * The process exits. Print the information.
> + */
> +proc:::exit
> +/p_pid[pid] != 0 && p_exec[pid] != ""/
> +{
> + printf("%-16s (%d) executed %s (%d) for %d microseconds\n",
> + p_name[pid], p_pid[pid], p_exec[pid], pid, (timestamp - time[pid])/1000);
> +}
> +
> +/*
> + * The process has forked itself and exits. Print the information.
> + */
> +proc:::exit
> +/p_pid[pid] != 0 && p_exec[pid] == ""/
> +{
> + printf("%-16s (%d) forked itself (as %d) for %d microseconds\n",
> + p_name[pid], p_pid[pid], pid, (timestamp - time[pid])/1000);
> +}
> +
> +/*
> + * Assign 0s to free memory associated with this pid.
> + */
> +proc:::exit
> +/p_pid[pid] != 0/
> +{
> + time[pid] = 0;
> + p_exec[pid] = NULL;
> + p_pid[pid] = 0;
> + p_name[pid] = NULL;
> +}
> diff --git a/examples/getting-started/activity1.d b/examples/getting-started/activity1.d
> new file mode 100755
> index 000000000..c63317689
> --- /dev/null
> +++ b/examples/getting-started/activity1.d
> @@ -0,0 +1,144 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * activity1.d - report on process create, exec and exit
> + *
> + * SYNOPSIS
> + * sudo dtrace -s activity1.d '"bash"'
> + *
> + * DESCRIPTION
> + * Show the processes that are created, executed and exited
> + * in the bash shell while the script is running.
> + *
> + * NOTES
> + * - This script uses the proc provider to trace the following process
> + * activities: create, exec, and exit.
> + *
> + * - A predicate is used to ensure that only those processes executed
> + * by bash are traced. While this could be hard coded, here the name
> + * is passed in as an argument.
> + *
> + * - This script is guaranteed to produce results if you start one or
> + * more bash commands while the script is running. There are two ways
> + * to do this:
> + * o Execute this script in the background, and type in the command(s).
> + * o Alternatively, run the script in the foreground and type the
> + * command(s) in a separate terminal window on the same system.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - Associative arrays are used to store the information from the
> + * proc provider.
> + *
> + * - There is on important subtlety to pay attention to. Since
> + * bash (and other shells) optimize for performance, it may happen
> + * that proc:::create does not fire, because there is no call to
> + * fork(), clone(), etc. This is why two different probes for
> + * proc:::exec are defined.
> + *
> + * - The DTrace User Guide documents the proc provider probe
> + * arguments like args[0] and also structures like psinfo_t. It is
> + * strongly recommended to check the documentation for this info.
> + */
> +
> +/*
> + * Fires when a process (or process thread) is created using fork() or
> + * vfork(), which both invoke clone(). The psinfo_t corresponding to
> + * the new child process is pointed to by args[0].
> + *
> + * Use a predicate to only execute the clause if the condition is met.
> + * In this case that means that only processes executed in the bash
> + * shell are traced.
> + */
> +proc:::create
> +/ execname == $1 /
> +{
> +/*
> + * Store the PID of both the parent and child process from the psinfo_t
> + * structure pointed to by args[0]. Use 3 associative arrays to store the
> + * various items of interest.
> + */
> + this->childpid = args[0]->pr_pid;
> + this->parentpid = args[0]->pr_ppid;
> +
> +/*
> + * Store the parent PID of the new child process.
> + */
> + p_pid[this->childpid] = this->parentpid;
> +
> +/*
> + * Parent command name.
> + */
> + p_name[this->childpid] = execname;
> +
> +/*
> + * Child has not yet been exec'ed.
> + */
> + p_exec[this->childpid] = "";
> +}
> +
> +/*
> + * The process starts. In case proc:::create has fired, store the
> + * absolute time and the full name of the child process.
> + */
> +proc:::exec
> +/ execname == $1 && p_pid[pid] != 0 /
> +{
> + time[pid] = timestamp;
> + p_exec[pid] = args[0];
> +}
> +
> +/*
> + * The process starts, but in this case, proc:::create has not fired.
> + * In addition to storing the name of the child process, store the
> + * various other items of interest.
> + */
> +proc:::exec
> +/ execname == $1 && p_pid[pid] == 0 /
> +{
> + time[pid] = timestamp;
> + p_exec[pid] = args[0];
> + p_pid[pid] = ppid;
> + p_name[pid] = execname;
> +}
> +
> +/*
> + * The process exits. Print the information.
> + */
> +proc:::exit
> +/p_pid[pid] != 0 && p_exec[pid] != ""/
> +{
> + printf("%-16s (%d) executed %s (%d) for %d microseconds\n",
> + p_name[pid], p_pid[pid], p_exec[pid], pid, (timestamp - time[pid])/1000);
> +}
> +
> +/*
> + * The process has forked itself and exits. Print the information.
> + */
> +proc:::exit
> +/p_pid[pid] != 0 && p_exec[pid] == ""/
> +{
> + printf("%-16s (%d) forked itself (as %d) for %d microseconds\n",
> + p_name[pid], p_pid[pid], pid, (timestamp - time[pid])/1000);
> +}
> +
> +/*
> + * Assign 0s to free memory associated with this pid.
> + */
> +proc:::exit
> +/p_pid[pid] != 0/
> +{
> + time[pid] = 0;
> + p_exec[pid] = NULL;
> + p_pid[pid] = 0;
> + p_name[pid] = NULL;
> +}
> diff --git a/examples/getting-started/calltrace.d b/examples/getting-started/calltrace.d
> new file mode 100755
> index 000000000..5de2b0f46
> --- /dev/null
> +++ b/examples/getting-started/calltrace.d
> @@ -0,0 +1,95 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * calltrace.d - time all system calls for the cp command
> + *
> + * SYNOPSIS
> + * sudo dtrace -s calltrace.d
> + *
> + * DESCRIPTION
> + * List and time all the system calls that are executed during
> + * a cp command.
> + *
> + * NOTES
> + * - This script traces all system calls that are executed when
> + * a cp command is run.
> + *
> + * This means that you need to execute the cp command while the
> + * script is running. There are two ways to do this:
> + * o Execute this script in the background, and type in the cp command.
> + * o Alternatively, run the script in the foreground and type
> + * the cp command in a separate terminal window on the same system.
> + *
> + * - You can use any file to copy, but you can also generate a file
> + * and then copy it. This is an example how to create a 500 MB file,
> + * copy it with the cp command and remove both files again:
> + * $ dd if=/dev/zero of=tmp_file bs=100M seek=5 count=0
> + * $ cp tmp_file tmp_file2
> + * $ rm tmp_file tmp_file2
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, we want to
> + * control the format of the output. This is why the results are
> + * printed using printa() in the END probe
> + */
> +
> +/*
> + * Set the base value of the timer. This is used as an offset in the
> + * return probe to calculate the time spent in a system call.
> + *
> + * A predicate is used to select the cp command. All other commands
> + * skip executing the clause and do not set ts_base.
> + */
> +syscall:::entry
> +/ execname == "cp" /
> +{
> + self->ts_base = timestamp;
> +}
> +
> +/*
> + * The predicate ensures that the base timing has been set.
> + * Since this is only done for the cp command, no information
> + * is collected for the other processes.
> + */
> +syscall:::return
> +/self->ts_base != 0/
> +{
> +/*
> + * Compute the time passed since the entry probe fired and
> + * convert the nanosecond value to microseconds.
> + *
> + * Update the aggregation called totals with this time. The
> + * execname (which is cp here) and the system call that caused
> + * the probe to fire, are the fields in the key.
> + */
> + this->time_call = (timestamp - self->ts_base)/1000;
> + @totals[execname,probefunc] = sum(this->time_call);
> +
> +/*
> + * Free the storage for ts_base.
> + */
> + self->ts_base = 0;
> +}
> +
> +/*
> + * Print the results. Use printf() to print a description of
> + * the contents of the aggregation. The format string in printa()
> + * is used to create a table lay-out.
> + */
> +END
> +{
> + printf("System calls executed and their duration:\n");
> + printa("%15s executed %18s - this took a total of %@8d microseconds\n",
> + @totals);
> +}
> diff --git a/examples/getting-started/countcalls.d b/examples/getting-started/countcalls.d
> new file mode 100755
> index 000000000..1c614228b
> --- /dev/null
> +++ b/examples/getting-started/countcalls.d
> @@ -0,0 +1,45 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * countcalls.d - count the open(), read(), and write() calls for 5 seconds
> + *
> + * SYNOPSIS
> + * sudo dtrace -s countcalls.d
> + *
> + * DESCRIPTION
> + * List and count the calls to write(), read(), and open() executed, while
> + * the script is running. The script automatically stops after 5 seconds.
> + *
> + * NOTES
> + * - This script uses the profile provider to stop the tracing after a
> + * certain amount of time. This time can easily be adjusted by changing.
> + * the number and unit.
> + *
> + * - An anonymous aggregation is used to store the results. Like a named
> + * aggregation, it is automatically printed when the tracing terminates.
> + */
> +
> +/*
> + * Fires every 5 seconds. Since exit() is called, the tracing terminates
> + * the first time this probe fires and the clause is executed.
> + */
> +profile:::tick-5sec
> +{
> + exit(0);
> +}
> +
> +/*
> + * Create the key by concatenating the function name and a string. An
> + * alternative is to only use probefunc as a key and print the string as
> + * part of a printa() in the END probe: printa("%s () calls\n",@);
> + */
> +syscall::write:entry, syscall::read:entry, syscall::open:entry
> +{
> + @[strjoin(probefunc,"() calls")] = count();
> +}
> diff --git a/examples/getting-started/countprogs.d b/examples/getting-started/countprogs.d
> new file mode 100755
> index 000000000..7674b8a24
> --- /dev/null
> +++ b/examples/getting-started/countprogs.d
> @@ -0,0 +1,44 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * countprogs.d - count processes invoked by a specific user
> + *
> + * SYNOPSIS
> + * sudo dtrace -s countprogs.d <uid>
> + *
> + * DESCRIPTION
> + * List and count every processes that is started by the user, while
> + * this script runs. The user id is passed in as an argument to the
> + * script.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - To ensure a process is started while the script is running,
> + * either execute this script in the background, and type in one
> + * or more commands, or run it in the foreground and type in the
> + * command(s) in a separate terminal window on the same system.
> + *
> + * - The results of an aggregation are automatically printed when
> + * the tracing terminates.
> + */
> +
> +/*
> + * Fires on every process that starts execution. An aggregation called
> + * proc_name uses the executable name as a key and counts the number of
> + * times this executable, or process, is started.
> + */
> +proc:::exec
> +/uid == $1/
> +{
> + @proc_name[execname] = count();
> +}
> diff --git a/examples/getting-started/countsyscalls.d b/examples/getting-started/countsyscalls.d
> new file mode 100755
> index 000000000..b77c6b2bb
> --- /dev/null
> +++ b/examples/getting-started/countsyscalls.d
> @@ -0,0 +1,38 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * countsyscalls.d - count system calls invoked by a specific user
> + *
> + * SYNOPSIS
> + * sudo dtrace -s countsyscalls.d <uid>
> + *
> + * DESCRIPTION
> + * List and count all the system calls executed by the specified user id.
> + * The user id is passed in as an argument to the script.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The results of the aggregation are automatically printed when
> + * the tracing terminates.
> + */
> +
> +/*
> + * Fires on every system call executed. An aggregation called syscalls
> + * uses the function name as a key and counts the number of calls to this
> + * function.
> + */
> +syscall:::entry
> +/pid == $1/
> +{
> + @syscalls[probefunc] = count();
> +}
> diff --git a/examples/getting-started/cswpercpu.d b/examples/getting-started/cswpercpu.d
> new file mode 100755
> index 000000000..814f4929d
> --- /dev/null
> +++ b/examples/getting-started/cswpercpu.d
> @@ -0,0 +1,88 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * cswpercpu.d - print the number of context switches per CPU per second
> + *
> + * SYNOPSIS
> + * sudo dtrace -s cswpercpu.d
> + *
> + * DESCRIPTION
> + * Every second, print the CPU id, the number of context switches each
> + * CPU performed, plus the total number of context switches executed
> + * across all the CPUs. For each info block, include a time stamp.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The results are stored in an aggregation called cswpersec.
> + * Every second, the results are printed with printa() and the
> + * aggregation is cleared.
> + *
> + * - In addition to using the CPU ID as a key in the cswpersec
> + * aggregation, also the string "total" is used. This entry
> + * is always printed last, because by default, printa() prints
> + * the results sorted by the value. The total count for any of
> + * the CPU Ids is always equal or less than "total".
> + */
> +
> +/*
> + * To avoid that the carefully crafted output is mixed with the
> + * default output by the dtrace command, enable quiet mode.
> + */
> +#pragma D option quiet
> +
> +/*
> + * Print the header.
> + */
> +BEGIN
> +{
> + printf("%-20s %5s %15s", "Timestamp", "CPU", "#csw");
> +}
> +
> +/*
> + * Fires when a process is scheduled to run on a CPU.
> + */
> +sched:::on-cpu
> +{
> +/*
> + * Convert the CPU ID to a string. This needs to be done because
> + * key "total" is a string.
> + */
> + this->cpustr = lltostr(cpu);
> +/*
> + * Update the count.
> + */
> + @cswpersec[this->cpustr] = count();
> + @cswpersec["total"] = count();
> +}
> +
> +/*
> + * Fires every second.
> + */
> +profile:::tick-1sec
> +{
> +/*
> + * Print the date and time first
> + */
> + printf("\n%-20Y ", walltimestamp);
> +
> +/*
> + * Print the aggregated counts for each CPU and the total for all CPUs.
> + * Use some formatting magic to get a special table lay-out.
> + */
> + printa("%5s %@15d\n ", @cswpersec);
> +
> +/*
> + * Reset the aggregation.
> + */
> + clear(@cswpersec);
> +}
> diff --git a/examples/getting-started/daterun.d b/examples/getting-started/daterun.d
> new file mode 100755
> index 000000000..59081598c
> --- /dev/null
> +++ b/examples/getting-started/daterun.d
> @@ -0,0 +1,42 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * daterun.d - display arguments to write() for the date command
> + *
> + * SYNOPSIS
> + * sudo dtrace -s daterun.d
> + *
> + * DESCRIPTION
> + * Trace the calls to write(), but only when executed by the date
> + * command. For such calls, print the file descriptor, the output
> + * string, and the number of bytes printed.
> + *
> + * NOTES
> + * - Execute this script in the background, and type in "date", or
> + * run it in the foreground and type in "date" in a separate window.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The output string is stored in arg1 and contains a newline (\n)
> + * character. This is why the byte count is printed on a separate line.
> + */
> +
> +syscall::write:entry
> +/execname == "date"/
> +{
> +/*
> + * Use copyinstr() to copy the string from user space into a DTrace
> + * buffer in kernel space. This function returns a pointer to the buffer.
> + */
> + printf("%s (fd=%d output=%s bytes=%d)\n",probefunc, arg0,
> + copyinstr(arg1), arg2);
> +}
> diff --git a/examples/getting-started/diskact.d b/examples/getting-started/diskact.d
> new file mode 100755
> index 000000000..31b5acd43
> --- /dev/null
> +++ b/examples/getting-started/diskact.d
> @@ -0,0 +1,106 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * diskact.d - for block devices show the distribution of I/O throughput
> + *
> + * SYNOPSIS
> + * sudo dtrace -s diskact.d
> + *
> + * DESCRIPTION
> + * The io provider is used to gather the I/O throughput for the block
> + * devices on the system. A histogram of the results is printed.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The bufinfo_t structure is the abstraction that describes an I/O
> + * request. The buffer that corresponds to an I/O request is pointed
> + * to by args[0] in the start, done, wait-start, and wait-done probes
> + * available through the io provider.
> + *
> + * - Detailed information about this data structure can be found in
> + * the DTrace User Guide. For more details, you can also check
> + * /usr/lib64/dtrace/<version>/io.d, where <version> denotes the
> + * kernel version.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, we want to
> + * control the format of the output. This is why the results are
> + * printed using printa() in the END probe
> + */
> +
> +/*
> + * To avoid that the carefully crafted output is mixed with the
> + * default output by the dtrace command, enable quiet mode.
> + */
> +#pragma D option quiet
> +
> +/*
> + * The pointer to bufinfo_t is in args[0]. Here it is used to get
> + * b_edev (the extended device) and b_blkno (the expanded block
> + * number on the device). These two fields are used in the key for
> + * associative array io_start.
> + */
> +io:::start
> +{
> + io_start[args[0]->b_edev, args[0]->b_blkno] = timestamp;
> +}
> +
> +io:::done
> +/ io_start[args[0]->b_edev, args[0]->b_blkno] /
> +{
> +/*
> + * We would like to show the throughput to a device in KB/sec, but
> + * the values that are measured are in bytes and nanoseconds.
> + * You want to calculate the following:
> + *
> + * bytes / 1024
> + * ------------------------
> + * nanoseconds / 1000000000
> + *
> + * As DTrace uses integer arithmetic and the denominator is usually
> + * between 0 and 1 for most I/O, the calculation as shown will lose
> + * precision. So, restate the fraction as:
> + *
> + * bytes 1000000000 bytes * 976562
> + * ----------- * ------------- = --------------
> + * nanoseconds 1024 nanoseconds
> + *
> + * This is easy to calculate using integer arithmetic.
> + */
> +
> + this->elapsed = timestamp - io_start[args[0]->b_edev, args[0]->b_blkno];
> +
> +/*
> + * The pointer to structure devinfo_t is in args[1]. Use this to get the
> + * name (+ instance/minor) and the pathname of the device.
> + *
> + * Use the formula above to compute the throughput. The number of bytes
> + * transferred is in bufinfo_t->b_bcount
> + */
> + @io_throughput[strjoin("device name = ",args[1]->dev_statname),
> + strjoin("path = ",args[1]->dev_pathname)] =
> + quantize((args[0]->b_bcount * 976562) / this->elapsed);
> +
> +/*
> + * Free the storage for the entry in the associative array.
> + */
> + io_start[args[0]->b_edev, args[0]->b_blkno] = 0;
> +}
> +
> +/*
> + * Use a format string to print the aggregation.
> + */
> +END
> +{
> + printa(" %s (%s)\n%@d\n", @io_throughput);
> +}
> diff --git a/examples/getting-started/errno.d b/examples/getting-started/errno.d
> new file mode 100755
> index 000000000..7c76d9678
> --- /dev/null
> +++ b/examples/getting-started/errno.d
> @@ -0,0 +1,56 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * errno.d - list and count the system calls with a non-zero errno value
> + *
> + * SYNOPSIS
> + * sudo dtrace -s errno.d
> + *
> + * DESCRIPTION
> + * Trace every system call that returns a non-zero value in errno.
> + * Show the name of the function, the value of errno and how often
> + * this function returned a non-zero value for errno.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The value of errno is available upon the return from the system call.
> + *
> + * - To present the results in a compact form, we use an aggregation
> + * called syscalls. Otherwise we may get several screens with the
> + * information. Plus that we then can't easily count the functions.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, we want to
> + * control the format of the output. This is why the results are
> + * printed using printa() in the END probe
> + */
> +
> +/*
> + * Store the information in an aggregation called syscalls.
> + * Use the predicate to only allow non-zero errno values that are
> + * within the range for errno.
> + */
> +syscall:::return
> +/ errno > 0 && errno <= ERANGE /
> +{
> + @syscalls[probefunc,errno] = count();
> +}
> +
> +/*
> + * The printf() line prints the header of the table to follow.
> + */
> +END
> +{
> + printf("%20s %5s %5s\n\n","syscall","errno","count");
> + printa("%20s %5d %@5d\n", at syscalls);
> +}
> diff --git a/examples/getting-started/errno1.d b/examples/getting-started/errno1.d
> new file mode 100755
> index 000000000..c8fbf8d44
> --- /dev/null
> +++ b/examples/getting-started/errno1.d
> @@ -0,0 +1,145 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * errno1.d - list and count the system calls with a non-zero errno value
> + *
> + * SYNOPSIS
> + * sudo dtrace -s errno1.d
> + *
> + * DESCRIPTION
> + * Trace every system call that returns a non-zero value in errno.
> + * Show the process name, the name of the function it executes, the
> + * user id, the name of the error that corresponds to the value of
> + * errno, a descriptive message for the error, and how often this
> + * combination occurred.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The value of errno is available upon the return from the system call.
> + *
> + * - To present the results in a compact form, we use an aggregation
> + * called syscalls. Otherwise we may get several screens with the
> + * information. Plus that we then can't easily count the functions.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, we want to
> + * control the format of the output. This is why the results are
> + * printed using printa() in the END probe
> + */
> +
> +BEGIN
> +{
> +/*
> + * Define an associative array called errno_code that maps a value
> + * of errno to an enum name. This information can be found in file
> + * /usr/include/asm-generic/errno-base.h.
> + *
> + * File /usr/include/asm-generic/errno.h has the codes for calling a system
> + * call that does not exist. This has not been used here though.
> + */
> + errno_code[EPERM] = "EPERM"; /* Operation not permitted */
> + errno_code[ENOENT] = "ENOENT"; /* No such file or directory */
> + errno_code[ESRCH] = "ESRCH"; /* No such process */
> + errno_code[EINTR] = "EINTR"; /* Interrupted system call */
> + errno_code[EIO] = "EIO"; /* I/O error */
> + errno_code[ENXIO] = "ENXIO"; /* No such device or address */
> + errno_code[E2BIG] = "E2BIG"; /* Argument list too long */
> + errno_code[ENOEXEC] = "ENOEXEC"; /* Exec format error */
> + errno_code[EBADF] = "EBADF"; /* Bad file number */
> + errno_code[ECHILD] = "ECHILD"; /* No child processes */
> + errno_code[EAGAIN] = "EAGAIN"; /* Try again or operation would block */
> + errno_code[ENOMEM] = "ENOMEM"; /* Out of memory */
> + errno_code[EACCES] = "EACCES"; /* Permission denied */
> + errno_code[EFAULT] = "EFAULT"; /* Bad address */
> + errno_code[ENOTBLK] = "ENOTBLK"; /* Block device required */
> + errno_code[EBUSY] = "EBUSY"; /* Device or resource busy */
> + errno_code[EEXIST] = "EEXIST"; /* File exists */
> + errno_code[EXDEV] = "EXDEV"; /* Cross-device link */
> + errno_code[ENODEV] = "ENODEV"; /* No such device */
> + errno_code[ENOTDIR] = "ENOTDIR"; /* Not a directory */
> + errno_code[EISDIR] = "EISDIR"; /* Is a directory */
> + errno_code[EINVAL] = "EINVAL"; /* Invalid argument */
> + errno_code[ENFILE] = "ENFILE"; /* File table overflow */
> + errno_code[EMFILE] = "EMFILE"; /* Too many open files */
> + errno_code[ENOTTY] = "ENOTTY"; /* Not a typewriter */
> + errno_code[ETXTBSY] = "ETXTBSY"; /* Text file busy */
> + errno_code[EFBIG] = "EFBIG"; /* File too large */
> + errno_code[ENOSPC] = "ENOSPC"; /* No space left on device */
> + errno_code[ESPIPE] = "ESPIPE"; /* Illegal seek */
> + errno_code[EROFS] = "EROFS"; /* Read-only file system */
> + errno_code[EMLINK] = "EMLINK"; /* Too many links */
> + errno_code[EPIPE] = "EPIPE"; /* Broken pipe */
> + errno_code[EDOM] = "EDOM"; /* Math argument out of domain of func */
> + errno_code[ERANGE] = "ERANGE"; /* Math result not representable */
> +
> +/*
> + * This associative array called errno_msg has a brief description of the
> + * error for each non-zero value of errno.
> + */
> + errno_msg[EPERM] = "Operation not permitted";
> + errno_msg[ENOENT] = "No such file or directory";
> + errno_msg[ESRCH] = "No such process";
> + errno_msg[EINTR] = "Interrupted system call";
> + errno_msg[EIO] = "I/O error";
> + errno_msg[ENXIO] = "No such device or address";
> + errno_msg[E2BIG] = "Argument list too long";
> + errno_msg[ENOEXEC] = "Exec format error";
> + errno_msg[EBADF] = "Bad file number";
> + errno_msg[ECHILD] = "No child processes";
> + errno_msg[EAGAIN] = "Try again or operation would block";
> + errno_msg[ENOMEM] = "Out of memory";
> + errno_msg[EACCES] = "Permission denied";
> + errno_msg[EFAULT] = "Bad address";
> + errno_msg[ENOTBLK] = "Block device required";
> + errno_msg[EBUSY] = "Device or resource busy";
> + errno_msg[EEXIST] = "File exists";
> + errno_msg[EXDEV] = "Cross-device link";
> + errno_msg[ENODEV] = "No such device";
> + errno_msg[ENOTDIR] = "Not a directory";
> + errno_msg[EISDIR] = "Is a directory";
> + errno_msg[EINVAL] = "Invalid argument";
> + errno_msg[ENFILE] = "File table overflow";
> + errno_msg[EMFILE] = "Too many open files";
> + errno_msg[ENOTTY] = "Not a typewriter";
> + errno_msg[ETXTBSY] = "Text file busy";
> + errno_msg[EFBIG] = "File too large";
> + errno_msg[ENOSPC] = "No space left on device";
> + errno_msg[ESPIPE] = "Illegal seek";
> + errno_msg[EROFS] = "Read-only file system";
> + errno_msg[EMLINK] = "Too many links";
> + errno_msg[EPIPE] = "Broken pipe";
> + errno_msg[EDOM] = "Math argument out of domain of func";
> + errno_msg[ERANGE] = "Math result not representable";
> +}
> +
> +/*
> + * Store the information in an aggregation called syscalls.
> + * Use the predicate to only allow non-zero errno values that are
> + * within the range for errno.
> + */
> +syscall:::return
> +/ errno > 0 && errno <= ERANGE /
> +{
> + @syscalls[execname,probefunc,uid,errno_code[errno],
> + errno_msg[errno]] = count();
> +}
> +
> +/*
> + * The printf() line prints the header of the table to follow.
> + */
> +END
> +{
> + printf("%-20s %-16s %-6s %-7s %-35s %5s\n\n","PROCESS","SYSCALL","UID",
> + "ERROR","DESCRIPTION","COUNT");
> + printa("%-20s %-16s %-6d %-7s %-35s %@5d\n", at syscalls);
> +}
> diff --git a/examples/getting-started/execcalls.d b/examples/getting-started/execcalls.d
> new file mode 100755
> index 000000000..ba829aacd
> --- /dev/null
> +++ b/examples/getting-started/execcalls.d
> @@ -0,0 +1,51 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * execcalls.d - show all processes that start executing
> + *
> + * SYNOPSIS
> + * sudo dtrace -s execcalls.d
> + *
> + * DESCRIPTION
> + * The probe in this script traces the exec() system call. It
> + * fires whenever a process loads a new process image.
> + *
> + * NOTES
> + * - This script traces the processes that start executing while
> + * the script is running. If no process is started during the
> + * time that the script runs, no output is produced.
> + *
> + * If that is the case, you can always execute a command yourself
> + * while this script is running. One such command is "date" that
> + * causes the probe to fire.
> + *
> + * - If you'd like to execute command(s) while the script is running,
> + * execute this script in the background, and type in one or more
> + * commands. If you started the script in the foreground, type in
> + * the command(s) in a separate terminal window on the same system.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + */
> +
> +proc:::exec
> +/ args[0] != NULL /
> +{
> +/*
> + * This information is from the DTrace user guide. The proc:::exec
> + * probe makes a pointer to a char available in args[0]. This has
> + * the path to the new process image.
> + *
> + * The strjoin() function is used to add a newline (\n) to the
> + * string that is to be printed.
> + */
> + trace(strjoin(stringof(args[0]),"\n"));
> +}
> diff --git a/examples/getting-started/fsact.sh b/examples/getting-started/fsact.sh
> new file mode 100755
> index 000000000..48b8adc98
> --- /dev/null
> +++ b/examples/getting-started/fsact.sh
> @@ -0,0 +1,114 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> +# Licensed under the Universal Permissive License v 1.0 as shown at
> +# http://oss.oracle.com/licenses/upl.
> +#
> +#------------------------------------------------------------------------------
> +# This example embeds a DTrace script in a bash script. The bash script
> +# is used to set the variables needed in the D script.
> +#
> +# fsact -- Display cumulative read and write activity across a file
> +# system device
> +#
> +# Usage: fsact [<filesystem>]
> +#------------------------------------------------------------------------------
> +
> +#------------------------------------------------------------------------------
> +# If no file system is specified, assume /
> +#------------------------------------------------------------------------------
> +[ $# -eq 1 ] && FSNAME=$1 || FSNAME="/"
> +[ ! -e $FSNAME ] && echo "$FSNAME not found" && exit 1
> +
> +#------------------------------------------------------------------------------
> +# Determine the mountpoint, major and minor numbers, and file system size.
> +#------------------------------------------------------------------------------
> +MNTPNT=$(df $FSNAME | gawk '{ getline; print $1; exit }')
> +MAJOR=$(printf "%d\n" 0x$(stat -Lc "%t" $MNTPNT))
> +MINOR=$(printf "%d\n" 0x$(stat -Lc "%T" $MNTPNT))
> +FSSIZE=$(stat -fc "%b" $FSNAME)
> +
> +#------------------------------------------------------------------------------
> +# Run the embedded D script.
> +#------------------------------------------------------------------------------
> +sudo dtrace -qs /dev/stdin << EOF
> +/*
> + * DESCRIPTION
> + * The io:::done probe from the io provider is used to get read and write
> + * statistics. In particular, the id of the block that is accessed for
> + * the read or write operation.
> + */
> +
> +BEGIN
> +{
> + printf("Show how often blocks are accessed in read and write operations\n");
> + printf("The statistics are updated every 5 seconds\n");
> + printf("The block IDs are normalized to a scale from 0 to 10\n");
> + printf("The file system is %s\n","$FSNAME");
> + printf("The mount point is %s\n","$MNTPNT");
> + printf("The file system size is %s bytes\n","$FSSIZE");
> +}
> +
> +/*
> + * This probe fires after an I/O request has been fulfilled. The
> + * done probe fires after the I/O completes, but before completion
> + * processing has been performed on the buffer.
> + *
> + * A pointer to structure devinfo_t is in args[1]. This is used
> + * to get the major and minor number of the device.
> + *
> + * A pointer to structure bufinfo_t is in args[0]. This is used
> + * to get the flags and the block number.
> + */
> +io:::done
> +/ args[1]->dev_major == $MAJOR && args[1]->dev_minor == $MINOR /
> +{
> +/*
> + * Check if B_READ has been set and assign a string to io_type
> + * based on the outcome of the check. This string is used as
> + * the key in aggregation io_stats.
> + */
> + io_type = args[0]->b_flags & B_READ ? "READ" : "WRITE";
> +
> +/*
> + * Structure member b_lblkno identifies which logical block on the
> + * device is to be accessed. Normalize thise block number as an
> + * integer in the range 0 to 10.
> + */
> + blkno = (args[0]->b_blkno)*10/$FSSIZE;
> +
> +/*
> + * Aggregate blkno linearly over the range 0 to 10 in steps of 1.
> + */
> + @io_stats[io_type] = lquantize(blkno,0,10,1)
> +}
> +
> +/*
> + * Fires every 5 seconds.
> + */
> +profile:::tick-5s
> +{
> + printf("%Y\n",walltimestamp);
> +
> +/*
> + * Display the contents of the aggregation.
> + */
> + printa("%s\n%@d\n", at io_stats);
> +
> +/*
> + * Reset the aggregation every time this probe fires
> + */
> + clear(@io_stats);
> +}
> +
> +/*
> + * Fires every 21 seconds. Since exit() is called, the tracing terminates
> + * the first time this probe fires and the clause is executed.
> + */
> +profile:::tick-21s
> +{
> + printf("Tracing is terminated now\n");
> + exit(0);
> +}
> +EOF
> diff --git a/examples/getting-started/goodbye.d b/examples/getting-started/goodbye.d
> new file mode 100755
> index 000000000..e0a1a67e2
> --- /dev/null
> +++ b/examples/getting-started/goodbye.d
> @@ -0,0 +1,36 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * goodbye.d - demonstrate the END probe
> + *
> + * SYNOPSIS
> + * sudo dtrace -s goodbye.d
> + *
> + * DESCRIPTION
> + * Demonstrates the use of the END probe. Function trace() is
> + * used to print a string.
> + *
> + * NOTES
> + * - The advantage of trace() is that it is simple and does not
> + * require a format string. If more control over the output is
> + * needed, printf() is a good alternative.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + */
> +
> +/*
> + * The END probe fires once when the tracing has terminated.
> + */
> +END
> +{
> + trace("Goodbye");
> +}
> diff --git a/examples/getting-started/hello.d b/examples/getting-started/hello.d
> new file mode 100755
> index 000000000..454822762
> --- /dev/null
> +++ b/examples/getting-started/hello.d
> @@ -0,0 +1,28 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * hello.d - demonstrate the BEGIN probe
> + *
> + * SYNOPSIS
> + * sudo dtrace -s hello.d
> + *
> + * DESCRIPTION
> + * Demonstrate the use of the BEGIN probe. Function trace() is
> + * used to print a string. The exit() function terminates the
> + * tracing.
> + */
> +
> +/*
> + * The BEGIN probe fires when the tracing script starts.
> + */
> +BEGIN
> +{
> + trace("Hello, world");
> + exit(0);
> +}
> diff --git a/examples/getting-started/readsizes.d b/examples/getting-started/readsizes.d
> new file mode 100755
> index 000000000..da2f43ecf
> --- /dev/null
> +++ b/examples/getting-started/readsizes.d
> @@ -0,0 +1,46 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * readsizes.d - show the distribution of bytes read when running find
> + *
> + * SYNOPSIS
> + * sudo dtrace -s readsizes.d
> + *
> + * DESCRIPTION
> + * Trace the calls to read() and use a predicate to only select those
> + * calls executed as part of executing the find command. For such
> + * calls, show the distribution of the sizes.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The results are stored in an aggregation called dist with
> + * the string "find" as the key.
> + *
> + * - The results of an aggregation are automatically printed when
> + * the tracing terminates.
> + */
> +
> +/*
> + * A predicate is used to guarantee that the clause for the read:entry
> + * probe is only executed in case the call to read() was issued by the
> + * find command.
> + *
> + * The quantize() function is used to show the distribution of the sizes
> + * read, or attempted to be read, by the read() call. This value is
> + * passed to the syscall function and to the D probe in arg2.
> + */
> +syscall::read:entry
> +/execname == "find"/
> +{
> + @dist["find"] = quantize(arg2);
> +}
> diff --git a/examples/getting-started/readtrace.d b/examples/getting-started/readtrace.d
> new file mode 100755
> index 000000000..c62da82b4
> --- /dev/null
> +++ b/examples/getting-started/readtrace.d
> @@ -0,0 +1,72 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * readtrace.d - show the time spent in the read() system call
> + *
> + * SYNOPSIS
> + * sudo dtrace -s readtrace.d
> + *
> + * DESCRIPTION
> + * For each combination of executable name and process id, show the
> + * total time in microseconds that is spent in the read() system call(s).
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - An aggregation is used to accumulate the timings. An alternative
> + * is to print the results in the read:return probe and if required,
> + * post process the output when the script has completed.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, the results
> + * are printed in the END probe. The format string is optional,
> + * but is used to produce a table lay-out.
> + */
> +
> +/*
> + * Set the base value of the timer. This is used as an offset in the
> + * read:return probe to calculate the time spent.
> + */
> +syscall::read:entry
> +{
> + self->ts_base = timestamp;
> +}
> +
> +/*
> + * The predicate ensures that the base timing has been set.
> + */
> +syscall::read:return
> +/self->ts_base != 0/
> +{
> +/*
> + * Clause-local variable time_read is used to store the time passed
> + * since the read:entry probe fired. This time is converted from
> + * nanoseconds to microseconds.
> + *
> + */
> + this->time_read = (timestamp - self->ts_base)/1000;
> + @totals[execname,pid] = sum(this->time_read);
> +
> +/*
> + * Free the storage for ts_base.
> + */
> + self->ts_base = 0;
> +}
> +
> +/*
> + * Print the results.
> + */
> +END
> +{
> + printa("%15s (pid=%-7d) spent a total of %5 at d microseconds in read()\n",
> + @totals);
> +}
> diff --git a/examples/getting-started/readtrace1.d b/examples/getting-started/readtrace1.d
> new file mode 100755
> index 000000000..6dc032854
> --- /dev/null
> +++ b/examples/getting-started/readtrace1.d
> @@ -0,0 +1,78 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * readtrace.d - show the time spent in the read() system call
> + *
> + * SYNOPSIS
> + * sudo dtrace -s readtrace.d
> + *
> + * DESCRIPTION
> + * For each combination of executable name and process id, show the
> + * total time in microseconds that is spent in the read() system call(s)
> + * executed by the df command.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - An aggregation is used to accumulate the timings. An alternative
> + * is to print the results in the read:return probe and if required,
> + * post process the output when the script has completed.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, the results
> + * are printed in the END probe. The format string is optional,
> + * but is used to produce a table lay-out.
> + */
> +
> +/*
> + * Set the base value of the timer. This is used as an offset in the
> + * read:return probe to calculate the time spent.
> + *
> + * A predicate is used to select the df command. All other commands
> + * skip the clause and do not set ts_base.
> + */
> +syscall::read:entry
> +/ execname == "df" /
> +{
> + self->ts_base = timestamp;
> +}
> +
> +/*
> + * The predicate ensures that the base timing has been set. Since this
> + * is only done for the df command, no information is collected for the
> + * other processes.
> + */
> +syscall::read:return
> +/self->ts_base != 0/
> +{
> +/*
> + * Clause-local variable time_read is used to store the time passed
> + * since the read:entry probe fired. This time is converted from
> + * nanoseconds to microseconds.
> + */
> + this->time_read = (timestamp - self->ts_base)/1000;
> + @totals[execname,pid] = sum(this->time_read);
> +
> +/*
> + * Free the storage for ts_base.
> + */
> + self->ts_base = 0;
> +}
> +
> +/*
> + * Print the results. The format is tailored to the df command.
> + */
> +END
> +{
> + printa("%-3s (pid=%-7d) spent a total of %5 at d microseconds in read()\n",
> + @totals);
> +}
> diff --git a/examples/getting-started/rwdiskact.d b/examples/getting-started/rwdiskact.d
> new file mode 100755
> index 000000000..5389270a1
> --- /dev/null
> +++ b/examples/getting-started/rwdiskact.d
> @@ -0,0 +1,112 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * rwdiskact.d - for block devices show the read() and write() performance
> + *
> + * SYNOPSIS
> + * sudo dtrace -s rwdiskact.d
> + *
> + * DESCRIPTION
> + * The io provider is used to display the throughput of the read()
> + * and write calls() for the block devices on the system. The
> + * tracing automatically stops after 10 seconds.
> + *
> + * NOTES
> + * - The bufinfo_t structure is the abstraction that describes an I/O
> + * request. The buffer that corresponds to an I/O request is pointed
> + * to by args[0] in the start, done, wait-start, and wait-done probes
> + * available through the io provider.
> + *
> + * - Detailed information about this data structure can be found in
> + * the DTrace User Guide. For more details, you can also check
> + * /usr/lib64/dtrace/<version>/io.d, where <version> denotes the
> + * kernel version.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, we want to
> + * control the format of the output. This is why the results are
> + * printed using printa() in the END probe
> + */
> +
> +/*
> + * To avoid that the carefully crafted output is mixed with the
> + * default output by the dtrace command, enable quiet mode.
> + */
> +#pragma D option quiet
> +
> +/*
> + * Fires every 10 seconds. Since exit() is called, the tracing terminates
> + * the first time this probe fires and the clause is executed.
> + */
> +profile:::tick-10sec
> +{
> + exit(0);
> +}
> +
> +/*
> + * The pointer to bufinfo_t is in args[0]. Here it is used to get
> + * b_flags (the flags), b_edev (the extended device) and b_blkno (the
> + * expanded block number on the device). These three fields are used
> + * in the key for associative array io_start.
> + */
> +io:::start
> +{
> + io_type = args[0]->b_flags & B_READ ? "READ" : "WRITE";
> + io_start[args[0]->b_edev, args[0]->b_blkno, io_type] = timestamp;
> +}
> +
> +io:::done
> +{
> +/*
> + * We would like to show the throughput to a device in KB/sec, but
> + * the values that are measured are in bytes and nanoseconds.
> + * You want to calculate the following:
> + *
> + * bytes / 1024
> + * ------------------------
> + * nanoseconds / 1000000000
> + *
> + * As DTrace uses integer arithmetic and the denominator is usually
> + * between 0 and 1 for most I/O, the calculation as shown will lose
> + * precision. So, restate the fraction as:
> + *
> + * bytes 1000000000 bytes * 976562
> + * ----------- * ------------- = --------------
> + * nanoseconds 1024 nanoseconds
> + *
> + * This is easy to calculate using integer arithmetic.
> + */
> + io_type = args[0]->b_flags & B_READ ? "READ" : "WRITE";
> + this->elapsed = timestamp -
> + io_start[args[0]->b_edev,args[0]->b_blkno,io_type];
> +
> +/*
> + * The pointer to structure devinfo_t is in args[1]. Use this to get the
> + * name (+ instance/minor) and the pathname of the device.
> + *
> + * Use the formula above to compute the throughput. The number of bytes
> + * transferred is in bufinfo_t->b_bcount
> + */
> + @io_throughput[strjoin("device name = ",args[1]->dev_statname),
> + strjoin("path = ",args[1]->dev_pathname),
> + io_type] =
> + quantize((args[0]->b_bcount * 976562) / this->elapsed);
> +
> +/*
> + * Free the storage for the entry in the associative array.
> + */
> + io_start[args[0]->b_edev, args[0]->b_blkno,io_type] = 0;}
> +
> +/*
> + * Use a format string to print the aggregation.
> + */
> +END
> +{
> + printa(" %s (%s) %s \n%@d\n", @io_throughput);
> +}
> diff --git a/examples/getting-started/syscalls.d b/examples/getting-started/syscalls.d
> new file mode 100755
> index 000000000..365c99688
> --- /dev/null
> +++ b/examples/getting-started/syscalls.d
> @@ -0,0 +1,50 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * syscalls.d - show the read() system calls executed
> + *
> + * SYNOPSIS
> + * sudo dtrace -s syscalls.d
> + *
> + * DESCRIPTION
> + * Count the read() system calls that are executed while the script
> + * is running. Count by the name of the process and the file
> + * descriptor used in the read operation.
> + *
> + * NOTES
> + * - This script traces the running processes and the probe fires
> + * if there are calls to read(). If there are no such calls, no
> + * output is produced.
> + *
> + * If that is the case, you can always execute a command that
> + * executes calls to read(). One such command is "date". It causes
> + * the probe to fire, but any other command that issues calls to
> + * read() will do.
> + *
> + * - Execute this script in the background, and type in the command,
> + * or run it in the foreground and type in the command in a separate
> + * terminal window on the same system.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - The results of the aggregation are automatically printed when
> + * the tracing terminates.
> + */
> +
> +/*
> + * The file descriptor used in the read() call is passed to the syscall
> + * and to the D probe in arg0.
> + */
> +syscall::read:entry
> +{
> + @totals[execname,arg0] = count();
> +}
> diff --git a/examples/getting-started/syscalls1.d b/examples/getting-started/syscalls1.d
> new file mode 100755
> index 000000000..9f2bbe411
> --- /dev/null
> +++ b/examples/getting-started/syscalls1.d
> @@ -0,0 +1,62 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * syscalls1.d - show the read() system calls executed
> + *
> + * SYNOPSIS
> + * sudo dtrace -s syscalls1.d
> + *
> + * DESCRIPTION
> + * Count the read() system calls that are executed while the script
> + * is running. Count by the process id, the user id, the name of
> + * the process and the file descriptor used in the read operation.
> + *
> + * NOTES
> + * - This script traces the running processes and the probe fires
> + * if there are calls to read(). If there are no such calls, no
> + * output is produced.
> + *
> + * If that is the case, you can always execute a command that
> + * executes calls to read(). One such command is "date". It causes
> + * the probe to fire, but any other command that issues calls to
> + * read() will do.
> + *
> + * - Execute this script in the background, and type in the command,
> + * or run it in the foreground and type in the command in a separate
> + * terminal window on the same system.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - Although the results of an aggregation are automatically
> + * printed when the tracing terminates, in this case, we want to
> + * control the format of the output. This is why the results are
> + * printed in the END probe
> + */
> +
> +/*
> + * The file descriptor used in the read() call is passed to the syscall
> + * and to the D probe in arg0.
> + */
> +syscall::read:entry
> +{
> + @totals[pid,uid,execname,arg0] = count();
> +}
> +
> +/*
> + * The printf() statement prints a header. The format string in the
> + * printa() call is optional. Here it is used to produce a table layout.
> + */
> +END
> +{
> + printf("%8s %6s %20s %3s %5s\n","PID","UID","EXECNAME","FD","COUNT");
> + printa("%8d %6d %20s %3d %5 at d\n", at totals);
> +}
> diff --git a/examples/getting-started/tick.d b/examples/getting-started/tick.d
> new file mode 100755
> index 000000000..b1a2a9440
> --- /dev/null
> +++ b/examples/getting-started/tick.d
> @@ -0,0 +1,55 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * tick.d - perform an action at regular intervals
> + *
> + * SYNOPSIS
> + * sudo dtrace -s tick.d
> + *
> + * DESCRIPTION
> + * Use the tick probe from the profile provider to execute a block of code
> + * at regular intervals. In this case, this is the update of a variable
> + * called "i", but the clause can contain any valid D statements. The
> + * final value of "i" is printed in the END probe.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - Instead of printf(), trace() can be used and vice-versa. The
> + * difference is that trace() does not support a format string.
> + */
> +
> +/*
> + * Initialize variable "i" to zero. This is a global variable that
> + * can be read and written by any probe.
> + */
> +BEGIN
> +{
> + i = 0;
> +}
> +
> +/*
> + * This probe fires every 100 milliseconds. When it fires, it updates
> + * variable "i" and prints the result.
> + */
> +profile:::tick-100msec
> +{
> + printf("i = %d\n",++i);
> +}
> +
> +/*
> + * Print the final result.
> + */
> +END
> +{
> + trace(i);
> +}
> diff --git a/examples/getting-started/tick1.d b/examples/getting-started/tick1.d
> new file mode 100644
> index 000000000..244c79100
> --- /dev/null
> +++ b/examples/getting-started/tick1.d
> @@ -0,0 +1,56 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * tick1.d - perform an action at regular intervals
> + *
> + * SYNOPSIS
> + * sudo dtrace -s tick1.d
> + *
> + * DESCRIPTION
> + * Use the tick probe from the profile provider to execute a block of code
> + * at regular intervals. In this case, this is the update of a variable
> + * called "i", but the clause can contain any valid D statements. The
> + * final value of "i" is printed in the END probe. The printf() function
> + * is used to format the output.
> + *
> + * NOTES
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - Instead of printf(), trace() can be used and vice-versa. The
> + * difference is that trace() does not support a format string.
> + */
> +
> +/*
> + * Initialize variable "i" to zero. This is a global variable that
> + * can be read and written by any probe.
> + */
> +BEGIN
> +{
> + i = 0;
> +}
> +
> +/*
> + * This probe fires every 100 milliseconds. When it fires, it updates
> + * variable "i" and prints the result.
> + */
> +profile:::tick-100msec
> +{
> + printf("i = %d\n",++i);
> +}
> +
> +/*
> + * Print the final result. Use printf() to format the output.
> + */
> +END
> +{
> + printf("\nFinal value of i = %d\n",i);
> +}
> diff --git a/examples/getting-started/wrun.d b/examples/getting-started/wrun.d
> new file mode 100755
> index 000000000..3e242f592
> --- /dev/null
> +++ b/examples/getting-started/wrun.d
> @@ -0,0 +1,55 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * NAME
> + * wrun.d - display arguments to write() for the w command
> + *
> + * SYNOPSIS
> + * sudo dtrace -s wrun.d
> + *
> + * DESCRIPTION
> + * Trace the calls to write(), but only when executed by the w
> + * command. For such calls, print the file descriptor, the
> + * output string, and the number of bytes printed.
> + *
> + * NOTES
> + * - Execute this script in the background, and type in "w", or
> + * run it in the foreground and type in "w" in a separate window.
> + *
> + * - The script needs to be terminated with ctrl-C. In case the
> + * script is running in the background, get it to run in the
> + * foreground first by using the fg command and then use ctrl-C
> + * to terminate the process. Otherwise, typing in ctrl-C will do.
> + *
> + * - DTrace has a default limit of 256 bytes for strings. In this
> + * example, the output string may be longer than this. If so,
> + * either use the "-x strsize=<new-length>" command line option,
> + * or a "#pragma D option strsize=<new-length>" pragma in the
> + * script to increase the size. The latter is shown below.
> + */
> +
> +#pragma D option strsize=512
> +
> +/*
> + * Use a predicate to only execute the clause in case the w
> + * command causes the probe to fire.
> + */
> +syscall::write:entry
> +/execname == "w"/
> +{
> +/*
> + * Use copyinstr() to copy the string from user space into a DTrace
> + * buffer in kernel space. This function returns a pointer to the buffer.
> + * The string it points to, is null terminated.
> + * The third argument in the call to write() is the number of bytes
> + * to be printed. This could be used as an optional second argument
> + * in copyinstr() so that only this many bytes are copied.
> + */
> + printf("%s(fd=%d\noutput=\n%s\nbytes=%d)\n",probefunc, arg0,
> + copyinstr(arg1), arg2);
> +}
More information about the DTrace-devel
mailing list