11

Is it possible in Fortran to query the name of the function or subroutine that I am in? I.e., what do I put in place of '???' to get it to print 'my_subroutine' on the screen?

subroutine my_subroutine()
   write(*,*) ???
end subroutine my_subroutine

I am trying to find a way to implement a custom debugger/profiler using nothing but a text editor's search and replace mechanism. Programmatically querying my position in the code would be helpful.

drlemon
  • 1,525
  • 1
  • 12
  • 14
  • Might I suggest for debugging you use an existing debugger such as GDB, and for the pupose of identifying code to optimize you do some sort of [stack sampling](http://stackoverflow.com/questions/375913/what-can-i-use-to-profile-c-code-in-linux/378024#378024) from within that debugger. Either that or find a utility such as *pstack*. That is a different approach from measuring time, which I suspect you were thinking of. – Mike Dunlavey Aug 17 '11 at 02:27

5 Answers5

12

No, you can't. What you want to achieve is called reflection and it is not available in Fortran (nor in C or C++ for what matters).

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
12

You can use the preprocessor to print out the file name and line number. You might want take advantage of the predefined preprocessor symbols __LINE__ and __FILE__. Here's an example:

A preprocessor macro is defined in header file (so that it can be used in multiple locations), call it errormsg.h:

#define ERRORMSG(msg) write(0,'("There was an error at ",I4," in file ",/,A,/,"Error message: ",A)') __LINE__,__FILE__,msg

Then you can include this header file in your program, library or module files, for example:

#include "errormsg.h"

program main 

  ERRORMSG("not really an error...")
  call foo()

end program


subroutine foo()

  ERRORMSG("not an error too!")

end subroutine

The ERRORMSG("not really an error...") seems like weird syntax for fortran code, but it get's replaced by the c-preprocessor using the macro definition. So when this is compiled, it looks like:

write(0,'("There was an error at ",I4," in file ",/,A,/,"Error message: ",A)') __LINE__,__FILE__,"not really an error"

For my ERRORMSG macro, I chose to use the 0 file unit to print to stderr. You obviously have the freedom to write the message how ever you like, as long as it results in syntactical correct FORTRAN code.

Getting this to compile requires you to pass flags to the compiler, and they differ slightly from compiler to compiler. This worked for me, for example:

gfortran -cpp -o errorTest errorTest.f90

That is, for gfortran, -cpp invokes the c-preprocessor before compiling. The output from the above program looks like this:

There was an error at    5 in file 
errorTest.f90
Error message: not really an error...
There was an error at   13 in file 
errorTest.f90
Error message: not an error too!

This might have the effect you are looking for, especially if you write only one subroutine per file.

Yann
  • 33,811
  • 9
  • 79
  • 70
  • With gfortran and some other compilers, you can also invoke the C-preprocessor by using filetype "F90", with an upper-case "F". – M. S. B. Aug 14 '11 at 16:17
  • Thanks! This is an interesting idea. Unfortunately, it will not work for my case, because for my small, self-contained projects I prefer to have a single file with the code. But what you've suggested it is a handy tool for future exploration. – drlemon Aug 15 '11 at 23:10
4

I found an easy semi-automated way out of this situation: use regex to add a hardcoded definition of __FUNCTION__ right after the SUBROUTINE declaration. Done from within the makefile will take care that every compilation refreshes the __FUNCTION__ macro.

Suppose we have a F77 listing that looks like this

file 'my-file.F'

  SUBROUTINE my_sub(var1, var2, var3)
  INCLUDE 'some-include.PRM'
  INTEGER var1
  INTEGER var2

  ! the rest of my code here
  WRITE(*,*)__FUNCTION__

  END SUBROUTINE

I want to convert it to

file 'my_file.F.F'

  SUBROUTINE my_sub(var1, var2, var3)
#undef __FUNCTION__
#define __FUNCTION__ "my_sub"
  INCLUDE 'some-include.PRM'
  INTEGER var1
  INTEGER var2

  ! the rest of my code here
  WRITE(*,*)__FUNCTION__

END SUBROUTINE

Note the amended code is now located in another source file: my-file.F.F

To do this I added the following lines to 'Makefile'

my-file.o: my-file.F
    perl -pne 's/^(\s+SUBROUTINE\s*)([^(]+)(\(.*\))/$$1$$2$$3\n#undef __FUNCTION__\n#define __FUNCTION__ _S($$2)/ixms' $< > $<.F; \
    $(FC) $(CPPFLAGS) $(FCFLAGS) -c $<.F -o $@

Assuming FC is defined as the fortran compiler executable, this should perform the following procedure on all the subroutines in the file:

  • undefine a __FUNCTION__ macro that was possibly defined earlier.
  • Add a __FUNCTION__ directive two lines below the SUBROUTINE definition, containing the subroutine's name.
  • save the file under another name.
  • compile the new source into the required object file.

The result should be my-file.o in this case.

You may have noticed that I'm using the macro _S() as well. This is a 'stringify' macro. You just need to add it to the top of your fortran file (I place it inside a config.h that I include everywhere)

There is a different implementation for GNU and intel:

#ifdef __INTEL_COMPILER
#define _S(x) #x
#else
#define _S(x) "x"
#endif 
superk
  • 351
  • 2
  • 6
3

There are sometimes non-standard features in compilers to help you to print where you currently are. These are highly compiler specific and should be used only for debugging.

In gfortran you can use subroutine BACKTRACE. From the manual:

BACKTRACE shows a backtrace at an arbitrary place in user code. Program execution continues normally afterwards.

The output will look like an error message, but it may be helpful.

Community
  • 1
  • 1
2

Why don't you just hard write the name of the subroutine you're in, in the WRITE statement?

You cannot programmatically (dynamically) give or change the name of the subroutine, therefore I see no reason to try to access it either that way (about that: while I'm not sure that it is impossible to access it somehow, I'm quite sure that it is the wrong way to go ... you will cause yourself more trouble going that way, than just hard coding it).

Btw, why are you trying to print it out anyway? Wouldn't a well phrased diagnostic message be more informative?

talonmies
  • 70,661
  • 34
  • 192
  • 269
Rook
  • 60,248
  • 49
  • 165
  • 242
  • You may be right, hardcoding the name of the subroutine is the easiest way to go. I was just looking for a way to save on code editing. It would be nice to search for "end subroutine" and replace it with "call digagnostic_message(); end subroutine". But if the name of the subroutine needs to be passed to diagnostic_message(), then I either have to use regexp search/replace, or go in and type the name. Doing it at 50 places across the code looked like an annoying task, especially if there was a way to automate it. Thanks for your reply! – drlemon Aug 15 '11 at 23:04
  • 2
    @drlemon - Ugh, don't take this the wrong way or anything, cause it's not ment as such. But you're literally following the one "He had a problem. Then he thought, "hey I'll use regular expressions". Now he has two problems." ... You say you have small self contained codes. First of all, in such - thinking about "automating" these things wastes more time than just doing it by hand. Second, don't think too hard about this. Put a lot of diagnostic messages where needed. If needed put them with an IF expression so you can easily turn them off. And that's it. I know a lot of people for whom this – Rook Aug 15 '11 at 23:23
  • principle worked for decades (successfully). Much better than a lot of today's modern ways of doing it. – Rook Aug 15 '11 at 23:23
  • Sure, I have no problem with that. I prefer to debug using print statements instead of a debugger, and collect timing statistics by querying system clock instead of running a profiler. I think it is very efficient and simple. With this question I wanted to find out whether there is a more elegant way doing it than my primitive method of hard-coding function names by hand. – drlemon Aug 16 '11 at 23:33