Event triggering allows users to cause specific actions to be performed when specific events or sequences of events occur on the target. If you have included triggering in your VxWorks configuration (see Triggering), VxWorks tests when an instrumented point is hit to see whether triggering is enabled and performs the appropriate actions. For detailed information on how triggering is implemented, see C. Triggering API.
Triggering is not a WindView function. It is a VxWorks function with general applications. Its close relationship to WindView arises from the fact that both triggering and logging use the same instrumented VxWorks kernel. A trigger can be set at any of the instrumented points. Once the trigger is set, when the event occurs, the action related to the trigger is performed. Actions are defined by the user and range from controlling WindView to performing user-specific requests.
To work with triggering, click the
button in the main tool bar. This brings up the Triggering window, shown in Figure 6-1. The Triggering window allows you to define, modify, download, and control triggers. Until you set one or more triggers, the window displays only the legend "None." Even after you have defined a trigger, it appears as "not set." You must download it to the target before it is armed and ready to fire.
To define a trigger, click the
button at the left of the Triggering tool bar. This brings up the Trigger Maintenance dialog box, shown in Figure 6-2. When you open the Trigger Maintenance dialog box, the next available trigger number is shown in the first field.
In most cases you should leave the Enabled box checked. If you are going to chain this trigger and have it enabled by an earlier trigger or if you are going to start it from your code, uncheck this box. The remaining specification boxes allow you to narrow the circumstances under which your trigger will fire.
You can specify the type of event or a user event by selecting it from the Event Matches drop-down menu. If you chose User Event, the right hand field becomes active so you can enter the name of the user event. This user event is one you create using the trgEvent( ) routine. (See the trgEvent( ) in the VxWorks API Reference. For an example that uses trgEvent( ), see Example 6-6.)
You can cause your trigger to fire on an event that occurs in any context, any task, a specific task, any ISR (interrupt service routine), or the system context. If you choose Specific Task from the Context Matches drop-down menu, you must fill in the task name in the right-hand field. If you choose Specific Object from the Object Matches drop-down menu, you must enter the object ID in the right-hand field.
At each potential triggering point, tests are performed to see if the criteria you have specified for event, context, and object conditions have been met. If they have, triggering proceeds to evaluate any additional criteria you may have set.
You can make your trigger conditional upon a variable or function taking a specific value or range of values. Change (unconditionally) to conditional upon and select Var (variable) or Fn (function) from the drop-down menu. Enter the variable or function name, select the matching criterion from the drop-down menu (==, <=, and so on), and enter the value to test in the last field.
|
|
|||||||||||||||||||
In order to fire your trigger when a function takes a specified value, you must provide a function on the target which will be executed if all of the criteria specified for event, context, and object conditions have been met. Both the format and content of the function provided are important for correct operation.
The conditional function provided must be of the form
int condFunc (void)
{
int returnValue;
/*
* Function body. Must contain only code that can be executed in ISR
* context. See VxWorks Programmer's Guide: Basic OS
* Use of logMsg() and printf() are also prohibited.
*/
...
return (returnValue);
}
|
|
|||||||||||||||||||
Having provided a valid conditional function, it should be compiled for the target architecture and the resulting object module loaded onto the target. For example, if a function condFunc( ) is contained in a C module called condFunc.c and compiled into an object module condFunc.o, the object module can be loaded on to the target from a Tornado shell using the following command:
% ld < condFunc.o
If your function returns 1 under the conditions where you want your trigger to fire, you would specify the following:
Here, <operand 1> is treated as a function name and <operand 2> is treated as a 32-bit integer constant.
|
|
NOTE:
There is a major difference between the function used as a condition and the function used as a trigger action. The function used as a condition is executed, in line, during trigger evaluation, provided that the criteria for event, context, and object conditions have been satisfied. Unlike a trigger action function, a conditional function can not be deferred. You can check the Safe box for an action function to assure that it will not run within ISR or system context (see Specifying an Action). There is no Safe option for conditional functions, so you must write or call a function that can be safely executed within an ISR or system context.
|
||||||||||||||||||
For example, the following conditional function is invalid:
int condFunc (void)
{
if (vxTicks >= (sysClkRateGet() * 60 * 60))
{
semTake (mySem); /* semTake NOT ALLOWED in ISR context*/
printf ("The system has been up for more than an hour\n");
/* printf NOT ALLOWED in ISR context*/
return 1;
}
return 0;
}
This is because semTake( ) must not be used in an ISR. In addition, due to the fact that trigger evaluation points are present throughout the VxWorks kernel, you can not use printf( ) or logMsg( ) in condition functions. (For information about which routines can safely be called from ISRs, see the VxWorks Programmer's Guide: Basic OS.) This example could be corrected by making the printf( ) into a trigger action function and making it Safe to achieve the same result (see Example 6-7).
The bottom section of the Trigger Maintenance dialog box allows you to specify what happens when your trigger fires. You can select (no action), Start WV, Stop WV, or Call function. If you select Call function, the right two fields become active. Fill in the function name in the first field and an integer argument in the second field.
When selecting an action function for a trigger, the nature of the function to be called is important. It should be of the form:
int actionFunc (int arg)
{
int returnValue;
/* function body */
return (returnValue);
}
The entry in the Action section of the Trigger Maintenance dialog box is of the form:
If the function contains calls to any function that is not permitted in ISR context, then the Safe box must be checked. (This is the default.) In this case, the function is not executed at the time when trigger evaluation is performed and found to be satisfied; instead it is added to a trigger work queue. Triggering automatically spawns a task called tActDef whose responsibility it is to execute functions on the trigger work queue when the system is out of any critical region. If your application runs at a higher priority than tActDef, you may have to adjust the priority if you want the trigger action to be executed.
The last item you can specify is what happens after the trigger fires. You may disable this trigger once it fires (the default), or allow it to remain active so that it will fire again if the conditions are met again. You can also chain another trigger to it, which is enabled when the first trigger fires. You must specify the second trigger by number, so you need to define it before you can select it from the drop-down menu.
A saved trigger configuration can be loaded using the
button. Load the sample file installDir/host/src/windview/samples/simpleCond.trg by clicking the
button and selecting it in the browse menu. Once this trigger has loaded, the Triggering window looks just like Figure 6-1; the window no longer says "None", but the trigger is not yet set.
Before you start triggering, you can review the trigger configuration you have loaded. Click on the
button and select Trigger # 0 from the drop-down combo box. This displays the trigger specification and action that you loaded. (See Figure 6-4.) You can see that this sample trigger fires when variable foo takes the value 1. When that condition is met, printf( ) is called with the value helloString.
Once you have created your triggers, either by filling in the Trigger Maintenance dialog box or by loading a trigger configuration file, they must be compiled and downloaded to the target. Clicking the
button compiles and downloads your trigger. The Target ID changes from "not set" to a hexadecimal ID number and the status is "Armed." (See Figure 6-3).
Chaining triggers provides a way to be sure of the order in which your triggers will fire. For instance, to use triggering to collect a WindView log you must define two triggers, one having the action to start WindView log collection and one with the action to stop WindView log collection. In most cases, these triggers are chained since the order of their firing is critical.
To chain triggers, define both triggers using the Trigger Maintenance dialog box. After defining both triggers, open the first one in the Trigger Maintenance dialog box and select the name of the second trigger in the last drop-down combo box.
Triggering provides a powerful mechanism for isolating the sequence that you wish WindView to capture for later analysis. If you can define two triggers to enclose the precise region of interest in your application, you can make the best use of both your system resources and your own time spent in analysis.
The main challenge in defining a trigger to start data collection is isolating an event which precedes the sequence of interest but is as close to the start of the sequence as possible. Examining your application code or collecting a broad log which incorporates the sequence of interest may allow you to identify unique events preceding that sequence that may be used as potential trigger points.
For instance, you may observe that, shortly before the sequence of interest, a semaphore is given. It may be possible to identify the task context from which the semGive( ) is performed and possibly the ID of the semaphore being given. These criteria can be used to define when the trigger which initiates logging fires.
However, this may not be sufficient to uniquely detect the start of the region of interest if this task takes and gives the same semaphore more than once. In this case it will be necessary to further refine the trigger by using a conditional qualification. This may be achieved by examining some variable (by symbol or address) for a specific value or range of values or by invoking a specific function and testing the result.
Note that it is possible to use functions written specifically for this purpose, by making the function available on the target and entering its function name as the condition item. (For a simple example, see Example 6-1.) These functions can also further assist in diagnosis by collecting data such as the value of the system tick count when the trigger fired or some internal value of the target application.
Once you have determined when the trigger will fire, set the action of the trigger to Start WV (start WindView logging). Calling StartWV more than once results in an error, so it is important to disable the trigger that starts logging after it fires. Defining a trigger which references WindView in this way will invoke the WindView tool and request its configuration if this has not already been done.
Although not required, it is often desirable to define a trigger to stop log collection. This focuses the log on the sequence of interest and saves both resources and analysis effort.
Approach defining a trigger to stop log collection in the same way you defined the trigger to start collection: identify an event that will not occur until the sequence of interest has completed. One technique is to write a function that fires after a specified time period, for instance, by comparing the value of the system tick count against its value when the trigger to start the sequence fired.
Once you have defined when the trigger will fire, set the action of the trigger to StopWV (stop WindView logging). If you chain this trigger to the trigger that starts logging, you can ensure that this trigger can not fire before the log collection has been started.
Example 6-1 and Example 6-2 demonstrate loading and running saved trigger configurations.
Example 6-1: Simple Conditional Trigger
To run the trigger you loaded from the file simpleCond.trg (see Loading a Trigger Configuration File), you must download it. However, before downloading the simpleCond trigger, you need to prepare your target. The triggering implementation requires that all the variables and functions referred to in the trigger definition be declared before triggering is started.
Start WindSh and create the variables foo and helloString as follows:
-> foo=0 new symbol "foo" added to symbol table. foo = 0x125980: value = 0 = 0x0 -> helloString="hello\n" new symbol "helloString" added to symbol table. helloString = 0x125970: value = 1202552 = 0x125978 = helloString + 0x8
Before downloading your trigger, open the Trigger Maintenance dialog box and look again at the definition. Note that the Safe box must be checked since a call to printf( ) is not allowed in ISR or system context. See Figure 6-4.
Click the
button. This compiles and downloads your trigger. The Target ID changes from "not set" to a hexadecimal ID number and the status is "Armed." (See Figure 6-3). To fire your trigger, the condition for foo must be met. Enter the following in WindSh:
-> foo = 1 foo = 0x125980: value = 1 = 0x1
The string "hello" prints on the target console (or wherever you are directing target output). Click the
button. The trigger status is updated to show that it has fired.
It is important to note that the following is not valid:
This is because the action function is expecting an integer argument, whereas "hello\n" is a string.
Example 6-2: Chaining Triggers
To see a simple example of chained triggers, click
and load the configuration file installDir/host/src/windview/samples/wv10secs.trg. These chained triggers start and stop WindView. If you have not already configured WindView, the Collection Configuration dialog box opens; configure WindView so it is ready to start when the first trigger fires. If you wish, click
and review the specifications for triggers #0 and #1. Trigger #0 starts WindView logging when vxTicks is greater than 0x1800. Trigger #1 stops WindView logging when vxTicks is greater than 0x1a58.
|
|
|||||||||||||||||||
Click
to download the triggers. Click the
button until the Triggering window shows that both triggers have fired. Click
to stop triggering. If you have WindView configured for deferred upload mode, the Control window shows that there is data in the buffer and the
button is active. If you have WindView configured for continuous upload mode, there is a new view graph displayed.
Example 6-3, Example 6-4, and Example 6-5 demonstrate how conditional triggers are evaluated and highlight issues in creating conditional triggers. Because <operand2> is interpreted as a numeric constant, you may generate unexpected results by entering a symbol in this box.
Suppose we have the following identifiers in the symbol table:
Specifying the conditional as foo2 has a less obvious result than Example 6-3. As in that example, the left-hand side evaluates to the current value of foo1,which is 30 decimal. Since the "foo1" is not numeric, the system performs a symbol table lookup every time the conditional clause is evaluated to find the identifier foo1 and resolve it to an address.
The symbol table is searched for foo2 and 0x0a001004 is obtained. Because <operand 2> is interpreted as a numeric constant, not the address of a variable, the value 0x0a001004 is used for the comparison.
The comparison therefore resolves to 30 >= 0x0a001004 which is clearly not satisfied at this time. Thus, if you were expecting the expression to be evaluated as 30 >= 20, your trigger would not fire when expected.
Specifying the conditional as shown is similar to Example 6-4, except that we treat the identifier pFoo a pointer. As before a symbol table lookup is performed for both sides of the operator. pFoo yields address 0x0a001008 and is de-referenced to value 0x0a001000. foo1 yields 0x0a001000, which is treated as an integer constant. The comparison therefore resolves to 0x0a001000 == 0x0a001000 and the condition is satisfied. If you expected the expression to be evaluated as 0x0a001000 == 30, your trigger would have fired unexpectedly.
Example 6-6 shows a highly simplified situation which allows you to experiment with starting and stopping WindView using the triggering facility.
|
|
|||||||||||||||||||
% trgEvent(40010)
The Control window will show that WindView logging has started. If necessary, click
to update the Control window.
% trgEvent(40020)
Example 6-7 shows how to write an action function for your trigger.
Example 6-7: Specifying an Action with a Trigger
Example 6-1 showed an example of a very simple action resulting from a conditional trigger. The same result could have been obtained by writing an action function. While this would not be an efficient solution in this simple case, the function would allow you to specify additional processing if it were needed at this point. A simple action function could be written as follows:
int myActionFunc (int parm)
{
printf ("Trigger fired\n");
/* any other code you desire */
return 1; /* return value is ignored */
}
Compile and download the code onto the target. In the Action section of the Trigger Maintenance window, enter:
|
|
|||||||||||||||||||
Triggering is a flexible diagnostic tool in its own right. It has applications that extend beyond starting and stopping WindView logging. The methods described in previous sections can serve as building blocks for more complex actions by specifying a function call as the trigger action.