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

The below .XS routine performs a readdir(), but returns the inode numbers of the directory entries at the same time (this information should always be available, and in some situations it might save you a fair few calls to stat(), such as in unify-dirs).

The code seems to work perfectly, but for one directory, I get a segfault every time. The segfault happens after the sub has returned, in Perl_sv_setsv (eval.c:41). Can anyone see anything glaringly wrong with this Xsub?

/* <=-*- C -*-=> */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <sys/types.h> #include <dirent.h> #include <stdio.h> MODULE = ReadDir PACKAGE = ReadDir void readdir_inode(dirname) char* dirname INIT: struct dirent *ent; DIR* dir; SV* record[2]; AV *entry, *ret_val; PPCODE: dir = opendir(dirname); if (dir) { while ((ent=readdir(dir))) { record[0] = newSVpv(ent->d_name, 0); record[1] = newSViv((IV)ent->d_ino); PUSHs(sv_2mortal(newRV_inc((SV*)av_make(2, record)))); } closedir(dir); }

Here is a transcript of the debugger session:

vshost:~# gdb /usr/bin/perl GNU gdb 5.0rh-5 Red Hat Linux 7.1 Copyright 2001 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and y +ou are welcome to change it and/or distribute copies of it under certain cond +itions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for det +ails. This GDB was configured as "i386-redhat-linux"... (no debugging symbols found)... (gdb) run -d ./unify-dirs -dil /vservers/*/usr/share/man/man1 Starting program: /usr/bin/perl -d ./unify-dirs -dil /vservers/*/usr/s +hare/man/man1 Default die handler restored. Loading DB routines from perl5db.pl version 1.07 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(./unify-dirs:187): my ($action, @dirs, $immutable, $linka +ge, $mode); DB<1> Program received signal SIGINT, Interrupt. 0x40133f44 in __libc_read () from /lib/i686/libc.so.6 (gdb) b XS_ReadDir_readdir_inode Breakpoint 1 at 0x401e7b48: file ReadDir.c, line 22. (gdb) c Continuing. c unify-dirs: Unifying: /vservers/babelfish/usr/share/man/man1 /vservers +/compileit/usr/share/man/man1 /vservers/dev/usr/share/man/man1 /vserv +ers/easystrike/usr/share/man/man1 /vservers/master-/usr/share/man/man +1 /vservers/master/usr/share/man/man1 /vservers/vs1/usr/share/man/man +1 /vservers/vs2/usr/share/man/man1 /vservers/webrt/usr/share/man/man1 unify-dirs: Processing /vservers/babelfish/usr/share/man/man1... Breakpoint 1, XS_ReadDir_readdir_inode (cv=0x82f7114) at ReadDir.c:22 22 dXSARGS; (gdb) c Continuing. unify-dirs: Readdir OK unify-dirs: Processing /vservers/compileit/usr/share/man/man1... Breakpoint 1, XS_ReadDir_readdir_inode (cv=0x82f7114) at ReadDir.c:22 22 dXSARGS; (gdb) n 23 if (items != 1) (gdb) 27 char* dirname = (char *)SvPV(ST(0),PL_na); (gdb) 25 SP -= items; (gdb) 27 char* dirname = (char *)SvPV(ST(0),PL_na); (gdb) 21 dir = opendir(dirname); (gdb) 22 if (dir) { (gdb) 23 while ((ent=readdir(dir))) { (gdb) 24 record[0] = newSVpv(ent->d_name, 0); (gdb) p ent->d_name $1 = ".\000\000\000\000\203%\020\000\030\000\000\000\020\000\004..\000 +\000\000\002x\t\0000\000\000\000 \000\bbuiltins.1.gz\000\000\000\000\ +000\000\000\000\003x\t\000@\000\000\000\030\000\nsh.1.gz\000\000\000\ +000\000\000ÿw\t\000T\000\000\000\030\000\brbash.1.gz\000\000\000\000x +\t\000h\000\000\000\030\000\bbash.1.gz\000\000\000\000\001x\t\000|\00 +0\000\000\030\000\bbashbug.1.gz\000\213u\t\000\230\000\000\000 \000\b +sensible-editor.1.gz\000\214u\t\000¬\000\000\000\030\000\bmktemp.1".. +. (gdb) n 26 PUSHs(sv_2mortal(newRV_inc((SV*)av_make(2, record)))); (gdb) n 24 record[0] = newSVpv(ent->d_name, 0); (gdb) n 25 record[1] = newSViv((IV)ent->d_ino); (gdb) 26 PUSHs(sv_2mortal(newRV_inc((SV*)av_make(2, record)))); (gdb) p $ent->d_ino Attempt to extract a component of a value that is not a structure poin +ter. (gdb) p ent->d_ino $2 = 1058181 (gdb) fin Run till exit from #0 XS_ReadDir_readdir_inode (cv=0x82f7114) at Read +Dir.xs:26 0x0809dd2c in Perl_pp_entersub () at eval.c:41 41 eval.c: No such file or directory. in eval.c (gdb) fin Run till exit from #0 0x0809dd2c in Perl_pp_entersub () at eval.c:41 0x08098658 in Perl_runops_standard () at eval.c:41 41 in eval.c (gdb) fin Run till exit from #0 0x08098658 in Perl_runops_standard () at eval.c +:41 Program received signal SIGSEGV, Segmentation fault. 0x080a1ea9 in Perl_sv_setsv () at eval.c:41 41 in eval.c (gdb) bt #0 0x080a1ea9 in Perl_sv_setsv () at eval.c:41 #1 0x080a5509 in Perl_sv_mortalcopy () at eval.c:41 #2 0x0809a3a6 in Perl_pp_aassign () at eval.c:41 #3 0x08098658 in Perl_runops_standard () at eval.c:41 #4 0x0805bf91 in perl_run () at eval.c:41 #5 0x0805bd2b in perl_run () at eval.c:41 #6 0x08059a21 in main () at eval.c:41 #7 0x40076177 in __libc_start_main (main=0x80599b0 <main>, argc=13, ubp_av=0xbffff9f4, init=0x8058b80 <_init>, fini=0x80df83c <_fini>, + rtld_fini=0x4000e184 <_dl_fini>, stack_end=0xbffff9ec) at ../sysdeps/generic/libc-start.c:129 (gdb)

