Language elements

This section defines the basic syntax of the Vue language and language elements that are common to many Vue scripts.

Variables

The Vue language supports most of the traditional C data types, namely those recognized by the C-89 specification. In addition, Vue includes some extensions to make powerful dynamic tracing programs be written easily.

Vue supports variables with three different scope rules:

  • Variables that are local to one action block only
  • Variables that have global scope
  • Variables that have thread-local scope

In addition, Vue can access variables with external scope like global variables in the kernel or user data in an application being probed.

In general, variables need to be declared before their first use in the script, although Vue also supports a very limited form of implicit type recognition. Variable declaration statements inside an action block must appear before any of the executable statements. They cannot be inside nested blocks like within an if statement. In some cases, you can declare variables outside any of the action blocks, but in this case, all such declarations must appear before the first action block.

Variable classes

Vue supports several classes of variables with varying rules on scope, on how they are initialized, on whether they can be updated or not and on how their types are determined. As in the C language, any declaration statement for a variable must textually precede its first use in the script.

Vue provides special type qualifiers that are added to the declaration statement to indicate the class of the variables being declared. For example, the __global keyword is a class qualifier that you can include in the declaration statement to specify that the variables being declared have "global" class.

In the following example, both foo and bar are declared to be variables of global class:

__global int foo, bar;

Vue also supports implicit recognition of the type of a variable based on its first usage in the script. In this case, there is no declaration statement, but the class of the variable can still be provided by directly attaching a class qualifier to the variable as follows on its first textual reference in the script:

global:count = 5;	/* First reference to variable count in the script */

In the preceding example, the global: keyword is a qualifier that specifies the count variable to be a variable of global class. This variable will also implicitly be assigned the int type because the first reference to it is an assignment expression whose right hand side is an integer constant.

Note: You need to use the __global keyword when specifying the class qualifier with the declaration statement, but the global: keyword when defining it at the first use of the variable in the script. The syntax rules are similar for the other class qualifiers supported by Vue.

Automatic class variables

An automatic variable is clause-specific and is similar to an automatic or stack variable in C. It has scope only within the action block portion of the clause where it is defined or used and is recreated for each invocation of the action block. Automatic variables are always undefined at the start of an action block and must be initialized through an assignment statement before you can use them in an expression or in any other executable statement.

An automatic variable is identified by using the auto: prefix, for example auto:lticks indicates an automatic variable. You can also declare automatic variables using the __auto declaration statement in which case the auto: prefix can be omitted.

You cannot use automatic class variables in the predicate section of a Vue clause.

The following script is an example of the __auto declaration statement:

	__auto int i;      /* Explicit declaration */
	auto:j = 0;        /* Implicit declaration */

Thread-local class variables

A thread-local variable is instantiated per traced thread the first time it issues an action block that assigns a value to the variable. Once created, the thread-local variable exists as long as the Vue script is active and the traced thread does not exit. The value of the thread-local variable is thread-specific and retained across executions of any of the clauses of the same program. In other words, variables of this class are visible everywhere within the Vue script. However, each thread that issues the Vue script obtains its own copy of these variables and the variables in each such copy are accessible and modifiable anywhere within the script only by the thread that instantiated them.

A thread-local variable is distinguished by using the thread: prefix. For example, thread:count indicates a thread-local variable. You can also declare thread-local variables using the __thread declaration statement in which case the thread: prefix can be omitted with the following one exception.

You can use a thread-local variable in the predicate section of a Vue clause even before it is instantiated. Predicates with un-instantiated thread-local variables are always evaluated to a value of FALSE. When used in the predicate section, the thread: prefix must always be included to identify it as a thread-local variable.

The following script is an example of the __thread declaration statement:

	__thread int i;      /* Explicit declaration */
	thread:j = 0;        /* Implicit declaration */
Note: Although you can declare thread-locals inside the @@BEGIN and @@END probes, any other references to them in these special probes can produce undefined behavior. A declaration statement by itself does not cause the thread-local variable to be instantiated.

Global class variables

Variables of global class have global scope and are visible everywhere within a Vue script. You can use a global variable in one or more clauses of a Vue script. They can also be declared at the beginning textually before the first clause for clarity. Global variables are initialized to zero or NULL as appropriate.

All variables in a Vue script are by default assigned global class, unless an explicit non-global class specifier is prefixed to the declaration. You can also explicitly declare global variables by using the __global class specifier when declaring a variable. List variables are, by definition, always created as variables of global class.

Reads and updates of global variables are not serialized unless they are of the list type. There are no guarantees on data races when probes are issued simultaneously. Global variables, which are not of the list type, are useful for collecting profiling and other statistics.

You can use global variables in the predicate section of a Vue clause.

The following scripts are examples for initializing and using global variables:

int wcount;			/* Global variable declared before first clause */

	@@BEGIN
	{
		int f_count;		/* Global variable declared inside @@BEGIN */
		__global int z_count;	/* Global variable declared with __global prefix */

		f_count = 12;
	}

	@@syscall:*:read:entry 
		when (z_count == 0)
	{
		int m_count;		/* Global variable declared inside a probe */
		m_count += f_count;	/* f_count already declared in earlier probe */
		printf("m_count = %d\n", m_count);
		if (wcount == 1)
			exit();
	}
	

	@@syscall:*:write:entry
	{
		m_count++;		/* m_count already declared in earlier probe */

	}

	@@syscall:*:write:exit
	{
		wcount = 1;		/* w_count declared globally */
	}

Kernel global class variables

In ProbeVue, a privileged user can access kernel global variables inside the action block of any Vue clause, even for probe points in user space like the uft probe points. Before using or referring to the kernel variable in the Vue script, you must explicitly declare it using the __kernel declaration statement. Only variables exported by the kernel, that is, only those are present in the export list of /unix are accessible.

You can access integral type kernel variables and kernel variables that are structures or unions and even pointers. Further, you can also refer to the member names of kernel structures and unions in a Vue script. Kernel arrays can also be accessed but there is no support for copying kernel character data into a ProbeVue string.

Access only pinned kernel variables. If the page containing the kernel variable is not in memory (has been paged out), ProbeVue returns a value of zero for that variable.

For an example of how kernel variables can be declared and used in a Vue script.

Kernel variables cannot appear in the predicate section of a clause. Kernel variables are always treated as read-only variables in a Vue script. Any attempt to write to a kernel variable either causes a syntax error or fail later with a script abort message.

Entry class variables

Clauses associated with a probe point that is at the entry location point of a system call or a user function can access the arguments passed to the system call or function being probed.

Probes at entry location points are supported by the system call and the user function tracing probe managers. For example, the read system call takes three arguments: a file descriptor ID, a pointer to a user buffer, and a value for the number of bytes of data to be read. The values of these three arguments can be accessed if the probe specification is @@syscall:*:read:entry, which specifies a probe at the read system call entry point.

Parameters to functions are referenced using the special built-in entry class variable names __arg1, __arg2, __arg3, ... up to the number of arguments passed to the function. For example, in the clause associated with the read system call entry point, __arg1 refers to the value of file descriptor id parameter, __arg2 refers to the value of the buffer pointer parameter, and __arg3 to the size of the data to be read.
Note: When one or more probe point tuples are specified, then __arg <x> variables are not allowed in the Action Block and will result in error as shown in the example below.
@@syscall:*:read:entry,@@syscall:*:write:entry
{
        char *argument;        
        argument=__arg2;  -> Not Allowed.
}
Probevue would exit with the following error message: arg builtin cannot be used. No defined function.

Use of entry class variables in a Vue clause is legal only if the C-style declaration of the function being probed, specifically the data type of the parameters being passed to the function, are also provided in the Vue script. This must appear textually before the first Vue clause that references the entry clause. Place the declaration textually before any Vue clause at the top of the Vue script.

The following script is an example of using entry class variables:

	int read(int fd, char *buf, unsigned long size);

	@@syscall:*:read:entry 
	{
		printf("Number of bytes to read = %d\n", __arg3);
	}
Note: In the preceding example, the definition of the read system call function specified in the script does not exactly match what is given in the /usr/include/unistd.h file, but it works just as well.

A second requirement is that the probe specification associated with the clause identify a unique probe point. Entry class variables cannot be used in a Vue clause that has multiple probe points specified in the probe specification irrespective of whether the functions being probed are the same or have similar function prototypes. The following script is an illegal script and will cause the ProbeVue compiler to fail with a syntax error since the probe specification includes two probe points:

	int read(int fd, char *buf, unsigned long size);
	int write(int fd, char *buf, unsigned long size);

	@@syscall:*:read:entry, @@syscall:*:write:entry 
	{
		/* Cannot use __arg3 in here, as this clause has multiple probe 
		 * points associated with it. This script will fail with a
		 * syntax error in the compilation phase of the probevue command.
		 */
		printf("Number of bytes to read/write = %d\n", __arg3);
	}

The following modified script can work:

	int read(int fd, char *buf, unsigned long size);
	int write(int fd, char *buf, unsigned long size);

	@@syscall:*:read:entry
	{
		printf("Number of bytes to read = %d\n", __arg3);
	}
	@@syscall:*:write:entry 
	{
		printf("Number of bytes to write = %d\n", __arg3);
	}

Exit class variables

Clauses associated with a probe point that is at the exit location points of a system call or user function can access the return value of the system call or user function.

There is only one exit class variable that is defined by the Vue language. This is the return value from a function or a system call, which can be accessed by using the special built-in variable name __rv.

Probes at exit location points are supported by the system call probe manager. For example, the read system call returns the actual number of bytes read or an error return code of -1. This returned value can be accessed at the @@syscall:*:read:exit probe point, which identifies all exit points from the read system call.

Similar to entry class variables, the use of exit class variables in a Vue clause is legal only if the probe specification associated with the clause identifies a unique probe point. Thus, __rv cannot be used in a Vue clause that has multiple probe points specified in the probe specification. Furthermore, the C-style declaration of the function being probed, specifically the data type of the return value, must be explicitly provided in the Vue script. In fact, it is an error to specify a function declaration without providing its return type.

You can use exit class variables in the predicate section of a clause.

