ProbeVue: Extended Users Guide Specification (Draft)

Version 0.03
10/23/2007

Copyright International Business Machines Corporation 2007. All rights reserved.
US Government Users Restricted Rights -- Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.





Contents

Scripts Index

Figure 1 ..... count.e Count number of times read/write system call is entered.
Figure 2 ...... globals.e Global variables examples
Figure 3 ...... implicit2.e Implicit assignment for integer types
Figure 4 ...... implicit3.e Implicit assignment for string type
Figure 5 ...... chkfilewrite.e Capture when someone writes a particular word to a specific file
Figure 6 ...... bind.e Catch when bind() fails with a specific errno
Figure 7 ...... signal.e Trace SIGKILL sent to my process
Figure 8 ...... myscript.e Access command-line argument in Vue script
Figure 9 ...... myscript2.e Access shell environment variable in Vue script
Figure 10 ...... stringshell.e Access environment variable as string in Vue script
Figure 11 ...... tentative.e Tentative tracing example
Figure 12 ...... list.e Collect execution time statistics for read system call using list variable
Figure 13 ...... string2int.e Read an integer from user space
Figure 14 ...... ffork.e Trace fast forks
Figure 15 ...... pthreadlocks.e Trace pthread mutex activity for a multi-threaded process
Figure 16 ...... hello.e Print hello world.
Figure 17 ...... hello2.e Print hello world on Ctrl-C.
Figure 18 ...... countbytes.e Count the number of bytes written to a file
Figure 19 ...... ttrace.e Another tentative tracing example. Trace arguments to failed read system calls.
Figure 20 ...... kernel.e Access kernel variables



1. Introduction

ProbeVue is a dynamic tracing facility. It has the following features:

ProbeVue can be used for performance analysis as well as for debugging problems. It is designed to be safe to run on production systems and provides protection against errors in the instrumentation code.

The next sub-section of this chapter defines some of the terminology used in this document. The subsequent sub-sections of this chapter introduce Vue, the programming language used by ProbeVue and the probevue command that is used to start a tracing session.

1.1 Terminology

Some of the terms used in this document are described below.

A probe is a software mechanism that interrupts normal system action to investigate and obtain information about current context and system state. This is also commonly referred to as tracing. Tracing actions or probe actions refer to the actions performed by the probe. Typically, they include the capturing of information by dumping the current values of global and context-specific information to a trace buffer . The obtained information, thus captured in the trace buffer, is called trace data. The system usually provides facilities to consume the trace, that is, read the data out of the trace buffer and make it available to the users of the system.

A probe point identifies the points during normal system activity that are capable of being probed. With dynamic tracing, probe points do not have any probes installed in them unless they are being probed. Enabling a probe is the operation of adding a probe to a probe point and disabling a probe is the operation of removing a probe from a probe point. Triggering or firing of a probe refers to the condition where a probe is entered and the tracing actions are performed.

ProbeVue supports two kinds of probe points.

  1. Probe Location
    This is a location in user or kernel code where some tracing action like the capture of trace data is to be performed. Enabled probes at a probe location fire when any thread executing code reaches that location.
  2. Probe Event
    This is an abstract event at whose occurrence some tracing action is to be performed. Probe events do not easily map to a specific code location. Enabled probes that indicate a probe event fire when the abstract event occurs.

ProbeVue also distinguishes probe points by their type. A probe type identifies a set of probe points that share some common characteristics, for instance probes that when enabled fire at the entry and exit of system calls, or probes that when enabled fire when system statistics are updated. Distinguishing probes by probe types induce a structure to the wide variety of probe points.

ProbeVue requires a probe manager to be associated with each probe type. The probe manager is the software code that defines and provides a set of probe points of the same probe type. For instance, the system call probe manager.

1.2 Vue Programming Language

The Vue programming language is used to provide your tracing specifications to ProbeVue. In this document, The Vue programming language is often abbreviated to the "Vue language" or just to "Vue". A Vue script or Vue program is a program written in Vue. You can use a Vue script to:

  1. Identify the probe points where a probe is to be dynamically enabled.
  2. Identify the conditions, if any, which must be satisfied for the actions to be executed when a probe fires.
  3. Identify the actions to be executed including what trace data to capture.
  4. Associate the same set of actions for multiple probe points.

In short, a Vue script tells ProbeVue where to trace, when to trace and what to trace.

It is recommended that Vue scripts have a file suffix of ".e" to distinguish them from other file types, although this is not a requirement.

1.3 Probevue command

The probevue command is used to start a dynamic tracing session or a probevue session. The probevue command takes a Vue script as input reading from a file or from the command-line and activates a probevue session. Any trace data that is captured by the probevue session can be printed to the terminal or saved to a user-specified file as per options passed in the command line.

The probevue session stays active until a Ctrl-C in typed on the terminal or an exit action is executed from within the Vue script.

Each invocation of the probevue command activates a separate dynamic tracing session. Multiple tracing sessions may be active at one time, but each session presents only the trace data that is captured in that session.

Running the probevue command is considered a privileged operation and privileges are required for non-root users who wish to initiate a dynamic tracing session.

2. Vue: An overview

Vue is both a programming and a script language. It is NOT simply an extension of C or a simple mix of C and "awk". It has been specifically designed as a dedicated dynamic tracing language. Vue supports a subset of C and scripting syntax that is most beneficial for dynamic tracing purposes.

The following list provides a summary of Vue features:

Each of these features is described in further detail in subsequent chapters. The rest of this chapter describes the basic layout and elements of a Vue script.

2.1 Structure of a Vue script

A Vue script consists of one or more clauses. The clauses in a Vue script can be specified in any order. The following is a typical layout of a Vue script.

Each clause of a Vue script consists of the following three elements:

  1. Probe point specification
    The probe point specification identifies the probe points to be dynamically enabled.

  2. Action Block
    The action block is used to identify the set of probe actions to be performed when the probe fires.

  3. An optional predicate
    The predicate, if present, identifies a condition that is to be checked at the time the probe is triggered. The predicate must evaluate to TRUE for the probe actions of the clause to be executed.

In the following example clause, the superscripts identify the elements of a Vue clause.

Figure 1
	/* 
	 * File: count.e
	 *
	 * Count number of times the read or write system call is entered
	 * by process with Id 400 
	 */

	@@syscall:*:read:entry, @@syscall:*:write:entry [1]
		when (__pid == 400)[3]

	{
		int count;
		count++;[2]

	}

The elements of a Vue clause are covered in more detail following sub-sections.

2.2 Probe Point Specification

A probe point specification identifies the code location whose execution or the event whose occurrence that should trigger the probe actions. Multiple probe points can be associated with the same set of probe actions and the predicate, if any, by providing a comma-separated list of probe specifications at the top of the Vue clause.

AIX 6.1.0 supports the following probe types:

  1. User Function Entry probes (or uft probes)
  2. System Call Entry/Exit probes (or syscall probes)
  3. Probes that fire at specific time intervals (or interval probes)

