31 Oct, 1992 CodeWatcher - Resource tracking on an individual program basis This document reflects the version 1.4 release of the software. For a list of 1.0 to 1.4 changes, see the end of this document. OVERVIEW CodeWatcher is a program that creates an environment identical to that of a CLI under which any application can be executed. While the application is executing, all resource allocations and releases are tracked. The value of this is that it now becomes a simple matter to reassure yourself that for example, all memory allocated is freed at the time of process termination. Another use is to monitor the resources used by an existing application if you beleive that bugs exist in someone else's product, or if you simply want to get a better idea of what system resources a particular application uses. THE PARTICULARS - INVOKING CodeWatcher, AND WHAT IT WILL DO FOR YOU Starting up CodeWatcher is a simple matter. At the CLI prompt, simply type: CodeWatcher [arg1, arg2...argN] where: is the name of the program you wish to execute. [arg1, arg2...argN] are the arguments required by the program to be watched. CodeWatcher itself has no command line arguments or switches. Programs started under CodeWatcher believe that they are running under their own CLI processes. At this time, there is no way to launch a program as though it came from Workbench. CodeWatcher can run any Amiga program except for those that have knowledge of the BCPL internals of AmigaDOS (e.g. most of the commands in the C: directory of a standard pre-2.0 Workbench disk). Additionally, it is not recommended that programs which leave behind a piece of code in the system which has SetFunction()'ed any library vectors be run under CodeWatcher. CodeWatcher begins tracking resources just before the first instruction of the program is executed and stops tracking immediately after the last instruction has been executed. Resources are tracked only for this particular CLI process, and no others in the system. The following resources are tracked: Process/task Address - the address of the Exec task structure. SegList elements. Files - any files which were opened, but not not closed are listed at process termination time. Locks - any locks which were obtained through Lock(), DupLock() or CreateDir() that were not unlocked are listed. Sprites - the numbers of any sprites allocated by your program which were not freed before exit are output. Open Fonts - at the time of process termination, a list of all fonts accessed by the program will be output. Any non-zero open counts mean that a font was either not closed enough times (positive open count) or closed too many times (negative open count). Interrupt Servers - all interrupt servers added by the program will be listed. The list can be used to verify that servers were added to the system. The "Rem" field indicates with a "Yes" or "No" whether the server was removed from the system by the program. In the output, the "Interrupt" field is the addres of the Exec interrupt structure and "Number" is the number of the custom chip interrupt (e.g. 5 is a VERTB interrupt). Tasks - all tasks launched by the program will be listed. The list can be used to verify that tasks/processes were actually added to the system. The "Rem" field indicates wether the Task was removed by the program ("No" - not removed, "Self" removed with RemTask(0) or by executing a final RTS, "Yes" - removed). In the output, some labels may seem obscure because they have been made short enough to fit in most windows (template is as follows: task name, address, priority, initialpc, finalpc, stack addr, removed). Message Ports - all message ports added to the system wide list by the program are tracked, and listed in the output. The "Rem" field in the output is the same as for Interrupt Servers. Open Libraries - at the time of process termination, a list of all shared libraries accessed by the program will be output. Any non-zero open counts mean that a library was either not closed enough times (positive open count) or closed too many times (negative open count). Open Devices - when the program exits, a list of all exec devices accessed by the program will be output. Any non-zero open counts mean that a device was either not closed enough times (positive open count) or closed too many times (negative open count). Memory Allocation - all of the memory allocated and released during process execution, both indirectly by system calls and directly by calls to Exec's memory management routines are tracked. Any allocations that are outstanding at process termination time, are listed including the address of the allocated memory, the size, and the return address of the calling routine. With this information, it is easy to determine whether your program is directly allocating memory which is not freed or whether the system is doing it on your behalf. NOTE: Partial frees of memory blocks are not tracked. To help pin down any offending routines, the hunk number and offset into the hunk of the instruction after the AllocMem() call are output. Process MemEntry - the contents of the process MemEntry structure are output at termination time. This structure may contain elements that were allocated by the process and specifically placed there by the program, and elements entered into the structure by AmigaDOS before actually adding the process to the system. All allocations listed here are freed by the system when the process is removed. Thus, this is not truly tracking anything, but is here simply for educational purposes, and to allay any doubts that elements being added to this structure by a program are being added correctly. Any elements listed both here, and in the aformentioned list of memory allocations were done directly by your process or by system code which your process called and is not part of the memory which was allocated by AmigaDOS to actually support your process. Priority - if the program terminates without restoring the process priority to its initial state, it will be noted. Traps - any traps that remain allocated at termination time are noted. Signal Bits - any signal bits that remain allocated at termination time are noted. Interrupt Disable Count - the value of the interrupt disable count as modified by the Enable() and Disable() calls, is output at program exit time. Task Disable Count - the value of the task disable count as modified by the Forbid() and Permit() calls, is output at process termination time. Current Directory - if the program exits without restoring the current directory to where it was when the program was started, it will be noted. Window Pointer - if the program exits without restoring the process window pointer to its initial state, it will be noted. Stack usage - the amount of stack space used by your program. Return Code of the watched program. WHAT CodeWatcher DOES NOT DO CodeWatcher will not attempt to release any resources which were allocated by the process but not freed, because these may still be in use by the system to be released at a later time, or may be used by another task or process, or interrupt server which the one that was being watched launched. Currently, CodeWatcher only tracks those resources that are "invisible" - one cannot tell visually without some sort of aid whether a resource was allocated and freed or not. An example of a resource that does not fall into this category is an Intuition Window. POINTS OF INTEREST Do not be alarmed if the tracked code runs a bit slower than normal, especially when doing things like window resizing, because a linked list of allocated memory is being maintained. When allocating and freeing many tiny chunks searching this list can take some time. Do not be alarmed by dos.library open counts that are greater than zero. It appears that AmigaDOS opens itself in the normal way, but closes itself through some sort of internal call which I am not able to track. Chunks of memory which are allocated but not freed and have a return address in the range of $F80000-$FFFFFF are being allocated by the system and will be freed at a later time unless they were allocated as say part of a window which was opened, but not closed. All programs should be able to have four or less SegList elements unless they are leaving behind a particular element which is part of a new process, some sort of code or data which is intentionally being left behind after the process terminates or code or data that was loaded as part of an overlay operation. If your program does not fall into one of these categories and you have more than four SegList elements, then you may be linking less efficiently than you otherwise could. There is absolutely NO excuse for SegList elements of size zero being loaded. These are generally unused BSS data hunks that can be eliminated through proper linking (this may be because you have a poor linker e.g. Alink). CodeWatcher is compatible with both the pre 2.0 and 2.0/3.0 releases of the dos.library which have data stored in a different format in the library code vectors. Programs that SetFuntion certain library vectors and do not undo the SetFunction before exiting will cause the system to die a horrible death. Another useful function of CodeWatcher is to make sure that programs do not rely on the settings of the condition codes as error flags. When CodeWatcher returns after a system call, the value of the condition codes and the contents of the scratch registers D1, A0 and A1 are almost always different from the "normal" system return values which should not be relied upon, but which some programs do. Programs which terminate for some "unknown" reason when run under CodeWatcher, but function normally when run without CodeWatcher are usually relying on the scratch registers or condition codes for return values from the system rather than checking D0. An example of this is the pre-2.0 dos.library which returns the same value in D1 as D0. In this case, some programs are relying on D1 rather than D0 for return values. This should NEVER be done, and these programs may very well break under AmigaDOS 2.0. -------- 1.3 to 1.4 changes Miscellaneous internal. -------- 1.2 to 1.3 changes The "Rem:" output of Task reports now tells exactly how a task terminated: RemTask(0) if the task exited by calling RemTask(0) (Processes created by CreateProc() seem to do this), RemTask()ed if it was removed by RemTask() but not by itself, and RTS if it simply did an RTS from its toplevel routine. Fixed a couple of bugs with respect to processes that did SegList detaching through the cli_Module. Should now work well with any method of SegList splitting/detachment. Fixed a problem where a process would spawn children, exit, my requester would appear, all children would exit before you select "Yes" or "No" from the requester, and as a result the CLI would be hung. -------- 1.1 to 1.2 changes Child tasks that were not terminated by RemTask() were always reported as not having terminated, when they really had. This has been fixed, and in addition, you can opt to have CodeWatcher stop monitoring if the main process quits but there are still children active. The Hunk and offset info has been added to the memory tracking. Stack usage is now monitored. -------- 1.0 to 1.1 changes Font resource tracking added. Task resource tracking added. Interrupt Server resource tracking added. Field labels in the output were shortened to prevent line wrapping as much as possible. Unnamed ports which were not removed are given the name "UNNAMED" in the output (as are Tasks and Interrupt Servers). All added ports are listed in the output with the addition of the Pri and Rem fields. CTRL-C/D/E/F now passed to child process. Internal cleaning and re-coding. Cleaned up this document. -------- To Be Done Allow tracking of child processes/tasks. Allow processes to be launched as though from Workbench. Allow user selection of memory free checking method. -------- This program is placed in the public domain. If there is sufficient interest, periodic updates will be posted. Feel free to suggest any new types of resources to be tracked and, of course, bugs. I have been using this code to check resource management for several years and have found it to be reliable in all situations except those specified above and through all OS revisions from 1.3 to 3.0. Please direct all inquiries to: Michael Plitkins Until end of November 1992: 110 Oak Rim Ct. #29 Los Gatos, CA 95032 (408) 356-8067 After November 1992 ("permanent address no matter where in the world I might really be :-)"): 1 Woolheater rd. Fleischmanns, NY 12430 (914) 254-5310 bix: octree