The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* This code uses several internal APIs. I'm declaring PERL_CORE
 * purely so this is visible to anyone grepping CPAN for code that
 * does this sort of thing.
 *
 *   Copied and pasted structure of S_visit from sv.c
 *   Used PL_sv_arenaroot
 *   Used do_sv_dump (instead of sv_dump)
 *   Used pv_display
 */
#define PERL_CORE

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"

void DumpPointer( pTHX_ PerlIO *f, SV *sv ) {
  if ( &PL_sv_undef == sv ) {
    PerlIO_puts(f, "PL_sv_undef");
  }
  else if (&PL_sv_yes == sv) {
    PerlIO_puts(f, "PL_sv_yes");
  }
  else if (&PL_sv_no == sv) {
    PerlIO_puts(f, "PL_sv_no");
  }
  else if (&PL_sv_placeholder) {
    PerlIO_printf(f, "%#x", (int)sv);
  }
}

void
DumpAvARRAY( pTHX_ PerlIO *f, SV *sv) {
  I32 key = 0;

  PerlIO_printf(f,"AvARRAY(%#x) = {",(int)AvARRAY(sv));
  if ( AvMAX(sv) != AvFILL(sv) ) {
    PerlIO_puts(f,"{");
  }
  
  for ( key = 0; key <= AvMAX(sv); ++key ) {
    DumpPointer(aTHX_ f, AvARRAY(sv)[key]);
    
    /* Join with something */
    if ( AvMAX(sv) == AvFILL(sv) ) {
      if (key != AvMAX(sv)) {
        PerlIO_puts(f, ",");
      }
    }
    else {
      PerlIO_puts(
        f,
        AvFILL(sv) == key ? "}{" :
        AvMAX(sv) == key  ? "}" :
        ","
      );
    }
  }
  PerlIO_puts(f,"}\n\n");
}

void
DumpHvARRAY( pTHX_ PerlIO *f, SV *sv) {
  I32 key = 0;
  HE *entry;
  SV *tmp = newSVpv("",0);

  PerlIO_printf(f,"HvARRAY(%#x)\n",(int)HvARRAY(sv));

  for ( key = 0; key <= HvMAX(sv); ++key ) {
    for ( entry = HvARRAY(sv)[key]; entry; entry = HeNEXT(entry) ) {
      if ( HEf_SVKEY == HeKLEN(entry) ) {
        PerlIO_printf(
          f, "  [SV %#x] => ",
          (int)HeKEY(entry));
      }
      else {
        PerlIO_printf(
          f, "  [%#x %s] => ",
          (int)HeKEY(entry),
          pv_display(
            tmp,
            HeKEY(entry), HeKLEN(entry), HeKLEN(entry),
            0 ));
      }
      DumpPointer(aTHX_ f, HeVAL(entry));
      PerlIO_puts(f, "\n");
    }
  }
  PerlIO_puts(f,"\n");

  SvREFCNT_dec(tmp);
}

/*
void
DumpHashKeys( aTHX_ PerlIO *f, SV *sv) {
  I32 key = 0;
  HE *entry;
  SV *tmp = newSVpv("",0);

  PerlIO_printf(f,"HASH KEYS at %#x\n",sv);
  
  for ( key = 0; key <= HvMAX(sv); ++key ) {
    for ( entry = HvARRAY(sv)[key]; entry; entry = HeNEXT(entry) ) {
      if ( HEf_SVKEY == HeKLEN(entry) ) {
        PerlIO_printf(f, "    SV %#x\n", HeKEY(entry) );
      }
      else {
        PerlIO_printf(f, "    %#x %s\n", HeKEY(entry), pv_display( (SV*)tmp, (const char*)HeKEY(entry), HeKLEN(entry), HeKLEN(entry), 0 ) );
      } 
    }
  }
  PerlIO_puts(f,"\n\n");

  SvREFCNT_dec(tmp);
}
*/

void
DumpArenasPerlIO( pTHX_ PerlIO *f) {
  SV *arena;
  
  for (arena = PL_sv_arenaroot; arena; arena = (SV*)SvANY(arena)) {
    const SV *const arena_end = &arena[SvREFCNT(arena)];
    SV *sv;

    /* See also the static function S_visit in perl's sv.c
     * This is a copied and pasted implementation of that function.
     */
    PerlIO_printf(f,"START ARENA = (%#x-%#x)\n\n",(int)arena,(int)arena_end);
    for (sv = arena + 1; sv < arena_end; ++sv) {
      if (SvTYPE(sv) != SVTYPEMASK
          && SvREFCNT(sv)) {

        /* Dump the plain SV */
        do_sv_dump(0,f,sv,0,0,0,0);
        PerlIO_puts(f,"\n");
        
        /* Dump AvARRAY(0x...) = {{0x...,0x...}{0x...}} */
        switch (SvTYPE(sv)) {
        case SVt_PVAV:
          if ( AvARRAY(sv)
               && AvMAX(sv) != -1 ) {
            DumpAvARRAY( aTHX_ f,sv);
          }
          break;
        case SVt_PVHV:
          if ( HvARRAY(sv)
               && HvMAX(sv) != -1 ) {
            DumpHvARRAY( aTHX_ f,sv);
          }
          
          if ( ! HvSHAREKEYS(sv) ) {
            /* DumpHashKeys( aTHX_ f,sv); */
          }
          
          break;
        }
      }
      else {
        PerlIO_printf(f,"AVAILABLE(%#x)\n\n",(int)sv);
      }
    }
    PerlIO_printf(f,"END ARENA = (%#x-%#x)\n\n",(int)arena,(int)arena_end);
  }
}

void
DumpArenas( pTHX ) {
  DumpArenasPerlIO( aTHX_ Perl_error_log );
}

void
DumpArenasFd( pTHX_ int fd ) {
  PerlIO *f = (PerlIO*)PerlIO_fdopen( fd, "w" );
  DumpArenasPerlIO( aTHX_ f );
}

MODULE = Internals::DumpArenas  PACKAGE = Internals::DumpArenas
  
PROTOTYPES: DISABLE

void
DumpArenas()
    CODE:
        DumpArenas( aTHX );

void
DumpArenasFd( int fn )
    CODE:
        DumpArenasFd( aTHX_ fn );