7

Shell



7.1    Introduction

The Tornado shell, WindSh, allows you to download application modules, and to invoke both VxWorks and application module subroutines. This facility has many uses: interactive exploration of the VxWorks operating system, prototyping, interactive development, and testing.

WindSh can interpret most C language expressions; it can execute most C operators and resolve symbolic data references and subroutine invocations. You can also interact with the shell through a Tcl interpreter, which provides a full set of control structures and lower-level access to target facilities. For a more detailed explanation of the Tcl interface, see 7.7 Tcl: Shell Interpretation.

WindSh executes on the development host, not the target, but it allows you to spawn tasks, to read from or write to target devices, and to exert full control of the target.1 Because the shell executes on the host system, you can use it with minimal intrusion on target resources. As with other Tornado tools, only the target agent is required on the target system. Thus, the shell can remain always available; you can use it to maintain a production system if appropriate as well as for experimentation and testing during development.

Shell operation involves three components of the Tornado system, as shown in Figure 7-1.

  • The shell is where you directly exercise control; it receives your commands and executes them locally on the host, dispatching requests to the target server for any action involving the symbol table or target-resident programs or data.

  • The target server manages the symbol table and handles all communications with the remote target, dispatching function calls and sending their results back as needed. (The symbol table itself resides entirely on the host, although the addresses it contains refer to the target system.)

  • The target agent is the only component that runs on the target; it is a minimal monitor program that mediates access to target memory and other facilities.

Figure 7-1:   Tornado and the Shell

The shell has a dual role:

  • It acts as a command interpreter that provides access to all VxWorks facilities by allowing you to call any VxWorks routine.

  • It can be used as a prototyping and debugging tool for the application developer. You can run application modules interactively by calling any application routine. The shell provides notification of any hardware exceptions. See System Modification and Debugging, for information about downloading application modules.

The capabilities of WindSh include the following:

  • task-specific breakpoints
  • task-specific single-stepping
  • symbolic disassembler
  • task and system information utilities
  • ability to call user routines
  • ability to create and examine variables symbolically
  • ability to examine and modify memory
  • exception trapping


7.2    Using the Shell

The shell reads lines of input from an input stream, parses and evaluates each line, and writes the result of the evaluation to an output stream. With its default C-expression interpreter, the shell accepts the same expression syntax as a C compiler with only a few variations.

The following sections explain how to start and stop the shell and provide examples illustrating some typical uses of the shell's C interpreter. In the examples, the default shell prompt for interactive input in C is "->". User input is shown in bold face and shell responses are shown in a plain roman face.

7.2.1   Starting and Stopping the Tornado Shell

There are three ways to start a Tornado shell:

  • From the Tornado Launch toolbar: Click the button. This launches a shell for the currently selected target server (see Tornado Launch Toolbar).

  • From the Tools menu: Click on Shell. The dialog box shown in Figure 7-2 appears, which allows you to select a target server from the Targets drop-down list.

Figure 7-2:   Shell Target-Selection Dialog Box

  • From the Windows command prompt: Invoke windsh, specifying the target-server name, as in the following example:2

C:\> windsh phobos 

If you start a Tornado shell from the IDE, a shell window like the one shown in Figure 7-3 appears, with the arrow prompt (->). If you start a shell from the Windows command prompt, WindSh executes in the environment where you call it, using the command-prompt window.

Figure 7-3:   Shell Initial Display

Regardless of how you start it, you can terminate a Tornado shell session by executing the exit( ) or the quit( ) command or by typing CTRL+D. If the shell is not accepting input (for instance, if it loses the connection to the target server) you can use the interrupt key (CTRL+BREAK).

You may run as many different shells attached to the same target as you wish. All functions called from a shell have their output redirected to the WindSh window from which they received input unless you changed the shell defaults using shConfig (see WindSh Environment Variables).

7.2.2   Downloading From the Shell

One of the most useful shell features for interactive development is the dynamic linker. With the shell command ld( ), you can download and link new portions of the application.

-> ld < /home/moduleDir/module.o 

Because the linking is dynamic, you only have to rebuild the particular piece you are working on, not the entire application. Download can be cancelled with CTRL+C or by clicking Cancel in the load progress indicator window. The dynamic linker is discussed further in VxWorks Programmer's Guide: Configuration and Build.

The WTX error (0x10197) EXCHANGE_TIMEOUT may occur when a WTX request keeps the target server busy longer than 30 seconds (default timeout). This may happen when loading a large object module. A Tcl procedure is available to change the default timeout. From WindSh use wtxTimeout sec where sec is the number of seconds before timeout:

-> ?wtxTimeout 120

7.2.3   Shell Features

The shell provides many features which simplify your development and testing activities. These include command name and path completion, command and function synopsis printing, automatic data conversion, calculation with most C operators and variables, and help on all shell and VxWorks functions.

I/O Redirection

Developers often call routines that display data on standard output or accept data from standard input. By default the standard output and input streams are directed to the same window as the Tornado shell. For example, in a default configuration of Tornado, invoking printf( ) from the shell window gives the following display:

-> printf("Hello World\n") 
Hello World! 
value = 13 = 0xd 
-> 

This behavior can be dynamically modified using the Tcl procedure shConfig as follows:

-> ?shConfig SH_GET_TASK_IO off 
-> 
-> printf("Hello World!\n") 
value = 13 = 0xd 
->

The shell reports the printf( ) result, indicating that 13 characters have been printed. The output, however, goes to the target's standard output, not to the shell.

To determine the current configuration, use shConfig. If you issue the command without an argument, all parameters are listed. Use an argument to list only one parameter.

-> ?shConfig SH_GET_TASK_IO 
SH_GET_TASK_IO = off

For more information on shConfig, see WindSh Environment Variables.

The standard input and output are only redirected for the function called from WindSh. If this function spawns other tasks, the input and output of the spawned tasks are not redirected to WindSh. To have all IO redirected to WindSh, you can start the target server with the options -C -redirectShell.

Target Symbol and Path Completion

Start to type any target symbol name or any existing directory name and then type CTRL+D. The shell automatically completes the command or directory name for you. If there are multiple options, it prints them for you and then reprints your entry. For example, entering an ambiguous request generates the following result:

-> C:\Tor [CTRL+D] 
Tornado/ TorClass/ 
-> C:\Tor

You can add one or more letters and then type CTRL+D again until the path or symbol is complete.

Synopsis Printing

Once you have typed the complete function name, typing CTRL+D again prints the function synopsis and then reprints the function name ready for your input:

-> _taskIdDefault [CTRL+D] 
taskIdDefault() - set the default task ID (WindSh) 
 
int taskIdDefault 
    ( 
    int tid     /* user-supplied task ID; if 0, return default */ 
    ) 
 
-> _taskIdDefault

If the routine exists on both host and target, the WindSh synopsis is printed. To print the target synopsis of a function add the meta-character @ before the function name.

