\input amiga \input texinfo @c -*-texinfo-*- @setfilename inspector.guide @settitle @code{inspector}'s manual @iftex @afourpaper @end iftex @setchapternewpage odd @finalout @titlepage @title inspector @subtitle A debugging tool for the Amiga @subtitle Version 1.0 @author by Sebastiano Vigna, Fabrizio Lodi and Reinhard Spisser @page @vskip 0pt plus 1filll Copyright @copyright{} 1993 Sebastiano Vigna, Fabrizio Lodi and Reinhard Spisser @end titlepage @ifinfo @node Top @top This file describes @code{inspector}, a powerful debugging tool for the Amiga. It allows for selective parameter checking and printing af almost all system and custom shared library calls. @noindent Copyright © 1993 Sebastiano Vigna, Fabrizio Lodi and Reinhard Spisser @menu * Introduction:: * A Sample Session:: * Inspecting Through Ready-Made Libraries:: * Writing Your Own Libraries:: * Workarounds:: * Acknowledgments:: * Concept Index:: @end menu @end ifinfo @node Introduction @chapter Introduction @cindex sushi @cindex SegTracker @code{inspector} is a new, powerful debugging tool for the Amiga. The original motivation for it was to allow a selective, intelligent parameter checking on the system functions. It was then natural to include reporting capabilities similar to @code{wedge}. @code{inspector} uses a standard mechanism of the Amiga operating system, the @code{SetFunction()} Exec call, which allows to change the jump tables of shared libraries in such a way to divert the execution flow of a system call to a suitable piece of code. This is called @dfn{patching a (shared library) function}. Restoring the execution flow to its original path will be called @dfn{unpatching a (shared library) function}, or @dfn{removing a patch}. In our case, the @dfn{patch code} will @dfn{inspect} the arguments of the function, i.e., will check if they correspond to the specification contained in Commodore's official documentation. For instance, it is not allowed to pass a @code{NULL} pointer to @code{FreeMem()}. If @code{FreeMem()} is patched, and a task tries to pass it such a pointer, an error message will be displayed on the serial or parallel port, as with many other debugging tools (@code{sushi} can be used to divert the output to a CLI). @code{SegTracker} support allows to know even the line of code which actually made the call. This requires, of course, the creation of (rather sophisticated) @dfn{structure inspection functions}. Currently only a very small part of these functions has been realized. They usually check if a certain system structure looks as it should (for instance, they can check if pointers to DOS structures are aligned on a longword). The other (fully implemented) half of @code{inspector} is formed by its reporting capabilities. Even if the patch code does not contain any argument inspection part, it can display a message when the function is called, show its arguments and so on. The use of @code{inspector} is entirely straightforward. A few ARexx functions, AmigaDOS commands or shared library functions allow to activate and control every feature of the system. This multiple interface allows for very a simple to use, but flexible debugging tool. @code{inspector} is distributed with a series of inspection libraries which allow to patch most system functions. However, @code{inspector} is an open system, built around a code generator which reads in the @file{.fd} and the prototype files of a library (for instance, @file{asl_lib.fd} and @file{asl_protos.h}), producing a skeleton for an @dfn{inspection library} which can already perform a series of reporting activities (such as parameter and result display). The code generator reads in also the contents of a @file{.insp} file (in our example, @file{asl.insp}) which describes what code should be inserted in order to check the correctness of the parameters of each function. The result is an Exec library which can then be used by @code{inspector} in order to @code{SetFunction()} the original library vectors. Thus, if you have any custom library of your that you would like to inspect, it is just a matter of a few commands. Our aim is to build a system which can be completely rebuilt in a few minutes whenever the system specifications change, new function are added to system libraries, or new libraries are added to the system. @node A Sample Session @chapter A Sample Session Supposing that you installed @code{inspector} and that you added @file{inspector.library} to the list of ARexx function libraries with a command like @example rx "call addlib('inspector.library',0,-30,0)" @end example @noindent (note that if you use WShell you can avoid @samp{rx} and the last quote) we can try to patch some function. I'm assuming here that you can read somehow what will be printed on the serial port. Either you have a high speed modem and a terminal program running, or another Amiga connected, or you can use @code{sushi}, which will redirect the serial output to a CLI. Let us first set up some flags: @example rx "isetflags(,,set,forcing,verbose,noexpunge,content)" @end example This command will force all the patches in the system to display entry points, arguments and return values of each patched function (note that ARexx considers the initial value of a new variable being its capitalized name). Moreover, even if we remove the patches the patch code will never be expunged from the system. This is much safer. Now we can try the following command: @example rx "ipatch('dos','Open',1)" @end example The AmigaDOS @code{Open} call is now under the control of @code{inspector}. Try to run a program which has to open some files, and look at the result; a series of block of text like the following one will be printed: @example Process AmigaGuide ($079860A8) enters Open [F=0 D=0 ID=14] name (d1): $079D74D4 454E563A 416D6967 61477569 64652F50 ENV:AmigaGuide/P accessMode (d2): 1005 Result: $01E5E0AD [ID=14] 00000001 00000000 07882B6C 00000000 ..........+l.... @end example @code{inspector} firstly displays a line describing the kind of event which is going to happen: in this case, a process called @code{AmigaGuide} is going to call @code{Open}. The call happens with interrupts enabled, no forbid, and gets a unique identificator, 14. Other headers are @example System code enters Disable [F=0 D=0 ID=14] @end example @noindent which is reserved for any code running on the supervisor stack (interrupts, for instace), and @example Task input.device enters Disable [F=0 D=0 ID=14] @end example @noindent which is reserved for tasks not being a process. Note that if a process has an associated CLI structure, the command currently executed is displayed, too. @example Process Shell ($079860A8), command type enters Open [F=0 D=0 ID=14] @end example The first two numbers between the square brackets denote the nesting depth of @code{Forbid()} and @code{Disable()} (in this case, none). The third number is a systemwide unique identificator which is of invaluable help in associating the report of the return value of a call with the report of its entry point. Moreover, since is systemwide, it can also help in case of multiple process reporting at the same time their function calls. Immediately after, we can see the first parameter, @code{name}, its value, the register it is in, and the first sixteen bytes of memory it points to, both in ASCII and sedecimal. The second register is an integer, and as such it is displayed differently: just a decimal number. Finally, we can see the result of the function call: @code{inspector} displays the memory it points to but, knowing it's a BCPL pointer, it shifts the address first. Near the result value you can see again the unique identificator. But this is just half of @code{inspector}. When a function is patched (and unless a special flag is active), the parameters passed to the function are inspected before the call. Thus, error such as passing a @code{NULL} pointer to @code{FreeMem()} are immediately tracked and reported. Of course, such checks rely on special code which cannot be generated automatically, but has to be carefully crafted looking at the documentation. @node Inspecting Through Ready-Made Libraries @chapter Inspecting Through Ready-Made Libraries In this chapter we will review deeply how to use @code{inspector} with ready-made libraries, i.e., without diddling with compilers, code generators and so on. @menu * The Overall Structure:: * The Flags:: * The Main Library:: * The Inspection Libraries:: * ARexx Functions:: * DOS Commands:: * Direct Function Calls:: @end menu @node The Overall Structure @section The Overall Structure @code{inspector} is a bit wierd, when compared to other debugging tools. First of all, there is no ``program'' running. Some code is loaded (or unloaded) by the library caching system, but that's all. The few commands supplied with the package execute for a very short time, set up some values and exit. The system core is @file{inspector.library}. It is a standard, shared Exec library with an ARexx function dispatcher at -30. The most comfortable way of controlling the system is indeed through ARexx. The first duty of @file{inspector.library} is to patch or unpatch functions, i.e., to change their entry point using @code{SetFunction()}, or to reset it again to its original value, respectively. Once you set up some patches, there can be visible effects only if you activate some reporting features, or if the argument inspection code of a patched function recognize a wrong argument. In this case, a message will be printed on the serial (or parallel, depending on a flag) port. @node The Flags @section The Flags @cindex sushi Everything in @code{inspector} is controlled by a series of @emph{flags}. There are three classes of them: @itemize @bullet{} @item @emph{global} flags are systemwide, i.e., there is a single copy of them in @code{InspectorBase}; @item @emph{semilocal} flags pertain to a single library, i.e, every inspection library has its own semilocal flags in its base; @item @emph{local} flags pertain to a single function. @end itemize For maximum flexibility, every flag, beside its value, has a @emph{mode}. There are currently three modes: normal, transparent and forcing. There are neither global transparent flags, nor local forcing flags, for reasons that will be clear in a few lines. When a flag is in forcing mode, it forces its value over all the flags which are less general. For instance, in our sample session the command @example rx "isetflags(,,set,forcing,verbose,noexpunge,content)" @end example @noindent forced all the functions in the system to be verbose and to display the content of the pointer arguments, and all the inspection libraries in the system to refuse to be expunged, by setting the global flags and putting them in forcing mode (note that the global flags are addressed by not specifying a library or a function name; @pxref{ISetFlags()}). When we talk about the @dfn{state} of a certain flag for a function (local) or a library (semilocal), we do not mean simply its value: we have also to consider the side-effects of the modes. After the previous @code{isetflags()} call, the state of @emph{all} local and semilocal @code{Verbose}, @code{NoExpunge} and @code{Content} flags in the system is @emph{true}. Their values, however, can be anything (more on this later). Forcing flags are also very useful in order to force behaviours on a per-library basis. For instance, if we would like to get the contents of the DOS calls only, we can just call @example rx "isetflags('dos',,set,forcing,content)" @end example Of course, there is no lower level than a single function, so a forcing flag for a function is nonsense. A normal flag does not force its value over lower levels: unless it is overriden by a higher-level forcing flag, it gives the state of the flag. A transparent flag is a flag whose state has to be determined by looking at the state of the upper level flag. For instance, if the semilocal @code{Verbose} flag is active, and a @code{Verbose} local flag is trasparent, its state will be considered true. The rôle of transparent flags is to allow a very easy setup of global, per-library behaviours with spare exceptions. You can put all local flag of a library in transparent mode, and then work just on the semilocal flag, in normal mode. Whenever a function needs special treatment, you can easily put its local flags in normal mode and set up the flags as needed. The exact algorithm for determining the state of a local flag is the following one (for semilocal flags, you just skip the middle step): @itemize @bullet{} @item if the global flag is forcing, return its value; @item if the semilocal flag is forcing, return its value; @item if the local flag is normal, return its value; @item if the semilocal flag is normal, return its value; @item return the global flag value. @end itemize It is now time to check the available flags: @table @code @item Patched can only be read by an ARexx function, in order to check if a given function is patched or not. It is automatically handled by @code{InspectorPatch()} @item Disabled imposes that no inspection or report is performed. The original function is called immediately, just after the few instructions needed in order to check the state of the flag. Note that it is usually safer to disable a patch rather than to unpatch the function, in particular if you plan to patch it again anyway. @item Entry tells @code{inspector} to display a message when the function is called. @item Result tells @code{inspector} to display the result of the function call. It implies @code{Entry}. @item Arguments tells @code{inspector} to display the arguments of the function call. It implies @code{Entry}. @item Verbose is a fast way for displaying the entry point, the arguments and the result. @item Abort tells @code{inspector} not to call the original function if some parameter is not correct. @item TaskInclude forces @code{Disabled} for all task which are @emph{not} in the task inclusion list (@pxref{The Main Library}). @item TaskExclude forces @code{Disabled} for all task which @emph{are} in the task exclusion list (@pxref{The Main Library}). @item Forbid tells @code{inspector} to surround with @code{Forbid()}/@code{Permit()} the argument and result printing, in order to minimize overlapping reports in case many process are inspected. @item Parallel chooses the parallel port instead of the serial port for reporting. This flag is only global. @item NoExpunge keeps the main and inspection libraries from being expunged (the patching system much safer this way). This flag is only global and semilocal. @item NoCheck disables the parameter checking. This option can be useful if for some reason the current inspection code is obsolete, and could mistakenly judge wrong some correct parameters. @item SegTracker enables @code{SegTracker} support. @code{SegTracker} is a tool provided with @code{Enforcer} which allows to obtain important informations about the code which made the function call. Usually, by using it you will be able to know the line of code which made the function call. @item Content forces the display (sedecimal and ASCII) of the memory pointed at by pointer parameters. @item NoStackCheck disables the stack checking. Usually, @code{inspector} will refuse to do any inspection or report activity on tasks with less than 512 bytes of stack. If for some reason (for instance, tasks which do not set up correctly their task stack structures) you want to check also those tasks, you can set this flag. Usually it is a very easy way of crashing your Amiga. @item Pause implies @code{Entry}; it tells @code{inspector} to stop just after the display of the function entry point, and to ask you for the permission to call actually the original function. If any character different from @samp{n} is typed, the original function is called. Note that this feature will not work when using @code{sushi}. @end table These flags can be modified by means of five operators. @code{InspectorSetFlags()} (and its ARexx and AmigaDOS counterparts) has two parameters: a mask of operators, and a mask of flags. The actions specified by the operators will be applied to the flags. @xref{ISetFlags()}, and @ref{ISetFlags}. @table @code @item Set turns on a a flag. @item Clear turns off a flag. @item Normal puts a flag in normal mode. @item Forcing puts a flag in forcing mode. @item Transparent puts a flag in transparent mode. @end table Of course, it is not very useful to specify more than one value or more than one mode modifiers. The behaviour of @code{InspectorSetFlags()}, in this case, is undefined. @node The Main Library @section The Main Library @cindex SegTracker The main library, @file{inspector.library}, provides a series of services which are vital to the inspection libraries. Some of these services are accessible as read-only fields of the public structure @code{InspectorBase}, and they include: @itemize @bullet{} @item caching of the @code{Forbid()} and @code{Permit()} calls, which can be patched as any other system function, but need to be called by the inspection functions if the @code{Forbid} flag is active; @item maintenance of the @code{print()} and @code{get()} functions; depending on the value of the @code{Parallel} flag, they can print or ask values along a serial or parallel connection; @item the address of the @code{SegTracker} address decoding call; @item the task inclusion and exclusion lists; they are extremely useful for screening out undesired reports. @xref{IIncludeTask()}, @ref{IExcludeTask()}, @ref{IInclusionList()}, @ref{IExclusionList()}. @end itemize But the most important components of @file{inspector.library} are, of course, the library functions. They are documented in the corresponding AutoDoc, so we will only scratch their surface. The task of patching or unpatching a function is accomplished by @code{InspectorPatch()}, which takes a library name (without @samp{.library} suffix) and an AmigaDOS pattern which designates the function(s) to act upon. If the last argument, a boolean, is true, the function(s) will be patched, otherwise the function(s) will be unpatched. An easy way of accessing @code{InspectorPatch()} is through the @code{IPatch()} ARexx function. If you added @file{inspector.library} to the list of ARexx function libraries, you can just call @code{IPatch()} with the same arguments as @code{InspectorPatch()} (the boolean being represented by 0 or 1) and obtain the same result. @xref{IPatch()}. Just in case you do not want to set up ARexx, there is an AmigaDOS command named @file{IPatch}. If the @samp{UnPatch} switch is specified, the function(s) are unpatched, otherwise they are patched. @xref{IPatch}. The function which controls everything in @code{inspector} (except, of course, the patches) is @code{InspectorSetFlags()}. Its C syntax is decribed in the AutoDoc. For usage through ARexx, see @ref{ISetFlags()}; for usage through a DOS command, see @ref{ISetFlags}. Finally, the main library contains a series of basic inspection functions which check if a pointer is a valid RAM pointer, and so on. They follow the standard for structure inspection functions (they return pointers to error messages), and should always be used instead of direct checking. For instance, the right way of checking for a @code{NULL} pointer is by calling @code{IsBadNonNULLP()}. @node The Inspection Libraries @section The Inspection Libraries The inspection libraries serve two purposes: first of all, they contain the code which will be used in order to apply the patches. This code does not appear in any way in the library jump table, and indeed if there is no special function programmed by the user the jump table of an inspection library is empty (there is a private entry @code{dummy()} which codes around a bug in @code{slink}). Usually, however, there is a second component: each inspection library is related to a standard library, which uses a series of structures; pointers to these structures are most frequently arguments for function calls. The functions of the inspection library are structure inspection functions for those structures; thus, they check if a pointer points to a valid structure of a certain kind. The standard is to have, for a structure @code{Foobar}, a function @code{IsBadFoobar()} which accepts a @code{struct Foobar *} pointer and returns a @code{STRPTR}; the return value is @code{NULL} if the pointer seems to point to a @code{Foobar}; otherwise, it is a pointer to a static string representing an error message. You can put an much intelligence as you want in the error messages, but they should start immediately with a verb (for instance, @code{has the wrong magic cookie}). This is the format required by @code{InspectorDisplayBadArg()}. @node ARexx Functions @section ARexx Functions This section reviews in detail the functions available through the ARexx function library in @file{inspector.library}. A first introduction is contained in the AutoDoc for @file{inspector.library}. @menu * IPatch():: * ISetFlags():: * IGetFlags():: * IIncludeTask():: * IExcludeTask():: * IInclusionList():: * IExclusionList():: * ISetSpeed():: @end menu @node IPatch() @subsection IPatch() @cindex IPatch() This function takes three arguments: a library name without the @samp{.library} suffix, an AmigaDOS pattern to be interpreted as a set of function names in the specified library, and a boolean specifying if you want to patch or unpatch the given functions. The function will return various errors in case something does not work. @xref{A Sample Session}. @node ISetFlags() @subsection ISetFlags() @cindex ISetFlags() This function takes from two to fifteen arguments. The first two optional arguments are a library name without the @samp{.library} suffix and an AmigaDOS pattern to be interpreted as a set of function names in the specified library. If both arguments are present, @code{inspector} assumes you want to modify the local flags of the specified function(s). If the AmigaDOS pattern is missing, the semilocal flags of the specified library are modified instead. If, finally, both parameters are missing, the global flags are modified (the only remaining case produces a syntax error). The remaining thirteen parameters can be any list of operators and flags. All the operations specified by the operators will be applied to all the flags. The order and the case are irrelevant. @xref{A Sample Session}. @node IGetFlags() @subsection IGetFlags() @cindex IGetFlags() This function takes two optional arguments, having the same meaning and conventions of @code{ISetFlags()} (@pxref{ISetFlags()}), but if a pattern is specified, it is associated only with the first matching function name. The current state and mode of the specified flag set will be returned as a string containing a series of lines, one for each flag. Each line contains the flag name, the current state and the current mode. @node IIncludeTask() @subsection IIncludeTask() @cindex IIncludeTask() This function takes two arguments, a task name and a boolean; it adds to or remove from the task inclusion list the task name, depending on the value of the boolean (true=add, false=remove). All functions for which the state of the @code{TaskInclude} flag is true will be inspected only for tasks whose name matches a name in the list. The comparison is case sensitive, and the special name @samp{System} can be used to specify any code running on the system stack, such as interrupts. A process with a CLI structure will be inspected if its task name or its command name are in the list. Note that you can use the task inclusion and exclusion lists at the same time. @xref{IExcludeTask()}. @node IExcludeTask() @subsection IExcludeTask() @cindex IExcludeTask() This function takes two arguments, a task name and a boolean; it adds to or remove from the task exclusion list the task name, depending on the value of the boolean (true=add, false=remove). All functions for which the state of the @code{TaskInclude} flag is true will be inspected only for tasks whose name @emph{does not} match a name in the list. The comparison is case sensitive, and the special name @samp{System} can be used to specify any code running on the system stack, such as interrupts. A process with a CLI structure will be excluded if its task name or its command name are in the list. Note that you can use the task inclusion and exclusion lists at the same time: you can, for instance, disable temporarily a task in the task inclusion list by putting it in the task exclusion list (and setting of course the @code{TaskExclude} flag). @xref{IIncludeTask()}. @node IInclusionList() @subsection IInclusionList() @cindex IInclusionList() This function takes no arguments. It just returns the task inclusion list as a string containing the task names, separated by a line feed (there is here a remote possibility of confusion if a task name contains a line feed). @node IExclusionList() @subsection IExclusionList() @cindex IExclusionList() This function takes no arguments. It just returns the task exclusion list as a string containing the task names, separated by a line feed (there is here a remote possibility of confusion if a task name contains a line feed). @node ISetSpeed() @subsection ISetSpeed() @cindex ISetSpeed() This function takes an optional argument, an integer specifying the desired bps rate of the serial port. If the argument is missing, the preferences value is used. This function is not really necessary, but it avoids the nuisance of opening a terminal program and set the speed. Note that since this function is called internally during the first setup of @code{inspector}, the default report serial speed is the preferences speed. @node DOS Commands @section DOS Commands This section reviews in detail the functions available through DOS commands. They behave exactly like their ARexx counterparts: we will limit the description to the syntactic differences, and cross-reference to the respective ARexx function description. @menu * IPatch:: * ISetFlags:: * IIncludeTask:: * IExcludeTask:: * ISetSpeed:: @end menu @node IPatch @subsection IPatch @cindex IPatch The template of this command is @example LibName/A,FuncName/A,UnPatch/S @end example The specified function(s) in the specified library will be patched, unless the @samp{UnPatch} switch is specified: in this case, the function(s) will be unpatched. @xref{IPatch()}. @node ISetFlags @subsection ISetFlags @cindex ISetFlags The template of this command is @example LibName,FuncName,Dis=Disabled/S,Entry/S,Res=Result/S, Args=Arguments/S,Ver=Verbose/S,Ab=Abort/S,TI=TaskInclude/S, TE=TaskExclude/S,For=Forbid/S,Par=Parallel/S,NE=NoExpunge/S, NC=NoCheck/S,ST=SegTracker/S,Cont=Content/S,NSC=NoStackCheck/S, Pause/S,S=Set/S,C=Clear/S,N=Normal/S,F=Forcing/S,T=Transparent/S @end example The specified flags will be modified using the selected modificators. @xref{ISetFlags()}. @node IIncludeTask @subsection IIncludeTask @cindex IIncludeTask The template of this command is @example TaskName/A,Remove/S @end example The specified task name will be added to the task inclusion list. If the @samp{Remove} switch is present, the task name will be removed instead. @xref{IIncludeTask()}. @node IExcludeTask @subsection IExcludeTask @cindex IExcludeTask The template of this command is @example TaskName/A,Remove/S @end example The specified task name will be added to the task exclusion list. If the @samp{Remove} switch is present, the task name will be removed instead. @xref{IExcludeTask()}. @node ISetSpeed @subsection ISetSpeed @cindex ISetSpeed The template of this command is @example Speed/N @end example The serial port will be set to the specified speed, or to the preferences speed if no argument is given. @xref{ISetSpeed()}. @node Direct Function Calls @section Direct Function Calls It is of course possible to call directly the function available in @file{inspector.library}. For instance, it is possible that you want to activate parameter checking just during a few line of your code. In this case, you can open @file{inspector.library} and call the functions specified in its AutoDoc. Usually, the best thing to do is to set up in advance a patch using ARexx, and then to disable it. In your code you can then include a function call like @example #include #include [...] InspectorSetFlags("exec", "Remove", OPF_Clear, FUNCF_Disabled); @end example @noindent which will enable again the inspection. @node Writing Your Own Libraries @chapter Writing Your Own Libraries @cindex Custom libraries @cindex makelib.rexx @code{inspector} is an open system. Rather than just providing you with some fixed set of inspectable functions, it gives you the tools for producing your own inspection libraries, which can apply special checks to system libraries, or even inspect the custom libraries you have written. Since the custom of distributing program code in several libraries is more and more spread out, this capability should be of some value. Note that the creation of such libraries is available only under SAS/C. We suggest the interested reader to upgrade as soon as possible at least to release 6.3. The heart of the library generation system is the code generator, @code{makelib.rexx}. It is an ARexx program which reads in the @file{.fd}, prototype and @file{.insp} files related to a library, and outputs ready-to-compile C and Assembler code. In the following sections we will explain exactly what should be the content of these three files, and how to put them together. @menu * The .fd file:: * The prototype file:: * The .insp file:: * The Library Functions:: * How to Compile:: * makelib.rexx:: @end menu @node The .fd file @section The @file{.fd} file The @file{.fd} files are function descriptors. They specify where the functions of a library should be called (i.e., their position in the library jump table), and some other information, such as the ``official'' base name for the library. They specify also the complete library name, but unfortunately in a comment in the first line of the file. It is not proper to rely on such a behaviour, but @code{makelib.rexx} has to discover somehow the name of the library, device or resource it has to open. @code{makelib.rexx} assumes the @file{.fd} files will be found in the @file{FD:} directory, and that for a library with base name @var{libname} the @file{.fd} file will be named @file{@var{libname}_lib.fd}. Note that the base name of some library is totally different from its full name. For instance, @file{workbench.library} has all related files (proto, etc.) derived from @samp{wb} (@file{wb_lib.fd}, @file{proto/wb.h}, etc.). There can be some problem with libraries which do not specify correctly their complete name, most notably @code{timer}, which specifies for unknown reasons the name @samp{Timer.Device}, and @code{ciaa}/@code{ciab}. Bug fixes are proposed in @ref{Workarounds}. @node The prototype file @section The prototype file This file provide ANSI C prototypes for all functions in the library. If a function does not have a prototype, it cannot be patched. @code{makelib.rexx} implements a rather dumb parsing scheme, which cannot handle at all the full grammar of ANSI C types; however, it is enough for all system libraries, and (we hope) for reasonably typed custom ones. @code{makelib.rexx} assumes the prototype files will be found in the @file{INCLUDE:clib} directory, and that for a library named @var{libname} the prototype file will be named @file{@var{libname}_protos.h}. @node The .insp file @section The @file{.insp} file This is the only optional file. @code{makelib.rexx} can generate a complete library even if no @file{.insp} file is available. The purpose of such a file is to specify C code for inspecting the arguments of each function of the library. The code is included in an @code{if} statements, and is executed only if the @code{NoCheck} flag is not active. The syntax of an @file{.insp} file is rather easy. Each chunk of code is enclosed between two lines contaning only @samp{[[} and @samp{]]}, respectively. Each chunk has to be preceded by a line containing the name of a function (without parenthesis), which specifies where to insert the code. @example FindName [[ /* Everything written here will be used to inspect the arguments of FindName(). */ ]] @end example Moreover, there are two fake function names, @code{InspectorGeneralCode} and @code{InspectorInspectionLibraries}. The chunk associated with @code{InspectorGeneralCode} is inserted at the start of the library C source, out of any function. You can use it to include other headers, or to declare prototypes and global variables (which anyway should never be used). The chunk associated with @code{InspectorInspectionLibraries} allows you to specify which inspection libraries you would like to open, in order to use their functions (@pxref{The Library Functions}). Each line of the chunk must contain the name of the library base (say, @samp{IAslBase}), the library base name (@samp{asl}) and the required revision number (say, @samp{37}) separated by one or more spaces. @code{makelib.rexx} will generate code which will open and close the required libraries (closing is performed in reverse order). Note that the library names are prefixed with @samp{libs:inspectors/}, so you cannot use this mechanism in order to open arbitrary libraries. @example InspectorGeneralCode [[ /* The code here can be anything */ void foobar(void); ]] InspectorInspectionLibraries [[ IAslBase iasl 37 IIntuitionBase iintuition 37 ]] @end example Usually the library writer provides functions accessible through the inspection library jump table, such as @code{IsBadNewWindow()} in @file{inspectors/iintuition.library}. @xref{The Library Functions}. The inspection code can freely access the function arguments: their names are taken from the @file{.fd} file, which can be used as a reference. Two additional variables are inherited from the outer level: @code{paramsOK} is a @code{BOOL} which should be set to @code{FALSE} if you detect a wrong parameter. @code{ID} is the function call unique identificator, and should be used whenever you have to print an error message related to a function. Moreover, you can read the contents of the @code{funcName} and @code{inspFlags} variables, which give the current function name and the current state of the local flags. There is however an important problem: if argument inspection is activated, and the @code{Entry} flag is not active, the user would not be able to understand in which function call a parameter is wrong. Indeed, @code{InspectorDisplayCaller()}, which displays the entry point and assigns the unique identificator, will not have been called. However, any reporting activity should use the @code{InspectorDisplayBadArg()} function, which can display in a standard way a bad argument, and will automatically display the entry point information if needed (returning a new identificator, if none has still been assigned). In special cases (but sparingly!) you can directly use the @code{print()} and @code{get()} functions (@pxref{The Main Library}). The best way of performing argument inspection is to set up a local @code{STRPTR} variable, @code{errMsg}. When a bad argument is detected, you should put the error message in @code{errMsg} (usually this can be done automatically by assigning the return value of a structure inspection function to @code{errMsg}), call @code{InspectorDisplayBadArg()}, assign its result to @code{ID} and set @code{paramsOK} to @code{FALSE}. @example STRPTR errMsg; [...] if (errMsg) @{ ID = InspectorDisplayBadArg(funcName, inspFlags, "parameterName", errMsg, ID); paramsOK = FALSE; @} @end example @node The Library Functions @section The Library Functions There is another optional component for a control library. If you plan to do some argument inspection, you will usually need several times code which checks if a certain pointer is a valid pointer, and points really to what it should point to. It is suggested that such structure inspection functions are inserted in the library function table. This allows to call them very easily, and at the same time makes the functions available to other inspection libraries. Indeed, while inspecting the arguments of function calls in clients of a given library this can be a real plus. For instance, @file{inspectors/iintuition.library} may want to check if a certain argument is really a @file{struct Font *}. The simplest thing to do is to use the @code{IsBadFont()} call in @file{inspectors/igraphics.library} The code for the structure inspection functions is usually contained in a source called @file{@var{libname}_checks.c}. For each structure relative to a library, there should be a corresponding function named after the structure (by prefixing with @samp{IsBad}). The standard is to return a @code{STRPTR}: @code{NULL}, if everything is OK, or a pointer to a constant string describing the kind of error otherwise. The string should start immediately with a verb (no spaces), i.e., should be ready to use with @code{InspectorDisplayBadArg()}. Note that the checking function @emph{can} assume that the structure pointer is non-@code{NULL}. The prefix @samp{IsBad} has been chosen because the function returns @code{NULL} on a good pointer. @code{inspector.library} contains a series of structure inspection functions which address very basic issues, for instance, if a pointer is in RAM and non-@code{NULL}. Just to see an example, this is the source for @code{IsBadNonNULLP()}: @example __asm STRPTR LIBIsBadNonNULLP(register __a0 APTR p, register __a6 struct InspectorLibrary *ISysBase) @{ if (p) return(NULL); else return("is a NULL pointer"); @} @end example Note that the function name has to be prefixed with @samp{LIB}, but the prefix must @emph{not} appear in the prototype files of the library. This trick allows to specify an additional, fake argument @code{ISysBase} which is never really passed, but it is loaded automatically when doing the function call. @node How to Compile @section How to Compile @cindex Compiling a library @cindex makefile.generic @cindex makefile.generic2 When the various files which are needed to build a control library are ready, it is time to compile. We will firstly suppose you have no library functions, in which case the process is very simple. The @file{makefile.generic} file distributed with @code{inspector} can be instantiated to any library by defining the macro @samp{LIBNAME} on the command line. For instance, supposing you would like to rebuild @file{inspectors/iasl.library}, just @code{cd} into the @file{libraries} directory of the @code{inspector} distribution and type @example smake -f makefile.generic LIBNAME=asl @end example @noindent (supposing @file{FD:}, @file{INCLUDE:} and @file{INSP:} have been assigned correctly). The first command to be executed will be @code{makelib.rexx}, which will produce a C source, @file{iasl.c}, and an Assembler source, @file{iasl_stubs.a}. Then, the compiler will be invoked in order to actually build the library. Note that the library initialization module is called @file{inspectorlibinit.o} and is rather different from @file{libinit.o} (the standard SAS/C initialization module), since it allows for a larger library structure, and takes care of aborting an expunge try, if necessary. Finally, the library will be linked and automatically copied into the directory @file{libs:inspectors}. If you have the additional @file{@var{libname}_checks.c} file, you should use @file{makefile.generic2} instead. The mechanism is essentially the same. @node makelib.rexx @section @code{makelib.rexx} @cindex makelib.rexx The code generator accepts three parameters, the last two being entirely optional. The first parameter specifies the @emph{library base name}, which is then used to build the names of all the files needed. Note that the base name can be different from the full library name without the @samp{.library} suffix: see, for instance, @file{workbench.library}, which has base name @samp{wb}. The second and third parameters are the name of the C source code and of the Assembler code to be generated. If they are missing, the files will be called @file{i@var{libname}.c} and @file{i@var{libname}_stubs.a}, respectively, where @var{libname} is the library base name. @code{makelib.rexx} generates first additional code for the library initialization and shutdown. The code generated will refuse to expunge the library is some function is patched. Then, for each public function in the main library, a stub with the same name prefixed with @samp{i} is generated in the Assembler code; this stub will then call the real C control function, which is prefixed with @samp{ii}. You may want to try to generate a small library code, such as with @example makelib asl @end example @noindent in order to peek at the code. @node Workarounds @chapter Workarounds It would be incredibly nice if everything in a system like @code{inspector} could work flawlessly from day one, with all specifications closely followed and implemented. Unfortunately, this is not possible. Many vital informations about library functions are @emph{not} in the @file{.fd} files. They have to be extracted from the AutoDocs, which means that, unless you plan to write a huge AI system, there is no hope to include these informations automatically in the control libraries. In the following section we will review the workarounds used in @code{inspector}. They should be of great interest for anyone wanting to write custom control libraries, or for discovering the origin of a bug. @menu * Register Preservation Conventions:: * Return Values Placement:: * Getting the Caller Address:: * Opening Devices:: * Bad Library Names:: * You Cannot Patch Everything:: * Unpatching:: * Recursion:: @end menu @node Register Preservation Conventions @section Register Preservation Conventions @cindex Register preservation All library functions on the Amiga preserve all registers, except @code{a0/a1/d0/d1}. Well, @emph{almost} all functions. There are a series of calls that, for several reasons, but essentially for allowing optimized register allocation, document that they will preserve some other register. A typical example: @code{WaitBlit()} preserves all registers. This fact is not documented formally, but only informally in the AutoDocs. In other words, there is no way of getting this information from the @file{.fd} files. The (very rough) approach to this problem is that since each patch is formed by an Assembler stub which calls a C function (the Assembler stub is really necessary for getting the caller address; @pxref{Getting the Caller Address}), we can add some code to preserve the registers. Thus, a very long series of if/then/else in @code{makelib.rexx} generates different Assembler stubs for functions which need special parameter preservation. You can modify this list if necessary. @node Return Values Placement @section Return Values Placement @cindex Return values Analogously, there are functions (currently only @code{FindName()}) which have the documented feature of setting correctly the Z flag on the result (which is anyway returned in @code{d0}). Again, we cannot trust C code for this, and a special assembler stub is needed. Other functions return their values not just in @code{d0}, but also in other register(s). Again, a series of if/then/else in @code{makelib.rexx} generates different Assembler stubs for functions which need special return value placement. @node Getting the Caller Address @section Getting the Caller Address @cindex SegTracker @code{SegTracker} support is great, but in order to have it you have to know who called you. And from a C function this is almost impossible, because the compiler could have pushed a lot of stuff on the stack before the first line of code of the function is executed. Thus, the C inspection functions are called by an Assembler stub which can access safely the caller address. Unfortunately, since any register could be used by a function, we cannot pass this address to the patch code. Rather, we push on the stack a magic cookie (it's @samp{insp} with the eighth bit set). For a series of reasons, which we will not discuss, it is very likely that searching backwards for the cookie on the stack (once in the C function) will lead us to the caller address (of course, another small Assembler function is needed for this purpose). Once we get out of the C function, we pop out the cookie. @node Opening Devices @section Opening Devices @cindex Devices When you patch a ``pure'' library, @code{inspector} opens it, so that the library cannot be expunged while some patches are still on. The same mechanism works (using a different call, and never closing) with resources. Unfortunately, it is not possible to do this with a device, because usually this will prevent the task which you want to check from opening the device. Essentially, all @code{inspector} can do is to check for the existence of the device in @code{SysBase->DeviceList} and then rely on the user not doing anything that could expunge a device which has been loaded from disk. @node Bad Library Names @section Bad Library Names @cindex timer.device @cindex ciaa.resource @cindex ciab.resource As we remarked, the full library, resource or device name for an @file{.fd} is contained in a comment in the first line of the file. The @file{.fd} file for the @code{timer.device} has the wrong name in it, namely @samp{Timer.Device}. You have to edit it, put everything in lower case, and save again. The @file{.fd} files for the two CIAs are gathered in a single file, @file{cia_lib.fd}, which contains @emph{both} library names. You should save this file in two additional versions, @file{ciaa_lib.fd} and @file{ciab_lib.fd}, containing as full resource name @samp{ciaa.resource} and @samp{ciab.resource}, respectively. An analogous duplication should be applied to the prototype file @samp{clib/cia_protos.h}, which should be copied with no modifications into @samp{clib/ciab_protos.h} and @samp{clib/ciab_protos.h}. Now you are ready to generate @file{inspectors/iciaa.library} and @file{inspectors/iciab.library}. @node You Cannot Patch Everything @section You Cannot Patch Everything @cindex Unpatchable functions There are a series of functions that cannot be patched. For instance, @code{GetCC()} is documented as being possibly implemented @emph{directly} in the jump table. Patching such a function can only produce a big crash. Some functions are used by @code{SetFunction()}. Unless one decides to patch libraries with his fingers, those functions are not patchable, because their jump instruction will probably be in an inconsistent state during the patch. @code{SumLibrary()} and @code{CacheClearU()} are in this family. There are libraries which return one distinct base pointer (and thus, one jump table) for each task which opens them. @code{inspector} can patch these libraries, but to no effect, because no task will be using the patched jump table. All math libraries (@file{mathtrans.library}, @file{mathieeesingtrans.library}, etc.) fall in this category, as well as @file{socket.library}. Some functions return a 64 bit value. For instance, most of the arithmetic routines in @file{utility.library}, and all the double precision IEEE libraries. Some work will be done in this direction using types like @code{DOUBLE}, but for the time being you cannot patch this kind of functions. @code{RawDoFmt()} and @code{FormatString()} are used by @code{kprintf}. Patching them results in an immediate crash caused by infinite recursion. Finally, @code{CallHookPkt()} seems not to return, but rather to jump directly to the caller address. The assembler stub is incompatible with this behaviour. @node Unpatching @section Unpatching @cindex Unpatching functions @cindex NoExpunge There is no safe way to remove the code of a patch. This is why the @code{NoExpunge} flag is provided. Control libraries are usually rather small, and you can leave them hanging around even if all patches have been removed. If you really need to unload their code, just turn off the @code{NoExpunge} flag and do an @samp{avail flush}. @node Recursion @section Recursion @cindex Recursion @cindex Recursion @code{inspector} uses several Exec functions. In order to avoid infinite recursions, which usually lead immediately to system crashes, three techniques have been adopted: @itemize @bullet{} @item All Exec function used in @file{inspector.library} are cached when the library is opened using @code{SetFunction()}. Almost all the calls in the library code use the cached versions, and the initialization code of the control libraries copies the address of some cached versions into suitable pointers of the control library base; however, this cannot solve the problem with Exec calls calling internally, and through the library jump table, other Exec calls. @item Each control function of @file{inspector.library} changes on entry the task name to a special identifier. The code generated by @code{makelib.rexx} checks for this special task name just after the function entry point, and calls directly the original function if the match is positive; the string has been chosen in such a way that usually the match will already be negative at the first character. @item Some functions which are used in the patch code cannot be patched (@code{RawDoFmt} and @code{FormatString}). @end itemize @node Acknowledgments @chapter Acknowledgments We would like to thank Davide Massarenti for many useful ideas, above all the pointer at ARexx function libraries. Many people contributed with suggestions and code: in particular, we would like to thank Mike Sinz and Douglas Walker. A great thanks goes also to the SAS team for the great compiler environment we were given the possibility to work in, and for the promptness in answering to all of our most twisted questions. @code{inspector} was designed by Sebastiano Vigna, and implemented by Sebastiano Vigna, Fabrizio Lodi and Reinhard Spisser. Some ideas and features were borrowed from existing tools like @code{wedge}. The library initialization code has been inspired by the SAS' @code{libinit.c}. Comments, complaints, desiderata are welcome. @example Sebastiano Vigna Via California 22 I-20144 Milano MI BIX: svigna@@bix.com INTERNET: vigna@@ghost.dsi.unimi.it UUCP: seba@@sebamiga.adsp.sub.org Fabrizio Lodi Via Faruffini 43 I-20149 Milano MI INTERNET: lodi@@ghost.dsi.unimi.it Reinhard Spisser Via Iglesias 21 I-20128 Milano MI BIX: reinhards@@bix.com INTERNET: spisser@@ghost.dsi.unimi.it @end example @page @page @node Concept Index @unnumbered Concept Index @printindex cp @contents @bye