The following script is an illegal script and will cause the ProbeVue compiler to fail with a syntax error since the return type of the read function is not specified:

/* Bad example.  */

	int read(int fd, char *buf, unsigned long size);

	@@syscall:*:read:exit
		when (__rv > 0)
	{
		/* Entered on read success: return value = # of bytes read */
		printf("Number of bytes read = %d\n", __rv);
	}

The following modified script can work:

/* Good example.  */

	int read(int fd, char *buf, unsigned long size);

	@@syscall:*:read:exit
		when (__rv > 0)
	{
		/* Entered on read success: return value = # of bytes read */
		printf("Number of bytes read = %d\n", __rv);
	}

Built-in class variables

In addition to the special built-in variables, __arg1 through __arg32 and __rv, Vue also defines a set of general-purpose built-in variables. These general-purpose built-in variables are discussed in more detail in this section and some probe manager specific built-in variables are discussed in their respective probe manager section. Built-in class variables are functions, but are treated as variables by ProbeVue. Therefore, you can use these built-in variables in the predicate section of a Vue clause.

The following built-in variables are supported in Vue:

__tid
Thread ID of traced thread.
__pid
Process ID of traced thread.
__ppid
Parent process ID of traced thread.
__pgid
Process group ID of traced thread.
__pname
Process name of traced thread.
__uid, __euid
Real and effective user ID of traced thread.
__trcid
Process ID of tracing process (that is, of the probevue command)
__errno
Current errno value for the traced thread.
__kernelmode
Current executable mode: is either 1 (in kernel mode) or 0 (in user mode).
__r3, ..., __r10
General purpose register values (for function parameters or return values).
__curthread
Current thread.
__curproc
Current process.
__ublock
User area of the current process.
__mst
Built-in variable to access the hardware register content of the current thread's machine state save area (MST).
__isISR
Built-in variable to identify if current context is an interrupt service routine.

The following script is an example using built-in variables:

	@@syscall:*:read:entry
	{
		printf("Thread ID:%d, Process ID:%d, Parent Process ID:%d\n",
				__tid, __pid, __ppid);
		printf("Process Group ID: %d\n", __pgid);
		printf("Process name = %s\n", __pname);
	
		printf("Real UID=%d, Effective UID=%d\n", __uid, __euid);S
	       
		printf("probevue command process ID = %d\n", __trcid);
	
		printf("Errno = %d\n", __errno);
		printf("Mode = %s\n", __kernelmode == 1 ? "kernel" : "user");
	
		printf("Current values of GPRs: r3=0x%016llx, r4=0x%016llx, r5=0x%016llx\n",
				__r3, __r4, __r5);
		printf("                        r6=0x%016llx, r7=0x%016llx, r8=0x%016llx\n",
				__r6, __r7, __r8);
		printf("                        r9=0x%016llx, r10=0x%016llx\n",
				__r9, __r10);
	}

__curthread built-in variable

__curthread is a special built-in using which the user can access some of the thread related information for the current thread. The information can be accessed using the ->operator on the __curthread built-in. This built-in cannot be used in systrace, BEGIN and END probes. Also it can be used in interval probes only if PID is mentioned. This built-in will basically provide functionality similar to getthrds/getthrds64 but only limited to the current thread. The data that can be accessed are

tid
Thread ID
pid
Process ID
policy
Scheduling Policy
pri
Priority
cpusage
CPU Usage
cpuid
Processor to which the current thread is bound to
sigmask
Signal blocked on the thread
lockcount
Number of kernel lock taken by the thread
ptid
The pthread identifier of this thread (0 if it is a kernel thread, 1 if it is a single-threaded application)
homecpu
Home CPU of a thread.
homesrad
Home srad of a thread

Usage Example

Tid of the current thread can be accessed using  __curthread->tid.

__curproc built-in variable

__curproc is a special built-in using which the user can access some of the process related information for the current process. The information can be accessed using the ->operator on the __curproc built-in. This built-in cannot be used in systrace, BEGIN and END probes. Also, it can be used in interval probes only if PID is mentioned. This built-in will basically provide functionality similar to getproc but only limited to the current process. The data that can be accessed are

pid
Process ID.
ppid
Parent Process ID
pgid
Process Group ID
uid
Real user ID
suid
Saved user ID
pri
Priority
nice
Nice value
cpu
Processor usage
adspace
Process Address Space
majflt
I/O Page Fault
minflt
Non I/O Page Fault
size
Size of image in pages
sigpend
Signals pending on the process
sigignore
Signals ignored by the process
sigcatch
Signals being caught by the process
forktime
Creation time of the process
threadcount
No of threads in the process
cwd
Current working directory. If a free page fault context is not available or the per-CPU computation stack size is less than 96 KB, or in a probe where a page fault is not allowed (for example, interval probe), then this built-in returns a null string

Usage Example

Parent process id of the current process can be accessed using  __curproc->ppid.

__ublock built-in variable

__ublock is a special built-in using which the user can access some of the process related information for the current process. This built-in cannot be used in systrace, BEGIN and END probes. Also it can be used in interval probes only if PID is mentioned. The information can be accessed using the ->operator on the __ublock built-in. The data that can be accessed are

text
Start of text
tsize
Text size (bytes)
data
Start of Data
sdata
Current Data Size (bytes)
mdata
Maximum data size (bytes)
stack
Start of stack
stkmax
Stack Max (bytes)
euid
Effective user id
uid
Real user id
egid
Effective group id
gid
Real group id
utime
Process User resource usage time in seconds
stime
Process System resource usage time in seconds
maxfd
Max fd value in user
is64u
Set to 1, if in context of a 64-bit process

Usage Example

Start of the text for the current process can be accessed using  __ublock->text.

__mst built-in variable

__mst is a special built-in variable in which you can access hardware register content of the current thread. This built-in variable cannot be used in the systrace, BEGIN, and END probes. Also, this built-in variable can be used in the interval probes only if PID is mentioned. The information can be accessed using the -> operator on the __ublock built-in. The registers that can be accessed follow:

r1-r10
General purpose register r1 to r10
r14-r31
General purpose register r14 to r31
iar
Instruction address register
lr
Link register

Usage Example

To access the lr value in a probe, use the following command:
__mst->lr

Value and type assignment

The classification in the preceding section is one way to view the variables in a Vue script. The variable classes can be examined from a different perspective, namely how their values are derived. Under this viewpoint, you can divide the variables into two categories:

External variables

Kernel class variables, entry and exit class variables and built-in variables are all external variables. They exist independent of the ProbeVue framework and derive their values outside the context of any Vue script. ProbeVue allows the current values of external variables to be made available inside a Vue script. These variables are always read-only within the context of the Vue script. Any program statements that attempt to modify the value of an external variable will be flagged by the compiler as an illegal statement.

Although external variables have a pre-defined type, ProbeVue requires explicit declarations of all external variables, except for the built-in ones, in the Vue script that accesses them. The following table describes how the types of external variables are determined:

Variable Type
Kernel global class From the __kernel declaration statement of the kernel variable.
Entry class From the function prototype declaration in the Vue script. Must specify the data types of each argument being used in the Vue script.
Return value from kernel functions From the function prototype declaration in the Vue script. Must provide the type of the return value.
Built-ins These are generally dependent upon the underlying kernel variable. Their defined types and the equivalent ProbeVue types are as follows:
Built-in Defined type ProbeVue type
__tid tid_t long long
__pid pid_t long long
__ppid pid_t long long
__pgid pid_t long long
__pname char [32] String [32]
__uid uid_t unsigned int
__euid uid_t unsigned int
__trcid pid_t long long
__errno int int
__kernelmode int int
__r3..__r10 32-bit for 32-bit process

64-bit for 64-bit process

unsigned long
__curthread N/A All the members is long long
__curproc N/A All the members except for cwd is long long. The cwd member is of type string.
__ublock N/A All the members is long long
Note: The maximum size of the returned data can be smaller than the size of the type. For example, process IDs in AIX® can fit in a 32-bit integer, while the pid_t data type is a 64-bit integer for 64-bit processes and the kernel.

Script variables

A script variable is either an automatic, thread-local, or global class variable. Script variables exist only inside the context of a Vue script and their values are assigned from the script. Further, they can only be accessed or modified inside the script that defines them.

In general, you must explicitly declare the data type of a script variable through a declaration statement. However, the compiler can implicitly determine the data type of a program variable in some limited cases if the first reference to the variable is an assignment operation with the variable on the left-hand side of the assignment operator.

Implicit type determination for integral types

To be assigned an integral type, the right-hand side of the assignment must be in one of the following situations:

  • A constant number.
  • Another variable of integral type including built-in variables. Assigning from a variable whose type is not known is an error.
  • A Vue function that returns an integral type like the diff_time function.
  • Casting the expression on the right-hand side to an integral type, although this might give a warning in some cases.
  • An expression involving any of the preceding situations.

The variable takes its type in addition to its value based on the expression on the right-hand side. In addition, the class of the variable can be assigned to the variable by prefixing it to the variable. The following script demonstrates some examples:

/*
 * File:	implicit2.e
 * Usage:	Demonstrates implicit assignment for integer types 
 */

int read(int fd, char *p, long size);

@@BEGIN
{
	count = 404;			/* count: int of global class */
	zcount = 2 * (count - 4);	/* zcount: int of global class */
	llcount = 33459182089021LL;  	/* lcount: long long of global class */
	lxcount = 0xF00000000245B20LL;	/* xcount: long long of global class */

}

