在混合的fortran / C程序中遇到奇怪的崩溃

时间:2010-02-12 06:02:10

标签: c fortran

我正在尝试替换一组fortran程序中的一些图形代码(而不是我自己的代码)。我得到了一个更简单的('psvdraw')工作得很好,用调用Cairo库的C调用替换了fortran postscript生成代码(graphic_output.c)。我已经能够成功地完成跨语言调用而不会有太多麻烦。

然而,当试图让第二个更大的程序('pssect')工作,调用相同的C代码时,我得到分段错误,或者在某些情况下,程序流程返回到fortran例程'错误' (我不会在我的C代码中调用此函数或任何fortran例程。)

在尝试诊断此问题时,我将一堆来自pssect的fortran代码链接到psvdraw('biglib.f'),并得到了相同的错误。请注意,实际上并未调用此添加的代码!此外,错误发生在从fortan到c代码的第一次调用中。所以:psvdraw与biglib.f链接失败,但没有它的psvdraw成功。

以下是makefile的相关位:

Makefile

COMP77 = gfortran

FFLAGS = -C -v -pedantic -Wunused -fno-underscoring

CC = gcc-4
CFLAGS = -v -pedantic -Wunused
CAIRO_INCLUDE = /sw/include/cairo
CAIRO_LIB = /sw/lib

# PSVDRAW Make setup that works:
psvdraw: psvdraw.o graphic_output.o tlib.o pscom.o
    $(COMP77) $(FFLAGS) $@.o graphic_output.o tlib.o pscom.o -L$(CAIRO_LIB) -lcairo -o $@

# PSVDRAW Make setup with errors:
#psvdraw: psvdraw.o graphic_output.o tlib.o pscom.o biglib.o
#   $(COMP77) $(FFLAGS) $@.o graphic_output.o  pscom.o tlib.o biglib.o -L$(CAIRO_LIB) -lcairo -o $@

pssect: pssect.o graphic_output.o pscom.o tlib.o biglib.o 
    $(COMP77) $(FFLAGS) $@.o graphic_output.o pscom.o tlib.o biglib.o -L$(CAIRO_LIB) -lcairo -o $@

pssect.o: pssect.f
    $(COMP77) $(FFLAGS) -c pssect.f
psvdraw.o: psvdraw.f
    $(COMP77) $(FFLAGS) -c psvdraw.f
pscom.o: pscom.f
    $(COMP77) $(FFLAGS) -c pscom.f
tlib.o: tlib.f
    $(COMP77) $(FFLAGS) -c tlib.f
biglib.o: biglib.f
    $(COMP77) $(FFLAGS) -c biglib.f

graphic_output.o: graphic_output.c
    $(CC) $(CFLAGS) $(INCL) -c -I$(CAIRO_INCLUDE) graphic_output.c

.c.o:
    $(CC) $(CFLAGS) $(INCL) -c $<

.f.o:
    $(FC) $(FFLAGS) $(INCL) -c $<

以下是令人讨厌的fortran代码:请注意,问题恰好发生在程序的开头:

pssect.f的开头:

PROGRAM PSSECT

implicit none

include 'perplex_parameters.h'

integer jop0, ier99

logical vertex, output, first

character*100 fname, yes*1

integer  iop0 
logical  debug
common / basic /iop0, debug

integer isec,icopt,ifull,imsg,io3p
common/ cst103 /isec,icopt,ifull,imsg,io3p
c----------------------------------------------------------------------
c   Look for the "debug_yes" file to turn on debugging messages
PRINT *,'Pre-PSOPEN1'
call psopen ('plot2')    
PRINT *,'Post-PSOPEN1'

这是被调用的c代码的一部分并产生错误:

graphic_output.c的一部分:

char dmh_debug = 0;

#define DEBUGPRINT(x) if (dmh_debug) {printf x;};

void psopen(char *fname, int fnamelen) {

    printf("Debug opened\n");

    char *outFileName;
    char outputType[255];
    char pageWidthString[255];
    char pageHeightString[255];

    /* Set debug status based upon presence of file named 'debug_yes' in directory */
    FILE *debugFile = fopen("debug_yes", "r");
    if (debugFile == NULL) {
        dmh_debug = 0;
    } else {
        dmh_debug = 1;
    }
    fclose(debugFile);

    dmh_debug = 1;
    DEBUGPRINT(("Debug closed\n"));

    fname[fnamelen]='\0';
    fname = trim(fname);
    outFileName = malloc((strlen(fname) + 50) * sizeof(char));
    strcpy(outFileName, fname);
    DEBUGPRINT(("Found file name:%s of length: %lu\n", fname, strlen(fname)));
[...]

运行程序的结果

pnr-rethington:source dave$ ./pssect
 Pre-PSOPEN1
Debug opened
Segmentation fault

5 个答案:

答案 0 :(得分:2)

如果在未使用的代码中链接会触发问题,那么这往往会表明某些地方(在Fortran代码或C代码中)覆盖了您不应该覆盖的内存。尝试在Valgrind下运行已编译的程序 - 它应该有助于确定这种情况发生的位置。

答案 1 :(得分:1)

我仍然怀疑你在Fortran和C之间的调用有问题,导致不一致。你是怎么打电话的?我认为最可靠的方法是使用ISO C Binding向Fortran指定如何调用C例程。

或者,您可以考虑具有Fortran界面或绑定的图形包。然后你不必在接口上工作,因为Fortran调用或Fortran接口已经存在。我一直在使用DISLIN。另一种可能性是PLplot。

使用当前的方法,我建议检查C例程入口处的参数,以确保它们是正确的。

答案 2 :(得分:1)

您使用单个参数从Fortran调用psopen,C例程需要两个参数。也许你的Fortran编译器会在每个字符串后面添加字符串的长度作为尾随参数。或者也许你第一次尝试就很幸运,它碰巧使用C例程在堆栈中找到的“随机”值。在第二种情况下,在另一种情况下,可能会发生特殊的崩溃。充其量,这是一种连接Fortran和C的非可移植方式。您可以尝试在Fortran调用中添加一个整数长度,看看会发生什么。或者打印或使用调试器在C例程的入口处查看第二个参数的值。

ISO C绑定效果更好。它受到众多编译器的支持(例如,gfortran&gt; = 4.3,ifort),并提供了一种连接Fortran和C的定义和可移植方式。在Fortran代码中指定一个“接口”,以便Fortran编译器生成正确的指令C-call。

尽管如此,使用已经提供Fortran接口的绘图包可能更容易。

答案 3 :(得分:1)

我从你的make文件中注意到你正在使用gfortran。 gfortran(&gt; = 4.3)&amp;的组合gcc支持ISO C绑定。 (Fortran 2003的一部分。)如果你包含接口示例并使用两个参数调用psopen,它应该可以工作。接口进入Fortran程序的声明,并且是C例程psopen的Fortran描述。由于我还没有测试过,因此无法保证...字符串是一种特殊情况 - 您将Fortran程序中的缩放器字符串与接口中的字符串数组匹配,因为C参数是字符数组或指向字符。

interface To_psopen

   subroutine psopen ( fname, fnamelen ) bind (C, name="psopen")

      use iso_c_binding

      implicit none

      character (kind=c_char, len=1), dimension (100), intent (inout) :: fname
      integer (c_int), value, intent (in) :: fnamelen

   end subroutine psopen

end interface To_psopen

答案 4 :(得分:0)

在调试器(gdb)下运行应告诉您segfault发生的位置。使用-O0 -g编译所有代码以获取准确信息。