9.10 Notes on Using Foreign Code

9.10.1 Memory Allocation

SWI-Prolog's heap memory allocation is based on the malloc(3) library routines. The stacks are allocated using mmap() on most Unix machines and using VirtualAlloc() on windows. SWI-Prolog provides the functions below as a wrapper around malloc(). Allocation errors in these functions trap SWI-Prolog's fatal-error handler, in which case PL_malloc() or PL_realloc() do not return.

Portable applications must use PL_free() to release strings returned by PL_get_chars() using the BUF_MALLOC argument. Portable applications may use both PL_malloc() and friends or malloc() and friends but should not mix these two sets of functions on the same memory.83These functions were introduced in SWI-Prolog 5.0.9 to realise guaranteed portability. Foreign code that must be compatible with older versions can check the PLVERSION macro.

void * PL_malloc(size_t bytes)
Allocate bytes of memory. On failure SWI-Prolog's fatal error handler is called and PL_malloc() does not return. Memory allocated using these functions must use PL_realloc() and PL_free() rather than realloc() and free().
void * PL_realloc(void *mem, size_t size)
Change the size of the allocated chunk, possibly moving it. The mem argument must be obtained from a previous PL_malloc() or PL_realloc() call.
void PL_free(void *mem)
Release memory. The mem argument must be obtained from a previous PL_malloc() or PL_realloc() call.

9.10.2 Compatibility between Prolog versions

Great care is taken to ensure binary compatibility of foreign extensions between different Prolog versions. Only much less frequently used stream interface has been responsible for binary incompatibilities.

Source-code that relies on new features of the foreign interface can use the macro PLVERSION to find the version of SWI-Prolog.h and PL_query() using the option PL_QUERY_VERSION to find the version of the attached Prolog system. Both follow the same numbering schema explained with PL_query().

9.10.3 Debugging and profiling foreign code (valgrind)

This section is only relevant for Unix users on platforms supported by valgrind. Valgrind is an excellent binary intrumentation platform. Unlike many other instrumentation platforms, valgrind can deal with code loaded through dlopen().

The callgrind tool can be used to profile foreign code loaded under SWI-Prolog. Compile the foreign library adding -g option to gcc or plld. By setting the environment variable VALGRIND to yes, SWI-Prolog will not release loaded shared objects using dlclose(). This trick is required to get source information on the loaded library. Without, valgrind claims that the shared object has no debugging information.84Tested using valgrind version 3.2.3 on x64. Here is the complete sequence using bash as login shell:

% VALGRIND=yes valgrind --tool=callgrind pl <args>
<prolog interaction>
% kcachegrind callgrind.out.<pid>

9.10.4 Name Conflicts in C modules

In the current version of the system all public C functions of SWI-Prolog are in the symbol table. This can lead to name clashes with foreign code. Someday I should write a program to strip all these symbols from the symbol table (why does Unix not have that?). For now I can only suggest to give your function another name. You can do this using the C preprocessor. If---for example---your foreign package uses a function warning(), which happens to exist in SWI-Prolog as well, the following macro should fix the problem.

#define warning warning_

Note that shared libraries do not have this problem as the shared library loader will only look for symbols in the main executable for symbols that are not defined in the library itself.

9.10.5 Compatibility of the Foreign Interface

The term-reference mechanism was first used by Quintus Prolog version 3. SICStus Prolog version 3 is strongly based on the Quintus interface. The described SWI-Prolog interface is similar to using the Quintus or SICStus interfaces, defining all foreign-predicate arguments of type +term. SWI-Prolog explicitly uses type functor_t, while Quintus and SICStus uses <name> and <arity>. As the names of the functions differ from Prolog to Prolog, a simple macro layer dealing with the names can also deal with this detail. For example:

#define QP_put_functor(t, n, a) PL_put_functor(t, PL_new_functor(n, a))

The PL_unify_*() functions are lacking from the Quintus and SICStus interface. They can easily be emulated or the put/unify approach should be used to write compatible code.

The PL_open_foreign_frame()/PL_close_foreign_frame() combination is lacking from both other Prologs. SICStus has PL_new_term_refs(0), followed by PL_reset_term_refs() that allows for discarding term references.

The Prolog interface for the graphical user interface package XPCE shares about 90% of the code using a simple macro layer to deal with different naming and calling conventions of the interfaces.