5

I am trying to use the PETSc (Portable, Extensible Toolkit for Scientific Computation) library in Go over cgo. After the call of PetscInitialize, the program will crash at a random point with the error:

[0]PETSC ERROR: ------------------------------------------------------------------------
[0]PETSC ERROR: Caught signal
[0]PETSC ERROR: Try option -start_in_debugger or -on_error_attach_debugger
[0]PETSC ERROR: or see https://petsc.org/release/faq/#valgrind and https://petsc.org/release/faq/
[0]PETSC ERROR: ---------------------  Stack Frames ------------------------------------
SIGABRT: abort
PC=0x732cd809eb2c m=0 sigcode=18446744073709551610

Every command that I use works and I also get the correct result, if it does not crash before... The simplified code looks something like this:

func main() {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()
    if err := PetscInitialize(os.Args, "", ""); err != nil {
        panic("could not initialize petsc")
    }
    doSomeNonPetscRelatedStuff()
}

where PetscInitialize is an interface to C.PetscInitialize.

I tried compiling Petsc in the debug mode without OpenMPI, but it still crashes. When I do everything in pure C, it works.

Edit: More Code Details:

import ...

func doSomeNonPetscRelatedStuff() {
    // some things, that take time.
    // The crash happens more consistently, if one goes slowly line by line in a debugger.
}

func PetscInitialize(args []string, file string, help string) error {
    cArgc, cArgv := argsToC(args)
    //defer c_freeArray_string(cArgv, c_size_t(cArgc))

    var cFile *c_char
    if file != "" {
        cFile = c_CString(file)
        defer c_free(unsafe.Pointer(cFile))
    }
    var cHelp *c_char
    if help != "" {
        cHelp = c_CString(help)
        defer c_free(unsafe.Pointer(cHelp))
    }

    if cIerr := c_PetscInitialize(&cArgc, &cArgv, cFile, cHelp); cIerr != 0 {
        return errors.New(fmt.Sprintf("PetscInitialize failed with errorcode: %v", int(cIerr)))
    }
    return nil
}

func argsToC(args []string) (argc c_int, argv **c_char) {
    argc = c_int(len(args))
    argv = c_makeArray_string(c_size_t(argc))
    for i, args_i := range args {
        cArgs_i := c_CString(args_i)
        c_writeArray_string(argv, cArgs_i, c_size_t(i))
    }
    return
}

This is a separate file, to have all C-stuff in one file and no import "C" in other files:

/*
#cgo CFLAGS: ...
#cgo LDFLAGS: ...

#include <slepceps.h>
#include <slepcnep.h>
#include <stdlib.h>

static void writeArray_string(char **arr, char *s, size_t n) {
    arr[n] = s;
}
static char**makeArray_string(size_t size) {
    return calloc(sizeof(char*), size);
}
*/
import "C"


type c_int = C.int
type c_char = C.char
type c_void = C.void
type c_size_t = C.size_t

func c_CString(str string) *c_char {
    return C.CString(str)
}

func c_writeArray_string(arr **c_char, s *c_char, n c_size_t) {
    C.writeArray_string(arr, s, n)
}

func c_makeArray_string(size c_size_t) **c_char {
    return C.makeArray_string(size)
}

func c_free(ptr unsafe.Pointer) {
    C.free(ptr)
}

func c_PetscInitialize(argc *c_int, args ***c_char, file *c_char, help *c_char) c_PetscErrorCode {
    return C.PetscInitialize(argc, args, file, help)
}

Notes:

  • My actual plan was to use SLEPc, but this uses PETSc which crashes.
  • The complete code can't be published, because it is part of a big non-published project, but this minimal subpart should be sufficient to reproduce the crash.
6
  • to help us help you update your question with a minimal yet complete program which exhibits the issue ... if its too unwieldy some folks give a link to a github repo ... that way we can see for ourselves the crash and possibly dig deeper Commented Oct 31 at 9:55
  • 3
    PETSC ERROR: Caught signal means PETSC installs its own signal handler. I don't know the how the Go runtime works, but if the Go runtime depends on its own signal handlers, the fact that PETSC also installs its own signal handler could be a huge problem. Nevermind "Caught signal" is a bad error message. What signal? From where? The signal and the callstack should also be part of that error message. Commented Oct 31 at 10:51
  • With respect to the PETSC message -on_error_attach_debugger, I'd love to see how PETSC does that in an async-signal-safe way. Commented Oct 31 at 10:52
  • 4
    [1/2] This doc, in its section on FORTRAN-related quirks, reads: «… hence if the Fortran traceback is desired one should put PetscCallA(PetscOptionsSetValue(PETSC_NULL_OPTIONS,"-no_signal_handler","true",ierr)) before the call to PetscInitialize(). This prevents PETSc from defaulting to using a signal handler.». And also: «There is a separate signal handler for floating-point exceptions. The option -fp_trap turns on the floating-point trap at runtime, and the routine PetscFPTrapPush(PetscFPTrap flag); can be used within a program.» … Commented Oct 31 at 11:18
  • 2
    [2/2] …so I'd do that: try to disable every possible signal handling before the library initialization. @AndrewHenle's suggestion is possibly a spot-on: the Go runtime powering any running program traps quite many of the UNIX signals, and even uses some of them for its own use (!), so yes, it's sensble to start with preventing PESTc from messing with the signals, if possible. Commented Oct 31 at 11:20

1 Answer 1

3

The solution thanks to @kostix:

I added before PetscInitialize:

if err := PetscOptionsSetValue(nil, "-no_signal_handler", "true"); err != nil {
    panic("could not set option")
}

with

func PetscOptionsSetValue(options c_PetscOptions, name, value string) error {
    c_name := c_CString(name)
    defer c_free(unsafe.Pointer(c_name))
    c_value := c_CString(value)
    defer c_free(unsafe.Pointer(c_value))
    if cIerr := c_PetscOptionsSetValue(options, c_name, c_value); cIerr != 0 {
        return errors.New("Could not PetscOptionsSetValue, error-code: " + strconv.Itoa(int(cIerr)) + "\n")
    }
    return nil
}

and

type c_PetscOptions = C.PetscOptions

func c_PetscOptionsSetValue(options c_PetscOptions, name *c_char, value *c_char) c_PetscErrorCode {
    return C.PetscOptionsSetValue(options, name, value)
}

It also seems working when I moved the setting of the option and the initialization in func init() and remove runtime.LockOSThread().

Sign up to request clarification or add additional context in comments.

1 Comment

Fabian, please mark your own answer as accepted - this is a normal (and suggested) practice on SO ;-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.