From: Eugene Loh <[email protected]>
The idea is to have lots of short scripts, each illustrating a
D language feature.
The numbering scheme serves two purposes:
*) the scripts are more or less in ascending order
of difficulty, although that's admittedly a tricky
concept in such a multi-dimensional space
*) there is some grouping of themes -- e.g.,
"introduction", "providers", "built-in variables", etc.
The combination of comments and code is intended to help either
a human user or an AI bot to learn how to use the D language.
Other examples have been based on functional objectives (doing
simple but useful tasks). These new examples are based on language
features. A future set of examples could be based on errors! That
is, the scripts generate errors of different sorts that we would
illustrate (and explain how to fix).
Signed-off-by: Eugene Loh <[email protected]>
---
examples/README.md | 13 +++
.../language_features/000intro-hello-world.d | 63 ++++++++++++
.../language_features/001intro-ERROR-probe.d | 30 ++++++
.../002intro-global-variables.d | 31 ++++++
examples/language_features/003intro-C-like.d | 75 +++++++++++++++
examples/language_features/004intro-ternary.d | 32 +++++++
examples/language_features/005intro-strings.d | 65 +++++++++++++
.../language_features/006intro-associative.d | 34 +++++++
.../007intro-this-variables.d | 49 ++++++++++
.../008intro-self-variables.d | 57 +++++++++++
examples/language_features/009intro-pragmas.d | 38 ++++++++
.../010intro-external-variables.d | 53 +++++++++++
.../language_features/011intro-preprocessor.d | 25 +++++
.../language_features/012intro-predicates.d | 52 ++++++++++
examples/language_features/100list_probes.sh | 11 +++
.../101count_probes_by_provider.sh | 12 +++
.../102list_probe_arguments.sh | 11 +++
.../language_features/110provider_profile.d | 29 ++++++
examples/language_features/120provider_proc.d | 37 ++++++++
.../130provider_sched_on_off_cpu.d | 28 ++++++
.../131provider_sched_enqueue_dequeue.d | 24 +++++
.../language_features/140provider_syscall.d | 20 ++++
examples/language_features/150provider_fbt.d | 48 ++++++++++
.../160provider_sdt_rawtp.sh | 36 +++++++
.../language_features/170provider_lockstat.sh | 17 ++++
examples/language_features/171provider_io.d | 31 ++++++
examples/language_features/172provider_ip.d | 26 +++++
examples/language_features/180provider_cpc.d | 52 ++++++++++
.../200built-in-variables-probe.d | 18 ++++
.../201built-in-variables-timestamps.d | 31 ++++++
.../202built-in-variables-args.d | 28 ++++++
.../203built-in-variables-process-info.d | 31 ++++++
.../204built-in-variables-uregs.d | 31 ++++++
examples/language_features/300actions-exit.sh | 18 ++++
examples/language_features/301actions-print.d | 32 +++++++
.../language_features/302actions-strings.d | 45 +++++++++
.../303actions-strings-paths.d | 25 +++++
.../language_features/304actions-system.d | 22 +++++
.../language_features/305actions-progenyof.d | 20 ++++
.../306actions-speculations.d | 57 +++++++++++
.../language_features/307actions-callstack.d | 54 +++++++++++
.../310actions-alloca-copy.d | 95 +++++++++++++++++++
examples/language_features/311actions-rand.d | 25 +++++
.../language_features/500aggregations-intro.d | 24 +++++
.../501aggregations-names-keys.d | 19 ++++
.../502aggregations-basic-funcs.d | 26 +++++
.../language_features/503aggregations-stack.d | 24 +++++
.../504aggregations-printa.d | 29 ++++++
.../505aggregations-quantize.d | 65 +++++++++++++
.../506aggregations-clear-trunc.d | 63 ++++++++++++
.../507aggregations-trunc5.d | 50 ++++++++++
.../508aggregations-normalize.d | 49 ++++++++++
52 files changed, 1880 insertions(+)
create mode 100755 examples/language_features/000intro-hello-world.d
create mode 100755 examples/language_features/001intro-ERROR-probe.d
create mode 100755 examples/language_features/002intro-global-variables.d
create mode 100755 examples/language_features/003intro-C-like.d
create mode 100755 examples/language_features/004intro-ternary.d
create mode 100755 examples/language_features/005intro-strings.d
create mode 100755 examples/language_features/006intro-associative.d
create mode 100755 examples/language_features/007intro-this-variables.d
create mode 100755 examples/language_features/008intro-self-variables.d
create mode 100755 examples/language_features/009intro-pragmas.d
create mode 100755 examples/language_features/010intro-external-variables.d
create mode 100755 examples/language_features/011intro-preprocessor.d
create mode 100755 examples/language_features/012intro-predicates.d
create mode 100755 examples/language_features/100list_probes.sh
create mode 100755 examples/language_features/101count_probes_by_provider.sh
create mode 100755 examples/language_features/102list_probe_arguments.sh
create mode 100755 examples/language_features/110provider_profile.d
create mode 100755 examples/language_features/120provider_proc.d
create mode 100755 examples/language_features/130provider_sched_on_off_cpu.d
create mode 100755 examples/language_features/131provider_sched_enqueue_dequeue.d
create mode 100755 examples/language_features/140provider_syscall.d
create mode 100755 examples/language_features/150provider_fbt.d
create mode 100755 examples/language_features/160provider_sdt_rawtp.sh
create mode 100755 examples/language_features/170provider_lockstat.sh
create mode 100755 examples/language_features/171provider_io.d
create mode 100755 examples/language_features/172provider_ip.d
create mode 100755 examples/language_features/180provider_cpc.d
create mode 100755 examples/language_features/200built-in-variables-probe.d
create mode 100755 examples/language_features/201built-in-variables-timestamps.d
create mode 100755 examples/language_features/202built-in-variables-args.d
create mode 100755 examples/language_features/203built-in-variables-process-info.d
create mode 100755 examples/language_features/204built-in-variables-uregs.d
create mode 100755 examples/language_features/300actions-exit.sh
create mode 100755 examples/language_features/301actions-print.d
create mode 100755 examples/language_features/302actions-strings.d
create mode 100755 examples/language_features/303actions-strings-paths.d
create mode 100755 examples/language_features/304actions-system.d
create mode 100755 examples/language_features/305actions-progenyof.d
create mode 100755 examples/language_features/306actions-speculations.d
create mode 100755 examples/language_features/307actions-callstack.d
create mode 100755 examples/language_features/310actions-alloca-copy.d
create mode 100755 examples/language_features/311actions-rand.d
create mode 100755 examples/language_features/500aggregations-intro.d
create mode 100755 examples/language_features/501aggregations-names-keys.d
create mode 100755 examples/language_features/502aggregations-basic-funcs.d
create mode 100755 examples/language_features/503aggregations-stack.d
create mode 100755 examples/language_features/504aggregations-printa.d
create mode 100755 examples/language_features/505aggregations-quantize.d
create mode 100755 examples/language_features/506aggregations-clear-trunc.d
create mode 100755 examples/language_features/507aggregations-trunc5.d
create mode 100755 examples/language_features/508aggregations-normalize.d
diff --git a/examples/README.md b/examples/README.md
index de4ba090b..b84a34031 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -86,3 +86,16 @@ These are the scripts included, plus a brief description.
| | thread ID as returned in the DTrace tid variable. |
| var-scope.d | Demonstrate some of the scoping rules for global and |B|
| | clause-local variables. |
+
+## Language-feature examples
+
+In the language_features/ subdirectory are short scripts,
+each illustrating a D language feature.
+
+The numbering scheme serves two purposes:
+
+* The scripts are more or less in ascending order of difficulty, although that's admittedly a tricky concept in such a multi-dimensional space
+
+* There is some grouping of themes -- e.g., "introduction", "providers", "built-in variables", etc.
+
+The scripts start at Beginner, and they arguably do not get much more complicated than that.
diff --git a/examples/language_features/000intro-hello-world.d b/examples/language_features/000intro-hello-world.d
new file mode 100755
index 000000000..a38f7cacc
--- /dev/null
+++ b/examples/language_features/000intro-hello-world.d
@@ -0,0 +1,63 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./000intro-hello-world.d
+ *
+ * or
+ *
+ * sudo /usr/sbin/dtrace -s 000intro-hello-world.d
+ *
+ * or
+ *
+ * sudo /usr/sbin/dtrace -n 'dtrace:::BEGIN
+ * {
+ * printf("hello world\n");
+ * exit(0);
+ * }
+ * dtrace:::END
+ * {
+ * trace("goodbye world");
+ * }'
+ *
+ * DESCRIPTION
+ * This is a simple "hello world" program in D. It
+ * illustrates a variety of very simple features of D.
+ */
+
+/*
+ * The BEGIN probe fires when the script is launched. It can
+ * be called by simply asking for BEGIN. Or, its provider can
+ * also be named: dtrace:::BEGIN. The other probe description
+ * fields, the module and function, are blank for this probe.
+ */
+dtrace:::BEGIN
+{
+ /*
+ * A C-like printf() function can be used to print
+ * data. Alternatively, the trace() function can be
+ * used, and it will discern the appropriate data type.
+ */
+ printf("hello world\n");
+
+ /*
+ * Variables can be assigned. They maybe typed implicitly.
+ */
+ x = 123;
+
+ exit(0);
+}
+
+dtrace:::BEGIN
+/x == 456/ /* A predicate can indicate whether to execute a clause. */
+{
+ trace("YOU SHOULD NOT SEE THIS MESSAGE!!!\n");
+}
+
+/*
+ * We can also gives actions to list at the end.
+ */
+dtrace:::END
+{
+ trace("goodbye world");
+}
diff --git a/examples/language_features/001intro-ERROR-probe.d b/examples/language_features/001intro-ERROR-probe.d
new file mode 100755
index 000000000..3c3a50eee
--- /dev/null
+++ b/examples/language_features/001intro-ERROR-probe.d
@@ -0,0 +1,30 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./001intro-ERROR-probe.d
+ *
+ * DESCRIPTION
+ * The ERROR probe can be used to handle run-time errors.
+ * If a clause encounters an error, its output is not seen
+ * and the ERROR probe is triggered. Other clauses associated
+ * with the guilty probe, however, do execute.
+ */
+
+dtrace:::ERROR
+{
+ printf("An error occurred.\n");
+}
+
+dtrace:::BEGIN
+{
+ printf("WE SHOULD NOT SEE THIS!\n");
+ printf("Dereference an illegal address: %d\n", *((int *)0x1234));
+ printf("WE SHOULD NOT GET HERE!\n");
+}
+
+dtrace:::BEGIN
+{
+ printf("This is a different clause.\n");
+ exit(0);
+}
diff --git a/examples/language_features/002intro-global-variables.d b/examples/language_features/002intro-global-variables.d
new file mode 100755
index 000000000..9b9ec6533
--- /dev/null
+++ b/examples/language_features/002intro-global-variables.d
@@ -0,0 +1,31 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./002intro-global-variables.d
+ *
+ * DESCRIPTION
+ * Rules for variable names are similar to those in C. By
+ * default, they are "global" -- that is, seen by every thread
+ * and every probe. So be careful that no thread overwrites any
+ * other thread's write. Variables may be typed implicitly.
+ */
+
+int explicitly_typed_variable;
+
+dtrace:::BEGIN
+{
+ explicitly_typed_variable = 1234;
+ implicitly_typed_variable = 5678;
+
+ printf("%d %d\n", explicitly_typed_variable,
+ implicitly_typed_variable);
+
+ my_char = 'a';
+ my_string = "hello world";
+ my_pointer = (int *) 0x123456;
+
+ printf("%c '%s' %p\n", my_char, my_string, my_pointer);
+
+ exit(0);
+}
diff --git a/examples/language_features/003intro-C-like.d b/examples/language_features/003intro-C-like.d
new file mode 100755
index 000000000..2e3ea8bde
--- /dev/null
+++ b/examples/language_features/003intro-C-like.d
@@ -0,0 +1,75 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./003intro-C-like.d
+ *
+ * DESCRIPTION
+ * Much of D syntax is very C-like. Examples are shown here.
+ * Check the documentation for more complete information.
+ *
+ * Differences include:
+ * - D variables can be typed implicitly
+ * - D has no "if" "switch" "case" "for"
+ * - D has no function definitions
+ */
+
+/*
+ * As you have already seen, one can use multi-line comments in D
+ * similarly to C.
+ */
+
+/*
+ * Variables can be declared explicitly. Data types like short, int, long,
+ * long long, char, struct, and union are familiar from C.
+ */
+int x, y, arr[5];
+
+dtrace:::BEGIN
+{
+ /* Assignments are similar to C. */
+ x = 1;
+ y = 2;
+ x += 3;
+
+ /* There are familiar operators. E.g., arithmetic operators. */
+ x = x + y;
+ y = 3 * x - 2 * y;
+
+ /* Relational operators. */
+ x = 2 > 3;
+ y = 2 >= 3;
+
+ /* Logical operators. */
+ x = (2 > 3) && (2 >= 3);
+
+ /* Bitwise operators. */
+ x = x & y;
+
+ /* Various unary operators. */
+ x = x++ + --y;
+
+ /* Types and casting. */
+ x = (long long) (sizeof(int) + ((int) 0xfedcba9876543210ull));
+
+ /*
+ * In contrast with C, D has a printf() whose %d, %x, and %i format
+ * conversions use type information about the corresponding argument
+ * to interpret that argument with its actual word size.
+ */
+ printf("%x\n", x);
+
+ /* Arrays can also be associative. More on that later. */
+ arr[0] = 10000;
+ arr[1] = 11111;
+ arr[2] = 12222;
+ arr[3] = 13333;
+ arr[4] = 14444;
+
+ /* One can get addresses and dereference addresses. */
+ /* (In this case, `nr_cpu_ids is a kernel variable.) */
+ ptr = &`nr_cpu_ids;
+ printf("nr_cpu_ids: %d should equal %d\n", *ptr, `nr_cpu_ids);
+
+ exit(0);
+}
diff --git a/examples/language_features/004intro-ternary.d b/examples/language_features/004intro-ternary.d
new file mode 100755
index 000000000..e6404f8b6
--- /dev/null
+++ b/examples/language_features/004intro-ternary.d
@@ -0,0 +1,32 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./004intro-ternary.d
+ *
+ * DESCRIPTION
+ * Much of D syntax is very C-like. Another example is
+ * ternary operations: ($condition ? $if_yes : $if_no).
+ * In D, however, ternary operations are argubly more
+ * useful than they are in C. The D language does not
+ * have conditional constructs like "if" statements.
+ * Therefore, other constructs, like predicates and
+ * speculations, must be used. Or, ternary operations.
+ */
+
+dtrace:::BEGIN
+{
+ x = 123;
+ y = 456;
+
+ /* basic ternary operation to implement z = MIN(x, y) */
+ z = (x < y) ? x : y;
+
+ /* a more novel use to conditionalize an assignment */
+ (x < y) ? (y = x) : 1;
+
+ /* all three variables should have the same value */
+ printf("x = %d; y = %d; z = %d\n", x, y, z);
+
+ exit(0);
+}
diff --git a/examples/language_features/005intro-strings.d b/examples/language_features/005intro-strings.d
new file mode 100755
index 000000000..5a758fcc3
--- /dev/null
+++ b/examples/language_features/005intro-strings.d
@@ -0,0 +1,65 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./005intro-strings.d
+ *
+ * DESCRIPTION
+ * But D does differ from C when it comes to strings.
+ * The D language has a built-in string data type.
+ */
+
+/*
+ * Strings are stored in arrays of fixed length. The
+ * default length 256 can be overridden with a D option.
+ */
+#pragma D option strsize=12
+
+/* string variables can be declared explicitly */
+string exp;
+string s1, s2, s3, s4;
+
+char carray[32];
+
+BEGIN
+{
+ /* strings are truncated, if necessary, per strsize */
+ printf("%s\n", "abcdefghijklmnopqrstuvwxyz");
+
+ /* string variables can be declared implicitly */
+ imp = "abcdefghijklmnopqrstuvwxyz";
+ printf("%d chars, string %s\n", sizeof(imp), imp);
+
+ /* operators can be used to order strings lexically (akin to strcmp) */
+ exp = "zyxwvutsrqponmlkjihgfedcba";
+ printf("%s is %s than %s\n", imp, imp < exp ? "less" : "greater", exp);
+
+ /* set up NULL-terminated char array for some illustration purposes */
+ carray[ 0] = 'A'; carray[ 1] = 'B'; carray[ 2] = 'C'; carray[ 3] = 'D';
+ carray[ 4] = 'E'; carray[ 5] = 'F'; carray[ 6] = 'G'; carray[ 7] = 'H';
+ carray[ 8] = 'I'; carray[ 9] = 'J'; carray[10] = 'K'; carray[11] = 'L';
+ carray[12] = 'M'; carray[13] = 'N'; carray[14] = 'O'; carray[15] = 'P';
+ carray[16] = 'Q'; carray[17] = 'R'; carray[18] = 'S'; carray[19] = 'T';
+ carray[20] = 'U'; carray[21] = 'V'; carray[22] = 'W'; carray[23] = 'X';
+ carray[24] = 'Y'; carray[25] = 'Z'; carray[26] = '\0';
+
+ /* char pointers can be turned into strings */
+ s1 = (string) carray; /* type casting */
+ s2 = stringof carray; /* stringof operator */
+
+ /* char pointers can be promoted automatically to strings */
+ s3 = carray;
+ s4 = strchr(carray, 'B');
+
+ /*
+ * Note that in the above assignments, we did not simply set
+ * a char* to some existing string buffer. Rather, contents
+ * are copied "by-value" to the destination string buffer.
+ * E.g., if the source buffer is modified, the output strings
+ * nevertheless all stay the same.
+ */
+ carray[2] = '\0';
+ printf("%s %s %s %s %s\n", s1, s2, s3, s4, carray);
+
+ exit(0);
+}
diff --git a/examples/language_features/006intro-associative.d b/examples/language_features/006intro-associative.d
new file mode 100755
index 000000000..b811c6028
--- /dev/null
+++ b/examples/language_features/006intro-associative.d
@@ -0,0 +1,34 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./006intro-associative.d
+ *
+ * DESCRIPTION
+ * The D language also supports associative arrays.
+ */
+
+/* associative arrays can be declared explicitly by key (if any) and value types */
+int exp[string, int];
+
+BEGIN
+{
+ /* associative arrays can also be declared implicitly */
+ imp["hello", 5] = 1234;
+
+ /* values not set are assumed to be 0 */
+ printf("set value is %d; unset value is %d\n",
+ imp["hello", 5],
+ exp["hello", 5]);
+
+ /* when you are done, set a value back to 0 to free the memory */
+ imp["hello", 5] = 0;
+
+ /*
+ * By the way, you cannot promote a char associative array
+ * to a string -- whether with stringof or (string) -- since
+ * it is not a consecutive block of memory holding chars in sequence.
+ */
+
+ exit(0);
+}
diff --git a/examples/language_features/007intro-this-variables.d b/examples/language_features/007intro-this-variables.d
new file mode 100755
index 000000000..4d9ba5390
--- /dev/null
+++ b/examples/language_features/007intro-this-variables.d
@@ -0,0 +1,49 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./007intro-this-variables.d
+ *
+ * DESCRIPTION
+ * We saw earlier that D supports global variables, allowing
+ * convenient access from every probe and every thread. In
+ * the same way, however, this causes the danger of different
+ * probes and threads overwriting one another's values.
+ *
+ * For variables that are to used only within a set of clauses
+ * for a specific probe, specify "this->". Such variables have
+ * undefined values at the start of the set of clauses.
+ */
+
+/* "this" variables can be declared explicitly */
+this int exp;
+
+dtrace:::BEGIN
+{
+ /* the value is undefined by default, define its value */
+ this->exp = 1234;
+
+ /* variables can also be typed implicitly */
+ this->imp = 5678;
+}
+
+dtrace:::BEGIN
+/ 1 == 1 /
+{
+ /* the variables are available in this later clause for the same probe */
+ printf("%d %d\n", this->exp, this->imp);
+
+ /*
+ * There is no need to zero out this-> variables at the end of a
+ * set of clauses: they will no longer exist anyhow and their
+ * values when they are used in a different set of clauses, for
+ * some other probe firing, will be undefined at first.
+ */
+
+ exit(0);
+}
+
+END
+{
+ /* the variables are not available here, for a different probe */
+}
diff --git a/examples/language_features/008intro-self-variables.d b/examples/language_features/008intro-self-variables.d
new file mode 100755
index 000000000..68a5dcf59
--- /dev/null
+++ b/examples/language_features/008intro-self-variables.d
@@ -0,0 +1,57 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./008intro-self-variables.d
+ *
+ * DESCRIPTION
+ * In D, we can also have variables that are local to a thread.
+ * We preface them with "self->".
+ */
+
+/* Thread-local "self" variables can be declared explicitly */
+self int exp, dummy;
+
+syscall::write:entry
+{
+ /* we initialize one variable to the fd of the write() call */
+ self->exp = arg0;
+
+ /* we can declare a self->variable implicitly, setting it to the requested number of bytes */
+ self->imp = arg2;
+}
+
+fbt::ksys_write:entry
+/ self->exp /
+{
+ self->kernel_value = arg2;
+}
+
+/*
+ * We look for a write() return probe on the same thread where
+ * self->exp has been set. Using thread-local variables allows
+ * us to connect two probes for the same thread, without interference
+ * from other threads.
+ */
+syscall::write:return
+/ self->exp /
+{
+ printf("on fd %d\n", self->exp);
+ printf("bytes (requested): %d\n", self->imp);
+ printf("bytes (requested in kernel function): %d\n", self->kernel_value);
+ printf("bytes (actual): %d\n", arg1);
+
+ /* uninitialized self-> variables are 0 */
+ printf("uninitialized is zero: %d\n", self->dummy);
+
+ /*
+ * It is good practice to zero out variables when you are
+ * done with them to free up the associated memory.
+ */
+ self->exp = 0;
+ self->imp = 0;
+ self->kernel_value = 0;
+
+ /* but for this example, we are finished */
+ exit(0);
+}
diff --git a/examples/language_features/009intro-pragmas.d b/examples/language_features/009intro-pragmas.d
new file mode 100755
index 000000000..a4e359976
--- /dev/null
+++ b/examples/language_features/009intro-pragmas.d
@@ -0,0 +1,38 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./009intro-pragmas.d
+ *
+ * DESCRIPTION
+ * D options can be set on the command line. E.g.,
+ *
+ * sudo /usr/sbin/dtrace -xq ...
+ * sudo /usr/sbin/dtrace -xw ...
+ * sudo /usr/sbin/dtrace -xstrsize=12 ...
+ *
+ * They can also be set via pragmas inside a script.
+ * Respectively,
+ */
+
+/* -q */
+#pragma D option quiet
+
+/* -w, e.g., for the system() call */
+#pragma D option destructive
+
+/* -xstrsize=12 */
+#pragma D option strsize=12
+
+/*
+ * Simple options could alternatively be set on the first
+ * "shebang" line. E.g., "#!/usr/sbin/dtrace -qws".
+ */
+
+dtrace:::BEGIN
+{
+ /* string truncated to strsize */
+ system("echo abcdefghijklmnopqrstuvwxyz");
+
+ exit(0);
+}
diff --git a/examples/language_features/010intro-external-variables.d b/examples/language_features/010intro-external-variables.d
new file mode 100755
index 000000000..a763a36f6
--- /dev/null
+++ b/examples/language_features/010intro-external-variables.d
@@ -0,0 +1,53 @@
+#!/usr/sbin/dtrace -s
+
+#pragma D option quiet
+#pragma D option strsize=64
+
+/*
+ * SYNOPSIS
+ * sudo ./010intro-external-variables.d
+ *
+ * DESCRIPTION
+ * We can examine external variables -- that is, variables from outside
+ * the D program, such as kernel variables. They are distinguished by
+ * a back quote ` and constitute a different name space from that of
+ * the D variables. When such a variables is named, the kernel and all
+ * loaded modules are searched for the name. Or, you can specify a
+ * module before the back quote. The kernel itself is module "vmlinux".
+ * D scripts cannot modify values of kernel variables.
+ *
+ * Such external variables are not part of D and are subject to change.
+ */
+
+dtrace:::BEGIN
+{
+
+ printf("\n==== print some kernel variables ====\n\n");
+
+ printf("cad_pid->level: %d\n", ((struct pid *)`cad_pid)->level);
+ printf("crashing_cpu: %d\n", `crashing_cpu);
+ printf("linux_banner: %s\n", (string)`linux_banner);
+ printf("linux_proc_banner: %s\n", (string)`linux_proc_banner);
+ printf("load averages: %d.%02d, %d.%02d, %d.%02d\n",
+ `avenrun[0] / 2048, ((`avenrun[0] % 2048) * 100) / 2048,
+ `avenrun[1] / 2048, ((`avenrun[1] % 2048) * 100) / 2048,
+ `avenrun[2] / 2048, ((`avenrun[2] % 2048) * 100) / 2048);
+ printf("max_pvn: %d\n", `max_pfn);
+ printf("nr_cpu_ids: %d\n", `nr_cpu_ids);
+ printf("saved_command_line: %s\n", (string)`saved_command_line);
+
+ printf("\n==== reprint some of them, explicitly naming vmlinux ====\n\n");
+
+ printf("crashing_cpu: %d\n", vmlinux`crashing_cpu);
+ printf("linux_banner: %s\n", (string)vmlinux`linux_banner);
+ printf("linux_proc_banner: %s\n", (string)vmlinux`linux_proc_banner);
+ printf("max_pvn: %d\n", vmlinux`max_pfn);
+ printf("nr_cpu_ids: %d\n", vmlinux`nr_cpu_ids);
+
+ printf("\n==== refer to a data type in a module ====\n\n");
+
+ printf("offset of atomic in struct rds_message: %d\n",
+ offsetof(struct rds`rds_message, atomic));
+
+ exit(0);
+}
diff --git a/examples/language_features/011intro-preprocessor.d b/examples/language_features/011intro-preprocessor.d
new file mode 100755
index 000000000..1ece9d2d6
--- /dev/null
+++ b/examples/language_features/011intro-preprocessor.d
@@ -0,0 +1,25 @@
+#!/usr/sbin/dtrace -Cqs
+
+/*
+ * SYNOPSIS
+ * sudo ./011intro-preprocessor.d
+ *
+ * DESCRIPTION
+ * D scripts can also use the C preprocessor. This requires using
+ * the "dtrace -C" option, or by adding 'C' to the first, shebang
+ * line, as illustrated above.
+ */
+
+#define KEYS "abcde", 12
+#define DEBUG
+
+dtrace:::BEGIN
+{
+#ifdef DEBUG
+ printf("\nabcde, 12 should get a count of 1\n");
+#endif
+
+ @[KEYS] = count();
+
+ exit(0);
+}
diff --git a/examples/language_features/012intro-predicates.d b/examples/language_features/012intro-predicates.d
new file mode 100755
index 000000000..3e4f7894d
--- /dev/null
+++ b/examples/language_features/012intro-predicates.d
@@ -0,0 +1,52 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./012intro-predicates.d
+ *
+ * DESCRIPTION
+ * We have already seen that predicates can be used to
+ * conditionalize execution in a D scripts. (Other
+ * mechanisms include speculation and ternary operators.)
+ */
+
+BEGIN
+{
+ ntimes = 0;
+}
+
+/*
+ * The write() system call has a file descriptor in arg0 and a
+ * requested number of bytes in arg2. If a nonzero number of
+ * bytes is requested, take note of the fd.
+ */
+syscall::write:entry
+/ (self->nbytes_requested = arg2) != 0 /
+{
+ ntimes++;
+ self->fd = arg0;
+}
+
+/*
+ * When the write() system call returns, we know the actual
+ * number of bytes written. Report it.
+ */
+syscall::write:return
+/ self->nbytes_requested != 0 /
+{
+ printf("fd %d: bytes requested %d, actual %d\n",
+ self->fd, self->nbytes_requested, arg1);
+
+ /* free memory associated with self-> variables */
+ self->nbytes_requested = 0;
+ self->fd = 0;
+}
+
+/*
+ * Only report a limited number of times.
+ */
+syscall::write:return
+/ ntimes >= 5 /
+{
+ exit(0);
+}
diff --git a/examples/language_features/100list_probes.sh b/examples/language_features/100list_probes.sh
new file mode 100755
index 000000000..23bfd0525
--- /dev/null
+++ b/examples/language_features/100list_probes.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# SYNOPSIS
+# ./100list_probes.sh
+#
+# DESCRIPTION
+# List all probes. There are many.
+
+sudo /usr/sbin/dtrace -l | head -10
+echo "[...]"
+sudo /usr/sbin/dtrace -l | tail -10
diff --git a/examples/language_features/101count_probes_by_provider.sh b/examples/language_features/101count_probes_by_provider.sh
new file mode 100755
index 000000000..e897a1062
--- /dev/null
+++ b/examples/language_features/101count_probes_by_provider.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# SYNOPSIS
+# ./101count_probes_by_provider.sh
+#
+# DESCRIPTION
+# The second column lists probes' providers. Count how
+# many probes there are for each provider. Use "tail" to
+# start at the second line of output, since the first line
+# has the column headers, which we want to ignore.
+
+sudo /usr/sbin/dtrace -l | tail -n +2 | awk '{print $2}' | sort | uniq -c
diff --git a/examples/language_features/102list_probe_arguments.sh b/examples/language_features/102list_probe_arguments.sh
new file mode 100755
index 000000000..3d6eff537
--- /dev/null
+++ b/examples/language_features/102list_probe_arguments.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# SYNOPSIS
+# ./102list_probe_arguments.sh
+#
+# DESCRIPTION
+# Some providers provide typed arguments for (some of) their
+# probes. Use "dtrace -lv" for a verbose listing to see the
+# arguments and their types, if any.
+
+sudo /usr/sbin/dtrace -lv -n 'syscall::open:entry'
diff --git a/examples/language_features/110provider_profile.d b/examples/language_features/110provider_profile.d
new file mode 100755
index 000000000..7f9e75b7d
--- /dev/null
+++ b/examples/language_features/110provider_profile.d
@@ -0,0 +1,29 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./110provider_profile.d
+ *
+ * DESCRIPTION
+ * The profile provider has basically two kinds of probes:
+ * - profile probes fire on all CPUs each time period
+ * - tick probes fire only once each time period
+ * One can specify either a frequency or a time period.
+ */
+
+/*
+ * A profile probe is used with higher frequency to sample
+ * what is executing on the system.
+ */
+profile:::profile-10hz
+{
+ printf("CPU %d: running %s\n", cpu, execname);
+}
+
+/*
+ * A tick probe is used to fire once to stop data collection.
+ */
+profile:::tick-2sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/120provider_proc.d b/examples/language_features/120provider_proc.d
new file mode 100755
index 000000000..7c442f39f
--- /dev/null
+++ b/examples/language_features/120provider_proc.d
@@ -0,0 +1,37 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./120provider_proc.d
+ *
+ * DESCRIPTION
+ * We can track when processes throughout their life cycle.
+ */
+
+proc:::create
+{
+ printf("%s %s\n", probename, args[0]->pr_fname);
+}
+
+proc:::exec
+{
+ printf("%s %s\n", probename, args[0]);
+}
+
+proc:::start
+{
+ printf("%s %s\n", probename, execname);
+}
+
+proc:::exit
+{
+ printf("%s %s\n", probename, execname);
+}
+
+/*
+ * A tick probe is used to fire once to stop data collection.
+ */
+profile:::tick-10sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/130provider_sched_on_off_cpu.d b/examples/language_features/130provider_sched_on_off_cpu.d
new file mode 100755
index 000000000..0d927bc3d
--- /dev/null
+++ b/examples/language_features/130provider_sched_on_off_cpu.d
@@ -0,0 +1,28 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./130provider_sched_on_off_cpu.d
+ *
+ * DESCRIPTION
+ * We can track when processes start and end execution
+ * on a CPU.
+ */
+
+sched:::on-cpu
+{
+ printf("on CPU %d is %s\n", curcpu->cpu_id, curlwpsinfo->pr_name);
+}
+
+sched:::off-cpu
+{
+ printf("off CPU %d is %s\n", curcpu->cpu_id, args[0]->pr_name);
+}
+
+/*
+ * A tick probe is used to fire once to stop data collection.
+ */
+profile:::tick-2sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/131provider_sched_enqueue_dequeue.d b/examples/language_features/131provider_sched_enqueue_dequeue.d
new file mode 100755
index 000000000..c773dc908
--- /dev/null
+++ b/examples/language_features/131provider_sched_enqueue_dequeue.d
@@ -0,0 +1,24 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./131provider_sched_enqueue_dequeue.d
+ *
+ * DESCRIPTION
+ * We can track when processes are enqueued and
+ * dequeued on CPUs.
+ */
+
+sched:::dequeue,
+sched:::enqueue
+{
+ printf(" %s %s\n", probename, args[0]->pr_name);
+}
+
+/*
+ * A tick probe is used to fire once to stop data collection.
+ */
+profile:::tick-2sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/140provider_syscall.d b/examples/language_features/140provider_syscall.d
new file mode 100755
index 000000000..370e7063d
--- /dev/null
+++ b/examples/language_features/140provider_syscall.d
@@ -0,0 +1,20 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./140provider_syscall.d
+ *
+ * DESCRIPTION
+ * With the syscall provider, you can probe the entry to
+ * and return from any system call, which is useful for
+ * watching how a program moves between user space and the
+ * kernel.
+ */
+
+/* probe on entry to system call write() */
+syscall::write:entry
+{
+ printf("write %d bytes to fd %d\n", args[2], args[0]);
+
+ exit(0);
+}
diff --git a/examples/language_features/150provider_fbt.d b/examples/language_features/150provider_fbt.d
new file mode 100755
index 000000000..6dd30e264
--- /dev/null
+++ b/examples/language_features/150provider_fbt.d
@@ -0,0 +1,48 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./150provider_fbt.d
+ *
+ * DESCRIPTION
+ * Function boundary tracing (fbt) can be used to trace the
+ * beginning and end of just about any function in the kernel.
+ * As such, it is very powerful.
+ *
+ * That said, there are some challenges:
+ *
+ * - One must know the kernel fairly well. There may
+ * be tens of thousands of functions available to trace.
+ *
+ * - Functions that can be traced will vary from one
+ * kernel build to another. For example, a function
+ * might be inlined in a kernel build, making that
+ * function not traceable in that build.
+ *
+ * Also, there are both:
+ *
+ * - an fbt provider, which supports typed probe arguments
+ * (seen with "dtrace -lvP fbt")
+ *
+ * - a rawfbt provider, whose probe arguments are not typed
+ * nor specially ordered; traceable functions include
+ * compiler-generated optimized variants of functions,
+ * named <func>.<suffix>.
+ */
+
+/* probe on entry to kernel function ksys_write() */
+fbt::ksys_write:entry
+{
+ printf("write %d bytes to fd %d\n", args[2], args[0]);
+}
+
+/* do the same thing with a rawfbt probe, so the args are not typed */
+rawfbt::ksys_write:entry
+{
+ printf("write %d bytes to fd %d\n", arg2, arg0);
+}
+
+fbt::ksys_write:return
+{
+ exit(0);
+}
diff --git a/examples/language_features/160provider_sdt_rawtp.sh b/examples/language_features/160provider_sdt_rawtp.sh
new file mode 100755
index 000000000..92ac621eb
--- /dev/null
+++ b/examples/language_features/160provider_sdt_rawtp.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# SYNOPSIS
+# ./160provider_sdt_rawtp.sh
+#
+# DESCRIPTION
+# Statically Defined Tracing (SDT) refers to probes placed
+# in the kernel source code by kernel developers to support
+# tracing in stable, semantically meaningful terms. In the
+# case of Linux, this means tracepoints.
+
+# You can see these tracepoints via the sdt provider.
+sudo /usr/sbin/dtrace -lP sdt
+
+# You can see the exact same probes via the rawtp provider.
+sudo /usr/sbin/dtrace -lP rawtp
+
+# The difference is that the rawtp provider gives DTrace users
+# access to the raw tracepoints exposed by the kernel tracing
+# system, including access to the untranslated arguments of the
+# associated tracepoint events. Regular tracepoints often expose
+# translated arguments based on the raw arguments that were passed
+# in the tracepoint call.
+
+# For example, the DTrace sched provider implements the "off-cpu"
+# probe by using "rawtp:sched::sched_switch" and the "wakeup" probe
+# by using "rawtp:sched::sched_wakeup". We can see how the typed
+# arguments for these probes depend on whether we are using the
+# sdt or the rawtp provider:
+
+sudo /usr/sbin/dtrace -lvn :sched::sched_switch | grep args
+sudo /usr/sbin/dtrace -lvn :sched::sched_wakeup | grep args
+
+# Here, the module "sched" is named, but the provider name,
+# expected before the first colon ":", is left blank as a
+# wildcard to pick up both providers with these probes.
diff --git a/examples/language_features/170provider_lockstat.sh b/examples/language_features/170provider_lockstat.sh
new file mode 100755
index 000000000..de7913a82
--- /dev/null
+++ b/examples/language_features/170provider_lockstat.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# SYNOPSIS
+# ./170provider_lockstat.sh
+#
+# DESCRIPTION
+# The lockstat provider can be used to study lock usage.
+# For example, here we count how many times spin locks
+# and adaptive locks are acquired by the command "date".
+
+sudo /usr/sbin/dtrace -qn '
+lockstat:::spin-acquire,
+lockstat:::adaptive-acquire
+/pid == $target/
+{
+ @locks[probename] = count();
+}' -c date
diff --git a/examples/language_features/171provider_io.d b/examples/language_features/171provider_io.d
new file mode 100755
index 000000000..b91ba6cf5
--- /dev/null
+++ b/examples/language_features/171provider_io.d
@@ -0,0 +1,31 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./171provider_io.d
+ *
+ * DESCRIPTION
+ * The io provider can be used to study I/O operations.
+ * For example, here we print whenever an I/O operation
+ * is started over the course of ten seconds, reporting
+ * the device name and whether it is a read or write
+ * operation. Consult the documentation for more
+ * information on the probes and their arguments.
+ */
+
+BEGIN
+{
+ printf("%10s %2s\n", "DEVICE", "RW");
+}
+
+io:::start
+{
+ printf("%10s %2s\n",
+ args[1]->dev_statname,
+ args[0]->b_flags & B_READ ? "R" : "W");
+}
+
+tick-10s
+{
+ exit(0);
+}
diff --git a/examples/language_features/172provider_ip.d b/examples/language_features/172provider_ip.d
new file mode 100755
index 000000000..dce852fbd
--- /dev/null
+++ b/examples/language_features/172provider_ip.d
@@ -0,0 +1,26 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./172provider_ip.d
+ *
+ * DESCRIPTION
+ * The ip provider can be used to study IP communications.
+ * Here is an example that tracks sends and receives,
+ * reporting their source and destination addresses for
+ * 10 seconds.
+ *
+ * Consult the documentation for further information about
+ * ip probe arguments.
+ */
+
+ip:::send,
+ip:::receive
+{
+ printf("%s: from %s to %s\n", probename, args[2]->ip_saddr, args[2]->ip_daddr);
+}
+
+tick-10s
+{
+ exit(0);
+}
diff --git a/examples/language_features/180provider_cpc.d b/examples/language_features/180provider_cpc.d
new file mode 100755
index 000000000..a2ce8b271
--- /dev/null
+++ b/examples/language_features/180provider_cpc.d
@@ -0,0 +1,52 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./180provider_cpc.d
+ *
+ * DESCRIPTION
+ * The cpc (CPU Performance Counter) provider can be used
+ * profile activity based on hardware performance counters.
+ *
+ * For a list of cpc probes, use "dtrace -lP cpc".
+ *
+ * Probe specifications are of the form:
+ *
+ * cpc:::<event name>-<mode>-<count>
+ *
+ * The "event name" tells you which counters are available
+ * on your hardware.
+ *
+ * The "mode" can be "kernel", "user", or "all", even if
+ * only "all" is listed.
+ *
+ * The "count" indicates how many times the event is seen
+ * before the probe fires. If the listed "count" is not
+ * suitable for your purposes, you can specify a different
+ * value. It is usually best to start with a higher value,
+ * decreasing it if there are not enough probe firings,
+ * rather than starting too low and inundating your system
+ * with very many probe firings.
+ *
+ * Here, an event perf_count_sw_cpu_clock-all-1000000000
+ * was listed by "dtrace -lP cpc". We tweak the "mode"
+ * and "count" for our purposes. The probe will fire every
+ * 300 usecs of kernel activity. We use aggregations to
+ * count how many times each call stack is seen. The
+ * aggregation will be printed by default when the D
+ * script exits, after 10 seconds.
+ *
+ * Consult the documentation for further information about
+ * cpc probes.
+ */
+
+cpc:::perf_count_sw_cpu_clock-kernel-300000000
+{
+ @[stack()] = count();
+}
+
+/* run for only 10 seconds */
+tick-10s
+{
+ exit(0);
+}
diff --git a/examples/language_features/200built-in-variables-probe.d b/examples/language_features/200built-in-variables-probe.d
new file mode 100755
index 000000000..5d7e97260
--- /dev/null
+++ b/examples/language_features/200built-in-variables-probe.d
@@ -0,0 +1,18 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./200built-in-variables-probe.d
+ *
+ * DESCRIPTION
+ * We can get a probe's name components when it fires.
+ * The fully qualified name is provider:module:function:name,
+ * which we get with the built-in variables probeprov,
+ * probemod, probefunc, and probename, respectively.
+ */
+
+syscall:::entry
+{
+ printf("%s:%s:%s:%s\n", probeprov, probemod, probefunc, probename);
+ exit(0);
+}
diff --git a/examples/language_features/201built-in-variables-timestamps.d b/examples/language_features/201built-in-variables-timestamps.d
new file mode 100755
index 000000000..c07ee0456
--- /dev/null
+++ b/examples/language_features/201built-in-variables-timestamps.d
@@ -0,0 +1,31 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./201built-in-variables-timestamps.d
+ *
+ * DESCRIPTION
+ * Built-in variables provide timestamps:
+ *
+ * - timestamp counts nanoseconds since
+ * some arbitrary point in the past.
+ * Use differences between values to measure
+ * elapsed time. The value is captured only
+ * once per clause.
+ *
+ * - walltimestamp counts nanoseconds since
+ * 00:00 Universal Coordinated Time, January 1, 1970.
+ */
+
+dtrace:::BEGIN
+{
+ printf("\n%d %d\n", timestamp, walltimestamp);
+ printf( "%d %d\n", timestamp, walltimestamp);
+}
+
+dtrace:::BEGIN
+{
+ printf("\n%d %d\n", timestamp, walltimestamp);
+ printf( "%d %d\n", timestamp, walltimestamp);
+ exit(0);
+}
diff --git a/examples/language_features/202built-in-variables-args.d b/examples/language_features/202built-in-variables-args.d
new file mode 100755
index 000000000..192fff8e1
--- /dev/null
+++ b/examples/language_features/202built-in-variables-args.d
@@ -0,0 +1,28 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./202built-in-variables-args.d
+ *
+ * DESCRIPTION
+ * A probe's arguments can be accessed through the built-in
+ * variables arg0, arg1, arg2, etc. These are all signed,
+ * 64-bit integers, whose meanings depend on the probe.
+ *
+ * Some probes have typed arguments args[0], args[1], args[2],
+ * etc. To see the types, use "dtrace -lvn $probe". Their
+ * meanings are described in the documentation.
+ *
+ * When arguments are typed, they may also be reordered.
+ * So there may be no correspondence between, say, arg0
+ * and args[0].
+ *
+ * Typically, it is better to use the typed args[] when
+ * the probe has them.
+ */
+
+syscall::write:entry
+{
+ printf("Write %d bytes to fd %d\n", args[2], arg0);
+ exit(0);
+}
diff --git a/examples/language_features/203built-in-variables-process-info.d b/examples/language_features/203built-in-variables-process-info.d
new file mode 100755
index 000000000..82600ba9e
--- /dev/null
+++ b/examples/language_features/203built-in-variables-process-info.d
@@ -0,0 +1,31 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./203built-in-variables-process-info.d
+ *
+ * DESCRIPTION
+ * A number of built-in variables describe the process
+ * that is running.
+ */
+
+syscall::write:entry
+{
+ printf("\n\n");
+ printf("The CPU is %d (%d).\n", curcpu->cpu_id, cpu);
+
+ printf("\n");
+ printf("The exec name can be accessed through\n");
+ printf(" the struct task_struct: %s\n", curthread->comm);
+ printf(" or a built-in variable: %s\n", execname);
+
+ printf("\n");
+ printf("The execargs are: %s\n", execargs);
+
+ printf("\n");
+ printf("The group id and user id are %d and %d.\n", gid, uid);
+ printf("The process id and thread id are %d and %d.\n", pid, tid);
+ printf("The parent process id is %d.\n\n", ppid);
+
+ exit(0);
+}
diff --git a/examples/language_features/204built-in-variables-uregs.d b/examples/language_features/204built-in-variables-uregs.d
new file mode 100755
index 000000000..55e333218
--- /dev/null
+++ b/examples/language_features/204built-in-variables-uregs.d
@@ -0,0 +1,31 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./204built-in-variables-uregs.d
+ *
+ * DESCRIPTION
+ * The uregs[] array can be used to access the current thread's
+ * saved user-mode register values at probe firing time. The
+ * available registers depend on the chip architecture:
+ *
+ * - x86_64
+ * R_SP R_PC R_FP R_TRAPNO R_ERR
+ * R_CS R_DS R_ES R_FS R_GS R_PS R_SS
+ * R_RSP R_EBP R_RFL R_RIP R_RDI R_RSI R_RAX R_RBX R_RCX R_RDX
+ * R_ESP R_RBP R_EFL R_EIP R_EDI R_ESI R_EAX R_EBX R_ECX R_EDX
+ * R_R0 R_R1 R_R8 R_R9 R_R10 R_R11 R_R12 R_R13 R_R14 R_R15
+ *
+ * - aarch64:
+ * R_SP R_PC R_PSTATE R_FP
+ * R_X0 R_X1 R_X2 R_X3 R_X4 R_X5 R_X6 R_X7 R_X8 R_X9
+ * R_X10 R_X11 R_X12 R_X13 R_X14 R_X15 R_X16 R_X17 R_X18 R_X19
+ * R_X20 R_X21 R_X22 R_X23 R_X24 R_X25 R_X26 R_X27 R_X28 R_X29
+ * R_X30
+ */
+
+BEGIN
+{
+ printf("0x%x 0x%x\n", uregs[R_PC], uregs[R_SP]);
+ exit(0);
+}
diff --git a/examples/language_features/300actions-exit.sh b/examples/language_features/300actions-exit.sh
new file mode 100755
index 000000000..fea6cb50d
--- /dev/null
+++ b/examples/language_features/300actions-exit.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# SYNOPSIS
+# sudo ./300actions-exit.sh
+#
+# DESCRIPTION
+# The exit() action terminates the script, returning an
+# unsigned 8-bit integer value.
+
+/usr/sbin/dtrace -n '
+BEGIN
+{
+ x = 123;
+ y = 456;
+ exit(x < y ? x : y);
+}'
+
+echo $?
diff --git a/examples/language_features/301actions-print.d b/examples/language_features/301actions-print.d
new file mode 100755
index 000000000..0b3742644
--- /dev/null
+++ b/examples/language_features/301actions-print.d
@@ -0,0 +1,32 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./301actions-print.d
+ *
+ * DESCRIPTION
+ * There are multiple ways to examine data values.
+ */
+
+BEGIN
+{
+ /* printf() is a C-like formatted print */
+ /* note: %d can be used with any size of integer */
+ printf("%c %d %s\n", 'a', 1234ll, "hello");
+
+ /* trace() uses data-type information to format */
+ trace('a');
+ trace(1234);
+ trace("hello");
+
+ /* tracemem() dumps a specified number of bytes */
+ tracemem(`linux_banner, 64);
+
+ /*
+ * print() uses data-type information to print and annotate
+ * contents at a pointer
+ */
+ print(curthread);
+
+ exit(0);
+}
diff --git a/examples/language_features/302actions-strings.d b/examples/language_features/302actions-strings.d
new file mode 100755
index 000000000..097eff064
--- /dev/null
+++ b/examples/language_features/302actions-strings.d
@@ -0,0 +1,45 @@
+#!/usr/sbin/dtrace -s
+
+#pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./302actions-strings.d
+ *
+ * DESCRIPTION
+ * There are functions to manipulate strings.
+ */
+
+BEGIN
+{
+ /* find the length of a string */
+ printf("length of 'abcde' is %d\n", strlen("abcde"));
+
+ /* various other functions also resemble libc */
+ printf("text starts with first 'd': %s\n", strchr("abcdefedbca", 'd'));
+ printf("text starts with last 'd': %s\n", strrchr("abcdefedbca", 'd'));
+ printf("text starts with 'def': %s\n", strstr("abcdefedbca", "def"));
+
+ /* break a string up into tokens */
+ printf("token #1: %s\n", strtok("hello world", " "));
+ printf("token #2: %s\n", strtok(NULL, " "));
+
+ /* join (concatenate) strings */
+ printf("join 'abc' and 'def': %s\n", strjoin("abc", "def"));
+
+ /* dump part of a string */
+ printf("start at char #4: %s\n", substr("abcdefghij", 4));
+
+ /* dump part of a string, only 2 chars */
+ printf("start at char #4: %s\n", substr("abcdefghij", 4, 2));
+
+ /* find index of a substring */
+ printf("found at char #%d\n", index("abcdefghij", "def"));
+ printf("found at char #%d\n", rindex("abcdefghij", "def"));
+ printf("found at char #%d\n", index("abcdefghij", "XXXX"));
+
+ /* convert an integer to a string */
+ printf("123000 + 456 = %s\n", lltostr(123000 + 456));
+
+ exit(0);
+}
diff --git a/examples/language_features/303actions-strings-paths.d b/examples/language_features/303actions-strings-paths.d
new file mode 100755
index 000000000..9588d08f5
--- /dev/null
+++ b/examples/language_features/303actions-strings-paths.d
@@ -0,0 +1,25 @@
+#!/usr/sbin/dtrace -s
+
+#pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./303actions-strings-paths.d
+ *
+ * DESCRIPTION
+ * There are functions to manipulate strings, specifically path names.
+ */
+
+BEGIN
+{
+ /* find the base name of a path */
+ printf("base is %s\n", basename("/usr/sbin/dtrace"));
+
+ /* find the directory of a path */
+ printf("directory is %s\n", dirname("/usr/sbin/dtrace"));
+
+ /* clean up a path name */
+ printf("cleaned up path is %s\n", cleanpath("////usr/bin/../sbin/././//dtrace"));
+
+ exit(0);
+}
diff --git a/examples/language_features/304actions-system.d b/examples/language_features/304actions-system.d
new file mode 100755
index 000000000..001566808
--- /dev/null
+++ b/examples/language_features/304actions-system.d
@@ -0,0 +1,22 @@
+#!/usr/sbin/dtrace -s
+
+#pragma D option quiet
+#pragma D option destructive
+
+/*
+ * SYNOPSIS
+ * sudo ./304actions-system.d
+ *
+ * DESCRIPTION
+ * One can launch system calls from within D script,
+ * but this must be explicitly allowed with the "destructive"
+ * option or dtrace -w switch.
+ */
+
+BEGIN
+{
+ system("ls");
+ system("echo hello world");
+
+ exit(0);
+}
diff --git a/examples/language_features/305actions-progenyof.d b/examples/language_features/305actions-progenyof.d
new file mode 100755
index 000000000..2aa3e8ea3
--- /dev/null
+++ b/examples/language_features/305actions-progenyof.d
@@ -0,0 +1,20 @@
+#!/usr/sbin/dtrace -s
+
+#pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./305actions-progenyof.d
+ *
+ * DESCRIPTION
+ * One can test whether one is progeny of another process.
+ */
+
+BEGIN
+{
+ printf("I am%s progeny of pid 1234\n", progenyof(1234) ? "" : " not");
+ printf("I am%s progeny of myself\n", progenyof(pid) ? "" : " not");
+ printf("I am%s progeny of my parent\n", progenyof(ppid) ? "" : " not");
+
+ exit(0);
+}
diff --git a/examples/language_features/306actions-speculations.d b/examples/language_features/306actions-speculations.d
new file mode 100755
index 000000000..7e0d21370
--- /dev/null
+++ b/examples/language_features/306actions-speculations.d
@@ -0,0 +1,57 @@
+#!/usr/sbin/dtrace -s
+
+#pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./306actions-speculations.d
+ *
+ * DESCRIPTION
+ * In DTrace, one can write output speculatively, deciding at a
+ * later time whether to commit or discard the speculative output.
+ */
+#pragma D option nspec=2
+
+BEGIN
+{
+ /* create two speculations */
+ s1 = speculation();
+ s2 = speculation();
+}
+
+BEGIN
+{
+ /* the clause has "speculate()" before data recording */
+ speculate(s1);
+ printf("one\n");
+}
+
+BEGIN
+{
+ speculate(s2);
+ printf("two\n");
+}
+
+BEGIN
+{
+ speculate(s1);
+ printf("three\n");
+}
+
+BEGIN
+{
+ speculate(s2);
+ printf("four\n");
+}
+
+BEGIN
+{
+ commit(s1); /* commit "one" "three" output */
+ discard(s2); /* discard "two" "four" output */
+}
+
+BEGIN
+{
+ /* cannot be in same clause as commit/discard */
+ exit(0);
+}
diff --git a/examples/language_features/307actions-callstack.d b/examples/language_features/307actions-callstack.d
new file mode 100755
index 000000000..8e3d6fe47
--- /dev/null
+++ b/examples/language_features/307actions-callstack.d
@@ -0,0 +1,54 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./307actions-callstack.d
+ *
+ * DESCRIPTION
+ * In DTrace, one can get information about the call stack.
+ */
+
+/* have a probe fire somewhere in the kernel */
+fbt::ksys_write:entry
+{
+ /* dump the kernel call stack */
+ stack();
+
+ /* a built-in variable gives the number of frames */
+ printf("%d frames\n", stackdepth);
+
+ /* another built-in variable gives the caller PC */
+ printf("second frame is the caller %p\n", caller);
+
+ /* we can get the symbolic name of its module */
+ printf( " module "); mod(caller);
+
+ /* we can get the symbolic name of its function */
+ printf("\n function "); func(caller);
+
+ /* sym() is an alias for func() */
+ printf("\n == symbol"); sym(caller);
+
+ /*
+ * Similarly, we can also get the user-space call stack
+ * through ustack(). The related built-in variables are
+ * ustackdepth and ucaller. Conversion to symbolic names
+ * can be made via umod(), ufunc(), usym(), and uaddr().
+ * User-space call stacks face a few challenges:
+ *
+ * - The BPF helper functions rely on frame-pointer
+ * chasing, which can be impaired or even incorrect
+ * unless -fno-omit-frame-pointer is specified.
+ *
+ * - Conversion to symbolic names does not take place when
+ * the probe fires but later, in user space, when results
+ * are printed. It is possible that the user application
+ * has already terminated and the information about its
+ * address space gone.
+ *
+ * - For a stripped executable, you must specify
+ * --export-dynamic when linking the program.
+ */
+
+ exit(0);
+}
diff --git a/examples/language_features/310actions-alloca-copy.d b/examples/language_features/310actions-alloca-copy.d
new file mode 100755
index 000000000..1a48c696b
--- /dev/null
+++ b/examples/language_features/310actions-alloca-copy.d
@@ -0,0 +1,95 @@
+#!/usr/sbin/dtrace -qs
+
+#pragma D option strsize=32
+
+/*
+ * SYNOPSIS
+ * sudo ./310actions-alloca-copy.d
+ *
+ * DESCRIPTION
+ * A DTrace program has scratch memory that it manages
+ * for its own purposes. The program can allocate scratch
+ * memory as well as copy into, out of, and between buffers.
+ * It is automatically freed at the end of each clause.
+ */
+
+/* alloca */
+BEGIN
+{
+ /* alloca() a string */
+ ptr = (string *) alloca(32);
+
+ /* set its contents (truncated to strsize chars, including NUL) */
+ *ptr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ /* print it */
+ trace(*ptr);
+
+ /* end of the clause: ptr is freed */
+}
+
+/* bcopy */
+BEGIN
+{
+ /* alloca() a string */
+ ptr = (string *) alloca(32);
+
+ /* copy from kernel memory to scratch memory */
+ bcopy(`linux_banner, ptr, 32);
+
+ /* print it */
+ printf("\n%s", *ptr);
+
+ /* end of the clause: ptr is freed */
+}
+
+/* copyinto */
+syscall::write:entry
+{
+ /* alloca() a string */
+ ptr = (string *) alloca(32);
+
+ /* copy from user space into scratch memory */
+ copyinto(arg1, 32, ptr);
+
+ /* print it */
+ printf("\n%s to fd %d, %d bytes", *ptr, arg0, arg2);
+
+ /* end of the clause: ptr is freed */
+}
+
+/* copyin */
+syscall::write:entry
+{
+ /* copy from user space to scratch memory, allocating the pointer */
+ ptr = copyin(arg1, 32);
+
+ /* print it */
+ printf("\n%s to fd %d, %d bytes", *ptr, arg0, arg2);
+
+ /* end of the clause: ptr is freed */
+}
+
+/* copyinstr */
+syscall::write:entry
+{
+ /* copy string from user space to scratch memory, allocating the pointer */
+ str = copyinstr(arg1);
+
+ /* print it */
+ printf("\n%s to fd %d, %d bytes", str, arg0, arg2);
+
+ /* end of the clause: ptr is freed */
+}
+
+/*
+ * There are also copyout() and copyoutstr() functions, that may be
+ * used to copy from scratch memory to user-space buffers. Since they
+ * alter the behavior of the user code, the "destructive" option -w
+ * must be specified.
+ */
+
+syscall::write:entry
+{
+ exit(0);
+}
diff --git a/examples/language_features/311actions-rand.d b/examples/language_features/311actions-rand.d
new file mode 100755
index 000000000..54500fb3b
--- /dev/null
+++ b/examples/language_features/311actions-rand.d
@@ -0,0 +1,25 @@
+#!/usr/sbin/dtrace -qs
+
+/*
+ * SYNOPSIS
+ * sudo ./311actions-rand.d
+ *
+ * DESCRIPTION
+ * The rand() function generates a random integer.
+ */
+
+BEGIN
+{
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+ printf(" %3d\n", rand() & 0xff);
+
+ exit(0);
+}
diff --git a/examples/language_features/500aggregations-intro.d b/examples/language_features/500aggregations-intro.d
new file mode 100755
index 000000000..54b5cbe6b
--- /dev/null
+++ b/examples/language_features/500aggregations-intro.d
@@ -0,0 +1,24 @@
+#!/usr/sbin/dtrace -s
+
+#pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./500aggregations-intro.d
+ *
+ * DESCRIPTION
+ * Aggregations can be used to collect statistical data on
+ * some value. Results are printed by default when the
+ * script terminates.
+ */
+
+tick-20hz
+{
+ /* do not even bother naming the aggregation; just "@" */
+ @ = count();
+}
+
+tick-1sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/501aggregations-names-keys.d b/examples/language_features/501aggregations-names-keys.d
new file mode 100755
index 000000000..06ede5301
--- /dev/null
+++ b/examples/language_features/501aggregations-names-keys.d
@@ -0,0 +1,19 @@
+#!/usr/sbin/dtrace -s
+
+/*
+ * SYNOPSIS
+ * sudo ./501aggregations-names-keys.d
+ *
+ * DESCRIPTION
+ * Aggregations can be named or use keys.
+ */
+
+tick-20hz
+{
+ @my_count[(timestamp % 2 == 0) ? "even" : "odd"] = count();
+}
+
+tick-1sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/502aggregations-basic-funcs.d b/examples/language_features/502aggregations-basic-funcs.d
new file mode 100755
index 000000000..b55a42117
--- /dev/null
+++ b/examples/language_features/502aggregations-basic-funcs.d
@@ -0,0 +1,26 @@
+#!/usr/sbin/dtrace -s
+
+# pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./501aggregations-basic-funcs.d
+ *
+ * DESCRIPTION
+ * Aggregations can include basic statistical functions.
+ */
+
+tick-20hz
+{
+ @my_count["count"] = count();
+ @my_sum["sum"] = sum(timestamp);
+ @my_avg["average"] = avg(timestamp);
+ @my_stddev["standard deviation"] = stddev(timestamp);
+ @my_min["minimum"] = min(timestamp);
+ @my_max["maximum"] = max(timestamp);
+}
+
+tick-1sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/503aggregations-stack.d b/examples/language_features/503aggregations-stack.d
new file mode 100755
index 000000000..3e297c34b
--- /dev/null
+++ b/examples/language_features/503aggregations-stack.d
@@ -0,0 +1,24 @@
+#!/usr/sbin/dtrace -s
+
+# pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./503aggregations-stack.d
+ *
+ * DESCRIPTION
+ * An interesting key to use is "stack()", giving you
+ * information, for example, about hot kernel call stacks.
+ * Each distinct kernel call stack found will be reported
+ * along with the number of times it was encountered.
+ */
+
+tick-20hz
+{
+ @[stack()] = count();
+}
+
+tick-1sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/504aggregations-printa.d b/examples/language_features/504aggregations-printa.d
new file mode 100755
index 000000000..6867511e2
--- /dev/null
+++ b/examples/language_features/504aggregations-printa.d
@@ -0,0 +1,29 @@
+#!/usr/sbin/dtrace -s
+
+# pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./504aggregations-printa.d
+ *
+ * DESCRIPTION
+ * While aggregations are printed by default when the D
+ * script terminates, it can be useful to print aggregations
+ * explicitly using the printa() function -- either to use
+ * detailed formatting or if results should be printed
+ * while the script is still running. In addition to the
+ * usual formatting directives used by printf(), printa()
+ * also uses "@" directives for the aggregation values.
+ */
+
+tick-20hz
+{
+ @["key"] = count();
+}
+
+tick-1sec
+{
+ printa("KEY BEFORE VALUE: %s %@d\n", @);
+ printa("VALUE BEFORE KEY: %@d %s\n", @);
+ exit(0);
+}
diff --git a/examples/language_features/505aggregations-quantize.d b/examples/language_features/505aggregations-quantize.d
new file mode 100755
index 000000000..5ae382843
--- /dev/null
+++ b/examples/language_features/505aggregations-quantize.d
@@ -0,0 +1,65 @@
+#!/usr/sbin/dtrace -s
+
+# pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./505aggregations-quantize.d
+ *
+ * DESCRIPTION
+ * While avg(), stddev(), min(), and max() are useful for
+ * characterizing the distribution of values crudely, one
+ * may also be interested in more detail on a distribution.
+ *
+ * Aggregations can count by value after the value has been
+ * "quantized" -- that is, grouped into bins.
+ */
+
+BEGIN
+{
+ /* time origin at the beginning of the script's execution */
+ t0 = timestamp;
+
+ /* for this example, use some dummy value that will change in scale */
+ dummy = t0;
+}
+
+tick-20hz
+{
+ /*
+ * If you have no sense for the distribution of values --
+ * not even of the scale -- then quantize(val) can be a
+ * good starting point. Its only input parameter is the
+ * value. Bins are powers of two -- you reduce values
+ * log2() -- giving you a sense of the scale of the values.
+ */
+ @crude = quantize(dummy >>= 1);
+
+ /*
+ * But perhaps you know the scale and want to chop some
+ * range of values up linearly. So we use lquantize().
+ */
+ @linear = lquantize(
+ (timestamp - t0) / 1000000, /* number of msecs since D script start */
+ 0, /* lower bound */
+ 2000, /* upper bound (msecs in 2 secs) */
+ 200); /* number per bin */
+
+ /*
+ * Finally, and this is a little complicated, what if you
+ * liked the original, logarithmic quantization, but you
+ * just want a little more detail. Then, there is a
+ * log-linear quantization.
+ */
+ @log_linear = llquantize(
+ dummy, /* number of msecs since some arbitrary time in the past */
+ 100, /* use log10() */
+ 3, /* lower bound is 10 to the 3 = 1000 */
+ 5, /* upper bound is 10 to the 9 = 1000000000 */
+ 4); /* number of steps per logarithmic range */
+}
+
+tick-2sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/506aggregations-clear-trunc.d b/examples/language_features/506aggregations-clear-trunc.d
new file mode 100755
index 000000000..26314d8a8
--- /dev/null
+++ b/examples/language_features/506aggregations-clear-trunc.d
@@ -0,0 +1,63 @@
+#!/usr/sbin/dtrace -s
+
+# pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./506aggregations-clear-trunc.d
+ *
+ * DESCRIPTION
+ * Here, we show clear() and trunc(), used with aggregations.
+ *
+ * One reason to use printa() is to get aggregation values
+ * periodically while the D script is executing. In that
+ * case, however, you may want to reset the aggregations.
+ * There are two ways of doing so.
+ */
+
+tick-20hz
+{
+ /* in this example, consider two aggregations */
+
+ /*
+ * "parity" counts whether the timestamp is even or odd.
+ * In each one-second time interval, both cases should
+ * get some values.
+ */
+ @parity[(timestamp & 1) ? "odd" : "even"] = count();
+
+ /*
+ * "low_bits" counts how often the low bits (0-255) appear.
+ * It is reasonable that some keys will not reappear.
+ */
+ @low_bits[timestamp & 0xff] = count();
+}
+
+tick-1sec
+{
+ /*
+ * Every second, we print the values for the recent
+ * one-second interval.
+ */
+ printa("parity: %-4s %@2d\n", @parity);
+ printa("low bits: %3d %@d\n", @low_bits);
+
+ /*
+ * We clear out the "parity" values, leaving the keys,
+ * since almost surely those keys will recur. And if
+ * a key ends up with no counts, we want to know about it!
+ */
+ clear(@parity);
+
+ /*
+ * We trunc() (truncate) "low_bits", however, since keys
+ * might not reoccur. Unused keys take up memory and
+ * pollute the periodic output.
+ */
+ trunc(@low_bits);
+}
+
+tick-4sec
+{
+ exit(0);
+}
diff --git a/examples/language_features/507aggregations-trunc5.d b/examples/language_features/507aggregations-trunc5.d
new file mode 100755
index 000000000..ca5fdae02
--- /dev/null
+++ b/examples/language_features/507aggregations-trunc5.d
@@ -0,0 +1,50 @@
+#!/usr/sbin/dtrace -s
+
+# pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./507aggregations-trunc5.d
+ *
+ * DESCRIPTION
+ * Here, is another use of trunc() -- with an additional
+ * argument to focus attention on the top values.
+ */
+
+BEGIN
+{
+ /*
+ * The mask will change size. When it is wide, it will
+ * spread values over many keys. When it is narrow, it
+ * will concentrate values over a few keys. So, some
+ * keys will get many values, while others will get few.
+ */
+ mask = 255;
+}
+
+tick-100hz
+{
+ /* the aggregation */
+ @[timestamp & mask] = count();
+
+ /* update the mask */
+ mask >>= 1;
+ mask = (mask == 0) ? 255 : mask;
+}
+
+tick-1sec
+{
+ printf("entire result\n");
+ printa(" %3d %@3d\n", @);
+
+ trunc(@, 5);
+
+ printf("top 5 keys\n");
+ printa(" %3d %@3d\n", @);
+
+ /*
+ * On exit, we will no longer print the aggregation
+ * by default, since it was just printed.
+ */
+ exit(0);
+}
diff --git a/examples/language_features/508aggregations-normalize.d b/examples/language_features/508aggregations-normalize.d
new file mode 100755
index 000000000..5c2429f74
--- /dev/null
+++ b/examples/language_features/508aggregations-normalize.d
@@ -0,0 +1,49 @@
+#!/usr/sbin/dtrace -s
+
+# pragma D option quiet
+
+/*
+ * SYNOPSIS
+ * sudo ./508aggregations-normalize.d
+ *
+ * DESCRIPTION
+ * Here, is another use of trunc() -- with an additional
+ * argument to focus attention on the top values.
+ */
+
+BEGIN
+{
+ treport = timestamp;
+ ttick = timestamp;
+}
+
+/* add the number of nanoseconds since the last tick */
+tick-100hz
+{
+ @ = sum(timestamp - ttick);
+ ttick = timestamp;
+}
+
+/* report at erratic intervals */
+tick-3100ms,
+tick-3600ms
+{
+ /* determine number of usecs since last report */
+ this->us_since_last = (timestamp - treport) / 1000;
+ treport = timestamp;
+ printf("%8d usecs since the last report\n", this->us_since_last);
+
+ /* normalize the total number of nsecs by the number of usecs */
+ normalize(@, this->us_since_last);
+
+ /* report aggregation (do not worry about formatting) */
+ printa(@);
+
+ /* clear the aggregation before resuming */
+ clear(@);
+}
+
+tick-12sec
+{
+ exit(0);
+}
--
2.47.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.