@@syscall:$1:read:entry
{
	__auto probev_timestamp_t ts1, ts2;
	int gsize;
	ts1 = timestamp();
	auto:dcount = llcount - lxcount;  /* dcount: long long of auto class */ 

	auto:mypid = __pid;	/* mypid:  pid_t (64-bit integer) of automatic class */
	fd = __arg1;		/* fd: int of global class */

	/* The following cast will likely cause a compiler warning
	 * but can be ignored here
	 */
	global:bufaddr = (long)__arg2;	/* bufaddr: long of global class */

	gsize = __arg3;
	thread:size =  gsize + 400;	/* size: int of thread-local class */

	printf("count = %d, zcount = %lld\n", count, zcount);
	printf("llcount = %lld, lxcount = 0x%016llx, diff = %lld\n",
			llcount, lxcount, dcount);
	printf("mypid = %ld, fd = %d, size = %d\n", mypid, fd, size);
	printf("bufaddr = 0x%08x\n", bufaddr);
	ts2 = timestamp();
	
	auto:diff = diff_time(ts1, ts2, MICROSECONDS);	/* diff: int of automatic class */
	
	printf("Time to execute = %d microseconds\n", diff);
	
	exit();
}
Note: The presence in the preceding script of a shell positional-like parameter, namely the $1 symbol in the @@syscall:$1:read:entry probe specification. The syscall probe manager allows a process ID for the second field to indicate that the system call probe point must be enabled for a specific process only. Rather than hard-code a specific process ID, the second field has been set to a shell positional parameter in this script to permit the actual process ID to be passed as an argument at the time the script is issued. The probevue command replaces any shell positional parameters in the script with the respective arguments passed on the command line.

Assuming that you are probing a process that has process ID 250000, the following script shows an example of running the implicit2.e script.

# probevue implicit2.e 250000
WRN-100: Line:29 Column:26 Incompatible cast
count = 404, zcount = 800
llcount = 33459182089021, lxcount = 0x0f00000000245b20, diff = -1080830451389212643
mypid = 250000, fd = 10, size = 4496
bufaddr = 0x20033c00
Time to execute = 11 microseconds

In the preceding example, the $1 symbol in the script is automatically replaced with "250000", thus restricting the read system call entry probe point to the process with process ID equal to 250000.

Implicit type determination for string type

To be assigned a string type, the right-hand side of the assignment must be in one of the following situations:

  • A string literal that is a sequence of characters within double quotation mark.
  • Another variable of type string including built-in variables.
  • A Vue function that returns a string like the et_userstring function.
  • An expression involving any of the above.

The following example demonstrates implicit string type assignment:

/*
 * File:	implicit3.e
 * Usage:	Demonstrates implicit assignment for string types 
 */

int write(int fd, char *p, long size);

@@BEGIN
{
	s1 = "Write system call:\n";
}

@@syscall:$1:write:entry
{
	String s2[40];
	
	wbuf = get_userstring(__arg2, __arg3);

	s2 = s1;

	zbuf = s2;

	pstring = zbuf + wbuf;

	printf("%s\n", pstring);
}

@@syscall:$1:write:exit
{
	ename = __pname;
	printf("Exec name = %s\n", ename);
	exit();
}

A process ID must be passed as an argument to the script when issuing it to replace the $1 shell positional parameter variable.

Implicit type determination for list type

To be assigned a list type, the right-hand side of the assignment must be the list() function. list() function is supported from any clause.

Useful kernel variables

The following table lists a few examples of useful kernel variables that can be accessed from within a Vue script. Be careful when using them in a Vue script because although not likely, the names of these variables or their meanings can change across different releases of AIX. All these kernel variables are pinned in memory and exported from the kernel.

Kernel variable Description Associated header files
struct system_configuration _system_configuration System configuration structure. sys/systemcfg.h
struct var v Base kernel tunable (and other) parameters. sys/var.h
struct timestruc_t tod Memory-mapped time of day clock. Seconds and nanoseconds since Epoch. sys/time.h
cpu_t high_cpuid Highest logical CPU ID ever online. sys/encap.h
struct vminfo vmminfo Data structure that contains the information shown by the vmstat command. sys/vminfo.h
time_t lbolt Number of ticks since last boot. sys/time.h
char spurr_version Identifies if current system supports the SPURR register 0=No SPURR, 1=CPUs have SPURR. sys/sysinfo.h
struct utsname utsname System name structure that includes the operating system name, node name, release level, and so on. sys/utsname.h

Data models for 32-bit and 64-bit processes

AIX supports two development environments: the 32-bit and the 64-bit development environments. Thus, compilers on AIX offer the following two programming models:

ILP32
ILP32, acronym for integer, long, and pointer 32, is the 32-bit programming environment in AIX. The ILP32 data model provides a 32-bit address space with a theoretical memory limit of 4 GB.
LP64
LP64, acronym for long, and pointer 64, is the 64-bit programming environment on AIX. With the exception of data type size and alignments, LP64 supports the same programming features as the ILP32 model and is backward compatible with the most widely used int data type.

Accordingly, a program on AIX can be compiled to run as either a 32-bit program or a 64-bit program. The same Vue script can be issued for a process running in 32-bit or 64-bit mode. As per the data model specification, an external variable of type long accessed in a Vue script must be treated as 4 bytes long when the probed (or traced) process is a 32-bit process. The same variable must be treated as 8 bytes long when the probed process is a 64-bit process. The layout and size of a structure or union, which contains members that are pointers or long variables, will depend upon whether it is being viewed from the perspectives of a 32-bit process or a 64-bit process. To avoid confusion, Vue provides semantic rules for handling the two different data models in a logical and consistent manner based on the variable's class.

Size-invariant variable types

The following variable types are size-invariant. They always have the same size whether in 32-bit mode or 64-bit mode irrespective of the class of the declared variable.

Type Size
long long 8
int 4
short 2
char 1

Size-variant variable types

The following variable types have both a 32-bit mode and a 64-bit mode:

Type 32-bit size 64-bit size
long 4 8
Pointer types 4 8

In the preceding table, a pointer type refers to types like char *, int *, struct foo *, unsigned long *, and so on.

The following semantic rules apply for variables that are defined with any of the preceding types, that is, for "longs" and "pointers". The rules apply whether the variables are members of a structure or union, or whether they are declared as individual variables:

Automatic class
The mode of the variable will depend upon the probed process's mode (32 or 64).
Thread-local class
The mode of the variable will depend upon the probed process's mode (32 or 64).
Global class
The variable is always treated as being in 64-bit mode irrespective of the probed process's mode. This allows the variable to be used safely by both 32-bit and 64-bit processes without losing any data.
Kernel global class
Kernel variables that are longs or pointers are always 64-bit mode as the only supported kernel for AIX 6.1 and beyond is the 64-bit kernel.
Entry class
If a long or pointer type is defined in the function prototype for any of the parameters to the function, the modes of the corresponding entry class variables (__arg1 through __arg32) will depend upon the mode of the probed process (32 or 64).
Exit class
If a long or pointer type is defined in the function prototype as the type of the return value of the function, the mode of the exit class variable (__rv) will depend upon the mode of the probed process (32 or 64).
Built-in class
These variables generally have a size-invariant type with the exception of the __r3 through __r10 built-ins that are defined as having an unsigned long type and hence are 32-bit long for 32-processes and 64-bit long for 64-bit processes.

The @@BEGIN and @@END probes are always issued in 64-bit mode.

Data types in Vue

The Vue language accepts three special data types in addition to the traditional C-89 data types.

Data types derived from the C language

The Vue language supports most of the data types defined in the C-89 specification. They include the signed and unsigned versions of the integral data types: char, short, int, long and long long. A "plain" char is treated as unsigned while the other integral types, if unqualified, are treated as signed. This matches the implementation of C on PowerPC®. The Vue language also supports the floating types: float and double. In addition to these basic types of the C language, Vue also supports derived types like the array, structure, union and pointer types, the enumeration type and some incomplete types like void.

All these types have the same syntax and semantics in Vue as their equivalent types in the C language with the following exceptions:

Floating types
You can only use a floating point type in simple assignment expressions and as arguments for Vue functions like printf. In particular, you cannot use floating-point variables as operands of any unary or binary operators other than the assignment operator.
Pointer types
You can use pointers to dereference kernel or application data. However, you cannot declare pointers to Vue script variables or take their addresses.
Character arrays
You cannot use a character array as a string as in C, but must use the string data type.
Incomplete types
You cannot use array types of unknown size.
Bit-field types
The Vue compiler ignores bit-field declarations and the layout of structure or union types that contain members, which are bit-fields, is undefined.
ILP32 and LP64 data models
Generally, a C program can be compiled in either 32-bit mode where it follows the ILP32 data model or in 64-bit mode where it follows the LP64 model. Because the same Vue clause can be issued by both 32-bit and 64-bit processes, Vue internally supports both models at the same time.

Range and bucket data type

The range data type in Vue is designed to handle the distribution of data points for certain defined ranges. Each range of the variable defined of the range data type displays the count of the number of elements within the corresponding range value. The range data type will support Integral and String types of ranges. The distribution of range values for integral ranges can be the power of two distributions or a linear distribution. An example of linear and power of two distributions of range values follow:

Linear distribution:

Range Count
0 - 5 2
5 - 10 4
10 - 15 1
Others 20

Power of 2 distribution:

Range Count
1 - 2 2
2 - 4 9
4 - 8 2005
8 - 16 4
16 - 32 1999
32 - 64 7
Others 5

The previous distributions indicates the count of the number of elements greater than or equal to the lower bound of the range and less than the upper bound of the range value. For example, in the power of two distributions, the count of data starting from 4 and lesser than 8 is 2005. The count of the value that does not come under the defined ranges is displayed in the Others range.

Example of string ranges

Example:

Range Count
Read, write, open 87
Close, foo1 3
foo2 1
Others 51

In the previous example, the distribution indicates the number of times a particular string occurs within the range values. In this example, read, write, and open has been called 87 times.

Declaration and initialization of the range data type:

The range data type can be declared by using the range_t keyword. For example, the following declaration in the Vue script defines two range data type variables:

range_t T1, T2;  // T1 and T2 are of Range data type variables.

The set_range and the add_range routines are used initialize the integral and string ranges for any particular range data type variables.

Initializing integral range data type: The set_range routine will be used to initialize the integral ranges. The syntax of the set_range differs for linear and power of two distributions of range values. The syntax of the set_range routine for linear distribution follows:

void set_range(range_t range_data, LINEAR, int min, int max, int step); 

Example:

set_range(T1, LINEAR, 0, 100, 10);