The format for a probe specification is probe-type specific. The probe specification is a tuple of ordered list of fields separated by colons. It has the following general format.

	@@<probetype>:<one or more probetype-specific fields separated by colons>:<location>

For example:

	- @@uft:34568:*:foo:entry
	  Probe at entry into any function called foo() (in the main executable
	  or any of the loaded modules including libraries) in process
	  with ID = 34568

	- @@syscall:*:read:exit
	  Probe at exit of read system call 

	- @@interval:*:clock:500
	  Probe to fire every 500 milliseconds (wall clock time).

The probe specification for a probe type is defined by its probe manager. However, the following general rules are followed by all probe managers when defining the probe specifications:

The process Id for a process is often not known at the time the Vue script is written. Vue provides a simple method to avoid having to hard-code a process Id in the second field of the probe specification or anywhere in a Vue script (for example, in the predicate section). Section 5.5 discusses this in more detail.

A single Vue script may contain probe points from multiple processes in user space and in the kernel. Any trace output generated is however always displayed in time order.

In addition to regular probe points defined by probe managers, Vue supports two special probe points. Each Vue script may contain a @@BEGIN probe point to indicate any action that needs to be executed before enabling any probes and an @@END probe point to indicate any action to be executed after tracing has been terminated.

2.3 Action Block

The action block identifies the set of actions to be performed when a thread hits the probe point. Supported actions are not restricted to the mere capturing and formatting of trace data but full power of Vue can be employed.

An action block in Vue is similar to a procedure in procedural languages. It consists of a sequence of statements that are executed in order. The flow of execution is essentially sequential. The only exceptions are that conditional execution is possible using the "if-else" statement and control may be returned from within the action block using the "return" statement.

Unlike procedures in procedural languages, an action block in Vue does not have an output or return value. Neither does it have inherent support for a set of input parameters. On the other hand, the context data at the point where a probe is entered can be accessed within the action block to parameterize the actions to be performed.

2.4 Predicates

Predicates should be used 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.

A predicate has the following format:

	when ( <condition> )

For example, when ( __pid == 1678 ).

Section 5.2 has more details about predicates.

3. Variables

The Vue language supports all the traditional C data types, namely those recognized by the C-89 specifications. In addition, Vue includes some extensions to allow simple, but powerful dynamic tracing programs to 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 and variables that have thread-local scope. In addition, Vue allows access to variables with external scope like global variables in the kernel or user data in application being probed.

The following subsection describes the special non-C variable types supported by Vue. The next two subsections describe the variable classes of Vue - each variable class has distinct rules on scope initialization and modification.

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. In some cases, Vue allows you to declare variables outside any of the action blocks, but in this case, all such declarations must appear before the first action block.

3.1 Data types in Vue

The Vue language supports all C-89 data types. This includes including the signed and unsigned versions of the following:

These types behave similar to the equivalent types in the C language. However, support for floating-point types are limited to simple assignment expressions and as arguments for Vue functions like printf(). In particular, floating-point variables cannot be used as operands of any other unary or binary operators.

The Vue language also supports C-style arrays, structures, and union declarations and pointers. Pointers can be used to dereference kernel or application data. However, there is no support for declaring or using pointers of the internal variables declared in a Vue script.

