On Wed, Feb 23, 2005 at 11:40:09PM -0500, Stas Bekman wrote:
> The scalars leaking reporting needs to be improved. At the moment if you 
> get a scalars leaked reported and it didn't happen when you've added that 
> new line of XS/C code you really have no idea where does it coming from. 
> With -DDEBUG_LEAKING_SCALARS the best you can get is:
> 
> Scalars leaked: 1
> leaked: sv=0x9d155d4 flags=0x0880003 refcnt=0, Perl interpreter: 0x89bdd40

The change I've committed below expands the behaviour of
    -DDEBUG_LEAKING_SCALARS
to record where each SV is created. It conditionally adds a few extra
fields to the SV struct, which record the filename, lineno and responsible
op type. This info is displayed in the output of Devel::Peek as well as in
the leaked message above, eg

    $ ./perl -Ilib -MDevel::Peek -e 'Dump $a+1'
    ALLOCATED at -e:1 for add
    SV = NV(0x8dbc590) at 0x8db06f4
      REFCNT = 1
      FLAGS = (PADTMP,NOK,pNOK)
      NV = 1

    $ ... some code that leaks ...
    Scalars leaked: 1 
    leaked: sv=0x857a0e0 flags=0x08400000 refcnt=1
            allocated at -e:1 by sin

When this is in force, it typically adds an extra 8 + length(filename)
bytes to each SV, so things run a bit slower and use more RAM
("perl -MPOSIX -e 1" uses 20% more RAM on my x86 system).

SVs allocated at runtime are displayed as 'by foo' where op_foo was the op
being executed at the time; SVs allocated at compile-time in the pad for
an op (ie a pad tmp or padsv) are displayed as 'for foo'.

SV's allocated within XS code will be shown as 'by entersub'.

Dave

-- 
SCO - a train crash in slow motion

Change 24088 by [EMAIL PROTECTED] on 2005/03/28 21:38:44

        expand -DDEBUG_LEAKING_SCALARS to instrument the creation of each SV

Affected files ...

... //depot/perl/dump.c#162 edit
... //depot/perl/ext/Devel/Peek/t/Peek.t#3 edit
... //depot/perl/pad.c#41 edit
... //depot/perl/perl.c#590 edit
... //depot/perl/pod/perlhack.pod#84 edit
... //depot/perl/sv.c#789 edit
... //depot/perl/sv.h#166 edit

Differences ...

==== //depot/perl/dump.c#162 (text) ====

@@ -1203,6 +1203,14 @@
     sv_catpv(d, ")");
     s = SvPVX(d);
 
+#ifdef DEBUG_LEAKING_SCALARS
+    Perl_dump_indent(aTHX_ level, file, "ALLOCATED at %s:%d %s %s%s\n",
+       sv->sv_debug_file ? sv->sv_debug_file : "(unknown)",
+       sv->sv_debug_line,
+       sv->sv_debug_inpad ? "for" : "by",
+       sv->sv_debug_optype ? PL_op_name[sv->sv_debug_optype]: "(none)",
+       sv->sv_debug_cloned ? " (cloned)" : "");
+#endif
     Perl_dump_indent(aTHX_ level, file, "SV = ");
     switch (type) {
     case SVt_NULL:

==== //depot/perl/ext/Devel/Peek/t/Peek.t#3 (text) ====

@@ -28,6 +28,8 @@
            local $/;
            $pattern =~ s/\$ADDR/0x[[:xdigit:]]+/g;
            $pattern =~ s/\$FLOAT/(?:\\d*\\.\\d+(?:e[-+]\\d+)?|\\d+)/g;
+           # handle DEBUG_LEAKING_SCALARS prefix
+           $pattern =~ s/^(\s*)(SV =.* at )/$1ALLOCATED at .*?\n$1$2/mg;
            print $pattern, "\n" if $DEBUG;
            my $dump = <IN>;
            print $dump, "\n"    if $DEBUG;

==== //depot/perl/pad.c#41 (text) ====

@@ -434,7 +434,11 @@
          "Pad 0x%"UVxf"[0x%"UVxf"] alloc:   %ld for %s\n",
          PTR2UV(PL_comppad), PTR2UV(PL_curpad), (long) retval,
          PL_op_name[optype]));
+#ifdef DEBUG_LEAKING_SCALARS
+    sv->sv_debug_optype = optype;
+    sv->sv_debug_inpad = 1;
     return (PADOFFSET)retval;
+#endif
 }
 
 /*

==== //depot/perl/perl.c#590 (text) ====

@@ -827,8 +827,16 @@
                if (SvTYPE(sv) != SVTYPEMASK) {
                    PerlIO_printf(Perl_debug_log, "leaked: sv=0x%p"
                        " flags=0x08%"UVxf
-                       " refcnt=%"UVuf pTHX__FORMAT "\n",
-                       sv, sv->sv_flags, sv->sv_refcnt pTHX__VALUE);
+                       " refcnt=%"UVuf pTHX__FORMAT "\n"
+                       "\tallocated at %s:%d %s %s%s\n",
+                       sv, sv->sv_flags, sv->sv_refcnt pTHX__VALUE,
+                       sv->sv_debug_file ? sv->sv_debug_file : "(unknown)",
+                       sv->sv_debug_line,
+                       sv->sv_debug_inpad ? "for" : "by",
+                       sv->sv_debug_optype ?
+                           PL_op_name[sv->sv_debug_optype]: "(none)",
+                       sv->sv_debug_cloned ? " (cloned)" : ""
+                   );
                }
            }
        }

==== //depot/perl/pod/perlhack.pod#84 (text) ====

@@ -2310,10 +2310,13 @@
 equivalent of setting this variable to the value 1.)
 
 If, at the end of a run you get the message I<N scalars leaked>, you can
-recompile with C<-DDEBUG_LEAKING_SCALARS>, which will cause
-the addresses of all those leaked SVs to be dumped; it also converts
-C<new_SV()> from a macro into a real function, so you can use your
-favourite debugger to discover where those pesky SVs were allocated.
+recompile with C<-DDEBUG_LEAKING_SCALARS>, which will cause the addresses
+of all those leaked SVs to be dumped along with details as to where each
+SV was originally allocated. This information is also displayed by
+Devel::Peek. Note that the extra details recorded with each SV increases
+memory usage, so it shouldn't be used in production environments. It also
+converts C<new_SV()> from a macro into a real function, so you can use
+your favourite debugger to discover where those pesky SVs were allocated.
 
 =head2 Profiling
 

==== //depot/perl/sv.c#789 (text) ====

@@ -165,8 +165,19 @@
  * "A time to plant, and a time to uproot what was planted..."
  */
 
