When things go wrong in Perl, you probably get an error message. When they go wrong in C, you get a segfault, a frozen terminal, or data corruption that only shows up three hours later. This chapter provides a survival kit for when things go very wrong and it's your job to figure it all out.

The Bug: The Vanishing Callback

A common source of crashes in FFI is the scope mismatch. Perl relies on reference counting while C expects you to know what you are doing.

# 1. C library (A Fake Event System)
# void register_handler(void (*cb)(void));
# void trigger_event();

# 2. Perl binding
affix $lib, 'register_handler', [ Callback[ [] => Void ] ] => Void;
affix $lib, 'trigger_event',    [] => Void;

# 3. The Buggy Code
sub setup {
    # We pass an anonymous subroutine.
    # Affix creates a trampoline for it.
    register_handler( sub { say "Event!" } );

    # END OF SCOPE
    # The anonymous sub may have a refcount of 0. If so, perl frees it and the reverse trampoline is destroyed.
}

setup();

# ... later ...
trigger_event(); # SEGFAULT! C jumps to freed memory.

The Fix

You must ensure the Perl CodeRef lives as long as the C library needs it.

my $KEEP_ALIVE; # Global or Object-level storage

sub setup {
    $KEEP_ALIVE = sub { say "Event!" };
    register_handler( $KEEP_ALIVE );
}

# ...
trigger_event(); # Works!

The Debugging Toolbox

Affix provides tools to inspect raw memory and internal states.

Affix::dump( $ptr, $bytes )

If you suspect struct alignment issues or garbage data, look at the raw bytes.

my $ptr = malloc(16);
# ... C writes to ptr ...

# Dump 16 bytes to STDOUT in Hex/ASCII format
Affix::dump($ptr, 16);

address( $ptr ) & is_null( $ptr )

Sanity check your pointers. A pointer value of 0 (NULL) or 0xFFFFFF... (Uninitialized memory) is a smoking gun.

my $ptr = some_c_function();
say sprintf("Pointer address: 0x%X", address($ptr));

errno()

If a C function returns -1 or NULL to indicate failure, it usually sets a system error code (errno/GetLastError).

if ( some_call() < 0 ) {
    die "C Error: " . errno();
}

4. sv_dump( $scalar )

Debugging the Perl side with this function that's a part of perl's internal API. If Affix refuses to accept a variable, checking its internal flags (Integer vs String, Readonly status) often reveals why.

my $val = 10;
Affix::sv_dump($val);
SV = IV(0x25b7a6b4118) at 0x25b7a6b4128
  REFCNT = 1
  FLAGS = (IOK,pIOK)
  IV = 10

Common Pitfalls