Three new data types are supported by Vue in addition to these traditional C-89 data types. These are described below:

  1. 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').

    	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 will automatically convert between a string data type and C-style character data types (char * or char[]) as needed.

    The following operators are supported for the string data types:

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

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

    	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 example below:

    	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.

  2. List Type

    A variable of type list is used to collect a set of integral type values. The list type is an abstract data type and a list variable cannot be used directly with the standard C unary or binary operators. The following operations are supported for the list type:

    • A constructor function, list() to create a list variable.
    • A concatenation function, append() to add an item to the list.
    • 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.

    The list() function returns a new empty list that can be assigned to a list variable. A list variable is never explicitly declared. There is no declaration statement to declare a list variable. Instead, a variable is automatically defined to be a list variable when the value returned by the list() function is assigned to it. However, this document uses the term "List" as a prefix to a variable to indicate that the variable has a list data type. For example, the following function prototype declaration for the append() function indicates that the append() function takes two parameters, the first is a List data type and the second is a long long (64-bit integer) data type.

    	void append(List lst, long long val);
    

    The following example creates a new list variable called l_opens:

    	l_opens = list();
    

    Currently, the list() function can only be called from within the @@BEGIN probe.

    You can use the append() function to add a value to a list variable. Each call to append adds a new value to the set of value 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. Note that the value added to the list must be a parameter of integral type and it is an error below 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.

    	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 append() function has no return values.

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

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

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

    	/* below we assume n_opens1=4, n_opens2=6, n_opens3=2 and n_opens = 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.

  3. Timestamp Data Type

    A variable of type probev_timestamp_t is used to hold the return value from the ProbeVue function timestamp(), which returns a timestamp in internal AIX format. This can be later passed as a parameter into the diff_time()() function which returns the difference between two timestamps.

    Although, the probevue compiler currently does not type check when a long is used instead of the probev_timestamp_t data type for storing timestamps, such a use is not recommended as this may break in future.

    A variable of probev_timestamp_t data type and global class will be initialized to zero. Additionally, a later timestamp is guaranteed to be larger than an earlier timestamp. Timestamp variable checks that are based on either of the above are valid syntax.

3.2 Variable Classes

Several classes of variables are supported by E-Lang with varying rules on scope, on how they are initialized, on whether they may be updated or not and on how their types are determined.

  1. Automatic Class Variables

    An automatic is a clause-specific variable 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 before they can be used in an expression or 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.

    Automatic class variables may not be used in the predicate section of the e-clause.

    	__auto int i;      /* Explicit declaration */
    	auto:j = 0;        /* Implicit declaration */
    
  2. Thread-local Class Variables

    A thread-local variable is instantiated per traced thread the first time it executes an action block that uses 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 executes the Vue script obtains its own copy of these variables and the variables in each such copy are accessible and modifiable 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 one exception, which is indicated below.

    A thread-local variable may be used 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 currently always be included to identify it as a thread-local variable.

    	__thread int i;      /* Explicit declaration */
    	thread:j = 0;        /* Implicit declaration */
    

    Note:

    Although thread-locals may be declared inside the @@BEGIN and @@END probes, any other references to them in these special probes produce undefined behavior.

  3. Global Class Variables

    Variables of global class have global scope and are visible everywhere within a Vue script. A global variable can be used 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. Global variables may also be explicitly declared 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 a list type. There are no guarantees on data races when probes are executed simultaneously. Global variables, which are not of type list, are useful for collecting profiling and other statistics, but may not be a good choice when accuracy is important.

    Global variables may be used in the predicate section of a Vue clause .

    The following are examples for initializing and using global variables:

    Figure 2
     	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;
    		printf("m_count = %d\n", m_count);
    		if (wcount == 1)
    			exit();
    	}
    	
    
    	@@syscall:*:write:entry
    	{
    		m_count++;
    	}
    
    	@@syscall:*:write:exit
    	{
    		wcount = 1;
    	}
    
    


  4. Kernel Global Class Variables

    ProbeVue allows you to 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, it should be explicitly declared 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, although AIX does not export any useful pointer types). Further, you can also refer to the member names of kernel structures and unions or unions in a Vue script. It is preferable to access pinned kernel variables. If the page containing the kernel variable is not in memory (has been paged out), ProbeVue will return a value of zero for that variable.

    There is an example of how kernel variables may be declared and used in a Vue script in the sample programs section (number 5).

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

  5. Entry Class Variables

    Clauses associated with a probe point that is at the entry location point of a kernel or user routine can access the arguments passed to the routine 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 size of the 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 routines are specified indicated using the special built-in entry class variable names __arg1, __arg2, __arg3, ... up to the number of arguments passed to the function. For instance, in the clause associated with the read system call entry point as described above, __arg1 would refer to the value of file descriptor Id parameter, __arg2 would refer to the value of the buffer pointer parameter and __arg3 to the size of the data to be read.

    Use of entry class variables in a Vue clause is legal only if the probe specification associated with the clause identifies a unique probe point. Thus, entry class variables 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 parameters being passed to the function, must be provided in the Vue script.

    Exit class variables can be used in the predicate section of a clause.

    	int read(int fd, char *buf, unsigned long size);
    
    	@@syscall:*:read:entry 
    	{
    		printf("Number of bytes to read = %d\n", __arg3);
    	}
    

    Note that in the above, the definition of the read system call function specified in the script does not exactly match what is given in /usr/include/unistd.h, but it should work just as well.

    The following 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 script can be modified as follows for it to 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);
    	}
    
  6. Exit Class Variables

    Clauses associated with a probe point that is at the exit location points of a routine can access the return state of the routine being probed.

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

    Probes at exit location points are currently supported by the system call probe manager. For instance, 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.

    Exit class variables can be used in the predicate section of a clause.

    	/* Bad example. Will fail with syntax error since the
    	 * return type of the read() function is not specified
    	 */
    	read(int fd, char *buf, unsigned long size);
    
    	@@syscall:*:read:entry
    		when (__rv < 0)
    	{
    		/* Entered on read success: return value = # of bytes read */
    		printf("Number of bytes read = %d\n", __rv);
    	}
    

    The script can be modified as follows for it to work:

    	/* Good example.  */
    
    	int read(int fd, char *buf, unsigned long size);
    
    	@@syscall:*:read:entry
    		when (__rv < 0)
    	{
    		/* Entered on read success: return value = # of bytes read */
    		printf("Number of bytes read = %d\n", __rv);
    	}
    
  7. Built-in Class Variables

    In addition to the special built-in variables, __argv1 through __argv32 and __rv, Vue also defines a set of general-purpose built-in variables. Built-in class variables are essentially functions, but are treated as variables by ProbeVue. Because of this, built-in variables can be used in the predicate section of a Vue clause.

    The following built-in variables are supported in Vue. The table shows the largest value that can be returned by the built-in variable, which is not necessarily the same as its defined type. Section 3.3.1 discusses the types of built-in variables.

    __tid Thread ID of traced thread. 32-bit integer
    __pid Process ID of traced thread. 32-bit integer
    __ppid Parent process ID of traced thread. 32-bit integer
    __pgid Process group ID of traced thread. 32-bit integer
    __pname Process name of traced thread. String
    __uid, __euid Real and effective user ID of traced thread. 32-bit integers
    __trcid Process ID of tracing traced (probevue command) 32-bit integer
    __errno Current errno value for the traced thread. 32-bit integer
    __kernelmode Current executable mode: Is either 1 (in kernel mode) or 0 (in user mode). 32-bit integer
    __r3, ..., __r10   General Purpose Register values (for function parameters or return values).  
    64-bit integers  

    The following is an example script that uses 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);
    	
    		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);
    	}
    

3.3 Value and Type Assignment

The classification in the previous 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:

3.3.1 External Variables

Kernel class variables, entry and exit class variables and built-in variables are all external variables. They exist independent of 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, currently, 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 routines From the function prototype declaration in the Vue script. Must provide the type of the return value.
Built-ins These are dependent upon the built-variable. Their types are provided below:

Built-in Type
 __tid tid_t
 __pid pid_t
 __ppid pid_t
 __pgid pid_t
 __pname char [32]
 __uid uid_t
 __euid uid_t
 __trcid pid_t
 __errno int
 __kernelmode int
 __r3..__r10 32-bit for 32-bit process
 64-bit for 64-bit process


Note that the maximum size of the returned data may be smaller than the size of the type. For instance, 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.


3.3.2 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 within the script. Further, they may only be accessed or modified inside the script that defines them.

A script variable may have any legal C-89 data type including the String data type. A global variable may also have the list data type.

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 as described below if the first reference to the variable is an assignment operation with the variable on the left-hand side of the assignment operator.

4. Elements of C

Vue supports a subset of C. Not all C features are supported and even supported features may work differently. some differences even for features that are supported. The table below describes how C-keywords are handled by the probevue compiler. All C-keywords remain restricted in Vue. Use of any of them as variable names or other symbols is not flagged as a syntax error. However, you are strongly discouraged from doing this, as the behavior of such usage is undefined.

The following list describes the set of C-language features that are supported by Vue.

4.1 Differences from C language

Vue has a different behavior for some C features. Some restrictions have been imposed for maintaining efficiency or for ensuring that a Vue script can be executed safely inside the kernel and that it does not affect the probed process.

5. Other Vue Features

5.1 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 routines provided by the AIX system libraries or any user libraries. However, Vue supports its own special library of functions useful for dynamic tracing programs. Functions include:

  1. Tracing-specific functions
  2. Trace capture functions
  3. List functions
  4. C-library functions
  5. Functions to support tentative tracing
  6. Miscellaneous functions

The Vue string functions can be applied only on variables of string type and not on a pointer variable. Standard string functions like strcpy(), strcat(), etc., are not necessary in Vue, because they are supported through the language syntax itself.

The probevue compiler will validates the data types of the parameters passed to Vue functions. However, it does not validate that the arguments passed to the printf() function match the conversion specifications indicated in the format string. Casting is allowed to circumvent type mismatches, but incompatible casts usually generate a warning.

Functions are not legal in the predicate section of a Vue clause unlike built-in variables.

Chapter 6 documents the complete list of supported functions.

5.2 Predicates

As mentioned earlier, predicates should be used 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 allowed inside the predicate section:

Conditional execution of specific actions within a clause are possible by using the "if ... else" statement which works like the analogous statement in C. However, if the entire clause is to be executed conditionally, it is preferable to use predicates instead because ProbeVue is designed to optimize execution of predicates. Note that when a probe point can fire for more than one process, then using thread-local variables inside the predicate is an excellent way to reduce overall performance impact of enabling the probe. In future, the predicate section will be further optimized to execute even more efficiently, so putting your 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 (see Section 5.5 for details on passing parameters to a Vue script).