In the previous example, the set_range routine initializes the range data T1. The range data T1 has the linear distribution of values. The lower bound of T1 is 0 and the upper bound is 100. The size of each range is 10. The distribution for the previous example will look like the following:

Range Count
0 - 10 ...
10 - 20 ...
20 - 30 ...
... ...
... ...
90 - 100 ...

The syntax for the initialization of the power of 2 distribution follows:

set_range(range_t range_data, POWER, 2);

Example:

set_range(T2, POWER, 2);

In this example the routine initializes the range data type T2 as the power of 2 distribution range type.

Initializing the string range data type: The add_range routine initializes the string range data type.

Sytax:

void add_range(range_t range_data , String S1, String S2, ..., String Sn);

Example:

add_range(T1, “read”, “write”, “open”);

This routine add the strings read, write and open to a single slot of range_t data T1. Another add_range on the same range_t data T1 adds the strings to the next slot.

add_range(T1, “close”, “func1”, “func2”);

This routine adds the strings close, func1 and func2 to the range_t data T1 in the next slot.

Note: The range_t range data type is a special data type for Vue, which only can be used to store as a value inside an associative array. For any other operations (such as arithmetic, logical, bitwise, and relational) on the range_t data type, they will fail and error out.
Notes: This information discusses various uses and initialization routines of the range_t data type.
  1. Declaration of the range_t data type can be done only in the @@BEGIN clause.
  2. Initialization of the set_range routine can be used only inside the @@BEGIN clause.
  3. A range data type whose range values are integral can be initialized only once. The same variable cannot be initialized twice.

    Example:

    set_range(T1, LINEAR, 0, 50, 5);        // Valid syntax
        set_range(T1, LINERA, 10, 100, 10); // Error, cannot initialize an already
                                            // initialized T1.
        set_range(T1, POWER, 2);            // Error, T1 has already initialized.
        add_range(T1, “read”, “write”);     // Error, T1 has already initialized.
  4. The parameters of min, max and step are integral constants for set_range routine.

Storing and printing the range data type:

The range data type can be stored in an associative array as a value by using the qrange routine. The qrange routine finds the slot number whose frequency and count need to be incremental.

Example:

For this example, T1 is a range_t data type whose range values are of the integral type.

qrange(aso[“read”], T1, time_spent);

In this example, the qrange routine finds the slot number in which the time_spent fails and the count for that slot number are incremented for the associative aso array corresponding to the read key.

In the following example, the T2 is a range_t data type and range values are of the string type.

qrange(aso[“function usage”], T2,get_function());

In this example, qrangeroutine finds the slot number in which the function passed as the third argument fails and increments the count for that slot for aso associative array corresponding to the function usage key.

Notes:
  1. For any ASO, only one range_t type variable can be stored as a value. Using qrange for two different types of range_t variable type for the same ASO will fail.

    Example:

    qrange(aso[“read”], T1,time_spent);  // Correct syntax.
    qrange(aso[“read”], T2,time_spent);  // Error. Two different range_t types
                                           // cannot be used for the same ASO.

    The quantize and lquantize functions of the associative array whose value type is range_t shows the visual quantization of frequency and count of ranges.

  2. While printing the string range a maximum of 40 characters (including the comma) can be printed for a particular slot. If the strings in a slot have more than 40 characters, the string range is truncated and is printed with the last 3 characters as dots(…).

Examples of range data type and the qrange routine:

@@BEGIN
{
  __thread start ;
  range_t T1;
  set_range(T1, LINEAR, 0, 150, 10) ;
}
@@syscall :$__CPID :read :entry
{
  thread :tracing = 1 ;
  start = timestamp() ;
}
@@syscall :$__CPID :read :exit
         when(thread :tracing == 1)
{
  __auto long time_spent;
  currtime = timestamp() ;
  time_spent = diff_time(start, currtime, MICROSECONDS);
  qrange(aso[“read”], T1, time_spent);
}
@@END
{
  print(aso);
  quantize(aso);
}

Expected output for this example:

Key                                     Value

Read           Range                    count
               0-11	                     4
               10-20                     6
               60-70                     7
               Others                   32

Key                                     Value

Read           Range                    count
               0-10                       4      ===
               10-20                      6      ====
               60-70                      7      =====
               Others                    32      ================

Stack trace type

A variable of type stktrace_t is used to hold the return value from the ProbeVue function get_stktrace, which returns the current stack trace. The stack trace returned is the stack trace of the current thread. This variable can also be stored in an associative array either as a key or as a value. The stktrace_t type is an abstract data type, and this variable cannot be used directly with the standard C uninary or binary operators. Internally, this variable is an array of unsigned longs.

Vue supports the following characteristics and operations for the stack trace type variables:

Declaration of stack trace type variable

A variable can be declared to be of type stack trace by declaring it as follows in the script:
      stktrace_t  st;              // st is a stktrace_t variable.
      st = get_stktrace(5);        //  Get the stack trace up to five levels. 
      a_st[0] = get_stktrace(-1);  // Get the stack trace up to the extent possible and
                                  // store in the associative array a_st as value.
The qualifiers signed, unsigned, register, static, auto, thread, kernel, and const are not supported for the stktrace_t type variables.

Assignment operation

The assignment (=) operator allows a stktrace_t type variable to be assigned to another stktrace_t type variable. The original values in the target stktrace_t variables are destroyed. No type casting is allowed from or to the stktrace_t variable types. In the following example, the content of the stack trace t1 is assigned to t2.
      stktrace_t     t1, t2;          // Declares two stack trace variables.
      t1 = get_stktrace();            // Get the current stack trace in t1.
      t2 = t1 ;                       // Get the content of t1 into t2.

Comparison operation

Only equality (==) and inequality (! =) operators are allowed for the stktrace_t variables. The result of theses operator is either True(1) or False(0) based on the entire entries of the stktrace_t variables. Comparisons of individual entries of stktrace_t variables are not allowed. No other comparison operators (>=, >, < or =<) are allowed for the stktrace_t type variables.
            if( t1 == t2)  // comparing two stktrace_t type variables.
                     printf(“Entries are similar”);
              else
                     printf(“Entries are not similar”);

Printing stack trace type variable

The stktrace_t variable can be printed with the %t format specifier in the printf function of Vue. The output is the symbolic stack trace of the thread that is saved in the variable. The symbol with the address (symbol plus address) are printed only when the thread that corresponds to the stktrace_t variable is in the running state; otherwise only the stack trace as the address is printed for the variable.

The stktrace_t type variable stored in associative array either as key or value can be printed with print function of associative array. Addresses with symbol (symbol name + offset) is printed if thread that corresponds to the stktrace_t type stored in the associative array is running; otherwise only addresses is printed.
    stktrace_t t1;
    t1 = get_stktrace (5);
    printf (“%t”, t1);       // Displays the stack trace stored in variable t1.
    a[__tid] = t1;           // Store t1 as value in an associative array a.
     print(a) ;              // Print associative array a, whose value 
																			//	type is stktrace_t variable. 
                                                     

Limitations for stack trace type variable

  • The array of the stktrace_t variable cannot be declared.
  • The stktrace_t variables cannot be used as a member of a struct or a union.
  • Access of any individual entry of the stack trace is not allowed.
  • The operations (assignment, comparison, and printing) of the stktrace_t type variables is not supported in the systrace probe.

Special data types

In addition to the traditional C-89 data types, the Vue language also accepts seven special data types.

String type

The string data type is a representation of string literals. Unlike in C, the string is a basic data type in Vue. Having a string type avoids some of the confusion in C which does not support a string type but permits a string to be represented both by a pointer to a char type and by a character array.

You can explicitly declare a string variable by using the string declaration statement. Explicitly declared string variables must also specify the maximum string length (similar to how character arrays are declared in C). Unlike C, strings in Vue are not explicitly terminated by a null character and you do not need to reserve space for it.

   String s[40];		/* Defines a string 's' of length 40 */
   s = "probevue";

Further, any string literal written in C-style with enclosing double-quotes is automatically assigned a string data type. Vue automatically converts an external variable that is declared as a C-style character data type (char * or char[]) to the string data type as needed.

You can use the following operators for the string data type:

  • The concatenation operator: "+" .
  • The assignment operator: "=" .
  • Relative operators for comparing strings: "==", "!=", ">", ">=", "<" and "<=".

You can set a string variable to the empty string by assigning "" to it as the following example:

s = "";		/* Sets s to an empty string */

Unlike the C language, a pair of adjacent string literals is not concatenated automatically. The concatenation operator (+) must be explicitly applied as in the following example:

String s[12];

	// s = "abc" "def";	
   /* ERROR: Commented out as this will result in a syntax error */
	s = "abc" + "def";	/* Correct way to concatenate strings */

Vue supports several functions that accept a string data type as a parameter or return a value that has a string data type.

List type

A variable of type list collects a set of integral type values. The list type is an abstract data type and you cannot use a list variable directly with the standard C unary or binary operators. You can use the following operations for the list type:

  • A constructor function, list() to create a new list variable if its not defined before, if the variable is already defined - it should be cleared.
  • A concatenation function, append to add an item to the list or to join two lists together.
  • The "=" operator that allows a list to be assigned to another.
  • A set of aggregation functions that operate on a list variable and return a scalar (integer) value like sum, avg, min, max and so on.

Although, you can use a list variable to collect any integral value, the values are always saved as 64-bit signed integers.

The list() function returns a new empty new list which must be assigned to a variable of list type. This will create a new variable of type list if the list variable on the left-hand side of the assignment operator had not been previously assigned to a list. It may also be assigned to an existing list variable in which case, any values collected in the target list are discarded. Further, a variable can be declared to be of type list by declaring it as follows anywhere in the Vue script:

       __list l_opens;

The effect of this is as if the list() function was invoked in the @@BEGIN probe and the return value assigned to this list variable.

The following example creates a new list variable called l_opens:

l_opens = list();

The list function can be invoked from any clause. If you specify an existing list name when invoking the list function, the existing list is cleared.

