spx2 has asked for the wisdom of the Perl Monks concerning the following question:

I've been reading through perlapi , perlxs , perlxstut and this here book , however I have a problem. I'm trying to create a class, and access one of its attributes from XS code and I get segfault. I tried to do a very simple thing, to write XS that would access an certain position of an arrayref and return it. Maybe I'm using the wrong API ?

this is my class SJT.pm :
package SJT; use 5.010000; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.01'; require XSLoader; XSLoader::load('SJT', $VERSION); sub new { my ($class) = @_; return bless { permutation => [2,2,2,2,2,2,2,2,2,2,2], },$class; }
Ok so this is my XS code( SJT.xs):
package SJT; use 5.010000; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); MODULE = SJT PACKAGE = SJT //THIS FUNCTION SHOULD JUST GET $self->permutation->[index] int get(self,index) SV* self int index CODE: char* key = "permutation"; AV* array; SV* hv = self; if(sv_isobject(hv)) { printf("self is object,moving on...\n"); } else { printf("SJT::get was expecting self to be an object"); }; HV* q = (HV *)SvRV(hv); array = *hv_fetch(q,"permutation",11,FALSE); if(array==NULL) { printf("array not found in self...\n"); exit(-1); }else { printf("array found in self %X\n",array); }; SV** res = av_fetch(array,index,FALSE); if(res==NULL) { printf("item not found in array...\n"); exit(-1); }else { printf("also found item in array at: %X\n",res); };
THE FOLLOWING LINE SEGFAULTS
IV a = SvIV(SvRV(*res)); printf("right before return\n"); RETVAL = a;//SvIV(*av_fetch(array,index,FALSE)); OUTPUT: RETVAL void set(self,index,value) SV* self int index; int value; CODE: OUTPUT:
This is the OUTPUT I get
ok 1 - use SJT; SV = RV(0x82807dc) at 0x82807d0 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x82bc750 SV = PVHV(0x8185174) at 0x82bc750 REFCNT = 1 FLAGS = (OBJECT,SHAREKEYS) STASH = 0x82bc9e0 "SJT" ARRAY = 0x82ccd20 (0:7, 1:1) hash quality = 100.0% KEYS = 1 FILL = 1 MAX = 7 RITER = -1 EITER = 0x0 Elt "permutation" HASH = 0x41573374 SV = RV(0x82bc6ac) at 0x82bc6a0 REFCNT = 1 FLAGS = (ROK) RV = 0x8242ce0 SV = PVAV(0x828d814) at 0x8242ce0 REFCNT = 1 FLAGS = () ARRAY = 0x82cbcb8 FILL = 10 MAX = 10 ARYLEN = 0x0 FLAGS = (REAL) Elt No. 0 SV = IV(0x82bc88c) at 0x82bc890 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 2 Elt No. 1 SV = IV(0x82bc70c) at 0x82bc710 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 2 Elt No. 2 SV = IV(0x82bc6ec) at 0x82bc6f0 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 2 Elt No. 3 SV = IV(0x82bc6fc) at 0x82bc700 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 2 self is object,moving on... array found in self 82BC6A0 also found item in array at: 8242CE4 19830 Segmentation fault
the code I used to get this result was:
use Devel::Peek; use Data::Dumper; use Test::More tests => 1; BEGIN { use_ok('SJT'); my $s1 = SJT->new; Dump($s1); print "HERE be 2:".$s1->get(1)."\n"; print "\n"; };
Why am I getting the segfault, am I using the API wrong ?

Replies are listed 'Best First'.
Re: simple XS
by chromatic (Archbishop) on Mar 01, 2010 at 07:29 UTC

    I'm not surprised you get a segfault. I'm surprised you don't get a compiler warning. Isn't this a big problem?

    int get(self,index) SV* self int index

    A Perl integer is also an SV with the IV flag set. Unless you (somehow) have a typemap set up to convert your SvIV to a C integer, I think you're letting C coerce the address of the pointer to an integer and trying to use that as the array index. That's a one-way ticket to segfault town.

      The typemap handles it.

      #!/usr/bin/perl use Inline C => <<'EOC'; void show(int index) { PerlIO_printf(PerlIO_stdout(), "%d\n", index); } EOC show(123456);
      123456

        Thanks for confirming. Clearly I need better typemaps!

Re: simple XS
by almut (Canon) on Mar 01, 2010 at 06:46 UTC
    IV a = SvIV(SvRV(*res));

    I think you don't want SvRV() here, as your *res already is an SV* holding a simple integer (returned by av_fetch()), which is what SvIV() wants.  In other words, AFAICT, *res doesn't hold an RV (a Perl reference), so no need to dereference it.

      ok I removed SvRV and I still get segfault

        Taking a closer look, I think there are also other problems with your code. In particular, you're not (Perl-)dereferencing (SvRV()) the arrayref you get from hv_fetch() as the value associated with the hash entry "permutation". Also, you're not checking that the SV** pointer which hv_fetch() returns is non-null before (C-)dereferencing it...

        Somewhat simplified, the sequence of steps should look something like this (I'm using Inline::C here for ease of demo):

        #!/usr/bin/perl -l use Inline C => <<'EOC'; int get(SV* self, int index) { HV* h = (HV*) SvRV(self); char* key = "permutation"; SV** aref = hv_fetch(h, key, strlen(key), FALSE); if (aref == NULL) { /* ... */ exit(-1); } AV* a = (AV*) SvRV(*aref); SV** res = av_fetch(a, index, FALSE); if (res == NULL) { /* ... */ exit(-1); } IV i = SvIV(*res); return i; } EOC my $obj = bless { permutation => [2,42,2,2,2,2,2,2,2,2,2] }, "Whatever +"; print get($obj, 1); # prints 42

        In real (production) code, you should also verify that you in fact have the appropriate types (using SvROK(), SvIOK(), etc.) before treating them as such.

        (As for chromatic's note below, the mapping of the Perl integer to the C int index is being taken care of by Inline::C in this example.)

Re: simple XS
by kthakore (Acolyte) on Mar 01, 2010 at 12:01 UTC
    I good way to understand where the perl XS code is segfaulting and why is to run:
     gdb --args perl script.pl 
    When it seg faults in gdb:
     (gdb) bt full
    Can you post that please?
      thanks kthakore, I had to install the perl-debug package in debian(so that I get a perl compiled with debugging symbols) and run gdb debugperl then run -I./blib/arch/auto/SJT/SJT.so  file.pl in order to use gdb on it, and then, contrary to intuition I had to put breakpoints on SJT.xs and not SJT.c like break SJT.xs:44, I highlighted with red the line that segfaulted above.