Figure 5
/*
 * 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>\"
 */

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:tracing = 1;
}

@@syscall:*:open:exit
	when (thread:tracing == 1)
{
	thread:fd = __rv;
	thread:tracing = 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, this would be executed as:

	probevue chkfilewrite.e \"foo.log\" \"Error\"

5.3 Symbolic Constants

Vue supports some pre-defined symbolic constants, which are commonly used while programming in AIX. These constants are treated as keywords in Vue. During compilation, the constants will be replaced by their definitions in AIX system header files.

5.4 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. However, it should not contain any C executable statements. C preprocessor operators and directives are generally ignored, but operators like "#ifdef" and "#if" may cause undefined behavior. Thus, standard AIX header files from the /usr/include directories cannot be directly included. Instead, you should 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 are used 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;
	}

The probevue command should be run as follows:

	probevue -I myheader.i myscript.e

5.5 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:

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

	{
		count++;
	}

In the following example, the process ID of the process running the myprog program replaces $1 in the preceding script:

	probevue myscript.e 'ps -ef | grep myprog | cut -d ' ' -f 6'

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

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

	{
		count++;
	}

	/* program to be traced has a function called 'foo' */
	@@user:$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 need to be recognized as a string inside the ProbeVue script, the value of the environment variable must include the enclosing double quotes that identify it as a string. For example, the following script captures trace output when a specific file is opened in the system.

Figure 10
	/* 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 quotes in its value. For instance:

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

If the value of an existing environment variable that does not have double quotes is required in a script, then a new environment variable will need to be constructed using double quotes around the existing environment variable. For 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

	#!/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 file from the command line. You might find this useful to test short scripts.

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

5.6 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 hexadecimal format to the trace buffer. This function is particularly useful for dumping the contents of strings and structures.

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, stack trace, etc 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 is internally sorted before being output.

5.6.1 Tentative Tracing

ProbeVue also has a facility to conditional capture trace data using the tentative tracing facilities. 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 may 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 discarded. The commit_tentative()() commits the entire collected tentative trace data and make 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 may even stop capture of regular trace data because the trace buffers fill up because of 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 actions 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. Currently, Vue requires a string literal to be used as the tentative trace ID, which reduces the usefulness of having multiple active tentative trace IDs.

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.

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

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

@@BEGIN
{
	unsigned long long ts1, ts2;
}

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

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

	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)
{
	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");
}

6. Vue Functions

The Vue language supports the following list of functions:


6.1  printf

Purpose

Syntax

Description

Parameters


6.2  trace

Purpose

Syntax

Description

Parameters


6.3  stktrace

Purpose

Syntax

Description

Parameters


6.4   get_probe

Purpose

Syntax

Description

Parameters



6.5  get_function

Purpose

Syntax

Description

Parameters



6.6  get_location_point

Purpose

Syntax

Description

Parameters



6.7  list

Purpose

Syntax

Description


Parameters



6.8  append

Purpose

Syntax

Description

Parameters


6.9  sum

Purpose

Syntax

Description

Parameters


6.10  avg

Purpose

Syntax

Description

Parameters


6.11  min

Purpose

Syntax

Description

Parameters


6.12  max

Purpose

Syntax

Description

Parameters


6.13  count

Purpose

Syntax

Description

Parameters


6.14  start_tentative, end_tentative

Purpose

Syntax

Description

Parameters


6.15  commit_tentative, discard_tentative

Purpose

Syntax

Description

Parameters


6.16  get_userstring

Purpose

Syntax

Description

Parameters


6.17  timestamp

Purpose

Syntax

Description

Parameters



6.18  diff_time

Purpose

Syntax

Description

Parameters


6.19  exit

Purpose

Syntax

Description

Parameters



6.20  atoi

Purpose

Syntax

Description

Parameters



6.21  strstr

Purpose

Syntax

Description

Parameters



7. Probe Managers

The probe manager is not part of the basic ProbeVue framework, but is, nevertheless, an essential component of dynamic tracing. Probe Managers are the providers of the probe points that can be instrumented by ProbeVue.

Probe managers generally support a set of probe points that belong to some common domain and share some common feature or attribute that distinguishes them from other probe points. Probe points are useful at points where control flow changes significantly, at points of state change or other similar points that of significant interest. Probe managers are careful to select probe points only in locations that are safe to instrument.

Probe managers may choose to define their own distinct rules for the probe specifications within the common style that must be followed for all probe specifications as discussed in Section 2.2.

ProbeVue currently supports the following three probe managers:

  1. System Call probe manager
  2. User function probe manager
  3. Interval probe manager

These are discussed in more detail below.

7.1 System Call Probe Manager

The syscall probe manager supports probes at the entry and exit of well-defined and documented base AIX system calls. These are the system calls that have the same interface at the libc.a" (or C library) entry point and in the kernel entry point. Either the system call is a pass-through (the C library, simply imports the symbol from the kernel and the exports it with no code in the library) or there is trivial code for the interface inside the library.

The syscall probe manager accepts a 4-tuple probe specification in one of the following formats:

where the <system_call_name> field is to be substituted by the actual system call name. These indicate that a probe be placed at the entry and exit of system calls. Assigning the "*" to the second field indicates that the probe will be fired for all processes. Note that different privileges are required for enabling system call probes and probing every process in the system requires higher privileges than probing a single process. This is described in more detail in Section 8.1.

Additionally, the syscall probe manager also accepts a 4-tuple probe specification in one of the following formats:

where a process Id can be specified as the second field of the probe specification to support probing of specific processes.

The system call names accepted by the syscall probe manager are the names of the libc.a interfaces and not the internal system call names. For instance, the read() subroutine is exported by libc.a, but the actual system call name or kernel entry point is kread. The syscall probe manager will internally translate a libc interface to its kernel entry point and enable the probe at entry into the kread kernel routine. Because of this, if multiple C library interfaces invoke the kread routine, the probe point will fire for those interfaces also. Generally, this is not a problem because for most of the system calls supported by the syscall probe manager, there is a 1-to-1 mapping between the libc interface and the kernel routine.

For each syscall probe, there is an equivalent probe point in the library code provided by the uft probe manager. The uft probe manager does support all library interfaces including those not supported by the syscall probe manager. However, the syscall probe manager has two advantages:

  1. It can be used to probe every process in the system by specifying asterisk as the second field.
  2. It is more efficient than the uft probe manager because it does not need the switch from user mode to kernel mode and back to run the probe actions.

Appendix A provides the full list of system calls currently supported by the syscall probe manager.

7.2 UFT Probe Manager

The uft or the user function tracing probe manager supports probing user space functions that are visible in the XCOFF symbol table of a process. However, currently, the uft probe manager restricts support only to probe points that are at entry points of functions whose source is a C language text file even the symbol table may contain symbols whose sources are from a language other than C.

The uft probe manager currently accepts a 5-tuple probe specification only in the following format:

	
	uft:<processID>:*:<function_name>:entry

Note that the uft probe manager requires the process ID for the process to be traced and the complete function name of the function at whose entry point the probe is to be placed. Further, the uft probe manager currently requires that the third field be set to '*" to indicate that the function name is to be searched in any of the modules loaded into the process address space including the main executable and shared modules. This implies that if a program contains more than one C function with this name (for example, functions with static class that are contained in different object modules), then probes will be applied to the entry point of every one of these functions.

ProbeVue supports enabling probes in more than one process at the same time. However, you will need privileges even for probing processes that belong to you. This is described in more detail in Section 8.1.

As indicated above, the uft probe manager supports probes in shared modules like shared library modules. The following shows an example script that traces mutex activity by enabling probes in the thread library's mutex lock and unlock routines.

Figure 15
/* pthreadlocks.e */
/* Trace pthread mutex activity for a given multi-threaded process */
/* The following defines are from /usr/include/sys/types.h */

