I'm calling a C function via cgo thusly:
var _outptr_7 C.double
var _outptr_8 C.double
var kk uintptr = uintptr(unsafe.Pointer(&_outptr_7))
gogsl.InitializeGslFunction(f)
_result := int32(C.gsl_integration_qags((*C.gsl_function)(unsafe.Pointer(f.CPtr())), C.double(a), C.double(b), C.double(epsabs), C.double(epsrel), C.size_t(limit), (*C.gsl_integration_workspace)(unsafe.Pointer(workspace.Ptr())), (*C.double)(&_outptr_7), (*C.double)(&_outptr_8)))
fmt.Printf("%10.10X\n",kk)
return _result, *(*float64)(unsafe.Pointer(&_outptr_7)), *(*float64)(unsafe.Pointer(&_outptr_8))
Now, the kk
is here for debugging purposes. I've modified the C function gsl_integration_qags
to output the 8th argument it receives (that's &_outptr_7
, the _<n>
count from 0!)
(The InitializeGslFunction
can be ignored for the purpose here...)
The two values - kk
and the C function output - match exactly.
However, I get a different number out than the C function returns - it sets the 8th argument via indirection. I'm sure this is correct, I've watched it work in gdb.
It looks like this: (omitting lines that just read (gdb)
where I hit enter to repeat command)
Breakpoint 3, qags (f=0xc20800a260, a=0, b=1, epsabs=0, epsrel=9.9999999999999995e-08, limit=1000, workspace=0x782ec0, result=0xc208031e28, abserr=0xc208031e20, q=0x7ffff79ad12c <gsl_integration_qk21>)
at qags.c:479
479 *result = res_ext;
(gdb) n
480 *abserr = err_ext;
(gdb) p *result
$1 = -4.0000000000000853
(gdb) p result
$2 = (double *) 0xc208031e28
(gdb) n
482 if (err_ext == GSL_DBL_MAX)
485 if (error_type || error_type2)
513 double max_area = GSL_MAX_DBL (fabs (res_ext), fabs (area));
515 if (!positive_integrand && max_area < 0.01 * resabs0)
520 double ratio = res_ext / area;
522 if (ratio < 0.01 || ratio > 100.0 || errsum > fabs (area))
526 goto return_error;
535 if (error_type > 2)
540 if (error_type == 0)
542 return GSL_SUCCESS;
573 }
gsl_integration_qags (f=0xc20800a260, a=0, b=1, epsabs=0, epsrel=9.9999999999999995e-08, limit=1000, workspace=0x782ec0, result=0xc208031e28, abserr=0xc208031e20) at qags.c:53
53 return status ;
54 }
// Correct value (-4.0) is being returned from C function.
_cgo_3ce45c051e63_Cfunc_gsl_integration_qags (v=0xc208031db8) at /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/numint.go:241
asmcgocall () at /usr/lib/golang/src/runtime/asm_amd64.s:669
669 MOVQ 48(SP), DI
670 MOVQ (g_stack+stack_hi)(DI), SI
671 SUBQ 40(SP), SI
672 MOVQ DI, g(CX)
673 MOVQ SI, SP
asmcgocall () at /usr/lib/golang/src/runtime/asm_amd64.s:674
674 RET
runtime.asmcgocall_errno () at /usr/lib/golang/src/runtime/asm_amd64.s:627
627 MOVL AX, ret+16(FP)
runtime.asmcgocall_errno () at /usr/lib/golang/src/runtime/asm_amd64.s:628
628 RET
runtime.cgocall_errno (fn=0x405210 <_cgo_3ce45c051e63_Cfunc_gsl_integration_qags>, arg=0xc20805fdb8, ~r2=0) at /usr/lib/golang/src/runtime/cgocall.go:132
132 exitsyscall()
134 return errno
// cgo C glue is about to return from call into Go caller
github.com/dtromb/gogsl/numint._Cfunc_gsl_integration_qags (p0=0xc20800a260, p1=0, p2=1, p3=0, p4=9.9999999999999995e-08, p5=1000, p6=0x782ec0, p7=0xc20805fe28, p8=0xc20805fe20, r1=0)
at /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/:92
92 /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/: No such file or directory.
// Bang, it's broken. p7 value in return val struct is /different than the value that went in/... the location of _outptr_7 has changed.
(gdb) p p7
$3 = (github.com/dtromb/gogsl/numint._Ctype_double *) 0xc20805fe28
(gdb) p *p7
$4 = 0
(gdb) n
github.com/dtromb/gogsl/numint.Qags (f=0xc20805ff58, a=0, b=1, epsabs=0, epsrel=9.9999999999999995e-08, limit=1000, workspace=0xc20800a1d0, ~r7=7753968, ~r8=0, ~r9=0)
at /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/numint.go:75
75 fmt.Printf("%10.10X\n",kk)
C208031E28
76 return _result, *(*float64)(unsafe.Pointer(&_outptr_7)), *(*float64)(unsafe.Pointer(&_outptr_8))
(gdb) p _outptr_7
$5 = 0
(gdb) p &_outptr_7
$6 = (float64 *) 0xc20805fe28
// But the correct value is still hanging out there.
(gdb) p $1
$7 = -4.0000000000000853
So, what gives? What is going on here? How to fix/workaround?
Here is the cgo binding showing the struct mapping, for completeness:
void
_cgo_a9ebceabba03_Cfunc_gsl_integration_qags(void *v)
{
struct {
gsl_function* p0;
double p1;
double p2;
double p3;
double p4;
size_t p5;
gsl_integration_workspace* p6;
double* p7;
double* p8;
int r;
char __pad76[4];
} __attribute__((__packed__, __gcc_struct__)) *a = v;
char *stktop = _cgo_topofstack();
__typeof__(a->r) r = gsl_integration_qags((void*)a->p0, a->p1, a->p2, a->p3, a->p4, a->p5, (void*)a->p6, (void*)a->p7, (void*)a->p8);
a = (void*)((char*)a + (_cgo_topofstack() - stktop));
a->r = r;
}
The bug occurs or does not occur randomly depending on some details of the compilation which should not affect semantics - adding a line which prints out &_outptr_7 before calling the C function causes it to return correctly, for example. Making other changes to the code reverts it back, etc etc.
Something I'm doing wrong? Or bug in cgo?
This is due to a bug in Go 1.4. Fixed in 1.5.