Profiling Fortran under George

When porting the Ratfor preprocessor it became obvious that some kind of profiling mechanism would be necessary.

It would be possible to write a preprocessor for Fortran that produced a modified version of the program including calls to profiling routines, maybe even including this function in the Ratfor preprocessor, but it is quite hard to recognise the start of the executable code in a Fortran routine.

As an alternative the profiling code could be piggybacked on the Fortran TRACE mechanism.

When a Fortran program is compiled with the TRACE 1 directive the compiler includes calls to library routines at routine entry and exit to allow recording the flow of control for post-morten analysis. If we could modify these routines we could include our profiling code.

The problem is that the source to the Fortran library, SUBGROUPSRF4, has been lost. But, luckily we have a partial copy of the document SCM which defines the semicompiled (link-ready) format used on 1900 systems. With this we can write a Disassembler to turn the semicompiled routines in the library to modifiable PLAN code. This is possible as there is a near 1-1 mapping between PLAN and the semicompiled format - the only information that is lost is the local names used in the original PLAN, and, of course, any comments.

Once the SUBGROUPSRF4 library has been disassembled we can include our tracing routines.

The original TRACE 1 routines

When compiling TRACE 1 code the Fortran compiler puts something like:
      CALL  2  %FPM
               8HSEGNAME
at the start of the main program and
      CALL  2  %FRL
               8HSUBNAME
at the start of every subroutine and
      CALL  2  %FRL+5
at the end of every subroutine.

The program will normaly call %FSTOP at the end

      CALL  1  %FSTOP
               n
               n words of message

The modifications for profiling

We will modyfy %FRL to call profile_entry(addr,name) and %FRL+5 to call profile_exit().

profile_entry looks like:

profile_entry (addr, name) {
   # accumulate time spent in previous routine
   func = stack [tos];        # find previous routine
   time [func] += now - start
   func = find_function (addr, name)
   tos = tos + 1
   stack [tos] = func
   calls [func] = calls [func] + 1
   start = now
}
and profile_exit like:
profile_exit () {
   func = stack [tos]
   time [func] += now - start
   tos = tos - 1
   start = now
}