Replies are listed 'Best First'.
Re: .XS segfault
by derby (Abbot) on Mar 08, 2002 at 14:18 UTC
    mugwumpjism

    I'm no XS expert (I prefer Inline::C) but SIGSEGV leads me to believe you're blowing the stack. PUSHs will not automatically expand the stack for you. Try XPUSHs instead. Just out of curiosity, does the dir you're SIGSEGV'ing on have many more entries than the dirs it works okay on?

    -derby

    update: I couldn't resist a little Inline::C rewrite:

    #!/usr/local/bin/perl -w use Inline C; use strict; foreach( @ARGV ) { my( @stuff ) = readdir_inode($_); foreach(@stuff) { foreach( @$_ ) { print $_, " "; } print "\n"; } } __END__ __C__ #include <sys/types.h> #include <dirent.h> #include <stdio.h> void readdir_inode ( char *dirname ) { struct dirent *ent; DIR *dir; SV *record[2]; AV *entry, *ret_val; Inline_Stack_Vars; Inline_Stack_Reset; dir = opendir(dirname); if( dir ) { while( ent=readdir(dir) ) { record[0] = newSVpv(ent->d_name, 0 ); record[1] = newSViv((IV)ent->d_ino); Inline_Stack_Push(newRV_noinc((SV*)av_make(2,r +ecord))); } closedir(dir); } Inline_Stack_Done; }

      That was it, fabulous!

      I actually tried Inline first (Ingy did a really good job of selling it at YAPC::2001), but couldn't figure out how to make it compile the .so at "make" time rather than run time. Now I've got this far, I can give it a try again.

      derby++

      (person who has a votebot downvoting me)--

        mugwumpjism,

        That's outlined in Inline (check out the section titled "Writing Modules with Inline"). To sum those instructions up:

        1. run h2xs -PAXn <ModuleName> 2. Edit ModuleName.pm and add your Inline Code 3. Edit Makefile.PL 3a. Change use ExtUtils::MakeMaker to use Inline::MakeMaker 3b. Change WriteMakefile to WriteInlineMakefile 4. perl Makefile.PL 5. make dist

        -derby