tags:

views:

225

answers:

5

I am trying to replace some graphics code in a set of fortran programs (not my own code). I got one of the simpler ones ('psvdraw') to work just fine, replacing the fortran postscript-generating code with C calls that call the Cairo library (graphic_output.c). I have been able to successfully work out the cross-language calls without too much trouble.

However, when trying to get the second, larger program ('pssect') to work, calling the same C code, I get Segmentation Faults, or in some cases, the program flow goes back to a fortran routine 'error' (I do not call this, or any fortran routine in my C code).

In trying to diagnose this problem, I linked in a bunch of the fortran code from pssect into psvdraw ('biglib.f'), and got the same errors. Note that none of this added code is actually called! Also the errors occur right at the first call from fortan into the c code. So: psvdraw with biglib.f linked in fails, but psvdraw without it succeeds.

Here are relevant bits of the 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) [email protected] 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) [email protected] 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) [email protected] 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 $<

Here is the offending fortran code: Note that the problem occurs right at the beginning of the program:

Beginning of 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'

And here is part of the c code that gets called and produces a fault:

Part of 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)));
[...]

Results of running the program

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

If linking in unused code triggers the problem, that would tend to indicate that somewhere (either in the Fortran code or the C code) you're overwriting memory that you shouldn't. Try running the compiled program under Valgrind - it should help pinpoint where this is happening.

caf
I've added more code. Note that the problem occurs right at the beginning of program execution. At program initiation, how could I have overwritten memory?
Dave Hirsch
Also, Valgrind apparently doesn't work with OSX 10.6. Any suggestions?
Dave Hirsch
Can you access an OSX 10.5 machine to run a Valgrind test on? Or alternatively, is the code portable enough that you could compile it in a Linux VM to test?
caf
A: 

I'd still suspect that you have a problem with ones of the calls between Fortran and C, resulting in an inconsistency. How are you making the calls? I think that the most reliable way is to use the ISO C Binding to specify to Fortran how the C routines should be called.

Or, you could consider a graphics package that has a Fortran interface or binding. Then you wouldn't have to work on an interface, since Fortran calls or a Fortran interface would already exist. I've been using DISLIN. Another possibility is PLplot.

With the current approach, I suggest examining the arguments at the entry to the C routines, to make sure that they are correct.

M. S. B.
There is only a single call from fortran to c. This exact same call works fine in the other program (psvdraw). I hate writing fortran, and so I'm unwilling to go the PLplot route (plus, I've already done all the work; I'd have to redo it all).
Dave Hirsch
+4  A: 

Running under the debugger (gdb) should tell you where the segfault happens. Compile all code with -O0 -g to get accurate information.

FX
I feel like an idiot for not looking for this. It turns out that biglib.f had a routine called "fopen"....Grr. As I said, program flow did sometimes go back into the fortran code. I thought I was not calling the fortran code, but I was, due to the (stupid in retrospect) -fno-underscoring compiler flag. GDB caught it immediately with a backtrace. I'd never used gdb directly before (I'm usually coding in XCode).
Dave Hirsch
A: 

You are calling psopen from Fortran with a single argument, and the C routine expects two arguments. Maybe your Fortran compiler adds the length of the character string as a trailing argument after each string. Or maybe you got lucky on your first attempt and it just happened to work with a "random" value that the C routine found on the stack. In the second case, in another situation, a peculiar crash is likely. At best, this is a non-portable way to interface Fortran and C. You could try adding an integer length to your Fortran call and see what happens. Or print or use the debugger to see the value of the second argument at entry to the C-routine.

The ISO C Binding works much better. It is supported by numerous compilers (e.g., gfortran >= 4.3, ifort) and provides a defined and portable way of connecting Fortran and C. You specify an "interface" in your Fortran code so that the Fortran compiler generate the correct instructions for the C-call.

Still, it might be easier to use a plotting package that already provides a Fortran interface.

M. S. B.
A: 

I notice from your make file that you are using gfortran. The combination of gfortran (>=4.3) & gcc supports the ISO C Binding. (Part of Fortran 2003.) If you include the interface example and call psopen with two arguments, it should work. The interface goes into the declaration of the Fortran program and is the Fortran description of the C routine psopen. Not guaranteed since I haven't tested it... Strings are a special case -- you match a scaler string in the Fortran program to a string array in the interface, because the C argument is an array of chars, or a pointer to chars.

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
M. S. B.