Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
This community is designed for sharing of public information. Please do not share Intel or third-party confidential information here.
27228 Discussions

Program compiles but crashes when running, possibly more compiler directives needed

andy_in_oxford
New Contributor I
930 Views

I am building and running the following in Windows using the Intel Fortran compiler. My program uses eccodes.lib and eccodes.mod, both of these are correctly compiled and my program (grib_copy_msg) compiles correctly. But when I try to run it an error is thrown:

 

I have got the following:

 

In the top part of my program (grib_copy_msg.f90) I have:

 

program copy

  use eccodes

  implicit none

  integer  :: infile

 

  print *,'I am here 0' 

  call codes_open_file(infile,'msl_000032_me.grb','r')

  print *,'I am here 1'

.....

 

 

In eccodes.f90 there is:

 

subroutine codes_open_file ( ifile, filename, mode, status )

    integer(kind=kindOfInt),intent(out)          :: ifile

    character(len=*), intent(in)                      :: filename

    character(LEN=*), intent(in)                    :: mode

    integer(kind=kindOfInt),optional, intent(out)     :: status

    !DIR$ ATTRIBUTES DLLEXPORT :: codes_open_file

    print *, "I am here 2"

 

    call grib_open_file ( ifile, filename, mode, status )

end subroutine codes_open_file 

 

 

