Debugging Device Drivers in Windows CE
Debugging Device Drivers in Windows CE
Debugging Device Drivers in Windows CE
by
Copyright 1999, Microsoft Corporation. All rights reserved. INTELLECTUAL PROPERTY DISCLAIMER THIS WHITEPAPER IS PROVIDED AS IS WITH NO WARRANTIES WHATSOEVER INCLUDING ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR ANY PARTICULAR PURPOSE, OR ANY WARRANTY OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION, OR SAMPLE. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED OR INTENDED HEREBY. MICROSOFT MAY HAVE TRADEMARKS, COPYRIGHTS, PATENTS OR PENDING PATENT APPLICATIONS, OR OTHER INTELLECTUAL PROPERTY RIGHTS COVERING THE SUBJECT MATTER IN THIS DOCUMENT. THE FURNISHING OF THIS DOCUMENT DOES NOT GIVE YOU A LICENSE TO SUCH INTELLECTUAL PROPERTY RIGHTS. MICROSOFT AND ITS SUPPLIERS DISCLAIM ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF PROPRIETARY RIGHTS, RELATING TO IMPLEMENTATION OF INFORMATION IN THIS SPECIFICATION. MICROSOFT DOES NOT WARRANT OR REPRESENT THAT SUCH IMPLEMENTATION(S) WILL NOT INFRINGE SUCH RIGHTS.
Sridhar Mandyam
Page 2
01/27/99
CESH, Debug Shell Tool: The second method of debugging under Windows CE involves the use of the Windows CE Debug Shell (CESH) tool. CESH can be used alone to get process, thread and debugging information. Or, Cesh can be used with the Microsoft program debugger WinDbg.exe Kernel Debugger and the Windbg tool: The third method involves the use of the Microsoft program debugger, Windbg along with the Kernel debugger component (also called the KDSTUB module). The Microsoft program debugger WinDbg is a fullfeatured debugger that allows kernel debugging and application debugging. Using the Kernel debugger provides a flexible and powerful approach that allows you to halt the system, or lookup call stacks and stack traces, or step into code, or debug system-level or driver-level crashes, and so on. Another interesting point to note here is that all these 3 debug methods can be used not only to debug system-level components such as device drivers, but also user-level components such as user applications. In addition, the Windows CE Platform Builder includes all the debug tools necessary to debug using the above methods. Debug Terminology The following is a list of terms associated with the Windows CE debug services: Debug Build Contains no optimizations and contains debug messages. The debug build is enabled by the SET WINCEDEBUG=debug command. Retail Build Contains optimizations and retail messages. Debugger is inaccurate due to optimizations. The retail build is enabled by the SET WINCEDEBUG=retail command. Debugger Image NK.BIN image which launches kdstub at boot. Enabled with SET IMGNODEBUGGER=. Disabled with SET IMGNODEBUGGER=1. DBGPARAM Debug parameter structure included in each driver or application to allow debug information to be registered and debug zones to be enabled or disabled. DebugZone Zone identifier defining a class of debug messages. Up to sixteen debug zones may be defined. RETAILMSG Macro for debug message displayed on either debug or retail build when the zone for the message is enabled. DEBUGMSG Macro debug message displayed only on debug build when the zone for the message is enabled. ERRORMSG Macro for error message which will display as Error: <file line number> your error message. RETAILLED Macro for display value to debug LED. DEBUGCHK(exp) Assert will call DebugBreak() if exp is false.
Sridhar Mandyam
Page 3
01/27/99
WinDbg KBSTUB
Text shell which runs over parallel or ether from the target device to the host desktop. The Text Shell provides several debug capabilities including allowing you to start applications, monitor processes, threads and memory information, and set debug zones. The kernel debugger The kernel debugger stub which is built into the image when the SET IMGNODEBUGGER= environment variable is set.
Ethernet Debugging Prior to Windows CE version 2.1, all the debug tools and services were tied to one particular debug transport. For example, PPSH tool always worked via the parallel connection, Windbg and CESH worked via the Serial connection. In Windows CE version 2.1 and later, the debug tools can be configured to any debug transport (such as serial, parallel, or Ethernet), which is done at the OAL level. For more information on how to configure the debug transports, please refer to the Platform Builder documentation. Note that Ethernet debugging is the preferred method, and there are several reasons for this. Debugging via the Ethernet transport is fast and flexible compared to parallel/serial connections, and Ethernet debugging requires fewer setup configurations. Another key advantage to Ethernet debugging is that you can debug multiple Windows CE target machines with a single desktop or host machine. However, there are certain requirements to use Ethernet debugging. First, you must have a dedicated Ethernet controller on the Windows CE target device, which is used exclusively for Ethernet debugging and can not be used for product Ethernet connections. The second requirement is that you must have the correct Ethernet bootloader program that is configured to work with the Ethernet transport. Consult the Platform Builder documentation for more information on the type of Ethernet controller, the Ethernet bootloader, and how to setup the debug Ethernet connection between the host and target machines.
Sridhar Mandyam
Page 4
01/27/99
#define ZONEID_ENTRY #define ZONEID_EXIT #define ZONEID_SECOND #define ZONEID_WARN #define ZONEID_ERROR
1 2 3 14 15
Next define a set of masks which will be used to define the zones in the dpCurSettings structure:
#define ZONEMASK_INIT #define ZONEMASK_ENTRY #define ZONEMASK_EXIT #define ZONEMASK_SECOND #define ZONEMASK_WARN #define ZONEMASK_ERROR (1<<ZONEID_INIT) (1<<ZONEID_ENTRY) (1<<ZONEID_EXIT) (1<<ZONEID_SECOND) (1<<ZONEID_WARN) (1<<ZONEID_ERROR)
Next define a set of macros which will be used as the first argument to DEBUGMSG:
#ifdef DEBUG #define ZONE_INIT #define ZONE_ENTRY #define ZONE_EXIT #define ZONE_SECOND #define ZONE_WARN #define ZONE_ERROR #else #define ZONE_INIT #define ZONE_ENTRY #define ZONE_EXIT #define ZONE_SECOND #define ZONE_WARN #define ZONE_ERROR #endif DEBUGZONE(ZONEID_INIT) DEBUGZONE(ZONEID_ENTRY) DEBUGZONE(ZONEID_EXIT) DEBUGZONE(ZONEID_SECOND) DEBUGZONE(ZONEID_WARN) DEBUGZONE(ZONEID_ERROR) 0 0 0 0 0 0
Declaring a DBGPARAM Structure Implement the debug zones and debug masks by declaring a DBGPARAM structure in the source code. DBGPARAM holds the debug output information by setting the global variable dpCurSettings. This variable defines three fields:
u u u
Module name that is used to look for zone initialization information in the development workstation registry. Debug zone names, as displayed by the control application, CESH. The control application turns the zones on and off. Initial zone mask for default, which turns on specific zones during startup.
The following example illustrates a DBGPARAM structure in the programs source code for the previously-defined debug zones and zone masks:
#ifdef DEBUG
Sridhar Mandyam
Page 5
01/27/99
// Init DBGPARAM structure DBGPARAM dwCurSettings={ TEXT(DbgSample1),{ TEXT(Init), TEXT(Entry), TEXT(Exit), TEXT(), TEXT(), TEXT(), TEXT(), TEXT(), TEXT(), TEXT(), TEXT(), TEXT(Warn), // By default, lets turn on zones for init, errors ZONEMASK_ERROR | ZONEMASK_INIT }; #endif
Registering Debug Zones After declaring the DBGPARAM structure, call the DEBUGREGISTER macro to register the structure with the debug subsystem. The syntax for this macro is DEBUGREGISTER(hMod|NULL). If debugging a dynamic-link library (file extension.dll), define the module name; if debugging an executable (file extension .exe), define NULL.
// First, we need to register with the debug subsystem DEBUGREGISTER(NULL);
After the debug zones are registered, you use macro calls in the source code to output debug messages. Retail and debug builds call different macros. Now you are ready to add messages to your code as follows:
// Then we conditionally display messages DEBUGMSG(ZONE_INIT,(TEXT(DbgSamp1 staring 1\n))); RETAILMSG(1,(TEXT(DbgSamp1 starting 2\n))); hThread= CreateThread(PeriodicThread) If (hThread == NULL) { ERRORMSG(1,(TEXT(!Error in CreateThread: %u\n),GetLastError())); Return(1); } DWORD PeriodicThread(LPVOID pUnused) { DEBUGMSG(ZONE_ENTRY,(TEXT(PeriodicThread: entry\n))); DEBUGMSG(ZONE_INIT,(TEXT(PeriodicThread starting\n))); While (1) { dwCurTime= GetTickCount()/1000; DEBUGMSG(ZONE_SECOND,(TEXT(Periodic, Seconds: %u\n),dwCurTime)); Sleep(1000); } DEBUGMSG(ZONE_EXIT(TEXT(PeriodicThread: exit\n))); return(0); }
Sridhar Mandyam
Page 6
01/27/99
DEBUGMSGs appear only on debug builds. RETAILMSGs will appear on either debug or retail builds. Debug messages can be totally removed by building with the SET WINCESHIP=1 environment variable. This allows messages to be totally removed from a shipping product, reducing the image size. This flag should only be turned on just prior to shipping, since normally displayed RETAILMSGs are suppressed in the retail build. Consult the Platform Builder documentation for further information on debug macros and the environment variables.
Sridhar Mandyam
Page 7
01/27/99
Starting the Debugger using a Debugger Image When you build an image with the SET IMGNODEBUGGER= environment variable setting, an image is created which includes KDSTUB the kernel debugger stub. This is called a debugger image. When the image is booted, the kernel debugger stub sends synchronization debug packets over the serial or Ethernet link to the host PC running Windbg. Windbg will recognize the synchronization packets and connect to the target platform, causing the image to start. If the image fails to start, you can enter .resync in the WinDbg command window to try to get WinDbg to connect to the target platform. If this does not work you may need to close WinDbg, restart WinDbg and download the image again. Note that with this method Windbg tool must have already been launched and running on the host PC prior to booting the Widows target machine. Or else the connection between the host and target will fail and the target machine will not boot and appear to be hung. Starting the Debugger using a NoDebugger Image Even if you are running a IMGNODEBUGGER image (i.e. one which does not have the kdstub linked in), you can still use the WinDbg debugger. LOADDBG.EXE and DBG.DLL provide a means of starting up the debugger on a IMGNODEBUGGER image. If you are running a development system which is connected to the host PC using CESH, you can start the debugger by entering the following command in the CESH command shell: S loaddbg If you are not connected to the host PC using CESH, you can copy loaddbg.exe and dbg.dll to the device via serial connectivity software and then start loaddbg from the shell on the device. Versions of loaddbg.exe and dbg.dll are included in the Platform Builder for all supported CPUs. You must have both files or loaddbg will not start.
Sridhar Mandyam
Page 8
01/27/99
Note that starting the debugger using loaddbg consumes more system resources than using a debugger image mentioned in the previous section. Loaddbg does not work with the debug Ethernet transport (i.e. you must run the debugger over a serial connection to use loaddbg). Useful WinDbg Commands The following are some WinDbg commands that you may find useful: !reload reloads all modules. Load notifications resent. Source level debugging capability regained. .resync resyncs with target. If the system appears to hang up, try this command before rebooting. Help or Help FOO obtain help k command obtain call stack for current thread !process (or gi all, gi thrd in CESH) lists running process/threads
!thrdstk <pThread> - call stack fir thread in list. Thread ID from debug output. Includes argument list, module name and line number.
Selecting Context for Variables You will sometimes need to give WinDbg additional context information in order for it to correct evaluate the variable. The format of a context expression is:
{[[<number>] <proc>] [, [<module>] [, [<exe>]]]}
For example, if you want to evaluate the variable dpCurSettings for filesys.exe you would first try:
?dpCurSettings
If you expand the context expression to include the module name as follows:
?{,,filesys.exe}dpCurSettings
Sridhar Mandyam
Page 9
01/27/99
Setting Break Points When setting break points, it is important to note that WinDbg modifies the code in order to generate the break point. If you are running your image from FLASH or ROM, you will not be able to set a break point for a module which is execute in place in the FLASH or ROM. In order to set a break point, you will first need to remove the module you wish to set the break point on from the image and let that module load into RAM using CESHs remote connection to the device using either the parallel or Ethernet transport. Once the module is loaded in RAM, you can then set a break point on that module. The module must also be in RAM in order to single step. If your image is being downloaded to RAM instead of FLASH or ROM, you do not need to remove the module you wish to debug from the image. You cant set a break point when the system is in the GO state, you must be in the HALTed state. You can get to the HALTed state either by entering the break command in CESH or by starting WinDbg without the g option. You can then use F9 in the source at the line you wish to break on or set the Break Point from the toolbar. You can also add a DebugBreak() in your code to break into the debugger. Dumping Memory When dumping a string it is important to note that Windows CE is Unicode, therefore you must use the U qualifier to dump strings, not 'A. Use the following commands to dump strings: du on CMD line displays string su in Watch windows displays string You may also use the following dump commands: db, dw, dd dump byte, word or dword r dumps registers fr dumps FP register
Sridhar Mandyam
Page 10
01/27/99
M00: unimodem.dll M01: TAPI.DLL M02: keybddr.dll M03: touch.dll M04: DDI.DLL M05: Redir.dll M06: irdastk.dll M07: netbios.dll M08: dhcp.dll M09: arp.dll M10: tcpstk.dll M11: ppp.dll M12: CXPORT.dll M13: AFD.Dll M14: ole32.dll M15: softkb.DLL M16: waveapi.dll M17: WINSOCK.dll M18: IRCOMM.DLL M19: irsir.dll M20: NDIS.Dll M21: wavedev.dll M22: msfilter.dll M23: CEDDK.dll M24: Serial.Dll M25: PCMCIA.DLL M26: toolhelp.dll M27: coredll.dll
8c5a1800 8c595c00 8c5a0000 8c59f800 8c5aa800 8c5eb7dc 8c5ed3ac 8c5ee050 8c5ee42c 8c5ee668 8c5ef21c 8c5efb3c 8c5efe0c 8c5effd0 8c5f08a4 8c5f0b9c 8c5f1840 8c5f1bd8 8c5f1ebc 8c5f28d4 8c5f3154 8c5f3f38 8c5f421c 8c5f4e94 8c5f506c 8c5f6050 8c5f70a0 8c5fdd9c
00000008 00000008 00000010 00000010 00000010 00000008 00000008 00000008 00000008 00000008 00000008 00000008 0000008 00000008 00000008 00000008 00000008 00000008 00000008 00000008 00000008 00000008 00000008 00000008 00000008 00000008 00000044 0000007f
01bc0000 01bd0000 01b10000 01b00000 01b30000 01ba0000 01be0000 01b90000 01c10000 01c60000 01bf0000 01cf0000 01ce0000 01c70000 01d50000 01e40000 01d90000 01cc0000 01cd0000 01ad0000 01c50000 01b20000 01ac0000 01d80000 01ae0000 01af0000 01f60000 01f90000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Example Output from Get Information on Processes: The following output is a result of running the CESH command gi proc for a sample ROM image to get the list of processes running in the system.
PROC: Name P00: NK.EXE P01: filesys.exe P02: shell.exe P03: device.exe P04: gwes.exe P05: taskman.exe P06: gi.exe hProcess: ec5fe7e2 ec5fca62 cc5f7cb2 ec5f6d4a 0c5ea9d6 0c590ee2 2c588302 CurAKY 00000001 00000002 00000004 00000008 00000010 00000020 00000040 :dwVMBase 02000000 04000000 06000000 08000000 0a000000 0c000000 0e000000 :CurZone 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Example Output from Get Information on Processes and Threads: The Windbg command !process displays much the same information as the CESH command gi all (get information, all) or gi thrd (get information, threads). Before you can use !process (or any WinDbg ! command), you must stop the process that is running on the target platform by entering the Break command from Windows CE.
Sridhar Mandyam
Page 11
01/27/99
PROC: Name hProcess: CurAKY :dwVMBase THRD: State :hCurThrd :hCurProc :CurAKY :Cp:Bp P00: NK.EXE ec5fe7e2 00000001 02000000 T Blockd 0c5fd05a ec5fe7e2 00000001 3 3 T Blockd ac5fd266 ec5fe7e2 00000001 7 7 T Blockd 0c5fd54a ec5fe7e2 ffffffff 2 2 T Blockd ec5fe6e2 ec5fe7e2 00000001 1 1 P01: filesys.exe ec5fca62 00000002 04000000 T Blockd ec5fca82 ec5fca62 00000003 3 3 P02: shell.exe cc5f7cb2 00000004 06000000 T Sleepg 2c5f8816 cc5f7cb2 00000005 1 1 P03: device.exe ec5f6d4a 00000008 08000000 T Sl/Blk 4c5bf3c2 ec5f6d4a 00000009 2 2 T Blockd 0c5ac4ea ec5f6d4a 00000009 2 2 T Blockd 0c5eb896 ec5f6d4a 00000009 3 3 T Blockd 0c5ebd2a ec5f6d4a 00000009 3 3 T Sl/Blk ec5eeaa6 ec5f6d4a 00000009 2 2 T Blockd 0c5f1012 ec5f6d4a 00000009 2 2 T Sl/Blk 0c5f1f76 ec5f6d4a 00000009 2 2 T Blockd 2c5f2182 ec5f6d4a 00000009 2 2 T Blockd 0c5f320e ec5f6d4a 00000009 1 1 T Blockd ac5f42d6 ec5f6d4a 00000009 3 3 T Blockd cc5f5346 ec5f6d4a 00000009 3 3 T Blockd ec5f5552 ec5f6d4a 00000009 0 0 T Blockd ec5f6d6a ec5f6d4a 00000009 3 3 P04: gwes.exe 0c5ea9d6 00000010 0a000000 T Blockd 2c584ec2 0c5ea9d6 00000011 3 3 T Blockd 0c5911f6 0c5ea9d6 00000011 3 3 T Blockd 0c59210e 0c5ea9d6 00000011 1 1 T Blockd 0c5948f6 0c5ea9d6 00000011 3 3 T Blockd 0c594b02 0c5ea9d6 00000011 1 1 T Blockd 0c595302 0c5ea9d6 00000011 1 1 T Blockd 0c5973c2 0c5ea9d6 00000011 1 1 T Sl/Blk 0c59f502 0c5ea9d6 00000011 1 1 T Blockd 0c5ea9f6 0c5ea9d6 00000011 3 3 P05: taskman.exe 0c590ee2 00000020 0c000000 T Blockd 0c590f02 0c5ea9d6 00000031 3 3 P06: gi.exe 6c5863e2 00000040 0e000000 T Runing 4c5bf3e2 6c5863e2 f fffffff 3 3
:CurZone :CPU Time 00000000 00:00:00.002 00:00:00.029 00:00:00.024 00:00:00.095 00000000 00:00:00.696 00000000 00:00:00.437 00000000 00:00:00.012 00:00:00.002 00:00:00.001 00:00:00.000 00:00:00.008 00:00:00.000 00:00:00.010 00:00:00.004 00:00:00.000 00:00:00.000 00:00:00.001 00:00:00.000 00:00:00.883 00000000 00:00:00.004 00:00:00.023 00:00:00.001 00:00:00.012 00:00:00.008 00:00:00.000 00:00:00.000 00:00:00.004 00:00:00.380 00000000 00:00:00.426 00000000 00:00:00.708
Sridhar Mandyam
Page 12
01/27/99
The current debug zone setting in effect for this process. This relates to the DEBUGZONE bits and the DBGPARAM structure. lpvAddr The base address for the process in the global virtual address space of the target platform. For example, if the relative address of a local variable in Gwes is 0x000568f0, you must add its address to the base address of Gwes, 0x06000000, and use address 0x060568f0 to access the variable in the debugger. Baseptr The real base address where the process was loaded. The linker typically picks an arbitrary base address that it uses to link and which you can see in your map file. The loader fixes up the process modules to this address. In Windows CE, all processes are loaded at 0x10000.
In addition to these entries, each process can have any number of threads associated with it. Note that Windows CE allows up to 32 processes. Each thread has the following fields:
State Indicates the state of the thread: Value Description 0 Thread is running. 1 2 Thread is runnable. Thread is blocked.
3 Thread is suspended. 4 Thread is sleeping. 5 Thread is blocked and sleeping. pRunQ Unused and is always zero. pThread The address of the kernel-maintained thread structure. You can look at the structure by typecasting pThread to a (PTHREAD). pCurProc A handle to a pProcess; you must translate this value to get the actual pProcess. For example, to translate the value of pCurProc for the file system (0x2c5fce9a) perform the following steps. 1. Set the high nibble of pCurProc to 0x8 (value is now 0x8c5fce9a) 2. Add 0x16 (value is now 0x8c5fceb0) 3. Display the DWORD value at the calculated address. In this example, at the prompt Windows CE>, enter dd 0x8c5fceb0 l1. The value translated according to the above procedure is the address of the process in whose context the thread is currently running. You can use this field to determine if a thread is inside a protected server library (PSL) by comparing this value to the values of the pProcess fields of the listed processes. The current address space is the one corresponding to this process. To look at variables in other address spaces you must map the variables address, as described in the discussion about the lpvAddr field. Aky The access keys for the current thread. This field is not valid for the currently running thread. If the thread is inside a PSL, Aky includes permission bits for the owner process and the PSL process. Priority Bits 4 through 7 of this value represent the current priority; bits 0 through 3 represent the base priority. The current priority is the priority at which the process is currently running, and the base
Comment The thread is currently running. The thread is currently runnable and will run when the OS is next timeslicing at that threads priority The thread is blocked on some resource: one or more events; mutexes, and/or critical sections. The thread has been suspended by a call to SuspendThread. The thread has called the function Sleep. (See previous descriptions for blocked and for sleeping).
Sridhar Mandyam
Page 13
01/27/99
priority is the priority at which the process will run if no inversion is occurring. Typically, the base priority and current priority are the same value (as happens to be the case in the above sample output).
Using the CESH Memory Information Tool The CESH Memory Information Tool provides information on current memory usage. The memory information command is invoked in the CESH remote shell running on the desktop system signified with the Windows CE> prompt. mi [kernel | full] - Displays memory information. The kernel option displays the kernel memory detail. The full option displays full memory maps. mi kernel dump: The mi kernel dump is useful for tracking down memory leaks. The display breaks memory usage down by each of the kernel object types. When you suspect that you have an operation that is leaking memory, you can call the mi kernel tool before and after the operation leaking memory. By comparing the before and after display, you can determine the type of objects leaked.
Page size=4096, 2884 total pages, 2658 free pages. 25 pages used by kernel, 0 pages held by kernel, 226 pages consumed. Inx Size Used Max Extra Entries Name 0: 168 4032 4872 840 24( 29) Thread 1: 184 4416 4416 0 24( 24) Module 2: 24 432 456 24 18( 19) APISet 3: 84 5628 5712 84 67( 68) Event 4: 16 160 240 80 10( 15) CleanEvent 5: 76 34732 35036 304 457(461) MemBlock 6: 40 1360 1360 0 34( 34) CallStack 7: 520 2080 2600 520 4( 5) Name 8: 44 88 88 0 2( 2) FSMap 9: 64 832 832 0 13( 13) FullRef 10: 116 0 0 0 0( 0) ThrdDbg 11: 36 0 0 0 0( 0) Proxy 12: 100 15600 15600 0 156(156) Crit 13: 104 104 104 0 1( 1) Mutex 14: 32 4000 4160 160 125(130) HData Total Used = 73464 Total Extra = 2012 Waste = 28
Sridhar Mandyam
Page 14
01/27/99
0c020000(0): --------------SS 0c030000(0): W--------------0c090000(0): --------------SS 0d830000(0): -CCCCCCCCCCCCCCC 0d840000(0): CCCCCCCCCCCCCCCC 0d850000(0): CCCCCCCCCCCCCCCC 0d860000(0): CCCCCCCCCCCCCCCC 0d870000(0): CCCCCCCCCCCCCCCC 0d880000(0): CCCCCCCCCWrrrrrr 0d890000(0): rrrrrrrrrrrrrrrr 0d8a0000(0): r-Page summary: code=92(0) data r/o=0 r/w=30 stack=4 reserved=142
In the mi full mode, the following characters are used to denote memory usage for each page in each process:
- - not used C code execute in place in ROM c code executing in RAM r read only data in RAM R read only data in ROM S stack O object store data P Peripheral memory used for driver globals, frame buffers, etc. W read/write data
Sridhar Mandyam
Page 15
01/27/99