You can use theappend() function to add a value to a list variable. Each call to the append function adds a new value to the set of values already saved in the list variable. The following example shows how the size of the list variable grows with each call to the append function:

	append(l_opens, n_opens1); /* l_opens = {n_opens1} */ 
	append(l_opens, n_opens2); /* l_opens = {n_opens1, n_opens2} */ 
	append(l_opens, n_opens3); /* l_opens = {n_opens1, n_opens2, n_opens3} */ 
	append(l_opens, n_opens4); /* l_opens = {n_opens1, n_opens2, n_opens3, n_opens4} */ 

The second parameter to the append() function can also be a variable of type list which will append all its values to the target list specified by the first parameter. So append can also be used to join two lists.

In the following example, the contents of list b are added to list a:

a=list()
b=list()
append(a,b)
Note: The value added to the list must be a parameter of integral or list type and it is an error if any of the variables n_opens1 -n_opens4 do not have an integral type. Any types that are smaller than a long long (like a short or an int) are automatically promoted to the long long type.

You can also use append to join two lists. The first argument is the target list and the second is the list source list. In the following example, the contents of list b are added to list a:

a=list()
b=list()
append(a,b)

The append() function has no return value.

A list can be assigned to another list using the assignment operator. The original values in the target list are destroyed. In the following example, the contents of the l_opens2 list are lost (the items are removed) and the contents of the l_opens list are copied over to the l_opens2 list.

	l_opens2 = list(); 
	append(l_opens2, n_opens5); 

	l_opens2 = l_opens; 
   /* l_opens and l_opens2 => {n_opens1, n_opens2, n_opens3, n_opens4}  */ 

The aggregation functions can be applied on a list variable as shown in the following examples:

	/* below we assume n_opens1=4, n_opens2=6, n_opens3=2 and n_opens4 = 4
	 * at the time they were added to the l_opens list variable 
	 */ 
	x = avg(l_opens); /* this will set x to 4 */ 
	y = min(l_opens); /* this will set y to 2 */ 
	z = sum(l_opens); /* this will set z to 16 */ 
	a = count(l_opens) /* this will set a to 4 */ 

A list variable is useful when accurate aggregate values need to be recorded. List variables are updated atomically, so use them only when required, as they are less efficient than regular variables.

Associative array type

An associative array is a map or look-up table consisting of a collection of keys and their associated values. There is a one-to-one mapping between keys and values. Associative arrays are supported by Perl, ksh93, and several other languages.

In Vue, the keys or index of an associative array must be string, timestamp, stacktrace, integral and floating point types. The associated values can be of string, timestamp, integral, floating point, stacktrace, list and range types.

Associative arrays are abstract data types in Vue: :

  • Binding a key to a value: This action adds the key to the associative array if it did not already exist; otherwise, it simply replaces the old value of the key with the new value. Keys that are not bound will have the default value of 0 or an empty string.
    count["ksh"] = 1;

    For a key in ASO, you can bind a LIST value in several ways:

    1. By assigning a LIST variable:
         assoc_array["ksh"]=ll  /* copies ll list into associative array */
         assoc_array["ksh"]=assoc_array["abc"]; /* copies a list in ASO to another list in ASO.
                                                          Here the value type of assoc_array is LIST */ 
    2. By assigning an empty list returned by list() constructor function:
      assoc_array["ksh"]=list(); /* assigns an empty list */ 
    3. By appending a list or integral value
      append(assoc_array["ksh"], 5);	/* integral value 5 is appended to the list in ASO */
      append(assoc_array["ksh"], ll);	/* appends the contents of LIST variable ll to the list in ASO*/
      append(assoc_array["ksh"], assoc_array["abc"]); /* appends the contents of list in ASO to another list in ASO */
  • Unbinding a key and removing it: The delete() function is used to remove a key from the associative array. An unbound key is assumed to have a value of 0 or an empty string.
    delete(count, "ksh");
  • Deleting a key: Deleting a key removes the entire element, both key and value, from the associative array. This operation is not the same as simply setting the value of a key to 0 or NULL. For instance, when looping through the elements of an associative array, a deleted element is not shown.

    The delete() function returns a value of 0 if the specified key exists and it was deleted; otherwise it returns a value of 1.

  • Finding the value for a key: This operation looks up the value that is bound to the specified key.

    total = count["ksh"] + count["csh"];

    A LIST value for a key can be retrieved by indexing the associative array with the key. All LIST functions, sum(), min(), max(), count(), and avg(), can be used on a List in an Associative array. You can also assign a list in associative array to a LIST variable.

    Example:
    /* copies associative array list into list variable "ll" */
    ll=assoc_array["ksh"];
    /* prints the sum of all elements of list in associative array indexed with ksh" */
    printf("sum of assoc_array %d\n",sum(assoc_array["ksh"]) ); 
    /* prints the minimum value */
    printf("min of assoc_array %d\n",min(assoc_array["ksh"]) );
    /* prints the maximum value */
    printf("max of assoc_array %d\n",max(assoc_array["ksh"]) ); 
    /* prints the number of values in list */
    printf("count of assoc_array %d\n",count(assoc_array["ksh"]) );
    /* prints average value of the list */
    printf("avg of assoc_array %d\n",avg(assoc_array["ksh"]) );  
  • Checking for the existence of a key: The exists () function checks for the existence of a key in an associative array.
    if (exists(count, "ksh"))
    printf("Number of ksh calls = %d\n", count["ksh"]);

The exists () function returns 1 if the specified key exists: otherwise it returns 0.

  • Increment and decrement operation: This operation can be used to increment or decrement the associative array values. These operations can not be performed on the associative array with the LIST value type.

  1. printf(“Incremented value = %d\n”, ++count[“ksh”]);
  2. printf(“Incremented value = %d\n”, count[“ksh”]++);
  3. printf(“Decremented value = %d\n”, --count[“ksh”]);
  4. printf (“Decremented value = %d\n”, count[“ksh”]--);

In example 1 the value that corresponds to ksh key would be incremented and the incremented value is printed.

In example 2 the value that corresponds to ksh is first printed and then the value is incremented. The decrement operation works the same way. However, the increment or decrement operation can be performed only on associative arrays whose value type is integer. The increment or decrement operation can also be used as an aggregator, where the value type of the associative array by default is set as integer. For example, on encountering statement, a[100]++ the first time, associative array a is created with the integer key type and the integer value type. The value stored for key 100 is 1. However, for a[100]--, -1 would be stored as the value for key 100. On encountering subsequent increment or decrement operations for the same associative array a increment and decrement operations is performed on the value for the specified key.

  • Printing contents of an associative array: This operation prints the <key, value> pairs stored in an associative array. You can specify the following print options:

Associative array print option Description Possible Values Default value
num-of-entries Specifies to print the first number of key-value pairs. n>=0. (If 0, all the entries are displayed.) 0
sort-type Specifies the sorting order. SORT_TYPE_ASCEND, SORT_TYPE_DESCEND SORT_TYPE_ASCEND
sort-by Specifies whether to sort based on key or value. SORT_BY_KEY, SORT_BY_VALUE SORT_BY_KEY
list-value Specifies which LIST attribute to sort or quantize when the value of the associative array is the list type USE_LIST_SUM, USE_LIST_MIN, USE_LIST_MAX, USE_LIST_COUNT, USE_LIST_AVG USE_LIST_AVG

When the sort-by flag is SORT_BY_KEY, SORT_BY_VALUE and the key and value pair is of a type where sorting cannot be done, then the num-of-entries option and other print options are applied to the printing of the individual key and value pair, if applicable. For example, if the sorting is by range type, the num-of-entries option, and the other print options are reserved for slots of each range.

The default associative array print options can be changed by using the set_aso_print_options()function in the BEGIN probe.

Example:
set_aso_print_options (10, SORT_TYPE_DESCEND|SORT_BY_VALUE);

As shown in the example, multiple flags can be provided by inserting a vertical bar symbol between them.

The print() function prints the key and value pairs of the associative array by using the default print options. If you want to view the associative array contents in a different format, he will provide the num-of-entries option and the print option flags as additional parameters to the print() function.

Example:

/* uses default print options to display the contents of associative array ‘count’ */
print(count); 	
/* prints the first 10 entries of sorted associative array ‘count’. 
Default sort-by and sort-type options are used */
print(count, 10);	
/* sorts the associative array ‘count’ in descending order of values and 
displays the first 10 entries of ‘count’ */ 
print(count, 10, SORT_BY_VALUE|SORT_TYPE_DESCEND);
  • The clear routine is used to either clear all the <key, value> variable present in an associative array or to reset the values without clearing key. The routine returns 0 on successfully clearing the associative array contents; otherwise, it returns 1 .
    clear(count);               // count is an associative array.
    The previous routine with only one argument of the associative array type clears all the key pairs present in the associative array count. After the previous clear operation, the associative array count is empty.
    clear(count, RESET_VALUE);       // count is an associative array. 
    The previous clear routine resets the value of all the key pairs in the associative array without clearing the key. The following default value is reset based on the type of value of the associative array:
Type Default value
Integral types (int, long, short, long long) 0
LIST Empty
Foat and double 0.0000000
String Empty
stktrace_t Empty
probev_timestamp_t 0
  • Quantize on associative array: This operation prints the key-value pairs of the specified associative array in a graphical format based on the value in the <key-value> pair.
    quantize(count);
    count is an associative array, and it prints out the following contents:
    key                  value
    1                     1             ========
    2                     2             =========
    3                     3             ==========
    4                     4             ===========
    5                     5             ============
    6                     6             =============

Similar to print() function, you can provide quantize() function print options to override the default print options.

Example:

/* sorts the associative array ‘count’ in descending order of values and displays 
the first 10 entries of ‘count’ in graphical format*/
quantize(count, 10, _BY_VALUE|SORT_TYPE_DESCEND);
  • Lquantize on associative array: This will print out the key, value pairs of the given associative array in a graphical format based on the logarithmic value of the value in the <key, value> pair.
    lquantize (count);
    Where count is an associative array would print out the following contents :
    key                    value
    500                     500           ====
    1000                   1000           ====
    2000                   2000           =====
    4000                   4000           =====
    8000                   8000           ======
    16000                 16000           ======
    32000                 32000           =======
    64000                 64000           =======