typedef long long pid_t;
typedef long long thread_t;

typedef struct {
	int	__pt_mutexattr_status;	
	int	__pt_mutexattr_pshared;	
	int	__pt_mutexattr_type;
} pthread_mutexattr_t;

typedef struct __thrq_elt thrq_elt_t;

struct __thrq_elt {
	thrq_elt_t	*__thrq_next;
	thrq_elt_t	*__thrq_prev;
};

typedef volatile unsigned char _simplelock_t;

typedef struct __lwp_mutex {
	char		__wanted;
	_simplelock_t	__lock;
} lwp_mutex_t;

typedef struct {
	lwp_mutex_t		__m_lmutex;
	lwp_mutex_t		__m_sync_lock;
	int			__m_type;
	thrq_elt_t		__m_sleepq;
	int			__filler[2];
} mutex_t;

typedef struct {
	mutex_t			__pt_mutex_mutex;
	pid_t			__pt_mutex_pid;
	thread_t		__pt_mutex_owner;
	int			__pt_mutex_depth;
	pthread_mutexattr_t	__pt_mutex_attr;
} pthread_mutex_t;

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

@@uft:$__CPID:*:pthread_mutex_lock:entry
{
	printf("thread %d: mutex 0x%08x locked\n", __tid, __arg1);
}

@@uft:$__CPID:*:pthread_mutex_unlock:entry
{
	printf("thread %d: mutex 0x%08x unlocked\n", __tid, __arg1);
}

7.3 Interval Probe Manager

The interval probe manager supports The interval probe manager provides probe points that fire at a user-defined time-interval. The probe points are not located in kernel or application code, but instead are based on wall clock time interval based probe events.

The interval probe manager accepts a 4-tuple probe specification in the following format:

        
        @@interval:*:clock:<# milliseconds>

The second field is * indicating that the probe can be fired in any process. Currently, the interval probe manager does not filter probe events by process IDs. Currently, the only value supported by the interval probe manager for the third field is the clock keyword that identifies the probe specification as being for a wall clock probe. The fourth or last field, that is, the <# milliseconds> field identifies the number of milliseconds between firings of the probe. Currently, the interval probe manager requires that the value for this field be exactly divisible by 100 and consist only of digits 0-9. Thus, probe events that are apart by 100ms, 200ms, 300ms, and so on, are allowed.

Note that the interval probe manager does not guarantee that a probe will be fired exactly the number of milliseconds apart as indicated by value of the fourth field. Higher-priority interrupts and code that runs after disabling all interrupts may cause the probe to fire later than the specification.

The interval probe manager requires only basic dynamic tracing privileges. See Section 8.1 for details. The interval probe manager enforces the following limits on the number of probes it supports to prevent malicious users from running the kernel out of memory by creating a huge numbers of interval probes.

Maximum number of interval probes per user 32
Maximum number of interval probes in system 1024

The interval probe manager currently does not support the following functions. If used inside an interval manager probe point, these functions will generate an empty string or zero as output.

The interval probe manager is useful for summarizing statistics collected over an interval of time.


8. Authorizations and Privileges

Dynamic tracing is only allowed for users with privileges or for the superuser. This is unlike the static tracing facilities in AIX, which enforce relatively limited privilege checking. There is a reason for requiring privileges to run probevue. A Vue script can potentially produce more severe impacts on system performance than a static tracing facility like AIX System Trace. This is because probe points for System Trace are pre-defined and restricted. ProbeVue can potentially support many more probe points and the probe locations can potentially be defined almost anywhere. Further, ProbeVue trace actions at a probe point can take much longer to execute than the System Trace actions at a probe point since those are limited to explicit data capture.

Further ProbeVue allows users to trace processes and read kernel global variables, both of which need to be controlled to prevent security exposures. Last but not least, a probevue session can consume a lot of pinned memory and restricting usage of ProbeVue to users with privilege reduces the risk of denial of service attacks. ProbeVue also allows administrators to control the memory usage of probevue sessions through the smitty interface.

Privileges for dynamic tracing are obtained differently depending upon whether RBAC (Role-based Access Control) is enabled or not. Please refer to the AIX man pages for more information about enabling and disabling RBAC.

8.1 RBAC-enabled mode

Privileges in an RBAC system are obtained through authorizations. An authorization is a text string associated with security-related functions or commands. Authorizations provide the mechanism to grant rights to users to perform privileged actions. Only a user with sufficient authorizations is allowed to execute the probevue command and start a dynamic tracing session.

The superuser (or root) has all these authorizations assigned by default. Other users will need to have authorizations assigned to them by first creating a "role" with a set of authorizations and assigning the role to the user. The user will also need to switch roles to a role that has the required authorizations defined for dynamic tracing before invoking the probevue command. Please refer to the RBAC man pages for more information on roles and authorizations. Here is an example of how to provide user "joe" authorization to enable user space and system call probes for processes started by joe.

	mkrole authorizations="aix.ras.probevue.trace.user.self,aix.ras.probevue.trace.syscall.self" apptrace
	chuser roles=apptrace joe
	setkst -t role		# Copy roles to kernel (Or wait until system reboots)

User "joe" may be set up to always have all roles acquired by default when logging in or can switch to the role as needed using the following command:

	swrole apptrace

Note that the interval probe manager does not have a specific authorization associated with it. You can enable interval probe points if you have any of the aix.ras.probevue.trace* authorizations.

8.1.1 ProbeVue Privileges

The privileges that are available for ProbeVue are listed in the following table. A description of each privilege and the authorizations that map to that privilege is provided. Privileges form a hierarchy where the parent privilege contains all of the rights that are associated with the privileges of its children or it can include additional privileges also.

