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 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.
button. This launches a shell for the currently selected target server (see Tornado Launch Toolbar).
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.
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).
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
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.
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.
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:
-> cd $WIND_BASE/target/src/projectX -> make synopsis
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]
-> @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.
-> (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 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.
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
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 . ).
Table 7-2 summarizes the WindSh commands that manage VxWorks tasks.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
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.
Table 7-3 summarizes the WindSh commands that report task information.
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 ->
Table 7-4 shows the WindSh commands that display information from the symbol table, from the target system, and from the shell itself.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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)
-> 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.
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.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
Modify the saved values of boot parameters (see 2.5.4 Description of Boot Parameters).
|
|||||||||||||||||||
|
If supported by the target-agent configuration, enter system mode. See 7.2.7 Using the Shell for System Mode Debugging.
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
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.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
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-8 summarizes the WindSh commands that display information about the VxWorks network.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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( ).
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.
-> 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 =
-> 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.
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:
-> reboot Rebooting...
Target connection has been lost. Restarting shell... Waiting to attach to target server......
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.
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:
|
|
|||||||||||||||||||
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
-> 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
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.
|
|
|||||||||||||||||||
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.
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.
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:
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
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.
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.
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.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
The shell interprets the operators in Table 7-12 in the same way as the C compiler.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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())
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.
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"
-> 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
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.
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:
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.
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)
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.
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.
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"
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";
þ -> 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 &.
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.
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:
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.
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.
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.
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.)
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
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 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.
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.
|
|
|||||||||||||||||||
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:
For additional information on initialization scripts, see 7.7 Tcl: Shell 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.
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.
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
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.
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.
|
|
|||||||||||||||||||
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.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
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.
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
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
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.
|
|
|||||||||||||||||||
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
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
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:
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
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:
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.
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( ).