grib_open_file is in a file called grib_f90.f90: 

 

  subroutine grib_open_file ( ifile, filename, mode, status )

        integer(kind=kindOfInt),intent(out)               :: ifile

        character(len=*), intent(in)                           :: filename

        character(LEN=*), intent(in)                         :: mode

        integer(kind=kindOfInt),optional, intent(out) :: status

        integer(kind=kindOfInt)                                 :: iret

 

        !DIR$ ATTRIBUTES DLLEXPORT :: grib_open_file

 

        interface

          integer(c_int) function grib_f_open_file(ifile,filename,mode) bind(c)

            use iso_c_binding, only: c_char,c_int

            integer(c_int), intent(out) :: ifile

            character(c_char), intent(in) :: filename,mode

          end function grib_f_open_file

        end interface

 

        print *, "I am here 3"

 

        iret=grib_f_open_file(ifile, filename, mode )

        if (present(status)) then

           status = iret

        else

           call grib_check(iret,'open_file','('//filename//')')

        endif

  end subroutine grib_open_file

 

 

When I try to run it, I get the output:

 

I am here 0

I am here 2

 

The program then crashes.

 

 

I have added in the two !DIR$ ATTRIBUTES lines, however have I got them right and do I need to add more !DIR$ lines?

0 Kudos
19 Replies
Arjen_Markus
Valued Contributor III
919 Views

Th compiler directives are only of importance if you put the routines in a DLL - they export the routines, so that they can be used by a program.

 

You do not tell HOW it crashes: any error messages, for instance? But from the code I think I see the problem: grib_f_open_file is a C function (or acts like one) and one of its arguments is a string - the name of the file to open. Typically, strings in C are terminate with a NUL character. But you also pass it a single character, instead of what you should do an array of characters.

Try:

 

character(len=1), dimension(*), intent(in) :: filename, mode

 

and change the arguments to "trim(filename) // c_nul_char" and "trim(mode) // c_nul_char" (without the quotation marks of course)

 

No guarantee, as I do not know the library and therefore do not know what the C interface actually is - perhaps you can copy that in a reply - but this is the direction you should be looking at.

 

andrew_4619
Honored Contributor I
909 Views

Is the compile of eccodes.f90  producing a DLL? the line !DIR$ ATTRIBUTES DLLEXPORT :: grib_open_file would suggest so. And grib_open_file in  grib_f90.f90, is that going in the same dll or a different dll? And Finally, do you expect to call grib_open_file from your main program or only from within the eccodes DLL?   

 

 

andy_in_oxford
New Contributor I
901 Views

Hi Andrew,

 

eccodes.f90 produces a dll, lib, and a mod. It is only the lib and mod that I need.

 

grib_open_file is only called from within eccodes.

 

Arjen_Markus
Valued Contributor III
888 Views

No, the "lib" file is a so-called import library and is needed at link time. The "mod" file is required by the compiler to import the module. You need the DLL at run-time.

andy_in_oxford
New Contributor I
904 Views

HI Arjen,

 

I am afraid no other message when it crashes. So are you suggesting that I replace the line:

 

character(c_char), intent(in) :: filename,mode

 

With the line:

 

character(len=1), dimension(*), intent(in) :: (trim(filename)//c_nul_char), (trim(mode)//c_nul_char)

Arjen_Markus
Valued Contributor III
882 Views

Ah, no, I meant: change the call to grib_f_open_file to:

iret=grib_f_open_file(ifile, trim(filename)//c_nul_char, trim(mode)//c_nul_char )

and change the declaration for filename and mode in the interface block.

andy_in_oxford
New Contributor I
861 Views

Hi Arjen,

 

Thank you. I now get:

 

I am here 0

I am here 2

I am here 3

 

However at that point it crashes with the message:

 

forrtl: severe (157): Program Exception - access violation

Arjen_Markus
Valued Contributor III
849 Views

Can you compile the program and the library using the compile option /traceback? Or run it in a debugger? The print statements in your program do not allow us to determine whether it failed in grib_f_open_file or later. The option should enable the program to print a traceback so that it becomes clearer where things go wrong.

FortranFan
Honored Contributor II
818 Views

@andy_in_oxford ,

Will it be possible for you to post here the C function prototype for `grib_f_open_file`?  As alluded to by others, the Fortran interface to this function needs to be corrected.  It may be as follows, however the actual C "signature" can confirm this:

interface
   integer(c_int) function grib_f_open_file(ifile, filename, mode) bind(C)
      use iso_c_binding, only: c_char, c_int
      integer(c_int), intent(out) :: ifile
      character(c_char), intent(in) :: filename(*), mode(*)  !<-- note the assumed-length declaration
   end function grib_f_open_file
end interface
andy_in_oxford
New Contributor I
784 Views

Hi FortranFan,

 

Here are the prototypes:

int grib_f_open_file(int *fid, char *name, char *op, int lname, int lop);
int grib_f_open_file_(int *fid, char *name, char *op, int lname, int lop);
int grib_f_open_file__(int *fid, char *name, char *op, int lname, int lop);

 

And here are the functions (in grib_fortran.c):

int grib_f_open_file_(int* fid, char* name , char* op, int lname, int lop) {
    FILE* f = NULL;
    int ioerr=0;
    char oper[1024]; /* GRIB-576: open mode */
    char *p;
    char fname[1024];
    int ret=GRIB_SUCCESS;
    char* iobuf=NULL;
    /*TODO Proper context passed as external parameter */
    grib_context* context=grib_context_get_default();

    cast_char(oper,op,lop);

    p=oper;

    while (*p != '\0') { *p=tolower(*p);p++;}

    f = fopen(cast_char(fname,name,lname),oper);
    if(!f) {
        ioerr=errno;
        grib_context_log(context,(GRIB_LOG_ERROR)|(GRIB_LOG_PERROR),"IO ERROR: %s: %s",strerror(ioerr),cast_char(fname,name,lname));
        *fid = -1;
        ret=GRIB_IO_PROBLEM;
    } else {
        if (context->io_buffer_size) {
            /* printf("setting vbuf = %d\n",context->io_buffer_size); */
#ifdef POSIX_MEMALIGN
            if (posix_memalign((void **)&iobuf,sysconf(_SC_PAGESIZE),context->io_buffer_size) ) {
                grib_context_log(context,GRIB_LOG_FATAL,"grib_f_open_file_: posix_memalign unable to allocate io_buffer\n");
            }
#else
            iobuf = (void*)malloc(context->io_buffer_size);
            if (!iobuf) {
                grib_context_log(context,GRIB_LOG_FATAL,"grib_f_open_file_: Unable to allocate io_buffer\n");
            }
#endif
            setvbuf(f,iobuf,_IOFBF,context->io_buffer_size);
        }
        *fid = push_file(f,iobuf);
        ret=GRIB_SUCCESS;
    }
    return ret;
}
int grib_f_open_file__(int* fid, char* name , char* op,  int lname, int lop){
    return grib_f_open_file_(       fid,       name ,       op,     lname,      lop);
}
int grib_f_open_file(int* fid, char* name , char* op,  int lname, int lop){
    return grib_f_open_file_(       fid,       name ,       op,     lname,      lop);
}

 

Arjen_Markus
Valued Contributor III
773 Views

Oh, that makes it clearer! The library you are using uses the OLD style interfacing between Fortran and C and as a consequence takes into account that Fortran passes string arguments with a hidden argument. In that case, the solution should be to leave out the interface block. The burden of the interfacing is on the C side.

Note: there are three versions to take care of common name mangling schemes found with Fortran compilers.

andy_in_oxford
New Contributor I
762 Views

Hi Arjen,

 

I did originally try to build it without the interface blocks, however the compiler didn't recognise the C function names, so in order to get it compiling I had to add interface blocks throughout the Fortran code. I am using the Intel Fortran 2019 Windows compiler. I would really like to not have to use them!  I had to add 112 interface blocks in, in order to get the code compiling!

andrew_4619
Honored Contributor I
772 Views

Well the first thing I note is that  grib_f_open_file on the c side is expecting 5 parameters but your interface to it defines 3. In Fortran to Fortran passing there are hidden length parameters  that get passed in a way the can be specific to the compiler. When you add bind C to the interface it stops that and does things in the way the companion C compiler would expect. You need to define the length parameters in the interface (passed by value) and actually pass them in the call. 

Arjen_Markus
Valued Contributor III
756 Views

The interface you have in the C code does not acknowledge the Intel style of name mangling - the Fortran names are converted to uppercase. One way to proceed here is to add an uppercase version in the same way as there are the versions with two underscores and with no underscores.

 

In the long term I would say the interface blocks are better, but then you need to add those hidden arguments that are visible on the C-side. You can then do away with the c_nul_chars I suggested, as that is all taken care of on the C-side.

andy_in_oxford
New Contributor I
726 Views

Thank you Andrew and Arjen.

 

One other consideration I have, is that this code needs to be able to be compiled in Linux and macOS. Will that mean interfaces blocks are better or uppercase versions of the function are the way to go?

FortranFan
Honored Contributor II
738 Views

@andy_in_oxford ,

 

As a quick test, you can try the following:

interface
   integer(c_int) function grib_f_open_file(ifile, filename, mode, lname, lop) bind(C, name="grib_f_open_file_")
      use iso_c_binding, only: c_char, c_int
      integer(c_int), intent(out) :: ifile
      character(c_char), intent(in) :: filename(*), mode(*)
      integer(c_int), intent(in), value :: lname, lop
   end function grib_f_open_file
end interface

You have a couple of options after that, to go with compiler-specific directives or with standard Fortran approach - the above interface faces the latter.

andy_in_oxford
New Contributor I
729 Views

Hi FortranFan,

 

Thanks, so what would the call to grib_f_open_file be in this case with the additional two parameters?

 

iret=grib_f_open_file(ifile, (trim(filename))//c_null_char, (trim(mode))//c_null_char, ...... , ...... )

Arjen_Markus
Valued Contributor III
721 Views

You should be able to call it as:

 

iret = grib_f_open_file( ifile, filename, mode, len(filename), len(mode) )

 

(no need for the explicit NUL character)

andy_in_oxford
New Contributor I
689 Views

Great, thank you that works.

 

Will these changes work in Linux and macOS?

Reply