Similar to print() function, you can provide with lquantize() function print options to override the default print options.

Example:

/* sorts the associative array ‘count’ in descending order of values, and displays 
the first 10 entries of ‘count’ in graphical 
format based on the logarithmic value*/
lquantize(count, 10, _BY_VALUE|SORT_TYPE_DESCEND);

Example:

# Trace all the alloc- related calls and store the entry 
# Time in ‘entry_time’ associative array
#
@@uft:$__CPID:*:"/alloc/":entry
{
        entry_time[get_function()]=timestamp();
}
#
# At exit, first check if entry for this function was traced
# If so, delete the entry time from ‘entry_time’ associative array 
# To ensure that next time no action is taken on exit if entry was not traced. 

@@uft:$__CPID:*:"/alloc/":exit
{
   func =get_function();
   if(exists(entry_time, func) )
   {
             append(time_taken[func],
                   diff_time(timestamp(),entry_time[func],MICROSECONDS));
	            delete(entry_time, func);
	  }
}
#
# Print the list attributes sum, min, max, count, and avg time taken in every 
# Alloc function.
#
@@syscall:$__CPID:exit:entry
{
  print(time_taken);
	 exit();
}
Note: With this way, you need not define multiple list variables explicitly and still get the complete functions of the list with help of associative arrays.

Timestamp data type

A variable of type probev_timestamp_t holds the return value from the timestamp ProbeVue function, which returns a timestamp in internal AIX format. This variable can be later passed as a parameter into the diff_time function that returns the difference between two timestamps. This data type can also be stored in an associative array either as a key or as a value.

Although the ProbeVue compiler does not type check when you use a long data type instead of the probev_timestamp_t data type for storing timestamps, if possible, avoid this usage.

The following operations are acceptable for a variable of the probev_timestamp_t type:

  • Can be explicitly initialized to zero.
    Note: A timestamp variable of global class or thread-local class is initialized to zero on start of the ProbeVue session.
  • Can be compared against zero. Timestamp values returned by the timestamp function are always greater than zero.
  • Can be compared against another timestamp variable. A later timestamp is guaranteed to be larger than an earlier timestamp.
  • Can be passed as a parameter to the diff_time function.
  • Can be printed using the printf or trace function.

File path data type

A variable of type path_t can be used to hold the value of __file->path (refer to __file built in for I/O probe manager) or function fd_path(). Only local or global variables of type path_t are supported. A variable of this type can also be the key or value in an associative Array.

Declaration of file path variable

path_t pth; // global variable of type path_t
auto:pth2 = fd_path(fd); // store in a local path_t variable
my_aso[__file->fname] = __file->path; // store in associative array

The signed, unsigned, register, static, thread, and kernel qualifiers are not supported for the path_t type variables.

Assignment operation

The assignment (=) operator allows one path_t variable to be assigned to another path_t variable. The original value of the variable is overwritten.

In the following example, after the assignment, p1 and p2 refer to the same file path:
path_t p1, p2;
p1 = fd_path(fd); // fd is a valid file descriptor value 
p2 = p1;

Comparison operation

Only equality (==) and inequality (!=) operators are allowed for the path_t variables. The result of the equality operator is true (1) if both represent the same absolute file path or false (0) otherwise.

The inequality operator is the exact compliment of this behavior. No other comparison operators (>=, >, <, or =<) are allowed for the path_t type variables.

Printing file path type variables

A path_t type variable can be printed with “%p” format specifier in the printf() function.

This printing of file path involves a time-consuming file search operation in the corresponding file system. Hence, it must be judiciously used in Vue scripts.

Note: For transient files, which might no longer exist when the printf() message, can get printed; a null string is printed as file path.
An associative array that has the path_t type variables as key or value (or both) can be printed by using the print() function.
printf(“file path=[%p]\n”, __file->path); 
my_aso[0] = fd_path(fd); // fd is valid file descriptor value 
print(my_aso);
Limitations file path type variables
  • Array of the path_t variables cannot be declared.
  • The path_t variables cannot be used as members of struct or union.
  • A pointer to the path_t variable is not allowed.
  • Typecasting of the path_t variable to any other type or typecasting any other type to the path_t type is not allowed.
  • No arithmetic operator (+, -, *, /, ++, --, and so on) can be used with the path_t type variable.

MAC address data type

A variable of type mac_addr_t is used to hold the value of MAC address (refer to __etherhdr and __arphdr built-ins in the network probe manager section for usage of mac_addr_t type variable).

This abstract data type cannot be used directly with standard C unary or binary operators. Only local or global variables of type mac_addr_t are supported.

A variable of this type can also be stored in an associate array either as a key or as a value.

Vue supports the following characteristics and operations for the MAC address type variables:

Declaration of MAC address variable

mac_addr_t m1;           																				// global variable of type 
__auto mac_addr_t m2;                             // auto variable of type 
m2 = __etherhdr->src_addr;                        // store source MAC address in a local variable
mac_aso[“src_mac_addr”] = __etherhdr->src_addr ;  // store in an associative array. 

The signed, unsigned, register, static, thread, and kernel qualifiers are not supported for the mac_addr_t type variables.

Assignment operation

The assignment (=) operator allows a mac_addr_t type variable to be assigned to another mac_addr_t type variable. The original values of the variable is overwritten. No typecasting is allowed from or to the mac_addr_t variable types.

In the following example, the content of the mac_addr_t m1 is assigned to m2.

mac_addr_t m1, m2;              // Declares two MAC address variables. 
m1 = __etherhdr->src_addr;      // Get the source MAC address of the packet in m1. 
m2 = m1 ;                       // Get the content of m1 into m2.

Comparison operation

Only equality (==) and inequality (!=) operators are allowed for the mac_addr_t variables. The result of the equality operator is True (1) if both contains the same MAC address values or False (0) otherwise.

The inequality operator is the exact compliment of this behavior. No other comparison operators (>=, >, <, or =<) are allowed for the mac_addr_t type variables.
if ( m1 == m2) // comparing two mac_addr_t type variables. 
printf(“Mac addresses are equal”); else printf(“Mac addresses are not equal”);

Printing MAC address type variables

A mac_addr_t type variable can be printed with “%M” format specifier in the printf() function of Vue. An associative array that has mac_addr_t type variables as key or value (or both) can be printed by using the print() function.
printf(“ Source MAC address=[%M]\n”, __etherhdr->src_addr); 
mac_aso[“src_mac_address”] = __etherhdr->src_addr ; // Store source MAC address as value in an associative array mac_aso. 
print(mac_aso);
Limitations for MAC address type variable
  • The array of the mac_addr_t variable cannot be declared.
  • The mac_addr_t variables cannot be used as a member of a struct or a union.
  • Pointer to mac_addr_t variable is not allowed.
  • Typecasting of mac_addr_t variable to any other type or typecasting any other type to mac_addr_t type is not allowed.
  • No arithmetic operator (+, -, *, /, ++, -- etc) can be used with mac_addr_t type variable.

IP address data type

A variable of type ip_addr_t is used to hold the value of IP address (refer to __ip4hdr, __ip6hdr and __proto_info built-ins in Network probe manager for usage of ip_addr_t type variable).

This is an abstract data type and cannot be used directly with standard C unary or binary operators. Only local or global variables of type ip_addr_t are supported. A variable of this type can also be stored in an associate array either as a key or as a value.

Vue supports the following characteristics and operations for the IP address type variables:

Declaration of IP address variable
ip_addr_t i1;                               // global variable of type ip_addr_t 
__auto ip_addr_t i2;                        // auto variable of type 
ip_addr_t i2 = __ip4hdr->src_addr;          // store source IP address in a local ip_addr_t variable. 
ip_aso[“src_ip_addr”] = __ip4hdr->src_addr; // store in an associative array.

The qualifiers signed, unsigned, register, static, thread and kernel are not supported for the ip_addr_t type variables.

Assignment operation

The assignment (=) operator allows a ip_addr_t type variable to be assigned to another ip_addr_t type variable and also it allows constant IP address or hostname to be assigned to ip_addr_t type variable. The original values of the variable is overwritten. No type casting is allowed from or to the ip_addr_t variable types.

In the following example, the content of the ip_addr_t i1 is assigned to i2.
ip_addr_t i1, i2;                   // Declares two IP address variables. 
ip_addr_t i3, i4, i5;               // Declares three IP address variables.
i1 = __ip4hdr->src_addr;            // Get the source IP address of the packet in i1. 
i2 = i1 ;                           // Get the content of i1 into i2.
i3 = “10.10.10.1”;                  // Assign the constant IPv4 address to i3 variable.
i4 = “fe80::2c0c:33ff:fe48:f903”;   // Assign the Ipv6 address to i4 variable.
i5 = “example.com”;                 // Assign the hostname to i5 variable.
                                   // Get the content of i1 into i2.

Comparison operation

