/*****************************************************************************
 *                                                                           *
 *                      C     P  R  O  F  I  L  E  R                         *
 *                                                                           *
 *               Copyright (c) 1987  by  Michael F. Purcell                  *
 *                                                                           *
 *                                                                           *
 *  The author grants permission for the non-commercial distribution of this *
 *  software, provided that this and other identifying information remains   *
 *  intact.                                                                  *
 *                                                                           *
 *  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    *
 *                                                                           *
 *  This software is provided "as-is" and carries with it no explicit or     *
 *  implicit promise of support by the author.  Nor will the author be held  *
 *  liable for any damages, real or imagined, which may result from the use  *
 *  or abuse of this software.                                               *
 *                                                                           *
 *  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    *
 *                                                                           *
 *                                                                           *
 *  These programs profile the execution counts and function elapsed times   *
 *  of standard C programs.  They are intended to be used for analyzing C    *
 *  program performance, and possibly as a debugging aid.                    *
 *                                                                           *
 *                                                                           *
 *  The author may be contacted via:                                         *
 *                                                                           *
 *     - People/Link: as 'ASTRO*NUT'                                         *
 *                                                                           *
 *     - CompuServe:  as '73647,1102'                                        *
 *                                                                           *
 *****************************************************************************/



    I. A request from the Author

       I would appreciate feedback from users of this program. Please
       inform me of errors found, or any other interesting results.
       If there is sufficient interest, I will make the source available.