+#ifdef DEBUG_LEAKING_SCALARS
+#  ifdef NETWARE
+#    define FREE_SV_DEBUG_FILE(sv) PerlMemfree((sv)->sv_debug_file)
+#  else
+#    define FREE_SV_DEBUG_FILE(sv) PerlMemShared_free((sv)->sv_debug_file)
+#  endif
+#else
+#  define FREE_SV_DEBUG_FILE(sv)
+#endif
+
 #define plant_SV(p) \
     STMT_START {                                       \
+       FREE_SV_DEBUG_FILE(p);                          \
        SvANY(p) = (void *)PL_sv_root;                  \
        SvFLAGS(p) = SVTYPEMASK;                        \
        PL_sv_root = (p);                               \
@@ -200,6 +211,17 @@
     SvANY(sv) = 0;
     SvREFCNT(sv) = 1;
     SvFLAGS(sv) = 0;
+    sv->sv_debug_optype = PL_op ? PL_op->op_type : 0;
+    sv->sv_debug_line = (U16) ((PL_copline == NOLINE) ?
+        (PL_curcop ? CopLINE(PL_curcop) : 0) : PL_copline);
+    sv->sv_debug_inpad = 0;
+    sv->sv_debug_cloned = 0;
+#  ifdef NETWARE
+    sv->sv_debug_file = PL_curcop ? savepv(CopFILE(PL_curcop)): NULL;
+#  else
+    sv->sv_debug_file = PL_curcop ? savesharedpv(CopFILE(PL_curcop)): NULL;
+#  endif
+    
     return sv;
 }
 #  define new_SV(p) (p)=S_new_SV(aTHX)
@@ -5822,7 +5844,14 @@
     SvREFCNT(sv) = 0;
     sv_clear(sv);
     assert(!SvREFCNT(sv));
+#ifdef DEBUG_LEAKING_SCALARS
+    sv->sv_flags  = nsv->sv_flags;
+    sv->sv_any    = nsv->sv_any;
+    sv->sv_refcnt = nsv->sv_refcnt;
+#else
     StructCopy(nsv,sv,SV);
+#endif
+
 #ifdef PERL_COPY_ON_WRITE
     if (SvIsCOW_normal(nsv)) {
        /* We need to follow the pointers around the loop to make the
@@ -10727,6 +10756,19 @@
 
     /* create anew and remember what it is */
     new_SV(dstr);
+
+#ifdef DEBUG_LEAKING_SCALARS
+    dstr->sv_debug_optype = sstr->sv_debug_optype;
+    dstr->sv_debug_line = sstr->sv_debug_line;
+    dstr->sv_debug_inpad = sstr->sv_debug_inpad;
+    dstr->sv_debug_cloned = 1;
+#  ifdef NETWARE
+    dstr->sv_debug_file = savepv(sstr->sv_debug_file);
+#  else
+    dstr->sv_debug_file = savesharedpv(sstr->sv_debug_file);
+#  endif
+#endif
+
     ptr_table_store(PL_ptr_table, sstr, dstr);
 
     /* clone */
@@ -11540,6 +11582,8 @@
 
 #  ifdef DEBUGGING
     Poison(my_perl, 1, PerlInterpreter);
+    PL_op = Nullop;
+    PL_curcop = Nullop;
     PL_markstack = 0;
     PL_scopestack = 0;
     PL_savestack = 0;
@@ -11572,6 +11616,8 @@
 
 #    ifdef DEBUGGING
     Poison(my_perl, 1, PerlInterpreter);
+    PL_op = Nullop;
+    PL_curcop = Nullop;
     PL_markstack = 0;
     PL_scopestack = 0;
     PL_savestack = 0;

==== //depot/perl/sv.h#166 (text) ====

@@ -68,6 +68,13 @@
     void*      sv_any;         /* pointer to something */
     U32                sv_refcnt;      /* how many references to us */
     U32                sv_flags;       /* what we are */
+#ifdef DEBUG_LEAKING_SCALARS
+    unsigned   sv_debug_optype:9;      /* the type of OP that allocated us */
+    unsigned   sv_debug_inpad:1;       /* was allocated in a pad for an OP */
+    unsigned   sv_debug_cloned:1;      /* was cloned for an ithread */
+    unsigned   sv_debug_line:16;       /* the line where we were allocated */
+    char *     sv_debug_file;          /* the file where we were allocated */
+#endif
 };
 
 struct gv {

Reply via email to