Only equality (==) and inequality (! =) operators are allowed for ip_addr_t types variables. The comparison allowed only between two ip_addr_t type variables and with constant string type (IP address or hostnames are provided in double quotes “192.168.1.1” or “example.com").

The result of the equality operator is True (1) if both contains the same IP address type (IPV4 or IPV6) and values. or False (0) otherwise. The inequality operator is the exact compliment of that. No other comparison operators (>=, >, < or =<) are allowed for the ip_addr_t type variables.

if( i1 == i2)                     // comparing two ip_addr_t type variables.
																											//IP address string	 
printf(“IP addresses are equal”); 
else printf(“IP addresses are not equal”); 
or 
if( i1 == “192.168.1.1”)          // comparing ip_addr_t type variable and constant string.
 printf(“IP addresses are equal”); 
else printf(“IP addresses are not equal”);
or
if (i1 = “example.com”)          // comparing ip_addr_t type variable and constant       
                                 //IP address string
printf(“IP addresses are equal”); 
else printf(“IP addresses are not equal”);
 
Printing IP address type variables

A ip_addr_t type variable can be printed with “%I” format specifier to print IP address in dotted decimal or hex format and “%H” format specifier to print hostname in the printf() function of Vue. This printing hostname involves a time consuming dns lookup operation. Hence it should be judiciously used in VUE scripts.

Note: When user uses the format specifier “%H” to print host name for IP address which may not exists in dns, for those IP addresses, it prints the IP addresses in dotted decimal/hex format instead of hostname.
An associative array that has ip_addr_t type variables as key or value (or both) can be printed using the print() function.
printf(“ Source IP address=[%I]\n”, __ip4hdr->src_addr); 
ip_aso[“src_ip_address”] = __ip4hdr->src_addr ; // Store source IP address as value in an associative array 
print(ip_aso); 
Limitations for IP address type variable
  • The array of the ip_addr_t variable cannot be declared.
  • Pointer to ip_addr_t variable is not allowed.
  • Typecasting of ip_addr_t variable to any other type or typecasting any other type to ip_addr_t type is not allowed.
  • No arithmetic operator (+, -, *, /, ++, -- etc) can be used with ip_addr_t type variable.

net_info_t data type

The net_info_t variable is structure or composite variable that is used to hold the network four tuples (local and remote IP addresses and port numbers) information from the specific socket descriptor through the sockfd_netinfo Vue function. The members of this structure are accessed like any other user-defined structure in Vue script. The net_info_t type is an abstract data type and this variable cannot be used directly with standard C unary or binary operators. This variable is a structure containing 4 tuple information. This variable elements can be accessed by using the “.” operator like C structure elements.

Elements of the net_info_t data type are as follow:
net_info_t 
{ 
    int local_port; 
    int remote_port; 
    ip_addr_t local_addr; 
    ip_addr_t remote_addr;
};

Vue supports the following characteristics and operations for the net_info_t type variables:

Declaration of net_info_t type variable

net_info_t n1,n2                       
// n1 is variable of type net_info_t
sockfd_netinfo(fd, n1);           
// fd is socket descriptor and n1 contains network 
// four tuple information from sockfd_netinfo Vue function.                                   
n2.local_addr = __ip4hdr->src_addr; 
n2.remote_addr = __ip4hdr->dst_addr; 
n1.local_port = __tcphdr->src_port; 
n1.remote_port = __tcphdr->dst_port;
The signed, unsigned, register, static, thread, local, global, and kernel qualifiers are not supported for the net_info_t type variables.

Limitations for net_info_t type variable

  • Structure and union member variable cannot be supported.
  • Pointer to the net_info_t variable cannot be declared.
  • This variable is not supported in associative array.
  • The array of the net_info_t variable cannot be declared.
  • Typecasting of the net_info_t variable to any other type or typecasting any other type to net_info_t type is not allowed.
  • Arithmetic operator (+, -, *, /, ++, --, and so on) cannot be used with net_info_t type variable.

Vue functions

Unlike programs written in C or in FORTRAN, or in a native language, scripts written in Vue do not have access to the subroutines provided by the AIX system libraries or any user libraries. However, Vue supports its own special internal library of functions useful for dynamic tracing programs.

Tracing-specific functions
get_function
Returns the name of the function that encloses the current probe. When the get_function function is called from interval, systrace, BEGIN, and END clause, the function returns an empty string.
timestamp
Returns the current timestamp.
diff_time
Finds the difference between two time stamps in microseconds or milliseconds.
Trace capture functions
printf
Formats and prints values of variables and expressions.
trace
Prints data without formatting.
stktrace
Formats and prints the stack trace.
List functions
list
Instantiates a list variable.
append
Appends a new item to list.
sum, max, min, avg, count
Aggregation functions that can be applied to a list variable.
C-library functions
atoi, strstr
Standard string functions.
Functions to support tentative tracing
start_tentative, end_tentative
Indicators for start and end of tentative tracing.
commit_tentative, discard_tentative
Commits or discards tentative trace data.
Miscellaneous functions
exit
Terminates the Vue script.
get_userstring
Reads string (or data) from user memory.
ptree
Prints the process tree of the probed process.

You can apply the Vue string functions only on variables of string type and not on a pointer variable. Standard string functions like strcpy, strcat, and so on are not necessary in Vue, because they are supported through the language syntax itself.

The ProbeVue compiler validates the data types of the parameters passed to Vue functions.

For printf function, validation is done to check whether there is an argument supplied in the printf function for each format specifier given in the format string. The total number of format specifiers and the total number of arguments passed to the printf function should be equal. In addition to this, validation is also done to match whether the type of the argument passed is compatible with the actual type mentioned as format specifier in the format string. If these checks fail, the Probevue throws an error message.

For example,
printf(“hello world %s, %d\n”, str);
would throw up an error message from the compiler as no argument is passed for %d. Similarly,
Printf(“The total count of elements is %d\n”, str);
also throws an error message as the format specified is %d whereas the argument passed, str variable is a string.

Other Features Functions

However, when given as
printf (“The total count of elements is %lld\n”, i);
Where i is a variable of type int, no error message is thrown because the variable i is a compatible type for the format specifier requested. Hence, no exact type checking is done; however, compatible type checking is.

You cannot put functions in the predicate section of a Vue clause.

Predicates

You cannot use predicates when execution of clauses at probe points must be performed conditionally. The predicate section is identified by the presence of the when keyword immediately after the probe specification section. The predicate itself consists of regular C-style conditional expressions with the enclosing parentheses.

There are some restrictions on the expressions inside the predicate section:

  • Kernel class variables are not permitted in the predicate.
  • Automatic class variables are not permitted in the predicate.
  • Floating-point type variables are not permitted in the predicate.
  • Vue functions are not permitted inside a predicate.
  • Side effects are not permitted inside a predicate and so the = assignment operator and its derivatives like +=, |=, and so on are not permitted.
  • The ninth and higher parameters passed to a function ( the entry class variables __arg9, __arg10, and so on) are not permitted in the predicate.

Conditional execution of specific actions within a clause is possible by using the if ... else statement which works like the analogous statement in C. However, if the entire clause is to be issued conditionally, it is preferable to use predicates instead because ProbeVue is designed to optimize execution of predicates.

Note: When a probe point can fire for more than one process, using thread-local variables inside the predicate is an excellent way to reduce the overall performance impact of enabling the probe. Putting conditional checks inside a predicate is preferable to using the if statement.

The following script uses thread-local variables inside predicates to efficiently detect when a particular character string is written to a specific file. It also shows an example of using the if statement within the action block of a clause with a predicate. Both the file name and character string are passed as parameters to the script using shell positional parameters.

/*
 * Filename : chkfilewrite.e
 *
 * Capture when someone writes a particular word to a specific file 
 * takes 2 arguments: filename and word
 *
 * assumes file name is < 128
 *
 * Usage: probevue chkfilewrite.e \"<filename>\" \"<string>\"
 *
 *	The backslashes above are necessary to prevent shell
 *	from stripping the double quotation mark.
 */

int open(char *fname, int m, int p);
int write(int fd, char *s, int size);

@@syscall:*:open:entry
{
	__auto String fname[128];

	fname = get_userstring(__arg1, -1);

	if (fname == $1)
		thread:opening = 1;
}

@@syscall:*:open:exit
	when (thread:opening == 1)
{
	thread:fd = __rv;
	thread:opening = 0;

}

@@syscall:*:write:entry
	when (thread:fd == __arg1)
{
	__auto String buf[128];

	if (__arg3 < 128)
		buf = get_userstring(__arg2, __arg3);
	else
		buf = get_userstring(__arg2, 128);

	if (strstr(buf, $2)) {
		printf("%d wrote word to file.\n", __pid);
		exit();
	}
}

To run this program to check when someone writes the string "Error" to the foo.log file, you can issue the following command:

probevue chkfilewrite.e \"foo.log\" \"Error\"
Note: You can enhance the preceding script by adding a close probe to detect when the file is closed to prevent the script from catching the word after the original file is closed and a new one is opened and the same file descriptor number is reused.

Symbolic constants

Vue supports some pre-defined symbolic constants, which are commonly used in AIX programming. These constants are treated as keywords in Vue. During compilation, the constants are replaced by their definitions in system header files. Probe manager-specific symbolic constants are explained in their respective sections. Following are the generic symbolic constants.

AF_INET
This specifies the address family of type IPv4. This ensures that the data is of IPV4 type.
AF_INET6
This specifies the address family of type IPv6. This ensures that the data is of IPV6 type.
NULL
To set pointer types to a NULL or zero value. You cannot use NULL to set a String variable to the empty string.
Error numbers or "errno" names
These are the standard error names like EPERM, EAGAIN, ESRCH, ENOENT, and so on, specified by the POSIX and ANSI standards and defined in the /usr/include/sys/errno.h header file.

The following script traces when the bind system call fails with errno set to EADDRINUSE (address already in use).

/*
 * File: bind.e
 */

/*
 * Okay to use void for parameters since we are not planning to
 * access them in this script.
 */
int bind(void);

@@syscall:*:bind:exit
	when (__rv == -1)
{
	/*
	 * The following check could also be moved to the predicate,
	 * although it may not buy a lot because we are already in an
	 * error path that should be executed only rarely
	 */
	if (__errno == EADDRINUSE) 
 /* This check could also be moved to the predicate */
		printf("%d failed with EADDRINUSE for bind() call.\n", __pid);
}
Signal names
These are the standard signal names like SIGSEGV, SIGHUP, SIGILL, SIGABRT, and so on, specified by the ANSI standards and defined in the /usr/include/sys/signal.h header file.

The following script shows how to debug "who" killed a particular process by sending it a specific signal.

/*
 * File: signal.e
 *
 * Who sent SIGKILL to my process ?
 */

/* Process IDs are < 2^32, so using an 'int' here instead of pid_t is
 * good enough
 */
int kill(int pid, int signo);

@@syscall:*:kill:entry
	when (__arg1 == $1 && __arg2 == SIGKILL)
{
	/* Trace sender of SIGKILL */
	printf("Stack trace of %s: (PID = %d)\n", __pname, __pid);
	stktrace(PRINT_SYMBOLS|GET_USER_TRACE, -1);
	exit();
}
FUNCTION_ENTRY
Identifies if a probe point is a function entry point. Used with the get_location_point function.
FUNCTION_EXIT
Identifies if a probe point is a function exit point. Used with the get_location_point function.

Header files

You can include header files in a Vue script through the -I option of the probevue command. The header file must be written in the syntax of the C language. It can include type, structure and union definitions that follow C-89 specifications. However, it must not contain any C executable statements. C preprocessor operators and directives, if present, are generally ignored, but operators like #ifdef and #if can cause undefined behavior. Thus, standard AIX header files from the /usr/include directories cannot be directly included. Instead, you must always run the C-preprocessor directly on the set of relevant header files and generate a post-processed header file, which can then be passed to the probevue command. Another option is to hand-code the header file to include the type definitions and function declarations that you use in your Vue script.

The following example shows a hand-coded header file with a typedef definition that is used in a Vue script:

/* My header file : myheader.i */

	typedef int myid_t;

The following Vue script uses this typedef definition:

/* Program name: myscript.e */
	@@BEGIN
	{
		myid_t id;
		id = 0;
	}

You can run the following probevue command:

probevue -I myheader.i myscript.e

You can specify multiple header files on the command line either by separating the header files with a comma (with no spaces between the comma and the file names) or by specifying each one separately with the -I flag. The following two examples are equivalent:

probevue -I myheader.i,myheader2.i myscript.e
probevue -I myheader.i -I myheader2.i myscript.e

C++ header file can be included for struct/class definitions and allows a probevue script to access struct/class data fields through a pointer. All C++ header files can be listed using #include directives between ##C++ and ##Vue directive in the ProbeVue script. For using this option IBM® C++ compiler must be installed on the system. Another option to include C++ header file is to first preprocess the C++ header file with –P option of probevue and then include the preprocessed file with –I option of probevue. With –P option probevue will generate the out file with same name as input C++ header file with a .Vue suffix.

The advantage of using –I option for preprocessed C++ header file is that IBM C++ compiler need not be installed on the system.

You can run the following command to preprocess C++ header file.

probevue –P myheader.h    
Note: To run the above command IBM C++ compiler is a pre-requisite.

The above command will generate a file called myheader.Vue. This file can be further shipped to another system and can be used to probe C++ application by including with –I option of probevue. While using the shipped preprocessed C++ header file the systems environment should be same for system being used for generating preprocessed C++ header file and system being used to include preprocessed header file with –I option of probevue for probing C++ application.

The C++ header file which is either being used for precompilation with –P option or being included between ##C++ and ##Vue should have .h extension for including standard Input/Output C++ header file. For including IOstream header use #include<iostream.h> instead of #include<iostream>.

You can run the following command for a C++ executable named cpp_executable and the script named myscript.e to probe the C++ application.

probevue –I myheader.Vue –X cpp_executable myscript.e  
Note: To run the above command IBM C++ compiler is a not a pre-requisite.

Supported shell elements

The Vue language syntax includes support for shell variables identified by the $ prefix like exported shell variables and positional parameters (arguments to the script).

Vue shell variables can appear anywhere in the Vue script. They can be part of the probe specification, be used in predicates or within statements in action blocks. However, unlike in a shell script, they are not expanded if used within double quoted strings.

The arguments passed from the command line to the script are referenced within the script as $1, $2, $3, and so on. Consider the following Vue script:

/* Program name: myscript.e */
	@@syscall:*:read:entry
		when (__pid == $1)

	{
		int count;
		count++;
	}

In the following example, the process ID of the process running the myprog program replaces $1 in the preceding script. It assumes that the prgrep shell program, which prints the process ID given the process name, is used to invoke the Vue script.

probevue myscript.e `prgrep myprog`

Environment variables exported from the shell can also be referenced in the script using the $ operator. Consider the following Vue script:

/* Program name: myscript2.e */
	@@syscall:*:read:entry
		when (__pid == $PID)

	{
		int count;
		count++;
	}

	/* program to be traced has a function called 'foo' */
	
	@@uft:$PID:*:foo:entry
	{
		printf("Read system call was invoked %d times\n", count);
	}

In the following example, 3243 replaces $PID in the preceding script:

PID=3423 probevue myscript2.e

If an environment variable needs to be recognized as a string inside the ProbeVue script, the value of the environment variable must include the enclosing double quotation mark that identify it as a string. For example, the following script captures trace output when a specific file is opened in the system:

/* Program name: stringshell.e */
	int open(char *path, int oflag);
	@@syscall:*:open:entry
	{
		String s[40]; 
		s = get_userstring(__arg1, -1);
		if (s == $FILE_NAME) {
			printf("pid %d (uid %d) opened %s\n",__pid,__uid, s);
			exit();
		}
	}

The script expects that $FILE_NAME is the name of an exported shell environment variable, which includes the double quotation mark in its value. The following script is an example:

	export FILE_NAME=\"/etc/passwd\"
	probevue stringshell.e

If the value of an existing environment variable that does not have double quotation mark is required in a script, then a new environment variable will need to be constructed using double quotation mark around the existing environment variable. The following script is an example:

	export FILE_NAME=\"$HOME\"
	probevue stringshell.e

Vue supports two special environment variables which are useful when the process to be probed is started by the probevue command itself using the -X flag. The $__CPID environment variable indicates the process ID of the child process created by the probevue command, and the $__CTID environment variable indicates its thread ID. The -X flag is useful to probe short-lived processes especially for debugging purposes.

A Vue script can be executed directly (like a shell script) by setting the first line to the following script:

#!/usr/bin/probevue

The probevue command can also read the Vue script from standard input like the shell does. This can be accomplished by omitting the script filename from the command line. This is useful to test short scripts.

Vue does not support special shell parameters such as $$ and $@ which are created internally by the shell.

Trace capture facilities

ProbeVue supports comprehensive trace capture facilities. The basic trace capture action is provided through the printf function that can be invoked from any probe as part of the action block. The Vue version of printf function is equipped with most of the power of the C library version. A second trace capture function is the trace function. The trace function accepts a single variable as a parameter and copies its value in printable hexadecimal format to the trace buffer. This function is particularly useful for dumping the contents of strings and structures. The stktrace function is another trace capture function which captures the stack trace of the traced thread at the current probe point.

In addition to the values of internal script variables, external variables like kernel global variables, context-specific data like parameters to the function being probed, return values from a function, and so on can also be captured and displayed through these trace capture functions.

The trace reporter always displays trace data in order of time of occurrence and thus the data captured from different CPUs are internally sorted before they are output.

Tentative tracing

Tentative tracing is a facility in ProbeVue to conditionally capture trace data. The start_tentative function is used inside an action block to indicate the start of capture of trace data that is tentative. The end_tentative function signifies the end of the tentative trace capture within an action block. The end_tentative function can be omitted in which case the end of the action block automatically signifies the end of any tentative tracing that was started in that block. Any trace captured while tentative tracing is enabled is marked as tentative trace data. In short, the start and end tentative tracing functions establish a tentative tracing section within the action block inside which all trace data captured is considered tentative.

Trace data captured tentatively is never made available to the trace consumer, but stays inside the trace buffers until it is either committed or discarded. The commit_tentative function commits the entire collected tentative trace data and makes it available to the trace consumer. The discard_tentative function, on the other hand, discards all the tentatively collected trace data and frees up space in the trace buffers.

Non-tentative, regular tracing of data is allowed concurrently with tentative tracing. All trace data is made available to the trace consumer in timestamp order, so tentative trace data that has been neither committed nor discarded can prevent regular trace data from being presented to the trace consumer. In the worst case, it can even stop capture of regular trace data because the trace buffers fill up with tentative trace data that has not been either committed or discarded.

The tentative tracing functions take a string to identify the ID under which the tentative trace data is being captured. This allows finer control when committing or discarding the captured tentative trace data. You can enclose multiple trace capture actions in one or more action blocks within start and end tentative trace functions that are passed the same tentative trace ID. This allows tentative trace data from multiple locations to be committed or discarded as a single block. Vue requires a string literal to be used as the tentative trace ID.

Tentative tracing permits intelligent filtering of data and will reduce the actual amount of trace data that is presented to you and which you need to analyze. This has the excellent side effect of preventing buffer overflow problems if you can ensure that the tentatively collected data be discarded or committed early.

The following script is an example of using tentative tracing functions to capture trace data only when necessary:

/* 
 * File: tentative.e
 *
 *	Print details when write system call takes longer than a
 *	specified number of microseconds
 *
 *	Usage: probevue tentative.e <processID> <microseconds> 
 */
int write(int fd, char *buf, int size);

@@BEGIN
{
	probev_timestamp_t  ts1, ts2;
}

@@syscall:$1:write:entry 
{
	__auto String buf[256];

	if (__arg3 < 256)
		buf = get_userstring(buf, __arg3);
	else
		buf = get_userstring(buf, 256);

	start_tentative("write");

	/* print out all the data associated with the write */
	stktrace(PRINT_SYMBOLS|GET_USER_TRACE, -1);

	printf("fd = %d, size = %d\n", __arg1, __arg3);

	/* Prints 256 bytes of buf, even though size may be < 256 */
	trace(buf);

	end_tentative("write");

	/* Get timestamp for when we entered write: do this at the end of
	 * the probe to reduce probe effect
	 */
	ts1 = timestamp();
}
	
/* If we started probing in the middle of write, ts1 will be zero,
 * ignore that case with a predicate
 */
@@syscall:$1:write:exit
	when (ts1 != 0)
{
	/* diff_time() may return up to a 64-bit value, but we 
	 * use an int here since we don't expect the difference to
	 * larger than a few hundred microseconds at the most.
	 */
	int micros;

	/* Get timestamp for when we exited write: do this at the beginning of
	 * the probe to reduce probe effect
	 */
	ts2 = timestamp();

	micros = diff_time(ts1, ts2, MICROSECONDS);

	start_tentative("write");
	printf("Return value from write = %d\n", __rv);
	end_tentative("write");

	if (micros > $2) {
		/* Can mix normal trace with tentative also  */
		printf("Time to write = %d, limit =%d micro seconds\n",
			micros, $2);
		commit_tentative("write");
		exit();
	}
	else
		discard_tentative("write");
}