|      This is version 1.2 of the program (Nov, 1987).


   II. Background

       I wrote this program because I was unable to find its equivalent
       in  the public domain.  There is another PD C profiler, but it is
       specifically for Manx C executable modules.  Since I happen to
       use Lattice C, I decided to write my own.  If the other profiler
       can handle Lattice (I haven't tried it), then at the very least
       you now have a choice of utilities.



  III. Arc file descriptions

       File		Description
       ----		-----------

       profile         Executable for the source profiler
       profrpt         Executable for the reporter
       profile.doc     Documentation
       profutl.o       Object module containing profiler run time functions.
       sieve.c         A sample program for profiling.
       p-sieve.c       The profiled source file.
       p-sieve.rpt     The statement execution report for the sieve sample.
       samptime.rpt    A sample function time report, from the profiler itself.


   IV. Program Features


       1.  Will report statement execution counts for C programs.

       2.  Will report accumulated elapsed time by function. 

       3.  Will report net elapsed time by function (time in a function,
           excluding other profiled functions which it calls).

       4.  Allows full control over generated source names.

       5.  Provides run time access to current profile data by the instrumented
           program.  This allows creation of intermediate data files when
           evaluating different facilities within your program. 

       6.  Should work well for standard C programs.  This includes
           Lattice C, Manx C, as well as Microsoft C for MS-DOS (statement
           counts only).  



    V. Program Limitations


       The source profiler utility is a very limited C parser.  The
       most significant limitation is that it does NOT support the C pre-
       processor facilities.  Therefore, if you use a 'define' to rename
       a standard C data type, or part of a statement syntax, the profiler
       may fail with an error message. You can examine the partial source
       output to determine the cause of the problem.

       An example of this is if you use "#ifdef ignore_this" to exclude
       a large comment block. Bracket the comment block with "/* ... */"
       instead.  Conditionally excluded source will be handled properly
       as long as the source is syntactically complete. Nested IFs with
       some of the IF statements conditionally excluded may get syntax
       errors when compiling. 

       Problems can also occur if you use TYPEDEFs for variables.
       The parser tries to avoid this problem by assuming that if it finds
       an unknown symbol where a declaration type is expected, and if the
       symbol contains NO lowercase letters, then that symbol is either
       a typedef or a macro.  Since the common convention is to make typedefs
       all uppercase, the parser will work for the vast majority of C
       programs.

|      The profiler uses the "onexit" function to trap program termination. If
|      your program already uses this function, then the profiled version may
|      not run correctly.


   VI. Running the Source Profiler


       The source profiler reads a sourcefile(s) and produces an instrumented
       version for each source file.  The names for the new files are based
       on the original names, with modifications controlled by options.
       If your program actually consists of several separately linked modules,
       then all of the source modules must be profiled at the same time.     
       The form for running the instrumentation program is:

           profile <options> <src1> <src2> <src3> ...

        where:

           <options> can be

             -fxxx : this changes the front of the new source file node from
|                    the default 'p_' to 'xxx'.
 
             -bxxx : this changes the back of the new source file node from
                     the default '' to 'xxx'.
 
             -exxx : this changes the extension of the new source file node
                     from the input file extension to 'xxx'.

             -pxxx : this changes the path of the new source file from the
                     input file path to 'xxx' (100 characters max).

             -dxxx : this changes the disk drive of the new source file
                     from the input file drive to 'xxx'.
 
             -xz   : this option is used to turn off either of the profiling
                     options.  Both options are on by default.  'z' can be
                     't' or 'c', where 't' turns off function timing and
                     'c' turns off statement counts.

           <src1>... are the source files which constitute your program. They
|                    do not need to be in any particular order, but the
|                    module which contains "main()" MUST be included.


        Examples:

           "profile myprog.c"

|             will produce "p_myprog.c" in the current directory.  Statement
              counts and function timings are included.

           "profile -dram: -pprof -f -xt -b-p dev/pgm1.c dev/pgm2.c dev/pgm3.c"

              will produce "prof/pgm1-p.c", "prof/pgm2-p.c", and 
              "prof/pgm3-p.c". Elapsed time by function will not be captured.
              The files will be written to 'ram:' (note that the path 'prof'
              should already be defined in the 'ram:' directory).


       If the function timings option is 'on', the source profiler will produce
       a file called "PROFFCN.XRF".  This is a list of the functions in your
       program.  It will be used by the profile report program when creating
       the function timings report.



  VII. Running your program

 
       Take the output source files and compile them as usual.  The object
|      module 'profutl.o' must be added when linking the final program.
       Note: if you select the function timing option, you will get
       'warning' messages from the C compiler for functions which are not
       defined as 'void'.  This is not a problem, and you should ignore these
       warnings.


       Run the compiled program as usual.  When it exits it will produce a data
|      file called "PROFILE.CTx", where 'x' is a number.  If your program has
       dumped intermediate data then there will be a sequence of these data
       files.  If you have requested function timings, then a series of files
|      called "PROFILE.TIx" will also be produced.



 VIII. Running Profile Reporter


       The profile reporter reads a data file, and any of the profiled source
       files to produce a report(s) showing statement counts and function
       times.


	The form for running the report program is:

           profrpt <options> <src1> <src2> ...

        where:

           <options> can be

|            -cxxx : this changes the count data file name from
|                    "PROFILE.CT1" to "xxx".

             -txxx : Get the timing data from 'xxx' rather than
|                    "PROFILE.TI1".

             -fzzz : Get the function list used by the timing report from 'zzz'
                     rather than "PROFFCN.XRF". 

             -xz   : this option is used to turn off either of the profile
                     report options.  Both options are on by default.  'z' can
                     be 't' or 'c', where 't' turns off the function timing
                     report and 'c' turns off the statement count report.


           <src1>  ... are any or all of the profiled source files composing
                     the original program.


        Examples:

           "profrpt p-myprog.c"

|              will read "PROFILE.CT1" with "p-myprog.c" to produce the
               report file "p-myprog.rpt".  It will read "PROFFCN.XRF" and
|              "PROFILE.TI1" to produce the report file "PROFTIME.RPT".

|          "profrpt -dPROFILE.CT3 -tPROFILE.TI3  p-prog1.c p-prog3.c"

|              will read "PROFILE.CT3" with "p-prog.c" and "p-prog3.c" to
               produce the report files "p-prog1.rpt" and "p-prog3.rpt".
               The function timings report will be produced using
|              "PROFILE.TI3" and "PROFFCN.XRF".

           "profrpt -xc"

|              will read "PROFFCN.XRF" and "PROFILE.TI1" to produce the
               report file "PROFTIME.RPT".  A statement count report will
               not be produced.
               


       The output consists of a series of reports called "<srcx>.rpt".  The
       report names correspond to the instrumented source file names.  The
       report shows an execution count for each C executable statement. 
       Declarations, etc., outside a function have a count of blanks.
       Unexecuted statements within functions will have a count of zero.
       The counts and statements are separated by a ':'.

       This report is based on the instrumented source file.  The report
       program attempts to 'clean up' the output so it looks as much like
       the original sourcefile as possible.  However, there will always be
       some extraneous lines.  If you compare the "sieve.c" and "p-sieve.rpt"
       files, you will see that the report looks quite reasonable.  


       The timings report shows a list of decimal numbers and function names.
       The column headings have the following meanings:

           "Count"       : The number of times this function was entered.

           "Grosstime"   : The total number of elapsed seconds charged to this
                           function.  This will include time spent in functions
                           called by this function.  If a function is called
                           recursively, only the first occurrence will be used.
                           As a result, the function 'main' will have a gross
                           time very close to the elapsed time of the entire
                           program. This value will be inaccurate if the program
                           terminates without properly exiting the function
                           (i.e., via an 'exit' statement). 

           "Nettime"     : The total number of elapsed seconds charged to this
                           function, excluding calls to other application
                           functions.  Therefore, this shows the time spent
                           executing just this function's code.

           "Net %"       : This is the net time as a percentage of the total
                           net time of all functions.

           "Function"    : The name of the function.


       At the end of the report is the total of all the net time values.  This
       should approximate the actual program elapsed time.


       Remember that all times are based on ELAPSED time.  If you are doing
       multiprocessing, or you have functions which wait for user input, then
       the time values will be skewed accordingly.
       

   IX. Overhead


       The instrumented source files will be 30% to 100% larger than the
       original source files.  This is very dependent on the grammatical
       complexity of your program.

       The instrumented load modules will be 25% to 50% larger than the
       originals.

       The execution times will be 5% to 70% longer.  This is also very
       dependent on the original program complexity.  I have a structurally
       simple astrophysics program which is mostly floating point calculations.
       The overhead for it was negligible.  On the other hand, the overhead
       for the sieve sample is 75%.  


    X. Accessing Run Time Profile Data


|      A function is included in the "profutl.o" object module which allows
       you to access the current profile data.  The idea here is that a
       complex program will have many different facilities, which you may
       want to analyze separately.  Yet, you may not want (be able) to run your
       program many times.  Therefore I have included a routine which will
       either dump or clear the profile data.

       The definition of this function is:

|          "short _P_ACCESS(short)".

       The values to pass as parameters are predefined as:

           "_P_DUMP"  to create a new data file using the current data.  The
|                     first files will be called "PROFILE.CT1" and
|                     "PROFILE.TI1".  The second will be "...2" and so on.

           "_P_CLEAR" to clear the current data (presumably after having
                      dumped the data).

       The value returned is nonzero if an error is detected.

       A dummy defined value called "_PROFILER_" is included in the new source
       files to allow you to conditionally compile code which accesses the
       profiler function.

       A typical example might be:

           #ifdef _PROFILER_

           if (_P_ACCESS(_P_DUMP)) exit(4);

           _P_ACCESS(_P_CLEAR);

           #endif



   XI. Misc.


|      The programs were created using Lattice C release 4.00 under
       AmigaDOS version 1.2.


  XII. Versions
 
       V1.0 (09/11/87)

          Base version uploaded to People-Link (PLINK).
 
       V1.1 (10/17/87)

          Contains a minor fix to the source processor which corrects a
          problem with subblocks and local variables.
 
|      V1.2 (11/03/87)
|
|         Cleaned up problems with short/long integers when porting to
|         IBM MS-DOS.
|
|         Minor documentation corrections.
|
|         Fixed a parser error involving SIZEOF in decls.
|
|         Recompiled everything with Lattice C version 4.00.
|
|         Changed file names to be compatible with MS-DOS.
|
|         Recognizes "_main()" as well as "main()". (Just don't use both!)
