I have a C binding for a C library function where I suspect that it is not correct and causes a wrong value being returned in rare cases β which make me suspect it is GC related. Is there a general good strategy to find or trigger these kind of problems?
For example, during development it could be helpful to check the integrity of the heap after every assignment in the C code or to force garbage collections.
Problems I am suspecting in the code below:
Field(v, 0) should not be used but caml_modify(&Field(v, 0), ...) instead
Use caml_acquire_runtime_system() around the actual library call
It would be nice to actually demonstrate that this is wrong and trigger a problem.
CAMLprim value
stub_statvfs(value filename)
{
CAMLparam1(filename);
CAMLlocal2(v, tmp);
int ret;
int i;
struct statvfs buf;
ret = statvfs(String_val(filename), &buf);
if (ret == -1)
uerror("statvfs", Nothing);
tmp = caml_copy_int64(0);
/*
* Allocate the thing to return and ensure each of the fields is set
* to something valid before attempting any further allocations
*/
v = alloc_small(11, 0);
for (i = 0; i < 11; i++) {
Field(v, i) = tmp;
}
Field(v, 0) = caml_copy_int64(buf.f_bsize);
Field(v, 1) = caml_copy_int64(buf.f_frsize);
Field(v, 2) = caml_copy_int64(buf.f_blocks);
Field(v, 3) = caml_copy_int64(buf.f_bfree);
Field(v, 4) = caml_copy_int64(buf.f_bavail);
Field(v, 5) = caml_copy_int64(buf.f_files);
Field(v, 6) = caml_copy_int64(buf.f_ffree);
Field(v, 7) = caml_copy_int64(buf.f_favail);
Field(v, 8) = caml_copy_int64(buf.f_fsid);
Field(v, 9) = caml_copy_int64(buf.f_flag);
Field(v, 10) = caml_copy_int64(buf.f_namemax);
CAMLreturn(v);
}
I had quite sucess in the past by simply calling Gc.full_major () after any binding call, usually a segfault would immediately occur after the offending one.
That looks indeed suspicious the idomatic way (cf rule 3) of doing this would be:
" Field(v, n) = w;is safe only if v is a block newly allocated by caml_alloc_small; that is, if no allocation took place between the allocation of v and the assignment to the field. In all other cases, never assign directly.
is strongly violated here since each of the caml_copy_int64 allocates.
Itβs always safer to omit. Itβs better to add if the bracketed C code may be long and/or may block and is guaranteed not to interact with the OCaml runtime system.