You can extend the synopsis printing function to include your own routines. To do this, follow these steps:

  1. Create the files that include the new routines following Wind River Coding Conventions. (See the VxWorks Programmer's Guide: Coding Conventions.)
  1. Include these files in your project. (See Creating, Adding, and Removing Application Files.)
  1. Add the file names to the DOC_FILES macro in your makefile.
  1. Go to the top of your project tree and run "make synopsis":
-> cd $WIND_BASE/target/src/projectX 
-> make synopsis

This adds a file projectX to the host/resource/synopsis directory.

HTML Help

Typing any function name, a space, and CTRL+W opens a browser and displays the HTML reference page for the function. Be sure to leave a space after the function name.

-> i [CTRL+W]

or

-> @i [CTRL+W]

Typing CTRL+W without any function name launches the HTML help tool in a new browser window.

Typing CTRL+W without a typing a space after the function name launches the HTML help tool if the function name is unique. If not, CTRL+W acts as CTRL+D and returns a list of functions whose names begin with the string you entered.

Data Conversion

The shell prints all integers and characters in both decimal and hexadecimal, and if possible, as a character constant or a symbolic address and offset.

-> 68 
value = 68 = 0x44 = 'D'
-> 0xf5de 
value = 62942 = 0xf5de = _init + 0x52
-> 's' 
value = 115 = 0x73 = 's'

Data Calculation

Almost all C operators can be used for data calculation. Use "(" and ")" to force order of precedence.

-> (14 * 9) / 3 
value = 42 = 0x2a = '*'
-> (0x1355 << 3) & 0x0f0f 
value = 2568 = 0xa08
-> 4.3 * 5 
value = 21.5

Calculations With Variables

-> (j + k) * 3 
value = ...
-> *(j + 8 * k) 
(address (j + 8 * k)): value = 
-> x = (val1 - val2) / val3 
new symbol "x" added to symbol table 
address =  
value = 
-> f = 1.41 * 2 
new symbol "f" added to symbol table 
f = (address of f): value = 2.82

Variable f gets an 8-byte floating point value.

-> ddd=5.2 
new symbol "ddd" added to symbol table. 
ddd = 0xba0e2c: value = 5.2
-> eee=10.5 
new symbol "eee" added to symbol table. 
eee = 0xba0e24: value = 10.5
-> fff=(double)ddd+(double)eee 
new symbol "fff" added to symbol table. 
fff = 0xba0e1c: value = 15.7 
-> 

WindSh Environment Variables

WindSh allows you to change the behavior of a particular shell session by setting the environment variables listed in Table 7-1. The Tcl procedure shConfig allows you to display and set how I/O redirection, C++ constructors and destructors, loading, and the load path are defined and handled by the shell. 

Table 7-1:   WindSh Environment Variables


Variable
Result

SH_GET_TASK_IO
Sets the I/O redirection mode for called functions. The default is "on", which redirects input and output of called functions to WindSh. To have input and output of called functions appear in the target console, set SH_GET_TASK_IO to "off."
LD_CALL_XTORS
Sets the C++ strategy related to constructors and destructors. The default is "target", which causes WindSh to use the value set on the target using cplusXtorSet( ). If LD_CALL_XTORS is set to "on", the C++ strategy is set to automatic (for the current WindSh only). "Off" sets the C++ strategy to manual for the current shell.
LD_SEND_MODULES
Sets the load mode. The default "on" causes modules to be transferred to the target server. This means that any module WindSh can see can be loaded. If LD_SEND_MODULES if "off", the target server must be able to see the module to load it.
LD_PATH
Sets the search path for modules using the separator ";". When a ld( ) command is issued, WindSh first searches the current directory and loads the module if it finds it. If not, WindSh searches the directory path for the module.
LD_COMMON_MATCH_ALL
Sets the loader behavior for common symbols. If it is set to on, the loader tries to match a common symbol with an existing one. If a symbol with the same name is already defined, the loader take its address. Otherwise, the loader creates a new entry. If set to off, the loader does not try to find an existing symbol. It creates an entry for each common symbol.
DSM_HEX_MOD
Sets the disassembling "symbolic + offset" mode. When set to "off" the "symbolic + offset" address representation is turned on and addresses inside the disassembled instructions are given in terms of "symbol name + offset." When set to "on" these addresses are given in hexadecimal.

Because shConfig is a Tcl procedure, use the ? to move from the C interpreter to the Tcl interpreter. (See 7.7.2 Tcl: Calling under C Control.)

Example 7-1:   Using shConfig to Modify WindSh Behavior

-> ?shConfig 
SH_GET_TASK_IO = on 
LD_CALL_XTORS = target 
LD_SEND_MODULES = on 
LD_PATH = C:/ProjectX/lib/objR4650gnutest/;C:/ProjectY/lib/objR4560gnuvx 
-> ?shConfig LD_CALL_XTORS on 
-> ?shConfig LD_CALL_XTORS 
LD_CALL_XTORS = on

7.2.4   Invoking Built-In Shell Routines

Some of the commands (or routines) that you can execute from the shell are built into the host shell, rather than running as function calls on the target. These facilities parallel interactive utilities that can be linked into VxWorks itself. By using the host commands, you minimize the impact on both target memory and performance.

The following sections give summaries of the Tornado WindSh commands. For more detailed reference information, see the windsh reference entry (either online, or in ).


*      
WARNING: Most of the shell commands correspond to similar routines that can be linked into VxWorks for use with the target-resident version of the shell (VxWorks Programmer's Guide: Target Shell). However, the target-resident routines differ in some details. For reference information on a shell command, be sure to consult the windsh entry in the online Tornado Tools Reference. Although there is usually an entry with the same name in the VxWorks API Reference, it describes a related target routine, not the shell command.

Task Management

Table 7-2 summarizes the WindSh commands that manage VxWorks tasks.

Table 7-2:   WindSh Commands for Task Management 


Call
Description

sp( )
Spawn a task with default parameters.
sps( )
Spawn a task, but leave it suspended.
tr( )
Resume a suspended task.
ts( )
Suspend a task.
td( )
Delete a task.
period( )
Spawn a task to call a function periodically.
repeat( )
Spawn a task to call a function repeatedly.
taskIdDefault( )
Set or report the default (current) task ID (see 7.3.4 The "Current" Task and Address for a discussion of how the current task is established and used).

The repeat( ) and period( ) commands spawn tasks whose entry points are _repeatHost and _periodHost. The shell downloads these support routines when you call repeat( ) or period( ). (With remote target servers, that download sometimes fails; for a discussion of when this is possible, and what you can do about it, see 7.6 Object Module Load Path.) These tasks may be controlled like any other tasks on the target; for example, you can suspend or delete them with ts( ) or td( ) respectively.

Task Information

Table 7-3 summarizes the WindSh commands that report task information.

Table 7-3:   WindSh Commands for Task Information 


Call
Description

i( )
Display system information. This command gives a snapshot of what tasks are in the system, and some information about each of them, such as state, PC, SP, and TCB address. To save memory, this command queries the target repeatedly; thus, it may occasionally give an inconsistent snapshot.
iStrict( )
Display the same information as i( ), but query target system information only once. At the expense of consuming more intermediate memory, this guarantees an accurate snapshot.
ti( )
Display task information. This command gives all the information contained in a task's TCB. This includes everything shown for that task by an i( ) command, plus all the task's registers, and the links in the TCB chain. If task is 0 (or the argument is omitted), the current task is reported on.
w( )
Print a summary of each task's pending information, task by task. This routine calls taskWaitShow( ) in quiet mode on all tasks in the system, or a specified task if the argument is given.
tw( )
Print information about the object the given task is pending on. This routine calls taskWaitShow( ) on the given task in verbose mode.
checkStack( )
Show a stack usage summary for a task, or for all tasks if no task is specified. The summary includes the total stack size (SIZE), the current number of stack bytes (CUR), the maximum number of stack bytes used (HIGH), and the number of bytes never used at the top of the stack (MARGIN = SIZE - HIGH). Use this routine to determine how much stack space to allocate, and to detect stack overflow. This routine does not work for tasks that use the VX_NO_STACK_FILL option.
tt( )
Display a stack trace.
taskIdFigure( )
Report a task ID, given its name.

The i( ) command is commonly used to get a quick report on target activity. (To see this information periodically, use the Tornado browser; see 9. Browser). If nothing seems to be happening, i( ) is often a good place to start investigating. To display summary information about all running tasks:

-> i 
  NAME       ENTRY      TID    PRI   STATUS    PC      SP     ERRNO  DELAY 
--------- ----------- -------- --- -------- ------- -------- ------- ----- 
tExcTask  _excTask      3ad290   0 PEND       4df10   3ad0c0       0     0 
tLogTask  _logTask      3aa918   0 PEND       4df10   3aa748       0     0 
tWdbTask  0x41288       3870f0   3 READY      23ff4   386d78  3d0004     0 
tNetTask  _netTask      3a59c0  50 READY      24200   3a5730       0     0 
tFtpdTask _ftpdTask     3a2c18  55 PEND       23b28   3a2938       0     0 
value = 0 = 0x0

The w( ) and tw( ) commands allow you to see what object a VxWorks task is pending on. w( ) displays summary information for all tasks, while tw( ) displays object information for a specific task. Note that the OBJ_NAME field is used only for objects that have a symbolic name associated with the address of their structure.

-> w 
 
NAME       ENTRY     TID       STATUS    DELAY OBJ_TYPE   OBJ_ID   OBJ_NAME 
---------- ---------- -------- --------- ----- ---------- -------- ---------- 
tExcTask   _excTask   3d9e3c   PEND          0 MSG_Q(R)   3d9ff4   N/A 
tLogTask   _logTask   3d7510   PEND          0 MSG_Q(R)   3d76c8   N/A 
tWdbTask   _wdbCmdLoo 36dde4   READY         0                 0 
tNetTask   _netTask   3a43d0   READY         0                 0 
u0         _smtask1   36cc2c   PEND          0 MSG_Q_S(S) 370b61   N/A 
u1         _smtask3   367c54   PEND          0 MSG_Q_S(S) 370b61   N/A 
u3         _taskB     362c7c   PEND          0 SEM_B       8d378   _mySem2 
u6         _smtask1   35dca4   PEND          0 MSG_Q_S(S) 370ae1   N/A 
u9         _task3B    358ccc   PEND          0 MSG_Q(S)    8cf1c   _myMsgQ 
value = 0 = 0x0 
->  
-> tw u1 
 
NAME       ENTRY     TID       STATUS    DELAY OBJ_TYPE   OBJ_ID   OBJ_NAME 
---------- ---------- -------- --------- ----- ---------- -------- ---------- 
u1         _smtask3   367c54   PEND          0 MSG_Q_S(S) 370b61   N/A 
 
Message Queue Id   : 0x370b61 
Task Queueing      : SHARED_FIFO 
Message Byte Len   : 100 
Messages Max       : 0 
Messages Queued    : 0 
Senders Blocked    : 2 
Send Timeouts      : 0 
Receive Timeouts   : 0 
 
Senders Blocked: 
TID        CPU Number Shared TCB 
---------- ---------- ---------- 
0x36cc2c       0      0x36e464 
0x367c54       0      0x36e47c 
 
value = 0 = 0x0 
->

System Information

Table 7-4 shows the WindSh commands that display information from the symbol table, from the target system, and from the shell itself.

Table 7-4:   WindSh Commands for System Information 


Call
Description

devs( )
List all devices known on the target system.
lkup( )
List symbols from symbol table.
lkAddr( )
List symbols whose values are near a specified value.
d( )
Display target memory. You can specify a starting address, size of memory units, and number of units to display.
l( )
Disassemble and display a specified number of instructions.
printErrno( )
Describe the most recent error status value.
version( )
Print VxWorks version information.
cd( )
Change the host working directory (no effect on target).
ls( )
List files in host working directory.
pwd( )
Display the current host working directory.
help( )
Display a summary of selected shell commands.
h( )
Display up to 20 lines of command history.
shellHistory( )
Set or display shell history.
shellPromptSet( )
Change the C-interpreter shell prompt.
printLogo( )
Display the Tornado shell logo.

The lkup( ) command takes a regular expression as its argument, and looks up all symbols containing strings that match. In the simplest case, you can specify a substring to see any symbols containing that string. For example, to display a list containing routines and declared variables with names containing the string dsm, do the following:

-> lkup "dsm" 
_dsmData                  0x00049d08 text     (vxWorks) 
_dsmNbytes                0x00049d76 text     (vxWorks) 
_dsmInst                  0x00049d28 text     (vxWorks) 
mydsm                    0x003c6510 bss     (vxWorks)

Case is significant, but position is not (mydsm is shown, but myDsm would not be). To explicitly write a search that would match either mydsm or myDsm, you could write the following:

-> lkup "[dD]sm"

Regular-expression searches of the symbol table can be as simple or elaborate as required. For example, the following simple regular expression displays the names of three internal VxWorks semaphore functions:

-> lkup "sem.Take" 
_semBTake                 0x0002aeec text     (vxWorks) 
_semCTake                 0x0002b268 text     (vxWorks) 
_semMTake                 0x0002bc48 text     (vxWorks) 
value = 0 = 0x0

Another information command is a symbolic disassembler, l( ). The command syntax is:

l [adr[, n]]

This command lists n disassembled instructions, starting at adr. If n is 0 or not given, the n from a previous l( ) or the default value (10) is used. If adr is 0, l( ) starts from where the previous l( ) stopped, or from where an exception occurred (if there was an exception trap or a breakpoint since the last l( ) command).

The disassembler uses any symbols that are in the symbol table. If an instruction whose address corresponds to a symbol is disassembled (the beginning of a routine, for instance), the symbol is shown as a label in the address field. Symbols are also used in the operand field. The following is an example of disassembled code for an MC680x0 target:

-> l printf 
_printf
    00033bce  4856                     PEA         (A6) 
    00033bd0  2c4f                     MOVEA .L    A7,A6 
    00033bd2  4878 0001                PEA         0x1 
    00033bd6  4879 0003 460e           PEA         _fioFormatV + 0x780 
    00033bdc  486e 000c                PEA         (0xc,A6) 
    00033be0  2f2e 0008                MOVE  .L    (0x8,A6),-(A7) 
    00033be4  6100 02a8                BSR         _fioFormatV 
    00033be8  4e5e                     UNLK         A6 
    00033bea  4e75                    RTS

This example shows the printf( ) routine. The routine does a LINK, then pushes the value of std_out onto the stack and calls the routine fioFormatV( ). Notice that symbols defined in C (routine and variable names) are prefixed with an underbar ( _ ) by the compiler.

Perhaps the most frequently used system information command is d( ), which displays a block of memory starting at the address which is passed to it as a parameter. As with any other routine that requires an address, the starting address can be a number, the name of a variable or routine, or the result of an expression.

Several examples of variations on d( ) appear below.

Display starting at address 1000 decimal:

-> d (1000)

Display starting at 1000 hex:

-> d 0x1000

Display starting at the address contained in the variable dog:

-> d dog

The above is different from a display starting at the address of dog. For example, if dog is a variable at location 0x1234, and that memory location contains the value 10000, d( ) displays starting at 10000 in the previous example and at 0x1234 in the following:

-> d &dog

Display starting at an offset from the value of dog:

-> d dog + 100

Display starting at the result of a function call:

-> d func (dog)

Display the code of func( ) as a simple hex memory dump:

-> d func 

When you use cd( ) in the host shell, you are changing the working directory on the host. It does not change the directory on the target. WindSh has no knowledge of the target file system. Thus if you mount a drive on the target from the host shell and try to cd( ) to it, you see the following:

-> cd "/ata0/"  
couldn't change working directory to "\ata0": no such file or directory  
value = -1 = 0xffffffff

However, the result is different if you execute cd( ) and ls( ) on the target by prefixing the commands with @:

-> @cd "/ata0/"  
value = 0 = 0x0 
-> @ls  
IO.SYS  
MSDOS.SYS  
DRVSPACE.BIN

@cd and @ls only work if you have the component INCLUDE_DISK_UTIL included in your target image.

The above also applies if you wish to use the target server file system (TSFS) from WindSh: cd "/tgtsvr" does not work but @cd "/tgtsvr" does. To use TSFS you must have the TSFS component, INCLUDE_WDB_TSFS, installed in VxWorks and start the target server with the "-R dirName" or "-R dirName -RW" option.

System Modification and Debugging

Developers often need to change the state of the target, whether to run a new version of some software module, to patch memory, or simply to single-step a program. Table 7-5 summarizes the WindSh commands of this type.

Table 7-5:   WindSh Commands for System Modification and Debugging 


Call
Description

ld( )
Load an object module into target memory and link it dynamically into the run-time.
unld( )
Remove a dynamically-linked object module from target memory, and free the storage it occupied.
m( )
Modify memory in width (byte, short, or long) starting at adr. The m( ) command displays successive words in memory on the terminal; you can change each word by typing a new hex value, leave the word unchanged and continue by typing ENTER, or return to the shell by typing a dot (.).
mRegs( )
Modify register values for a particular task.
b( )
Set or display breakpoints, in a specified task or in all tasks.
bh( )
Set a hardware breakpoint.
s( )
Step a program to the next instruction.
so( )
Single-step, but step over a subroutine.
c( )
Continue from a breakpoint.
cret( )
Continue until the current subroutine returns.
bdall( )
Delete all breakpoints.
bd( )
Delete a breakpoint.
reboot( )
Return target control to the target boot ROMs, then reset the target server and reattach the shell.
bootChange( )
Modify the saved values of boot parameters (see 2.5.4 Description of Boot Parameters).
sysSuspend( )
If supported by the target-agent configuration, enter system mode. See 7.2.7 Using the Shell for System Mode Debugging.
sysResume( )
If supported by the target agent (and if system mode is in effect), return to task mode from system mode.
agentModeShow( )
Show the agent mode (system or task).
sysStatusShow( )
Show the system context status (suspended or running).
quit( ) or exit( )
Dismiss the shell.

The m( ) command provides an interactive way of manipulating target memory.

The remaining commands in this group are for breakpoints and single-stepping. You can set a breakpoint at any instruction. When that instruction is executed by an eligible task (as specified with the b( ) command), the task that was executing on the target suspends, and a message appears at the shell. At this point, you can examine the task's registers, do a task trace, and so on. The task can then be deleted, continued, or single-stepped.

If a routine called from the shell encounters a breakpoint, it suspends just as any other routine would, but in order to allow you to regain control of the shell, such suspended routines are treated in the shell as though they had returned 0. The suspended routine is nevertheless available for your inspection.

When you use s( ) to single-step a task, the task executes one machine instruction, then suspends again. The shell display shows all the task registers and the next instruction to be executed by the task.

You can use the bh( ) command to set hardware breakpoints at any instruction or data element. Instruction hardware breakpoints can be useful to debug code running in ROM or Flash EPROM. Data hardware breakpoints (watchpoints) are useful if you want to stop when your program accesses a specific address. Hardware breakpoints are available on some BSPs; see your BSP documentation to determine if they are supported for your BSP. The arguments of the bh( ) command are architecture specific. For more information, run the help( ) command. The number of hardware breakpoints you can set is limited by the hardware; if you exceed the maximum number, you will receive an error.

C++ Development

Certain WindSh commands are intended specifically for work with C++ applications. Table 7-6 summarizes these commands. For more discussion of these shell commands, see VxWorks Programmer's Guide: C++ Development.

Table 7-6:   WindSh Commands for C++ Development


Call
Description

cplusCtors( )
Call static constructors manually.
cplusDtors( )
Call static destructors manually.
cplusStratShow( )
Report on whether current constructor/destructor strategy is manual or automatic.
cplusXtorSet( )
Set constructor/destructor strategy.

In addition, you can use the Tcl routine shConfig to set the environment variable LD_CALL_XTORS within a particular shell. This allows you to use a different C++ strategy in a shell than is used on the target. For more information on shConfig, see WindSh Environment Variables.

Object Display

Table 7-7 summarizes the WindSh commands that display VxWorks objects. The browser provides displays that are analogous to the output of many of these routines, except that browser windows can update their contents periodically; see 9. Browser.

Table 7-7:   WindSh Commands for Object Display 


Call
Description

show( )
Print information on a specified object in the shell window.
browse( )
Display a specified object in the Tornado browser.
classShow( )
Show information about a class of VxWorks kernel objects. List available classes with:
    -> lkup "ClassId"
taskShow( )
Display information from a task's TCB.
taskCreateHookShow( )
Show the list of task create routines.
taskDeleteHookShow( )
Show the list of task delete routines.
taskRegsShow( )
Display the contents of a task's registers.
taskSwitchHookShow( )
Show the list of task switch routines.
taskWaitShow( )
Show information about the object a task is pended on. Note that taskWaitShow( ) cannot give object IDs for POSIX semaphores or message queues.
semShow( )
Show information about a semaphore.
semPxShow( )
Show information about a POSIX semaphore.
wdShow( )
Show information about a watchdog timer.
msgQShow( )
Show information about a message queue.
mqPxShow( )
Show information about a POSIX message queue.
iosDrvShow( )
Display a list of system drivers.
iosDevShow( )
Display the list of devices in the system.
iosFdShow( )
Display a list of file descriptor names in the system.
memPartShow( )
Show partition blocks and statistics.
memShow( )
Display the total amount of free and allocated space in the system partition, the number of free and allocated fragments, the average free and allocated fragment sizes, and the maximum free fragment size. Show current as well as cumulative values. With an argument of 1, also display the free list of the system partition.
smMemShow( )
Display the amount of free space and statistics on memory-block allocation for the shared-memory system partition.
smMemPartShow( )
Display the amount of free space and statistics on memory-block allocation for a specified shared-memory partition.
moduleShow( )
Show the current status for all the loaded modules.
moduleIdFigure( )
Report a loaded module's module ID, given its name.
intVecShow( )
Display the interrupt vector table. This routine displays information about the given vector or the whole interrupt vector table if vector is equal to -1. Note that intVecShow( ) is not supported on architectures such as ARM and PowerPC that do not use interrupt vectors.

Network Status Display

Table 7-8 summarizes the WindSh commands that display information about the VxWorks network.

Table 7-8:   WindSh Commands for Network Status Display


Call
Description

hostShow( )
Display the host table.
icmpstatShow( )
Display statistics for ICMP (Internet Control Message Protocol).
ifShow( )
Display the attached network interfaces.
inetstatShow( )
Display all active connections for Internet protocol sockets.
ipstatShow( )
Display IP statistics.
routestatShow( )
Display routing statistics.
tcpstatShow( )
Display all statistics for the TCP protocol.
tftpInfoShow( )
Get TFTP status information.
udpstatShow( )
Display statistics for the UDP protocol.

In order for a protocol-specific command to work, the appropriate protocol must be included in your VxWorks configuration.

Resolving Name Conflicts between Host and Target

If you invoke a name that stands for a host shell command, the shell always invokes that command, even if there is also a target routine with the same name. Thus, for example, i( ) always runs on the host, regardless of whether you have the VxWorks routine of the same name linked into your target.

However, you may occasionally need to call a target routine that has the same name as a host shell command. The shell supports a convention allowing you to make this choice: use the single-character prefix @ to identify the target version of any routine. For example, to run a target routine named i( ), invoke it with the name @i( ).

7.2.5   Running Target Routines from the Shell

All target routines are available from WindSh. This includes both VxWorks routines and your application routines. Thus the shell provides a powerful tool for testing and debugging your applications using all the host resources while having minimal impact on how the target performs and how the application behaves.

Invocations of VxWorks Subroutines
-> taskSpawn ("tmyTask", 10, 0, 1000, myTask, fd1, 300) 
value = 
-> fd = open ("file", 0, 0) 
new symbol "fd" added to symbol table 
fd = (address of fd): value = 
Invocations of Application Subroutines
-> testFunc (123) 
value = 
-> myValue = myFunc (1, &val, testFunc (123)) 
myValue = (address of myValue): value = 
-> myDouble = (double ()) myFuncWhichReturnsADouble (x) 
myDouble = (address of myDouble): value = 

For situations where the result of a routine is something other than a 4-byte integer, see Function Calls.

7.2.6   Rebooting from the Shell

In an interactive real-time development session, it is sometimes convenient to restart everything to make sure the target is in a known state. WindSh provides the reboot( ) command or CTRL+SHIFT+X to make this easy.

When you execute reboot( ) or type CTRL+SHIFT+X, the following reboot sequence occurs:

  1. The shell displays a message to confirm rebooting has begun:
-> reboot 
Rebooting...
  1. The target reboots.
  1. The original target server on the host detects the target reboot and restarts itself, with the same configuration as previously. The target-server configuration options -Bt (timeout) and -Br (retries) govern how long the new server waits for the target to reboot, and how many times the new server attempts to reconnect; see the tgtsvr reference entry in the online Tornado Tools Reference.
  1. The shell detects the target-server restart and begins an automatic-restart sequence (initiated any time it loses contact with the target server for any reason), indicated with the following messages:
Target connection has been lost.  Restarting shell... 
Waiting to attach to target server......
  1. When WindSh establishes contact with the new target server, it displays the Tornado shell logo and awaits your input.


*      
CAUTION: If the target server timeout (-Bt) and retry count (-Br) are too low for your target and your connection method, the new target server may abandon execution before the target finishes rebooting. The default timeout is one second, and the default retry count is three; thus, by default the target server waits three seconds for the target to reboot. If the shell does not restart in a reasonably short time after a reboot( ), try starting a new target server manually.

7.2.7   Using the Shell for System Mode Debugging

The bulk of this chapter discusses the shell in its most frequent style of use: attached to a normally running VxWorks system, through a target agent running in task mode. You can also use the shell with a system-mode agent. Entering system mode stops the entire target system: all tasks, the kernel, and all ISRs. Similarly, breakpoints affect all tasks.


*      
CAUTION: When you use system mode debugging, you cannot execute expressions that call target-resident routines. You must use sp( ) to spawn a task with the target-resident routine as the entry point argument. A newly-spawned task will not execute until you allow the kernel to run long enough to schedule that task.

Depending on how the target agent is configured, you may be able to switch between system mode and task mode; see 4.7 Configuring the Target-Host Communication Interface. When the agent supports mode switching, the following WindSh commands control system mode:

sysSuspend( )

sysResume( )

The following commands are to determine the state of the system and the agent:

agentModeShow( )

sysStatusShow( )

The following shell commands behave differently in system mode:

b( )

c( )


*      
WARNING: If you are running either CrossWind or Look! you must not use c( ) from the shell to continue; instead continue from the debugger itself. Using c( ) from the shell when the debugger is running will confuse the debugger.

i( )

s( )

sp( )

The following example shows how to use system mode debugging to debug a system interrupt.

Example 7-2:   System-Mode Debugging

In this case, usrClock( ) is attached to the system clock interrupt handler which is called at each system clock tick when VxWorks is running. First suspend the system and confirm that it is suspended using either i( ) or sysStatusShow( ).

-> sysSuspend 
value = 0 = 0x0 
-> 
-> i 
NAME      ENTRY      TID      PRI   STATUS  PC      SP      ERRNO  DELAY 
--------- ---------- -------- ----- ------- ------- ------- -----  ----- 
tExcTask  _excTask   3e8f98   0     PEND    47982   3e8ef4  0      0 
tLogTask  _logTask   3e6670   0     PEND    47982   3e65c8  0      0 
tWdbTask  0x3f024    398e04   3     PEND    405ac   398d50  30067  0 
tNetTask  _netTask   3b39e0   50    PEND    405ac   3b3988  0      0 
 
Agent mode     : Extern 
System context : Suspended 
value = 0 = 0x0 
->  
-> sysStatusShow 
System context is suspended 
value = 0 = 0x0

Next, set the system mode breakpoint on the entry point of the interrupt handler you want to debug. Since the target agent is running in system mode, the breakpoint will automatically be a system mode breakpoint, which you can confirm with the b( ) command. Resume the system using c( ) and wait for it to enter the interrupt handler and hit the breakpoint.

-> b usrClock 
value = 0 = 0x0 
-> b 
0x00022d9a: _usrClock          Task:     SYSTEM Count:  0 
value = 0 = 0x0 
-> c 
value = 0 = 0x0 
->  
Break at 0x00022d9a: _usrClock               Task: SYSTEM

You can now debug the interrupt handler. For example, you can determine which task was running when system mode was entered using taskIdCurrent( ) and i( ).

-> taskIdCurrent 
_taskIdCurrent = 0x838d0: value = 3880092 = 0x3b349c 
-> i 
NAME      ENTRY      TID      PRI   STATUS  PC      SP      ERRNO  DELAY 
--------- ---------- -------- ----- ------- ------- ------- -----  ----- 
tExcTask  _excTask   3e8a54   0     PEND    4eb8c   3e89b4  0      0 
tLogTask  _logTask   3e612c   0     PEND    4eb8c   3e6088  0      0 
tWdbTask  0x44d54    389774   3     PEND    46cb6   3896c0  0      0 
tNetTask  _netTask   3b349c   50    READY   46cb6   3b3444  0      0 
 
Agent mode     : Extern 
System context : Suspended 
value = 0 = 0x0

You can trace all the tasks except the one that was running when you placed the system in system mode and you can step through the interrupt handler.

-> tt tLogTask 
4da78   _vxTaskEntry   +10 : _logTask (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 
3f2bc   _logTask       +18 : _msgQReceive (3e62e4, 3e60dc, 20, ffffffff) 
27e64   _msgQReceive   +1ba: _qJobGet ([3e62e8, ffffffff, 0, 0, 0, 0]) 
value = 0 = 0x0 
-> l 
_usrClock 
00022d9a  4856                     PEA         (A6) 
00022d9c  2c4f                     MOVEA .L    A7,A6 
00022d9e  61ff 0002 3d8c           BSR         _tickAnnounce 
00022da4  4e5e                     UNLK        A6 
00022da6  4e75                     RTS  
00022da8  352e 3400                MOVE  .W    (0x3400,A6),-(A2) 
00022dac  4a75 6c20                TST   .W    (0x20,A5,D6.L*4) 
00022db0  3234 2031                MOVE  .W    (0x31,A4,D2.W*1),D1 
00022db4  3939 382c 2031           MOVE  .W    0x382c2031,-(A4) 
00022dba  343a 3337                MOVE  .W    (0x3337,PC),D2 
value = 0 = 0x0 
-> s 
d0   =      3e   d1   =     3700   d2     =     3000   d3     =   3b09dc 
d4   =       0   d5   =        0   d6     =        0   d7     =        0 
a0   =   230b8   a1   =   3b3318   a2     =   3b3324   a3     =    7e094 
a4   =  38a7c0   a5   =        0   a6/fp  =    bcb90   a7/sp  =    bcb84 
sr   =    2604   pc   =    230ba 
    000230ba  2c4f                MOVEA .L    A7,A6 
value = 0 = 0x0

Return to task mode and confirm that return by calling i( ):

-> sysResume  
value = 0 = 0x0 
-> i 
NAME      ENTRY      TID      PRI   STATUS  PC      SP      ERRNO  DELAY 
--------- ---------- -------- ----- ------- ------- ------- -----  ----- 
tExcTask  _excTask   3e8f98   0     PEND    47982   3e8ef4  0      0 
tLogTask  _logTask   3e6670   0     PEND    47982   3e65c8  0      0 
tWdbTask  0x3f024    398e04   3     READY   405ac   398d50  30067  0 
tNetTask  _netTask   3b39e0   50    PEND    405ac   3b3988  0      0 
value = 0 = 0x0

If you want to debug an application you have loaded dynamically, set an appropriate breakpoint and spawn a task which runs when you continue the system:

-> sysSuspend 
value = 0 = 0x0 
-> ld < test.o 
Loading /view/didier.temp/vobs/wpwr/target/lib/objMC68040gnutest//test.o / 
value = 400496 = 0x61c70 = _rn_addroute + 0x1d4 
-> b address 
value = 0 = 0x0 
-> sp test 
value = 0 = 0x0 
-> c

The application breaks on address when the instruction at address is executed.

7.2.8   Interrupting a Shell Command

Occasionally it is desirable to abort the shell's evaluation of a statement. For example, an invoked routine may loop excessively, suspend, or wait on a semaphore. This may happen as the result of errors in arguments specified in the invocation, errors in the implementation of the routine itself, or simply oversight as to the consequences of calling the routine.

To regain control of the shell in such cases, press the interrupt character on the keyboard, usually CTRL+BREAK from Tornado or CTRL+C from the console. This makes the shell stop waiting for a result and allows input of a new statement. Any remaining portions of the statement are discarded and the task that ran the function call is deleted.


*      
CAUTION: CTRL+BREAK and CTRL+C do not interrupt non-blocking functions. If the task transmitting the break request is of lower priority than the task to be interrupted, the request is not conveyed until the original task completes.

Pressing CTRL+BREAK or CTRL+C is also necessary to regain control of the shell after calling a routine on the target that ends with exit( ) rather than return.

Occasionally a subroutine invoked from the shell may incur a fatal error, such as a bus/address error or a privilege violation. When this happens, the failing routine is suspended. If the fatal error involved a hardware exception, the shell automatically notifies you of the exception. For example:

-> taskSpawn -4 
Exception number 11: Task: 0x264ed8 (tCallTask) 

In cases like this, you do not need to type CTRL+BREAK to recover control of the shell; it automatically returns to the prompt, just as if you had interrupted. Whether you interrupt or the shell does it for you, you can proceed to investigate the cause of the suspension. For example, in the case above you could run the Tornado browser on tCallTask.

An interrupted routine may have left things in a state which was not cleared when you interrupted it. For instance, a routine may have taken a semaphore, which cannot be given automatically. Be sure to perform manual cleanup if you are going to continue the application from this point.



7.3    The Shell C-Expression Interpreter

The C-expression interpreter is the most common command interface to the Tornado shell. This interpreter can evaluate almost any C expression interactively in the context of the attached target. This includes the ability to use variables and functions whose names are defined in the symbol table. Any command you type is interpreted as a C expression. The shell evaluates that expression and, if the expression so specifies, assigns the result to a variable.

7.3.1   Data Types

The most significant difference between the shell C-expression interpreter and a C compiler lies in the way that they handle data types. The shell does not accept any C declaration statements, and no data-type information is available in the symbol table. Instead, an expression's type is determined by the types of its terms.

Unless you use explicit type-casting, the shell makes the following assumptions about data types:

  • In an assignment statement, the type of the left hand side is determined by the type of the right hand side.

  • If floating-point numbers and integers both appear in an arithmetic expression, the resulting type is a floating-point number.

  • Data types are assigned to various elements as shown in Table 7-9.

Table 7-9:   Shell C Interpreter Data-Type Assumptions


Element
Data Type

variable
int
variable used as floating-point
double
return value of subroutine
int
constant with no decimal point
int/long
constant with decimal point
double

A constant or variable can be treated as a different type than what the shell assumes by explicitly specifying the type with the syntax of C type-casting. Functions that return values other than integers require a slightly different type-casting; see Function Calls. Table 7-10 shows the various data types available in the shell C interpreter, with examples of how they can be set and referenced.

Table 7-10:   Data Types in the Shell C Interpreter


Type
Bytes
Set Variable
Display Variable

int      
4      
x = 99      
x 
(int) x      
long      
4      
x = 33 
x = (long)33      
x 
(long) x      
short      
2      
x = (short)20      
(short) x      
char      
1      
x = 'A' 
x = (char)65 
x = (char)0x41      
(char) x      
double      
8      
x = 11.2 
x = (double)11.2      
(double) x      
float      
4      
x = (float)5.42      
(float) x      

Strings, or character arrays, are not treated as separate types in the shell C interpreter. To declare a string, set a variable to a string value.3 For example:

-> ss = "shoe bee doo"

The variable ss is a pointer to the string shoe bee doo. To display ss, enter:

-> d ss

The d( ) command displays memory where ss is pointing.4 You can also use printf( ) to display strings.

The shell places no type restrictions on the application of operators. For example, the shell expression:

 *(70000 + 3 * 16)

evaluates to the 4-byte integer value at memory location 70048.

7.3.2   Lines and Statements

The shell parses and evaluates its input one line at a time. A line may consist of a single shell statement or several shell statements separated by semicolons. A semicolon is not required on a line containing only a single statement. A statement cannot continue on multiple lines.

Shell statements are either C expressions or assignment statements. Either kind of shell statement may call WindSh commands or target routines.

7.3.3   Expressions

Shell expressions consist of literals, symbolic data references, function calls, and the usual C operators.

Literals

The shell interprets the literals in Table 7-11 in the same way as the C compiler, with one addition: the shell also allows hex numbers to be preceded by $ instead of 0x.

Table 7-11:   Literals in the Shell C Interpreter


Literal
Example

decimal numbers
143967
octal numbers
017734
hex numbers
0xf3ba or $f3ba
floating point numbers
666.666
character constants
'x' and '$'
string constants
"hello world!!!"

Variable References

Shell expressions may contain references to variables whose names have been entered in the system symbol table. Unless a particular type is specified with a variable reference, the variable's value in an expression is the 4-byte value at the memory address obtained from the symbol table. It is an error if an identifier in an expression is not found in the symbol table, except in the case of assignment statements discussed below.

Some C compilers prefix user-defined identifiers with an underbar, so that myVar is actually in the symbol table as _myVar. In this case, the identifier can be entered either way to the shell--the shell searches the symbol table for a match either with or without a prefixed underbar.

You can also access data in memory that does not have a symbolic name in the symbol table, as long as you know its address. To do this, apply the C indirection operator "*" to a constant. For example, *0x10000 refers to the 4-byte integer value at memory address 10000 hex.

Operators

The shell interprets the operators in Table 7-12 in the same way as the C compiler.

Table 7-12:   Operators in the Shell C Interpreter


Operator Type
Operators

arithmetic
+
-
*
/
unary -
relational
==
!=
<
>
<=
>=
shift
<<
>>
logical
||
&&
!
bitwise
|
&
~
^
address and indirection
&
*

The shell assigns the same precedence to the operators as the C compiler. However, unlike the C compiler, the shell always evaluates both sub-expressions of the logical binary operators || and &&.

Function Calls

Shell expressions may contain calls to C functions (or C-compatible functions) whose names have been entered in the system symbol table; they may also contain function calls to WindSh commands that execute on the host.

The shell executes such function calls in tasks spawned for the purpose, with the specified arguments and default task parameters; if the task parameters make a difference, you can call taskSpawn( ) instead of calling functions from the shell directly. The value of a function call is the 4-byte integer value returned by the function. The shell assumes that all functions return integers. If a function returns a value other than an integer, the shell must know the data type being returned before the function is invoked. This requires a slightly unusual syntax because you must cast the function, not its return value. For example:

-> floatVar = ( float ()) funcThatReturnsAFloat (x,y)

The shell can pass up to ten arguments to a function. In fact, the shell always passes exactly ten arguments to every function called, passing values of zero for any arguments not specified. This is harmless because the C function-call protocol handles passing of variable numbers of arguments. However, it allows you to omit trailing arguments of value zero from function calls in shell expressions.

Function calls can be nested. That is, a function call can be an argument to another function call. In the following example, myFunc( ) takes two arguments: the return value from yourFunc( ) and myVal. The shell displays the value of the overall expression, which in this case is the value returned from myFunc( ).

myFunc (yourFunc (yourVal), myVal);

Shell expressions can also contain references to function addresses instead of function invocations. As in C, this is indicated by the absence of parentheses after the function name. Thus the following expression evaluates to the result returned by the function myFunc2( ) plus 4:

4 + myFunc2 ( )

However, the following expression evaluates to the address of myFunc2( ) plus 4:

4 + myFunc2

An important exception to this occurs when the function name is the very first item encountered in a statement. This is discussed in Arguments to Commands.

Shell expressions can also contain calls to functions that do not have a symbolic name in the symbol table, but whose addresses are known to you. To do this, simply supply the address in place of the function name. Thus the following expression calls a parameterless function whose entry point is at address 10000 hex:

0x10000 ()

You can assign the address of a function to a variable and then dereference the variable to call the function as in the following example:

-> aaa=printf 
-> (* aaa)("The clock speed is %d\n" ,sysClckRateGet())

Subroutines as Commands

Both VxWorks and the Tornado shell itself provide routines that are meant to be called from the shell interactively. You can think of these routines as commands, rather than as subroutines, even though they can also be called with the same syntax as C subroutines (and those that run on the target are in fact subroutines). All the commands discussed in this chapter fall in this category. When you see the word command, you can read subroutine, or vice versa, since their meaning here is identical.

Arguments to Commands

In practice, most statements input to the shell are function calls, often to invoke VxWorks facilities. To simplify this use of the shell, an important exception is allowed to the standard expression syntax required by C. When a function name is the very first item encountered in a shell statement, the parentheses surrounding the function's arguments may be omitted. Thus the following shell statements are synonymous:

-> rename ("oldname", "newname")
-> rename "oldname", "newname"

as are:

-> evtBufferAddress ( )
-> evtBufferAddress

However, note that if you wish to assign the result to a variable, the function call cannot be the first item in the shell statement--thus, the syntactic exception above does not apply. The following captures the address, not the return value, of evtBufferAddress( ):

-> value = evtBufferAddress 

Task References

Most VxWorks routines that take an argument representing a task require a task ID. However, when invoking routines interactively, specifying a task ID can be cumbersome since the ID is an arbitrary and possibly lengthy number.

To accommodate interactive use, shell expressions can reference a task by either task ID or task name. The shell attempts to resolve a task argument to a task ID as follows: if no match is found in the symbol table for a task argument, the shell searches for the argument in the list of active tasks. When it finds a match, it substitutes the task name with its matching task ID. In symbol lookup, symbol names take precedence over task names.

By convention, task names are prefixed with a u for tasks started from the Tornado shell, and with a t for VxWorks tasks started from the target itself. In addition, tasks started from a shell are prefixed by s1, s2, and so on to indicate which shell they were started from. This avoids name conflicts with entries in the symbol table. The names of system tasks and the default task names assigned when tasks are spawned use this convention. For example, tasks spawned with the shell command sp( ) in the first shell opened are given names such as s1u0 and s1u1. Tasks spawned with the second shell opened have names such as s2u0 and s2u1. You are urged to adopt a similar convention for tasks named in your applications.

7.3.4   The "Current" Task and Address

A number of commands--c( ), s( ), ti( )--take a task parameter that can be omitted. If omitted, the current task is used. The l( ) and d( ) commands use the current address if no address is specified. The current task and address are set when:

  • A task hits a breakpoint or an exception trap. The current address is the address of the instruction that caused the break or exception.

  • A task is single-stepped. The current address is the address of the next instruction to be executed.

  • Any of the commands that use the current task or address are executed with a specific task parameter. The current address will be the address of the byte following the last byte that was displayed or disassembled.

7.3.5   Assignments

The shell C interpreter accepts assignment statements in the form:

addressExpression = expression 

The left side of an expression must evaluate to an addressable entity; that is, a legal C value.

Typing and Assignment

The data type of the left side is determined by the type of the right side. If the right side does not contain any floating-point constants or noninteger type-casts, then the type of the left side will be an integer. The value of the right side of the assignment is put at the address provided by the left side. For example, the following assignment sets the 4-byte integer variable x to 0x1000:

-> x = 0x1000

The following assignment sets the 4-byte integer value at memory address 0x1000 to the current value of x:

-> *0x1000 = x

The following compound assignment adds 300 to the 4-byte integer variable x:

-> x += 300

The following adds 300 to the 4-byte integer at address 0x1000:

-> *0x1000 += 300

The compound assignment operator -=, as well as the increment and decrement operators ++ and --, are also available.

Automatic Creation of New Variables

New variables can be created automatically by assigning a value to an undefined identifier (one not already in the symbol table) with an assignment statement.

When the shell encounters such an assignment, it allocates space for the variable and enters the new identifier in the symbol table along with the address of the newly allocated variable. The new variable is set to the value and type of the right-side expression of the assignment statement. The shell prints a message indicating that a new variable has been allocated and assigned the specified value.

For example, if the identifier fd is not currently in the symbol table, the following statement creates a new variable named fd and assigns to it the result of the function call:

-> fd = open ("file", 0) 

7.3.6   Comments

The shell allows two kinds of comments. First, comments of the form /* */ can be included anywhere on a shell input line. These comments are simply discarded, and the rest of the input line evaluated as usual. Second, any line whose first nonblank character is # is ignored completely. Comments are particularly useful for Tornado shell scripts. See the section Scripts: Redirecting Shell I/O below.

7.3.7   Strings

When the shell encounters a string literal ("") in an expression, it allocates space for the string including the null-byte string terminator. The value of the literal is the address of the string in the newly allocated storage. For instance, the following expression allocates 12 bytes from the target-agent memory pool, enters the string in those 12 bytes (including the null terminator), and assigns the address of the string to x:

-> x = "hello there"

Furthermore, even when a string literal is not assigned to a symbol, memory is still permanently allocated for it. For example, the following uses 12 bytes of memory that are never freed:

-> printf ("hello there")

If strings were only temporarily allocated, and a string literal were passed to a routine being spawned as a task, then by the time the task executed and attempted to access the string, the shell would have already released--possibly even reused--the temporary storage where the string was held.

This memory, like other memory used by the Tornado tools, comes from the target-agent memory pool; it does not reduce the amount of memory available for application execution (the VxWorks memory pool). The amount of target memory allocated for each of the two memory pools is defined at configuration time; see Scaling the Target Agent.

After extended development sessions in Tornado shells, the cumulative memory used for strings may be noticeable. If this becomes a problem, restart your target server.

7.3.8   Strings and Path Names

In VxWorks, the directory and file segments of path names (for target-resident files and devices) are separated with the slash character (/). This presents no difficulty when subroutines require a path-name argument, because the / character has no special meaning in C strings.

However, you can also refer from the shell to files that reside on your Windows host. For host path names, you can use either a slash for consistency with the VxWorks convention, or a backslash (\) for consistency with the Windows convention.

Because the backslash character is an escape character in C strings, you must double any backslashes that you use in path names as strings. This applies only to path names in C strings. No special syntax is required for path names that are interpreted directly by the shell.

The shell's ld( ) command (System Modification and Debugging) can be used with all of these variations of path names. The following ld( ) invocations are all correct and equivalent:

-> ld < c:\fred\tests\zap.o
-> ld < c:/fred/tests/zap.o
-> ld 1,0,"c:\\fred\\tests\\zap.o"
-> ld 1,0,"c:/fred/tests/zap.o"

7.3.9   Ambiguity of Arrays and Pointers

In a C expression, a nonsubscripted reference to an array has a special meaning, namely the address of the first element of the array. The shell, to be compatible, should use the address obtained from the symbol table as the value of such a reference, rather than the contents of memory at that address. Unfortunately, the information that the identifier is an array, like all data type information, is not available after compilation. For example, if a module contains the following:

char string [ ] = "hello";

you might be tempted to enter a shell expression like:

þ    -> printf (string)

While this would be correct in C, the shell will pass the first 4 bytes of the string itself to printf( ), instead of the address of the string. To correct this, the shell expression must explicitly take the address of the identifier:

À    -> printf (&string)

To make matters worse, in C if the identifier had been declared a character pointer instead of a character array:

char *string = "hello";

then to a compiler þ would be correct and À would be wrong! This is especially confusing since C allows pointers to be subscripted exactly like arrays, so that the value of string[0] would be "h" in either of the above declarations.

The moral of the story is that array references and pointer references in shell expressions are different from their C counterparts. In particular, array references require an explicit application of the address operator &.

7.3.10   Pointer Arithmetic

While the C language treats pointer arithmetic specially, the shell C interpreter does not, because it treats all non-type-cast variables as 4-byte integers.

In the shell, pointer arithmetic is no different than integer arithmetic. Pointer arithmetic is valid, but it does not take into account the size of the data pointed to. Consider the following example:

-> *(myPtr + 4) = 5

Assume that the value of myPtr is 0x1000. In C, if myPtr is a pointer to a type char, this would put the value 5 in the byte at address at 0x1004. If myPtr is a pointer to a 4-byte integer, the 4-byte value 0x00000005 would go into bytes 0x1010-0x1013. The shell, on the other hand, treats variables as integers, and therefore would put the 4-byte value 0x00000005 in bytes 0x1004-0x1007.

7.3.11   C Interpreter Limitations

Powerful though it is, the C interpreter in the shell is not a complete interpreter for the C language. The following C features are not present in the Tornado shell:

  • Control Structures

The shell interprets only C expressions (and comments). The shell does not support C control structures such as if, goto, and switch statements, or do, while, and for loops. Control structures are rarely needed during shell interaction. If you do come across a situation that requires a control structure, you can use the Tcl interface to the shell instead of using its C interpreter directly; see 7.7 Tcl: Shell Interpretation.

  • Compound or Derived Types

No compound types (struct or union types) or derived types (typedef) are recognized in the shell C interpreter. You can use CrossWind instead of the shell for interactive debugging, when you need to examine compound or derived types.

  • Macros

No C preprocessor macros (or any other preprocessor facilities) are available in the shell. CrossWind does not support preprocessor macros either, but indirect workarounds are available using either the shell or CrossWind. For constant macros, you can define variables in the shell with similar names to the macros. To avoid intrusion into the application symbol table, you can use CrossWind instead; in this case, use CrossWind convenience variables with names corresponding to the desired macros. In either case, you can automate the effort of defining any variables you need repeatedly, by using an initialization script.

For the first two problems (control structures, or display and manipulation of types that are not supported in the shell), you might also consider writing auxiliary subroutines to provide these services during development; you can call such subroutines at will from the shell, and later omit them from your final application.

7.3.12   C-Interpreter Primitives

Table 7-13 lists all the primitives (commands) built into WindSh. (For discussion of these primitives by function, see 7.2.4 Invoking Built-In Shell Routines.) Because the shell tries to find a primitive first before attempting to call a target subroutine, it is best to avoid these names in the target code. If you do have a name conflict, however, you can force the shell to call a target routine instead of an identically-named primitive by prefacing the subroutine call with the character @. (See Resolving Name Conflicts between Host and Target.)

Table 7-13:   List of WindSh Commands 


agentModeShow( )
b( )
bd( )
bdall( )
bh( )
bootChange( )
browse( )
c( )
cd( )
ipstatShow( )
iStrict( )
l( )
ld( )
lkAddr( )
lkup( )
ls( )
m( )
memPartShow( )
smMemPartShow( )
smMemShow( )
so( )
sp( )
sps( )
sysResume( )
sysStatusShow( )
sysSuspend( )
taskCreateHookShow( )
checkStack( )
classShow( )
cplusCtors( )
cplusDtors( )
cplusStratShow( )
cplusXtorSet( )
cret( )
d( )
devs( )
h( )
help( )
hostShow( )
i( )
icmpstatShow( )
ifShow( )
inetstatShow( )
intVecShow( )
iosDevShow( )
iosDrvShow( )
iosFdShow( )
memShow( )
moduleIdFigure( )
moduleShow( )
mqPxShow( )
mRegs( )
msgQShow( )
period( )
printErrno( )
printLogo( )
pwd( )
quit( )
reboot( )
repeat( )
routestatShow( )
s( )
semPxShow( )
semShow( )
shellHistory( )
shellPromptSet( )
show( )
taskDeleteHookShow( )
taskIdDefault( )
taskIdFigure( )
taskRegsShow( )
taskShow( )
taskSwitchHookShow( )
taskWaitShow( )
tcpstatShow( )
td( )
tftpInfoShow( )
ti( )
tr( )
ts( )
tt( )
tw( )
udpstatShow( )
unld( )
version( )
w( )
wdShow( )

7.3.13   Terminal-Control Characters

The terminal-control characters are slightly different when WindSh runs as a console-based application and when it runs within Tornado.

Table 7-14 lists special terminal characters frequently used for shell control in both situations. For more information on the use of these characters, see 7.5 Shell Line Editing and 7.2.8 Interrupting a Shell Command.  

Table 7-14:   Shell Special Characters


Tornado Value
Console Values
Description

BACKSPACE (CTRL+H)
BACKSPACE (CTRL+H)
Delete a character.
CTRL+U
CTRL+U
Delete an entire line.
CTRL+BREAK
CTRL+BREAK
Interrupt a function call.
n/a
CTRL+S
Temporarily suspend output (UNIX only).
n/a
CTRL+Q
Resume output (UNIX only).
ESC
ESC
Toggle between input mode and edit mode.
CTRL+C
n/a
Copy selection to the clipboard.
CTRL+V
n/a
Paste text from the clipboard.

7.3.14   Redirection in the C Interpreter

The shell provides a redirection mechanism for momentarily reassigning the standard input and standard output file descriptors just for the duration of the parse and evaluation of an input line. The redirection is indicated by the < and > symbols followed by file names, at the very end of an input line. No other syntactic elements may follow the redirection specifications. The redirections are in effect for all subroutine calls on the line.

For example, the following input line sets standard input to the file named input and standard output to the file named output during the execution of copy( ):

-> copy < input > output

If the file to which standard output is redirected does not exist, it is created.

Ambiguity Between Redirection and C Operators

There is an ambiguity between redirection specifications and the relational operators less than and greater than. The shell always assumes that an ambiguous use of < or > specifies a redirection rather than a relational operation. Thus the ambiguous input line:

-> x > y

writes the value of the variable x to the stream named y, rather than comparing the value of variable x to the value of variable y. However, you can use a semicolon to remove the ambiguity explicitly, because the shell requires that the redirection specification be the last element on a line. Thus the following input lines are unambiguous:

-> x; > y
-> x > y;

The first line prints the value of the variable x to the output stream y. The second line prints on standard output the value of the expression "x greater than y."

The Nature of Redirection

The redirection mechanism of the Tornado shell is fundamentally different from that of the Windows command shell, although the syntax and terminology are similar.

In the Tornado shell, redirecting input or output affects only a command executed from the shell. In particular, this redirection is not inherited by any tasks started while output is redirected.

For example, you might be tempted to specify redirection streams when spawning a routine as a task, intending to send the output of printf( ) calls in the new task to an output stream, while leaving the shell's I/O directed at the virtual console. This stratagem does not work. For example, the shell input line:

-> taskSpawn (...myFunc...) > output

momentarily redirects the shell standard output during the brief execution of the spawn routine, but does not affect the I/O of the resulting task.

To redirect the input or output streams of a particular task, call ioTaskStdSet( ) once the task exists.

Scripts: Redirecting Shell I/O

A special case of I/O redirection concerns the I/O of the shell itself; that is, redirection of the streams the shell's input is read from, and its output is written to. The syntax for this is simply the usual redirection specification, on a line that contains no other expressions.

The typical use of this mechanism is to have the shell read and execute lines from a file. For example, the input lines:

þ    -> <startup
À    -> < c:\fred\startup

cause the shell to read and execute the commands in the file startup, either on the current working directory as in þ or explicitly on the complete path name in À. If your working directory is \fred, commands þ and À are equivalent.

Such command files are called scripts. Scripts are processed exactly like input from an interactive terminal. After reaching the end of the script file, the shell returns to processing I/O from the original streams.

During execution of a script, the shell displays each command as well as any output from that command. You can change this by invoking the shell with the -q option; see the windsh reference entry (online or in ).

An easy way to create a shell script is from a list of commands you have just executed in the shell. The history command h( ) prints a list of the last 20 shell commands. The following creates a file c:\tmp\script with the current shell history:

-> h > c:\tmp\script

The command numbers must be deleted from this file before using it a shell script.

Scripts can also be nested. That is, scripts can contain shell input redirections that cause the shell to process other scripts.


*      
CAUTION: Input and output redirection must refer to files on a host file system. If you have a local file system on your target, files that reside there are available to target-resident subroutines, but not to the shell or to other Tornado tools (unless you export them from the target using NFS, and mount them on your host).


*      
CAUTION: You should set the WindSh environment variable SH_GET_TASK_IO to off before you use redirection of input from scripts or before you copy and paste blocks of commands to the shell command line. Otherwise commands might be taken as input for a command that precedes them, and lost.

C-Interpreter Startup Scripts

Tornado shell scripts can be especially useful for setting up your working environment. You can run a startup script through the shell C interpreter5 by specifying its name with the -s command-line option to windsh. For example:

C:\> windsh -s c:\fred\startup

Like the autoexec.bat file, startup scripts can be used for setting system parameters to personal preferences: defining variables, specifying the target's working directory, and so forth. They can also be useful for tailoring the configuration of your system without having to rebuild VxWorks. For example:

  • creating additional devices
  • loading and starting up application modules
  • adding a complete set of network host names and routes
  • setting NFS parameters and mounting NFS partitions

For additional information on initialization scripts, see 7.7 Tcl: Shell Interpretation.



7.4    C++ Interpretation

Tornado supports both C and C++ as development languages; see VxWorks Programmer's Guide: C++ Development for information about C++ development. Because C and C++ expressions are so similar, the WindSh C-expression interpreter supports many C++ expressions. The facilities explained in 7.3 The Shell C-Expression Interpreter are all available regardless of whether your source language is C or C++. In addition, there are a few special facilities for C++ extensions. This section describes those extensions.

However, WindSh is not a complete interpreter for C++ expressions. In particular, the shell has no information about user-defined types; there is no support for the :: operator; constructors, destructors, and operator functions cannot be called directly from the shell; and member functions cannot be called with the . or -> operators.

To exercise C++ facilities that are missing from the C-expression interpreter, you can compile and download routines that encapsulate the special C++ syntax. Fortunately, the Tornado dynamic linker makes this relatively painless.

7.4.1   Overloaded Function Names

If you have several C++ functions with the same name, distinguished by their argument lists, call any of them as usual with the name they share. When the shell detects the fact that several functions exist with the specified name, it lists them in an interactive dialogue, printing the matching functions' signatures so that you can recall the different versions and make a choice among them.

You make your choice by entering the number of the desired function. If you make an invalid choice, the list is repeated and you are prompted to choose again. If you enter 0 (zero), the shell stops evaluating the current command and prints a message like the following, with xxx replaced by the function name you entered:

undefined symbol: xxx

This can be useful, for example, if you misspelled the function name and you want to abandon the interactive dialogue. However, because WindSh is an interpreter, portions of the expression may already have executed (perhaps with side effects) before you abandon execution in this way.

The following example shows how the support for overloaded names works. In this example, there are four versions of a function called xmin( ). Each version of xmin( ) returns at least two arguments, but each version takes arguments of different types.

-> l xmin 
"xmin" is overloaded - Please select: 
    1: _xmin(double,double) 
    2: _xmin(long,long) 
    3: _xmin(int,int) 
    4: _xmin(float,float) 
Enter <number> to select, anything else to stop: 1 
                _xmin(double,double): 
3fe710 4e56 0000        LINK    .W      A6,#0 
3fe714 f22e 5400 0008   FMOVE   .D      (0x8,A6),F0 
3fe71a f22e 5438 0010   FCMP    .D      (0x10,A6),F0 
3fe720 f295 0008        FB      .W      #0x8f22e 
3fe724 f22e 5400 0010   FMOVE   .D      (0x10,A6),F0 
3fe72a f227 7400        FMOVE   .D      F0,-(A7) 
3fe72e 201f             MOVE    .L      (A7)+,D0 
3fe730 221f             MOVE    .L      (A7)+,D1 
3fe732 6000 0002        BRA             0x003fe736 
3fe736 4e5e             UNLK            A6 
value = 4187960 = 0x3fe738 = _xmin(double,double) + 0x28
-> l xmin 
"xmin" is overloaded - Please select: 
    1: _xmin(double,double) 
    2: _xmin(long,long) 
    3: _xmin(int,int) 
    4: _xmin(float,float) 
Enter <number> to select, anything else to stop: 3 
                _xmin(int,int): 
3fe73a 4e56 0000        LINK    .W      A6,#0 
3fe73e 202e 0008        MOVE    .L      (0x8,A6),D0 
3fe742 b0ae 000c        CMP     .L      (0xc,A6),D0 
3fe746 6f04             BLE             0x003fe74c 
3fe748 202e 000c        MOVE    .L      (0xc,A6),D0 
3fe74c 6000 0002        BRA             0x003fe750 
3fe750 4e5e             UNLK            A6 
3fe752 4e75             RTS 
                _xmin(long,long): 
3fe7544e560000          LINK    .W      A6,#0 
3fe758202e0008          MOVE    .L      (0x8,A6),D0 
value = 4187996 = 0x3fe75c = _xmin(long,long) + 0x8

In this example, the disassembler is called to list the instructions for xmin( ), then the version that computes the minimum of two double values is selected. Next, the disassembler is invoked again, this time selecting the version that computes the minimum of two int values. Note that a different routine is disassembled in each case.

7.4.2   Automatic Name Demangling

Many shell debugging and system information functions display addresses symbolically (for example, the l( ) routine). This might be confusing for C++, because compilers encode a function's class membership (if any) and the type and number of the function's arguments in the function's linkage name. The encoding is meant to be efficient for development tools, but not necessarily convenient for human comprehension. This technique is commonly known as name mangling and can be a source of frustration when the mangled names are exposed to the developer.

To avoid this confusion, the debugging and system information routines in WindSh print C++ function names in a demangled representation. Whenever the shell prints an address symbolically, it checks whether the name has been mangled. If it has, the name is demangled (complete with the function's class name, if any, and the type of each of the function's arguments) and printed.

The following example shows the demangled output when lkup( ) displays the addresses of the xmin( ) functions mentioned in 7.4.1 Overloaded Function Names.

-> lkup "xmin" 
_xmin(double,double)    0x003fe710 text         (templex.out) 
_xmin(long,long)        0x003fe754 text         (templex.out) 
_xmin(int,int)          0x003fe73a text         (templex.out) 
_xmin(float,float)      0x003fe6ee text         (templex.out) 
value = 0 = 0x0 


7.5    Shell Line Editing

The WindSh front end provides a history mechanism similar to the UNIX Korn-shell history facility, including a built-in line editor (with keystrokes similar to the UNIX editor vi) that allows you to scroll, search, and edit previously typed commands. Line editing is available regardless of which interpreter you are using (C or Tcl6 ), and the command history spans both interpreters--you can switch from one to the other and back, and scroll through the history of both modes.

The ESC key switches the shell from normal input mode to edit mode. The history and editing commands in Table 7-15 are available in edit mode.

Some line-editing commands switch the line editor to insert mode until an ESC is typed (as in vi) or until an ENTER gives the line to one of the shell interpreters. ENTER always gives the line as input to the current shell interpreter, from either input or edit mode.

In input mode, the shell history command h( ) (described in System Information) displays up to 20 of the most recent commands typed to the shell; older commands are lost as new ones are entered. You can change the number of commands kept in history by running h( ) with a numeric argument. To locate a line entered previously, press ESC followed by one of the search commands listed in Table 7-15; you can then edit and execute the line with one of the commands from Table 7-15.

Table 7-15:   Shell Line-Editing Commands 


Basic Control
h [size]
Display shell history if no argument; otherwise set history buffer to size.
ESC
Switch to line-editing mode from regular input mode.
ENTER
Give line to shell and leave edit mode.
CTRL+D
Complete symbol or path name (edit mode), display synopsis of current symbol (symbol must be complete, followed by a space), or end shell session (if the command line is empty).
[tab]
Complete symbol or path name (edit mode).
CTRL+H
Delete a character (backspace).
CTRL+U
Delete entire line (edit mode).
CTRL+L
Redraw line (works in edit mode).
CTRL+S and CTRL+Q
Suspend output, and resume output.
CTRL+W
Display HTML reference page for a routine.
Movement and Search Commands
nG
Go to command number n.1
/s or ?s
Search for string s backward in history, or forward.
n
Repeat last search.
nk or n-
Get nth previous shell command.*
nj or n+
Get nth next shell command.*
nh
Go left n characters (also CTRL+H).*
nl or SPACE
Go right n characters.*
nw or nW
Go n words forward, or n large words. *2
ne or nE
Go to end of the nth next word, or nth next large word. *
nb or nB
Go back n words, or n large words.*
$
Go to end of line.
0 or ^
Go to beginning of line, or first nonblank character.
fc or Fc
Find character c, searching forward, or backward.
Insert and Change Commands
a or A
...ESC
Append, or append at end of line (ESC ends input).
i or I
...ESC
Insert, or insert at beginning of line (ESC ends input).
ns
...ESC
Change n characters (ESC ends input).*
nc SPACE
...ESC
Change n characters (ESC ends input).*
cw
...ESC
Change word (ESC ends input).
cc or S
...ESC
Change entire line (ESC ends input).
c$ or C
...ESC
Change from cursor to end of line (ESC ends input).
c0
...ESC
Change from cursor to beginning of line (ESC ends input).
R
...ESC
Type over characters (ESC ends input).
nrc
Replace the following n characters with c.*
~
Toggle case, lower to upper or vice versa.
Delete Commands
nx
Delete n characters starting at cursor.*
nX
Delete n character to left of cursor.*
dw
Delete word.
dd
Delete entire line (also CTRL+U).
d$ or D
Delete from cursor to end of line.
d0
Delete from cursor to beginning of line.
Put and Undo Commands
p or P
Put last deletion after cursor, or in front of cursor.
u
Undo last command.

1:  The default value for n is 1.

2:  words are separated by blanks or punctuation; large words are separated by blanks only.



7.6    Object Module Load Path

In order to download an object module dynamically to the target, both WindSh and the target server must be able to locate the file. If path naming conventions are different between WindSh and the target server, the two systems may both have access to the file, but mounted with different path names. This situation arises often in environments where UNIX and Windows systems are networked together, because the path naming convention is different: the UNIX /usr/fred/applic.o may well correspond to the Windows n:\fred\applic.o. If you encounter this problem, check to be sure the LD_SEND_MODULES variable of shConfig is set to "on" or use the LD_PATH facility to tell the target server about the path known to the shell.

Example 7-3:   Loading a Module: Alternate Path Names

-> ld < /usr/david/project/test/test.o 
Loading /usr/david/project/test/test.o 
WTX Error 0x2 (no such file or directory) 
value = -1 = 0xffffffff 
-> ?shConfig LD_PATH "/usr/david/project/test;C:\project\test" 
-> ld < test.o 
Loading C:\project\test\test.o 
value = 17427840 = 0x109ed80

For more information on using LD_PATH and other shConfig facilities, see WindSh Environment Variables.


*      
CAUTION: If you call ld( ) with an explicit argument list, any instances of the backslash character in Windows paths must be doubled: "n:\\fred\\applic.o". If you supply the module name with the redirection symbol instead, no double backslashes are necessary.

Certain WindSh commands and browser utilities imply dynamic downloads of auxiliary target-resident code. These subroutines fail in situations where the shell and target-server view of the file system is incompatible. To get around this problem, download the required routines explicitly from the host where the target server is running (or configure the routines statically into the VxWorks image). Once the supporting routines are on the target, any host can use the corresponding shell and browser utilities. Table 7-16 lists the affected utilities. The object modules are in wind\target\lib\objcputypegnuvx.

Table 7-16:   Shell and Browser Utilities with Target-Resident Components


Utility
Supporting Module

repeat( )
repeatHost.o
period( )
periodHost.o
tt( )
trcLib.o,
ttHostLib.o
Browser spy panel
spyLib.o



7.7    Tcl: Shell Interpretation

The shell has a Tcl interpreter interface as well as the C interpreter interface. This section illustrates some uses of the shell Tcl interpreter. If you are not familiar with Tcl, we suggest you skip this section and return to it after you have gotten acquainted with Tcl. (For an outline of Tcl, see C. Tcl.) In the interim, you can do a great deal of development work with the shell C interpreter alone.

To toggle between the Tcl interpreter and the C interpreter in the shell, type the single character ?. The shell prompt changes to remind you of the interpreter state: the prompt -> indicates the C interpreter is listening, and the prompt tcl> indicates the Tcl interpreter is listening.7 For example, in the following interaction we use the C interpreter to define a variable in the symbol table, then switch into the Tcl interpreter to define a similar Tcl variable in the shell itself, and finally switch back to the C interpreter:

-> hello="hi there" 
new symbol "hello" added to symbol table. 
hello = 0x3616e8: value = 3544824 = 0x3616f8 = hello + 0x10 
-> ? 
tcl> set hello {hi there} 
hi there 
tcl> ? 
-> 

If you start windsh from the Windows command line, you can also use the option -Tclmode (or -T) to start with the Tcl interpreter rather than the C interpreter.

Using the shell's Tcl interface allows you to extend the shell with your own procedures, and also provides a set of control structures which you can use interactively. The Tcl interpreter also acts as a host shell, giving you access to Windows command-line utilities on your development host.

7.7.1   Tcl: Controlling the Target

In the Tcl interpreter, you can create custom commands, or use Tcl control structures for repetitive tasks, while using the building blocks that allow the C interpreter and the WindSh commands to control the target remotely. These building blocks as a whole are called the wtxtcl procedures.

For example, wtxMemRead returns the contents of a block of target memory (given its starting address and length). That command in turn uses a special memory-block datatype designed to permit memory transfers without unnecessary Tcl data conversions. The following example uses wtxMemRead, together with the memory-block routine memBlockWriteFile, to write a Tcl procedure that dumps target memory to a host file. Because almost all the work is done on the host, this procedure works whether or not the target run-time environment contains I/O libraries or any networked access to the host file system.

# tgtMemDump - copy target memory to host file 
# 
# SYNOPSIS: 
#  tgtMemDump hostfile start nbytes
proc tgtMemDump {fname start nbytes} { 
    set memHandle [wtxMemRead $start $nbytes] 
    memBlockWriteFile $memHandle $fname 
}

For reference information on the wtxtcl routines available in the Tornado shell, see the Tornado API Programmer's Guide (or the online Tornado API Reference).

All of the commands defined for the C interpreter (7.2.4 Invoking Built-In Shell Routines) are also available, with a double-underscore prefix, from the Tcl level; for example, to call i( ) from the Tcl interpreter, run the Tcl procedure __i. However, in many cases, it is more convenient to call a wtxtcl routine instead, because the WindSh commands are designed to operate in the C-interpreter context. For example, you can call the dynamic linker using ld from the Tcl interpreter, but the argument that names the object module may not seem intuitive: it is the address of a string stored on the target. It is more convenient to call the underlying wtxtcl command. In the case of the dynamic linker, the underlying wtxtcl command is wtxObjModuleLoad, which takes an ordinary Tcl string as its argument, as described in the online WTX Tcl API.

Tcl: Calling Target Routines

The shParse utility allows you to embed calls to the C interpreter in Tcl expressions; the most frequent application is to call a single target routine, with the arguments specified (and perhaps capture the result). For example, the following sends a logging message to your VxWorks target console:

tcl> shParse {logMsg("Greetings from Tcl!\n")}  
32

You can also use shParse to call WindSh commands more conveniently from the Tcl interpreter, rather than using their wtxtcl building blocks. For example, the following is a convenient way to spawn a task from Tcl, using the C-interpreter command sp( ), if you do not remember the underlying wtxtcl command:

tcl> shParse {sp appTaskBegin} 
task spawned: id = 25e388, name = u1 
0

Tcl: Passing Values to Target Routines

Because shParse accepts a single, ordinary Tcl string as its argument, you can pass values from the Tcl interpreter to C subroutine calls simply by using Tcl facilities to concatenate the appropriate values into a C expression.

For example, a more realistic way of calling logMsg( ) from the Tcl interpreter would be to pass as its argument the value of a Tcl variable rather than a literal string. The following example evaluates a Tcl variable tclLog and inserts its value (with a newline appended) as the logMsg( ) argument:

tcl> shParse "logMsg(\"$tclLog\\n\")" 
32

7.7.2   Tcl: Calling under C Control

To dip quickly into Tcl and return immediately to the C interpreter, you can type a single line of Tcl prefixed with the ? character (rather than using ? by itself to toggle into Tcl mode). For example:

-> ?set test wonder; puts "This is a $test." 
This is a wonder. 
 
-> 

Notice that the -> prompt indicates we are still in the C interpreter, even though we just executed a line of Tcl.


*      
CAUTION: You may not embed Tcl evaluation inside a C expression; the ? prefix works only as the first nonblank character on a line, and passes the entire line following it to the Tcl interpreter.

For example, you may occasionally want to use Tcl control structures to supplement the facilities of the C interpreter. Suppose you have an application under development that involves several collaborating tasks; in an interactive development session, you may need to restart the whole group of tasks repeatedly. You can define a Tcl variable with a list of all the task entry points, as follows:

-> ? set appTasks {appFrobStart appGetStart appPutStart } 
appFrobStart appGetStart appPutStart 

Then whenever you need to restart the whole list of tasks, you can use something like the following:

-> ? foreach it $appTasks {shParse "sp($it)"} 
task spawned: id = 25e388, name = u0 
task spawned: id = 259368, name = u1 
task spawned: id = 254348, name = u2 
task spawned: id = 24f328, name = u3

7.7.3   Tcl: Tornado Shell lnitialization

When you execute an instance of the Tornado shell, it begins by looking for a file called .wind\windsh.tcl in the directory specified by the HOME environment variable (if that environment variable is defined). In each of these directories, if the file exists, the shell reads and executes its contents as Tcl expressions before beginning to interact. You can use this file to automate any initialization steps you perform repeatedly.

You can also specify a Tcl expression to execute initially on the windsh command line, with the option -e tclExpr. For example, you can test an initialization file before saving it as .wind\windsh.tcl using this option, as follows:

C:\> windsh phobos -e "source c:\\fred\\tcltest" 

Example 7-4:   Shell Initialization File

This file causes I/O for target routines called in WindSh to be directed to the target's standard I/O rather than to WindSh. It changes the default C++ strategy to automatic for this shell, sets a path for locating load modules, and causes modules not to be copied to the target server.

# Redirect Task I/O to WindSh  
shConfig SH_GET_TASK_IO off  
# Set C++ strategy  
shConfig LD_CALL_XTORS on  
# Set Load Path  
shConfig LD_PATH "/folk/jmichel/project/app;/folk/jmichel/project/test"  
# Let the Target Server directly access the module  
shConfig LD_SEND_MODULES off 


7.8    The Shell Architecture

7.8.1   Controlling the Target from the Host

Tornado integrates host and target resources so well that it creates the illusion of executing entirely on the target itself. In reality, however, most interactions with any Tornado tool exploit the resources of both host and target. For example, Table 7-17 shows how the shell distributes the interpretation and execution of the following simple expression:

-> dir = opendir ("/myDev/myFile") 

Parsing the expression is the activity that controls overall execution, and dispatches the other execution activities. This takes place on the host, in the shell's C interpreter, and continues until the entire expression is evaluated and the shell displays its result.

To avoid repetitive clutter, Table 7-17 omits the following important steps, which must be carried out to link the activities in the three contexts (and two systems) shown in each column of the table:

  • After every C-interpreter step, the shell program sends a request to the target server representing the next activity required.

  • The target server receives each such request, and determines whether to execute it in its own context on the host. If not, it passes an equivalent request on to the target agent to execute on the target.

Table 7-17:   Interpreting: dir = opendir ("/myDev/myFile")


Tornado Shell
(on host)
Target Server & Symbol Table
(on host)
Agent
(on target)

Parse the string
"/myDev/myFile".
Allocate memory for the string; return address A.
Write "/myDev/myFile"; return address A.
Parse the name
opendir.
Look up opendir;
return address B.
Parse the function call
B(A); wait for the result.
Spawn a task to run opendir( ) and signal result C when done.
Receive C from target agent and pass it to host shell.
Parse the symbol dir.
Look up dir (fails).
Request a new symbol table entry dir.
Define dir; return symbol D.
Parse the assignment
D=C.
Allocate agent-pool memory for the value of dir.
Write the value of dir.

The first access to server and agent is to allocate storage for the string "/myDev/myFile" on the target and store it there, so that VxWorks subroutines (notably opendir( ) in this case) have access to it. There is a pool of target memory reserved for host interactions. Because this pool is reserved, it can be managed from the host system. The server allocates the required memory, and informs the shell of its location; the shell then issues the requests to actually copy the string to that memory. This request reaches the agent on the target, and it writes the 14 bytes (including the terminating null) there.

The shell's C-expression interpreter must now determine what the name opendir represents. Because opendir( ) is not one of the shell's own commands, the shell looks up the symbol (through the target server) in the symbol table.

The C interpreter now needs to evaluate the function call to opendir( ) with the particular argument specified, now represented by a memory location on the target. It instructs the agent (through the server) to spawn a task on the target for that purpose, and awaits the result.

As before, the C interpreter looks up a symbol name (dir) through the target server; when the name turns out to be undefined, it instructs the target server to allocate storage for a new int and to make an entry pointing to it with the name dir in the symbol table. Again these symbol-table manipulations take place entirely on the host.

The interpreter now has an address (in target memory) corresponding to dir, on the left of the assignment statement; and it has the value returned by opendir( ), on the right of the assignment statement. It instructs the agent (again, through the server) to record the result at the dir address, and evaluation of the statement is complete.

7.8.2   Shell Components

The Tornado shell includes two interpreters, a common front end for command entry and history, and a back end that connects the shell to the global Tornado environment to communicate with the target server. Figure 7-4 illustrates these components:

Figure 7-4:   Components of the Tornado Shell

Line Editing

C-Expression Interpreter

Tcl Interpreter

WTX Tcl

7.8.3   Layers of Interpretation

In daily use, the shell seems to be a seamless environment; but in fact, the characters you type in WindSh go through several layers of interpretation, as illustrated by Figure 7-5. First, input is examined for special editing keystrokes (described in 7.5 Shell Line Editing). Then as much interpretation as possible is done in WindSh itself. In particular, execution of any subroutine is first attempted in the shell itself; if a shell built-in (also called a primitive) with that name exists, the built-in runs without any further checking. Only when a subroutine call does not match any shell built-ins does WindSh call a target routine. See 7.2.4 Invoking Built-In Shell Routines for more information. For a list of all WindSh primitives, see Table 7-13List of WindSh Commands.

Figure 7-5:   Layers of Interpretation in the Shell

       


1:  A target-resident version of the shell is also available; for more information, see VxWorks Programmer's Guide: Target Shell.

2:  As a special case of executing WindSh from the Windows command prompt, you can configure the properties of the command-prompt icon--or a shortcut--to run windsh targetname.

3:  Memory allocated for string constants is never freed by the shell. See 7.3.7 Strings for more information.

4:  d( ) is one of the WindSh commands, implemented in Tcl and executing on the host.

5:  You can also use the -e option to run a Tcl expression at startup, or place Tcl initialization in .wind/windsh.tcl under your home directory. See 7.7.3 Tcl: Tornado Shell lnitialization.

6:  The WindSh Tcl-interpreter interface is described in 7.7 Tcl: Shell Interpretation.

7:  The examples in this book assume you are using the default shell prompts, but you can change the C interpreter prompt to whatever string you like using shellPromptSet( ).