Privilege Description Authorizations Associated
Command
PV_PROBEVUE_TRC_USER_SELF Allows a process to enable dynamic user space probe points on another process with same real user ID. aix.ras.probevue.trace.user.self
aix.ras.probevue.trace.user
aix.ras.probevue.trace
aix.ras.probevue
probevue
PV_PROBEVUE_TRC_USER Allows a process to enable dynamic user space probe points. Includes the PV_PROBEVUE_TRC_USER_SELF privilege. aix.ras.probevue.trace.user
aix.ras.probevue.trace
aix.ras.probevue
probevue
PV_PROBEVUE_TRC_SYSCALL_SELF Allows a process to enable dynamic system call probe points on another process with same real user ID. aix.ras.probevue.trace.syscall.self
aix.ras.probevue.trace.syscall
aix.ras.probevue.trace
aix.ras.probevue
probevue
PV_PROBEVUE_TRC_SYSCALL Allows a process to enable dynamic system call space probe points. Includes the PV_PROBEVUE_TRC_SYSCALL_SELF privilege. aix.ras.probevue.trace.syscall
aix.ras.probevue.trace
aix.ras.probevue
probevue
PV_PROBEVUE_TRC_KERNEL Allows a process to access kernel data when dynamic tracing. aix.ras.probevue.trace
aix.ras.probevue
probevue
PV_PROBEVUE_MANAGE Allows a process to administer ProbeVue. aix.ras.probevue.manage
aix.ras.probevue
probevctrl
PV_PROBEVUE_ Equivalent to all above privileges (PV_PROBEVUE_*) combined aix.ras.probevue probevue, probevctrl

8.2 Legacy or RBAC-disabled mode

In legacy mode, there are no authorizations. Regular users cannot acquire privileges to run the probevue command to start a dynamic tracing session or run the probevctrl command to administer ProbeVue. Only the superuser will have privileges for both these functions. It is recommended that you do not disable RBAC when using ProbeVue unless and you prefer to restrict this facility to root users only.


9. ProbeVue parameters

AIX provides a set of parameters that allow you to tune ProbeVue. of the ProbeVue framework. The parameters allow you to specify both global limits on resource usage by the ProbeVue framework and to specify resource usage for individual users. Note that probe managers are not contained within the ProbeVue framework and hence these limits do not apply them.

All ProbeVue parameters can be modified through smitty interface (use the "smitty probevue" fast path) or directly through the probevctrl command. ProbeVue can be stopped if there are no active dynamic tracing sessions and it can be restarted after stopping it without requiring a reboot. However, currently, ProbeVue may fail to stop if any sessions that used thread-local variables had been previously active.

The following table summarizes the parameters defined for dynamic tracing sessions. In the description below, a privileged user refers to the superuser or a user with aix.ras.probevue.trace authorization and a non-privileged user is one who does not have this authorization.

Description
As in smitty
Value Details
Maximum Initial     Minimum
MAX pinned memory for ProbeVue framework 256 MB 16 MB 1 MB Maximum pinned memory in MB allocated for ProbeVue data structures, including per-CPU stacks and per-CPU local table regions and by all dynamic tracing sessions. This does not include any memory allocated by Probe Managers.

Note:

Although, this parameter can be modified at any time, the value will take effect only the next time ProbeVue is started.

Default per-CPU trace buffer size 256 KB 16 KB 8 KB Default size in KB of each per-CPU trace buffer. Two trace buffers will be allocated per CPU for each dynamic tracing session by ProbeVue, one active and used by the writer or the Vue program when it captures trace data and one inactive and used by the reader or the trace consumer. For example, on a 8-way with per-CPU trace buffer size set to 16K, the total memory consumed by the trace buffers for a probevue session is 256K. Users may specify a different buffer size (larger or smaller) when starting the probevue command as long as it is within the session memory limits.
MAX pinned memory for regular user sessions 256 MB 2 MB 0 MB Maximum pinned memory allocated for a non-privileged user probevue session including memory for the per-CPU trace buffers. A value of 0 effectively disables all non-privileged users. Privileged users have no limits on the memory used by their ProbeVue sessions. However, they are still limited by the maximum pinned memory allowed for the ProbeVue framework.
MIN trace buffer read rate for regular user 5000 ms 100 ms 10 ms The minimum period in milliseconds, that a non-privileged user can request the trace consumer to check for trace data. This value will be internally rounded to the next highest multiple of 10 milliseconds. Privileged users are not limited by this parameter, but the fastest read rate they can specify is 10 milliseconds.
Default trace buffer read rate 5000 ms 100 ms 10 ms The default period in milliseconds that the in-memory trace buffers will be checked for trace data by the trace consumer. Users may specify a different read rate (larger or smaller) when starting the probevue command as long as it is larger than the minimum buffer read rate.
MAX concurrent sessions for regular user 8 1 0 Number of concurrent probevue sessions allowed for a non-privileged user. A value of zero effectively disabled all non-privileged users.
Size of per-CPU computation stack 256 KB 12 KB 4 KB The size of the per-CPU computation stack used by ProbeVue when executing the Vue script. The value is rounded to the next highest multiple of 4K. Note that ProbeVue allocates a single stack per-CPU for all probevue sessions. The memory consumed for the stacks is not included in the per-session limits.

Note:

Although, this parameter can be modified at any time, the value will take effect only the next time ProbeVue is started.

Size of per-CPU local table size 32 KB 4 KB 4 KB The size of the per-CPU local table used by ProbeVue for saving variables of automatic class and for saving temporary variables. ProbeVue will also allocate a second region with the same size for saving temporary strings. Thus, the total pinned memory allocated per CPU for temporary and automatic variables is double the value specified for this parameter. The value is always rounded to the next highest multiple of 4K. Note that ProbeVue allocates a single local table and a single temporary string table per-CPU used by all probevue sessions. The memory consumed for the local tables is not included in the per-session limits.

Note:

Although, this parameter can be modified at any time, the value will take effect only the next time ProbeVue is started.

The initial values in the table above refer to the values when AIX 6.1 is initially installed. You can change any of these values through smitty or the probevctrl command. Both the current values and the values for next boot can be updated.

Please refer to the AIX man pages for more information on the probevue and probevctrl commands.


10. Miscellaneous

10.1 Running in a WPAR

Workload partitions or WPARs are virtualized operating system environments within a single instance of the AIX operating system. The WPAR environment is somewhat different from the standard AIX operating system environment. Please refer to the AIX man pages for more information about WPARs including how to start and stop them and how to make them mobile.

Dynamic tracing is supported in the WPAR environment. By default, when creating a WPAR, only the PV_PROBEVUE_TRC_USER_SELF and the PV_PROBEVUE_TRC_USER privileges are assigned to the WPAR and the superuser (root) on a WPAR system will be grated these privileges. An admin user from the global partition can change the value of the the default WPAR privilege set or can explicitly assign additional privileges when creating the WPAR.

