[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