2.9.4 Clause-Local Variables

The value of a D variable can be accessed whenever a probe fires. Section 2.9, “Variables” describes how variables could have a different scope. For a global variable, the same instance of the variable is accessed from every thread. For thread-local, the instance of the variable is thread-specific.

Meanwhile, for a clause-local variable, the instance of the variable is specific to that particular firing of the probe. Clause-local is the narrowest scope. When a probe fires on a CPU, the D script is executed in program order. Each clause-local variable is instantiated with an undefined value the first time it is used in the script. The same instance of the variable is used in all clauses until the D script has completed execution for that particular firing of the probe.

Clause-local variables can be referenced and assigned by prefixing with this->:

BEGIN
{
  this->secs = timestamp / 1000000000;
  ...
}

If you want to declare a clause-local variable explicitly before using it, you can do so by using the this keyword:

this int x;  /* an integer clause-local variable */
this char c; /* a character clause-local variable */

BEGIN
{
  this->x = 123;
  this->c = 'D';
}

Note that if your program contains multiple clauses for a single probe, any clause-local variables remain intact as the clauses are executed, as shown in the following example. Type the following source code and save it in a file named clause.d:

int me;       /* an integer global variable */
this int foo; /* an integer clause-local variable */

tick-1sec
{ 
  /*
   * Set foo to be 10 if and only if this is the first clause executed.
   */
  this->foo = (me % 3 == 0) ? 10 : this->foo;
  printf("Clause 1 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

tick-1sec
{
  /*
   * Set foo to be 20 if and only if this is the first clause executed.
   */
  this->foo = (me % 3 == 0) ? 20 : this->foo;
  printf("Clause 2 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

tick-1sec
{
  /*
   * Set foo to be 30 if and only if this is the first clause executed.
   */
  this->foo = (me % 3 == 0) ? 30 : this->foo;
  printf("Clause 3 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

Because the clauses are always executed in program order, and because clause-local variables are persistent across different clauses that are enabling the same probe, running the preceding program always produces the same output:

# dtrace -q -s clause.d
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
^C

While clause-local variables are persistent across clauses that are enabling the same probe, their values are undefined in the first clause executed for a given probe. Be sure to assign each clause-local variable an appropriate value before using it or your program might have unexpected results.

Clause-local variables can be defined using any scalar variable type, but associative arrays may not be defined using clause-local scope. The scope of clause-local variables only applies to the corresponding variable data, not to the name and type identity defined for the variable. When a clause-local variable is defined, this name and type signature can be used in any subsequent D program clause.

You can use clause-local variables to accumulate intermediate results of calculations or as temporary copies of other variables. Access to a clause-local variable is much faster than access to an associative array. Therefore, if you need to reference an associative array value multiple times in the same D program clause, it is more efficient to copy it into a clause-local variable first and then reference the local variable repeatedly.