Privileges on WPAR have generally the same meanings as on a global partition. Be careful when assigning PV_PROBEVUE_TRC_KERNEL or the PV_PROBEVUE_TRC_MANAGE to a WPAR. Any user with PV_PROBEVUE_TRC_KERNEL privilege can access global kernel variables while a user with PV_PROBEVUE_TRC_MANAGE can change the values of ProbeVue parameters or shutdown ProbeVue. These changes affect all users even those in other partitions.

When you execute the probevue command in a WPAR, processes running in other WPARs or in the global partition are not visible to it. Because of this, you can only probe processes in your same WPAR and the probevue command will fail if the probe specification contains a process ID that is outside its partition. The PV_PROBEVUE_TRC_USER and PV_PROBEVUE_TRC_SYSCALL privileges in a WPAR only allow you to probe user space functions or system calls of processes that are in your WPAR. When probing system calls, the second field of the syscall probe specification must be currently set to a valid WPAR-visible process ID. Assigning the value "*" to the second field is not supported.

When a probevue session is initiated in a mobile WPAR, that will temporarily switch the WPAR to a non-checkpointable state. After the probevue session terminates, the WPAR is checkpointable again.


11. Sample Programs

A few sample Vue scripts follow:

  1. The following canonical "Hello World" program prints "Hello World" into the trace buffer and exits.

    Figure 16
    	#!/usr/bin/probevue
    	
    	/* Hello World in probevue */
    	/* Program name: hello.e */
    	
    	@@BEGIN
    	{
    		printf("Hello World\n");
    		exit();
    	}
    


  2. The following "Hello World" program prints "Hello World" when Ctrl-C is typed on the keyboard.

    Figure 17
    	#!/usr/bin/probevue
    	
    	/* Hello World 2 in probevue */
    	/* Program name: hello2.e */
    	
    	@@END
    	{
    		printf("Hello World\n");
    	}
    


  3. The following program shows how to use thread-local variables. This Vue script counts the number of bytes written to a particular file. It assumes that the processes are single-threaded or those threads that open files are the same ones that write to them. It also assumes that all write operations are successful.

    Figure 18
    	#!/usr/bin/probevue
    	
    	/* Program name: countbytes.e */
    	int open( char * Path, int OFlag, int mode );
    	int read( int fd, char * buf, int sz);
    
    	@@syscall:*:open:entry
    		when (__arg1 == "/tmp/foo" )
    	{
    		thread:trace = 1;	
    	}
    	
    	@@syscall:*:open:exit
    		when (thread:trace)
    	{
    		thread:fd = __rv;
    	}
    	
    	@@syscall:*:write:entry
    		when (thread:trace && __arg1 == thread:fd)
    	{
    		bytes += __arg3;	/* number of bytes is 3rd arg */ 
    	}
    	
    	@@END
    	{
    		printf("Bytes written = %d\n", bytes);	
    	}
    
    


  4. The following tentative tracing program shows how to trace the arguments passed to the read system call only if it returns zero bytes when reading the foo.data file.

    Figure 19
    	#!/usr/bin/probevue
    	/* File: ttrace.e */
    	/* Example of tentative tracing */
    	/* Capture parameters to read system call only if read fails */
    	int open ( char* Path, int OFlag , int mode );
    	int read ( int fd, char * buf, int sz);
    	
    	@@syscall:*:open:entry
    	{
    		filename = get_userstring(__arg1, -1);
    		if (filename == "foo.data") {
    			thread:open = 1;
    			start_tentative("read");
    			printf("File foo.data opened\n");
    		}
    	}
    	
    	@@syscall:*:open:exit
    		when (thread:open == 1)
    	{
    		  thread:fd = __rv;
    		  start_tentative("read");
    		  printf("fd = %d\n", thread:fd);
    		  thread:open = 0;
    	}
    		 
    	@@syscall:*:read:entry
    		when (__arg1 == thread:fd)
    	{
    		start_tentative("read");
    		printf("Read fd = %d, input buffer = 0x%08x, bytes = %d,",
    			__arg1, __arg2, __arg3);
    		end_tentative("read");
    		thread:read = 1;
    	}
    	
    	@@syscall:*:read:exit
    		when (thread:read == 1)
    	{
    		if (__rv < 0) {
    			/* The printf below even though non-tentative is only
    			 * executed in error cases and merges with the 
    			 * previously printed tentative data
    			 */
    			printf(" errno = %d\n", __errno);
    			commit_tentative("read");
    		}
    		else
    			discard_tentative("read");
    		thread:read = 0;
    	}
    
    

    A possible output if the read failed because a bad address (say 0x1000) was passed as input buffer pointer could look like:



  5. The following Vue script prints the values of some kernel variables and exits immediately. Note the exit() function in the @@BEGIN probe.

    Figure 20
    /* File: kernel.e */
    /* Example of accessing kernel variables */
    /* System configuration structure from /usr/include/sys/systemcfg.h */
    struct system_configuration {
    	int architecture;	/* processor architecture */
    	int implementation;	/* processor implementation */
    	int version;		/* processor version */
    	int width;		/* width (32 || 64) */
    	int ncpus;		/* 1 = UP, n = n-way MP */
    	int cache_attrib;	/* L1 cache attributes (bit flags)	*/
    				/* bit		0/1 meaning		*/
    				/* -------------------------------------*/
    				/* 31	 no cache / cache present	*/
    				/* 30	 separate I and D / combined    */
    	int icache_size;	/* size of L1 instruction cache */
    	int dcache_size;	/* size of L1 data cache */
    	int icache_asc;		/* L1 instruction cache associativity */
    	int dcache_asc;		/* L1 data cache associativity */
    	int icache_block;	/* L1 instruction cache block size */
    	int dcache_block;	/* L1 data cache block size */
    	int icache_line;	/* L1 instruction cache line size */
    	int dcache_line;	/* L1 data cache line size */
    	int L2_cache_size;	/* size of L2 cache, 0 = No L2 cache */
    	int L2_cache_asc;	/* L2 cache associativity */
    	int tlb_attrib;		/* TLB attributes (bit flags)		*/
    				/* bit		0/1 meaning		*/
    				/* -------------------------------------*/
    				/* 31	 no TLB / TLB present		*/
    				/* 30	 separate I and D / combined    */
    	int itlb_size;		/* entries in instruction TLB */
    	int dtlb_size;		/* entries in data TLB */
    	int itlb_asc;		/* instruction tlb associativity */
    	int dtlb_asc;		/* data tlb associativity */
    	int resv_size;		/* size of reservation */
    	int priv_lck_cnt;	/* spin lock count in supevisor mode */
    	int prob_lck_cnt;	/* spin lock count in problem state */
    	int rtc_type;		/* RTC type */
    	int virt_alias;		/* 1 if hardware aliasing is supported */
    	int cach_cong;		/* number of page bits for cache synonym */
    	int model_arch;		/* used by system for model determination */
    	int model_impl;		/* used by system for model determination */
    	int Xint;		/* used by system for time base conversion */
    	int Xfrac;		/* used by system for time base conversion */
    	int kernel;		/* kernel attributes			    */
    				/* bit		0/1 meaning		    */
    				/* -----------------------------------------*/
    				/* 31	32-bit kernel / 64-bit kernel	    */
                                    /* 30   non-LPAR      / LPAR                */
                                    /* 29   old 64bit ABI / 64bit Large ABI     */
                                    /* 28   non-NUMA      / NUMA                */
                                    /* 27   UP            / MP                  */
                                    /* 26   no DR CPU add / DR CPU add support  */
                                    /* 25   no DR CPU rm  / DR CPU rm  support  */
                                    /* 24   no DR MEM add / DR MEM add support  */
                                    /* 23   no DR MEM rm  / DR MEM rm  support  */
                                    /* 22   kernel keys disabled / enabled	    */
                                    /* 21   no recovery   / recovery enabled    */
                                    /* 20   non-MLS    / MLS enabled	    */
    	long long physmem;	/* bytes of OS available memory		    */
    	int slb_attr;		/* SLB attributes			    */
    				/* bit		0/1 meaning		    */
    				/* -----------------------------------------*/
    				/* 31		Software Managed	    */
    	int slb_size;		/* size of slb (0 = no slb)		    */
    	int original_ncpus;	/* original number of CPUs		    */
    	int max_ncpus;		/* max cpus supported by this AIX image     */
    	long long maxrealaddr;	/* max supported real memory address +1     */
    	long long original_entitled_capacity;
    				/* configured entitled processor capacity   */
    				/* at boot required by cross-partition LPAR */
    				/* tools.				    */
    	long long entitled_capacity; /* entitled processor capacity	    */
    	long long dispatch_wheel; /* Dispatch wheel time period (TB units)  */
    	int capacity_increment;	/* delta by which capacity can change	    */
    	int variable_capacity_weight;	/* priority weight for idle capacity*/
    					/* distribution			    */
    	int splpar_status;	/* State of SPLPAR enablement		    */
    				/*	0x1 => 1=SPLPAR capable; 0=not	    */
    				/*	0x2 => SPLPAR enabled 0=dedicated;   */
    				/*			     1=shared       */
    	int smt_status;		/* State of SMT enablement                  */
    				/*    0x1 = SMT Capable  0=no/1=yes         */
    				/*    0x2 = SMT Enabled  0=no/1=yes         */
    				/*    0x4 = SMT threads bound true 0=no/1=yes */
    	int smt_threads;	/* Number of SMT Threads per Physical CPU   */
            int vmx_version;        /* RPA defined VMX version, 0=none/disabled */
    	long long sys_lmbsize;	/* Size of an LMB on this system. */
    	int num_xcpus;		/* Number of exclusive cpus on line */
    	signed char errchecklevel;/* Kernel error checking level */
    	char pad[3];		/* pad to word boundary		*/
            int dfp_version;        /* RPA defined DFP version, 0=none/disabled */
    				/* if MSbit is set, DFP is emulated         */
    };
    
    __kernel struct system_configuration _system_configuration;
    
    @@BEGIN
    {
    	String s[40];
    	int j;
    	__kernel int max_sdl;	/* Atomic RAD system decomposition level */
    	__kernel long lbolt;	/* Ticks since boot  */
    
    	printf("No. of online CPUs\t\t= %d\n", _system_configuration.ncpus);
    
    	/* Print SMT status */
    	printf("SMT status\t\t\t=");
    	if (_system_configuration.smt_status == 0)
    		printf(" None");
    	else {
    		if (_system_configuration.smt_status & 0x01)
    			printf(" Capable");
    		if (_system_configuration.smt_status & 0x02)
    			printf(" Enabled");
    		if (_system_configuration.smt_status & 0x04)
    			printf(" BoundThreads");
    	}
    	printf("\n");
    
    	/* Print error checking level */
    	if (_system_configuration.errchecklevel == 1)
    		s = "Minimal";
    	else if (_system_configuration.errchecklevel == 3)
    		s = "Normal";
    	else if (_system_configuration.errchecklevel == 7)
    		s = "Detail";
    	else if (_system_configuration.errchecklevel == 9)
    		s = "Maximal";
    	printf("Error checking level\t\t= %s\n",s);
    
    	printf("Atomic RAD system detail level\t= %d\n", max_sdl);
    
    	/* Long in the kernel is 64-bit, so we use %lld below */
    	printf("Number of ticks since boot\t= %lld\n", lbolt);
    
    	exit();
    }
    

    A possible output when running on a Power 5 dedicated partition with default kernel attributes is:




