【问题标题】:Getting weird crashes in mixed fortran/C program在混合 fortran/C 程序中出现奇怪的崩溃
【发布时间】:2010-02-13 19:24:28
【问题描述】:

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

但是,当试图让第二个更大的程序 ('pssect') 工作时,调用相同的 C 代码,我得到分段错误,或者在某些情况下,程序流回到 fortran 例程“错误” (我没有在我的 C 代码中调用它或任何 fortran 例程)。

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

以下是 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

【问题讨论】:

    标签: c fortran


    【解决方案1】:

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

    【讨论】:

    • 我添加了更多代码。请注意,问题发生在程序执行开始时。在程序启动时,我怎么会覆盖内存?
    • 另外,Valgrind 显然不适用于 OSX 10.6。有什么建议吗?
    • 你可以访问 OSX 10.5 机器来运行 Valgrind 测试吗?或者,代码是否足够便携,可以在 Linux VM 中编译以进行测试?
    【解决方案2】:

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

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

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

    【讨论】:

    • 只有一次从 fortran 到 c 的调用。这个完全相同的调用在另一个程序(psvdraw)中工作正常。我讨厌写fortran,所以我不愿意走PLplot路线(另外,我已经完成了所有工作;我必须全部重做)。
    【解决方案3】:

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

    ISO C 绑定工作得更好。它受到众多编译器(例如,gfortran >= 4.3、ifort)的支持,并提供了一种连接 Fortran 和 C 的已定义且可移植的方式。您在 Fortran 代码中指定一个“接口”,以便 Fortran 编译器为C-调用。

    不过,使用已经提供 Fortran 接口的绘图包可能更容易。

    【讨论】:

      【解决方案4】:

      我从您的 make 文件中注意到您正在使用 gfortran。 gfortran (>=4.3) 和 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
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 我觉得自己像个白痴,不找这个。事实证明,biglib.f 有一个名为“fopen”的例程.... Grr。正如我所说,程序流程有时确实会回到 fortran 代码中。我以为我没有调用 fortran 代码,但由于(回想起来很愚蠢)-fno-underscoring 编译器标志,我是。 GDB 通过回溯立即捕获了它。我以前从未直接使用过 gdb(我通常在 XCode 中编码)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多