Fortran查询和打印输出函数或子例程名称

时间:2011-08-11 21:56:27

标签: debugging profiling fortran

在Fortran中是否可以查询我所在的函数或子例程的名称?即,我应该代替'???'让它在屏幕上打印'my_subroutine'?

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

我试图找到一种方法来实现自定义调试器/分析器,只使用文本编辑器的搜索和替换机制。以编程方式查询我在代码中的位置会很有帮助。

5 个答案:

答案 0 :(得分:10)

不,你不能。您想要实现的目标称为reflection,它在Fortran中不可用(在C或C ++中也不可用)。

答案 1 :(得分:7)

您可以使用预处理器打印出文件名和行号。您可能希望利用预定义的预处理器符号__LINE____FILE__。这是一个例子:

预处理器宏在头文件中定义(以便可以在多个位置使用),称之为 errormsg.h

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

然后,您可以在程序,库或模块文件中包含此头文件,例如:

#include "errormsg.h"

program main 

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

end program


subroutine foo()

  ERRORMSG("not an error too!")

end subroutine

ERRORMSG("not really an error...")似乎是fortran代码的奇怪语法,但它被c-preprocessor替换为使用宏定义。因此,当编译它时,它看起来像:

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

对于我的ERRORMSG宏,我选择使用0文件单元打印到stderr。你显然可以随心所欲地编写消息,只要它能产生语法正确的FORTRAN代码。

将其编译为需要您将标志传递给编译器,它们与编译器略有不同。这对我有用,例如:

gfortran -cpp -o errorTest errorTest.f90

也就是说,对于gfortran,-cpp在编译之前调用c预处理器。上述程序的输出如下:

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!

这可能会产生您正在寻找的效果,特别是如果您只为每个文件编写一个子例程。

答案 2 :(得分:3)

我发现了一种简单的半自动方式:在SUBROUTINE声明之后使用正则表达式添加__FUNCTION__的硬编码定义。从makefile中完成后,每个编译都会刷新__FUNCTION__宏。

假设我们有一个看起来像这样的F77列表

文件'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

我想将其转换为

文件'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

请注意,修改后的代码现在位于另一个源文件中:my-file.F.F

为此,我将以下行添加到'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 $@

假设FC被定义为fortran编译器可执行文件,这应该对文件中的所有子例程执行以下过程:

  • 取消定义之前可能定义的__FUNCTION__宏。
  • 在SUBROUTINE定义下面添加一个__FUNCTION__指令两行,包含子程序的名称。
  • 以其他名称保存文件。
  • 将新源编译为所需的目标文件。

在这种情况下,结果应为my-file.o

您可能已经注意到我也在使用宏_S()。这是一个'stringify'宏。你只需要将它添加到你的fortran文件的顶部(我把它放在我包含在任何地方的config.h中)

GNU和intel有不同的实现:

#ifdef __INTEL_COMPILER
#define _S(x) #x
#else
#define _S(x) "x"
#endif 

答案 3 :(得分:2)

为什么不在WRITE语句中硬写下你所在的子程序的名称?

您无法以编程方式(动态)提供或更改子例程的名称,因此我认为没有理由尝试以这种方式访问​​它(关于那个:虽然我不确定它是不可能以某种方式访问​​它,我很确定这是错误的方式...你会让自己更麻烦这样做,而不仅仅是硬编码)。

顺便问一下,你为什么要把它打印出去呢?准确的诊断信息不会提供更多信息吗?

答案 4 :(得分:1)

编译器中有时会出现非标准功能,可帮助您打印当前所在的位置。这些是高度编译器特定的,应该只用于调试。

在gfortran中,您可以使用子程序BACKTRACE。从手册:

  

BACKTRACE在用户代码中的任意位置显示回溯。   之后程序继续执行。

输出看起来像是一条错误消息,但它可能会有所帮助。