Appendix A:
System Calls supported by the syscall probe manager

The following table lists the system calls currently supported by the syscall probe manager along with the actual entry name in the kernel. Note that the kernel entry name is provided here only for documentation purposes. The kernel entry names may change between releases or even after a service update.

System call name   Kernel entry name  
absinterval absinterval
accept accept1
bind bind
close close
creat creat
execve execve
exit _exit
fork kfork
getgidx getgidx
getgroups getgroups
getinterval getinterval
getpeername getpeername
getpid _getpid
getppid _getppid
getpri _getpri
getpriority _getpriority
getsockname getsockname
getsockopt getsockopt
getuidx getuidx
incinterval incinterval
kill kill
listen listen
lseek klseek
mknod mknod
mmap mmap
mq_close mq_close
mq_getattr mq_getattr
mq_notify _mq_notify
mq_open _mq_open
mq_receive _mq_receive
mq_send _mq_send
mq_setattr mq_setattr
mq_unlink mq_unlink
msgctl msgctl
msgget msgget
msgrcv __msgrcv
msgsnd __msgsnd
nsleep _nsleep
open kopen
pause _pause
pipe pipe
plock plock
poll _poll
read kread
reboot reboot
recv _erecv
recvfrom _enrecvfrom
recvmsg _erecvmsg
select _select,
sem_close _sem_close
sem_destroy sem_destroy
sem_getvalue sem_getvalue
sem_init sem_init
sem_open _sem_open
sem_post sem_post
sem_unlink sem_unlink
sem_wait _sem_wait
semctl semctl
semget semget
semop __semop
semtimedop __semtimedop
send _esend
sendmsg _esendmsg
sendto _esendto
setpri _setpri
setpriority _setpriority
setsockopt setsockopt
setuidx setuidx
shmat shmat
shmctl shmctl
shmdt shmdt
shmget shmget
shutdown shutdown
sigaction _sigaction
sigpending _sigpending
sigprocmask sigprocmask
sigsuspend _sigsuspend
socket socket
socketpair socketpair
stat statx
waitpid kwaitpid
write kwrite





Appendix B:
Shell helper programs for ProbeVue

The following shell scripts are useful when running ProbeVue.

  1. Shell script that wraps all arguments in double quotes:

  2. Shell script that prints process ID given process name

    Trademarks

    The following are trademarks of International Business Machines Corporation in the United States, other countries, or both:

    Other company, product, or service names may be trademarks or service marks of others.