Revision: 1224
Author: [email protected]
Date: Thu May 20 12:01:28 2010
Log: Allow heuristic that handles assignment of xsubs to source files
to consider string evals (but still prefer non-evals is any).
Only set NYTP_FIDf_HAS_SRC if GvAV(gv_fetchfile_flags()) is not empty.
Bail out of looking for a cop earlier - it never worked well.
Tweaked trace levels, again.
Fixed href_to_sub to handle xsubs in a more useful way.

http://code.google.com/p/perl-devel-nytprof/source/detail?r=1224

Modified:
 /trunk/Changes
 /trunk/NYTProf.xs
 /trunk/lib/Devel/NYTProf/Data.pm
 /trunk/lib/Devel/NYTProf/Reader.pm

=======================================
--- /trunk/Changes      Mon May  3 14:44:45 2010
+++ /trunk/Changes      Thu May 20 12:01:28 2010
@@ -25,6 +25,8 @@
     Each string eval executed gets it's own report page.
     String evals may be collapsed/merged in some cases.

+  Improved many sub-optimal behaviours related to string evals.
+
   Enabled savesrc=1 by default.

 Other changes:
=======================================
--- /trunk/NYTProf.xs   Fri May  7 04:51:32 2010
+++ /trunk/NYTProf.xs   Thu May 20 12:01:28 2010
@@ -525,6 +525,39 @@
     return hash;
 }

+/**
+ * Returns a pointer to the ')' after the digits in the (?:re_)?eval prefix.
+ * As the prefix length is known, this gives the length of the digits.
+ */
+static const char *
+eval_prefix(const char *filename, const char *prefix, STRLEN prefix_len) {
+    if (memEQ(filename, prefix, prefix_len)
+        && isdigit(filename[prefix_len])) {
+        const char *s = filename + prefix_len + 1;
+
+        while (isdigit(*s))
+            ++s;
+        if (s[0] == ')')
+            return s;
+    }
+    return NULL;
+}
+
+/**
+ * Return true if filename looks like an eval
+ */
+static int
+filename_is_eval(const char *filename, STRLEN filename_len)
+{
+    if (filename_len < 6 || filename[filename_len - 1] != ']')
+        return 0;
+    if (eval_prefix(filename, "(eval ", 6))
+        return 1;
+    if (eval_prefix(filename, "(re_eval ", 9))
+        return 1;
+    return 0;
+}
+

 /**
  * Fetch/Store on hash table.  entry must always be defined.
@@ -541,8 +574,9 @@
     Hash_entry* found = hashtable.table[h];
     while(NULL != found) {

-        if (found->key_len == entry.key_len &&
-        memEQ(found->key, entry.key, entry.key_len)) {
+        if (found->key_len == entry.key_len
+        && memEQ(found->key, entry.key, entry.key_len)
+        ) {
             *retval = found;
             return 0;
         }
@@ -751,6 +785,19 @@

     return match;
 }
+
+
+static Hash_entry *
+lookup_file_entry(pTHX_ char* file_name, STRLEN file_name_len) {
+    Hash_entry entry, *found;
+
+    entry.key = file_name;
+    entry.key_len = (unsigned int)file_name_len;
+    if (hash_op(entry, &found, 0) == 0)
+        return found;
+
+    return NULL;
+}


 /**
@@ -915,7 +962,8 @@
     /* source only available if PERLDB_LINE or PERLDB_SAVESRC is true */
     /* which we set if savesrc option is enabled */
if ( (src_av = GvAV(gv_fetchfile_flags(found->key, found->key_len, 0))) )
-        found->fid_flags |= NYTP_FIDf_HAS_SRC;
+        if (av_len(src_av) > -1)
+            found->fid_flags |= NYTP_FIDf_HAS_SRC;

     /* if it's a string eval or a synthetic filename from CODE ref in @INC,
      * or the command line -e '...code...'
@@ -1085,19 +1133,22 @@
                     OutCopFILE((COP*)o));
             return (COP*)o;
         }
-#ifdef CXt_LOOP
-        /* e.g. "eval $_ for @ary" */
-        if (CxTYPE(cx) == CXt_LOOP)
+        if (trace_level >= trace)
+ logwarn("\tstart_cop_of_context %s op '%s' isn't a cop, giving up\n",
+                cx_block_type(cx), OP_NAME(o));
+        return NULL;
+#if 0   /* old code that never worked very well anyway */
+        if (CxTYPE(cx) == CXt_LOOP) /* e.g. "eval $_ for @ary" */
             return NULL;
-#endif
         /* should never get here but we do */
-        if (trace_level >= trace || 1) {
+        if (trace_level >= trace) {
             logwarn("\tstart_cop_of_context %s op '%s' isn't a cop\n",
                 cx_block_type(cx), OP_NAME(o));
             if (trace_level >  trace)
                 do_op_dump(1, PerlIO_stderr(), o);
         }
         o = o->op_next;
+#endif
     }
     if (trace_level >= 3) {
logwarn("\tstart_cop_of_context: can't find next cop for %s line %ld\n",
@@ -1304,7 +1355,7 @@
         get_ticks_between(start_time, end_time, elapsed, overflow);
     }
if (overflow) /* XXX later output overflow to file */ - logwarn("profile time overflow of %ld seconds discarded\n", overflow); + logwarn("profile time overflow of %ld seconds discarded!\n", overflow);

     reinit_if_forked(aTHX);

@@ -1317,9 +1368,10 @@
             NYTP_write_time_line(out, elapsed, last_executed_fid,
                                  last_executed_line);

-        if (trace_level >= 4)
- logwarn("\twrote %d:%-4d %2ld ticks (%u, %u)\n", last_executed_fid, - last_executed_line, elapsed, last_block_line, last_sub_line);
+        if (trace_level >= 5)
+            logwarn("\...@%d:%-4d %2ld ticks (%u, %u)\n",
+                last_executed_fid, last_executed_line,
+                elapsed, last_block_line, last_sub_line);
     }

     if (!cop)
@@ -1898,7 +1950,7 @@
SV *pf_sv = *hv_fetch(pkg_fids_hv, subr_entry->called_subpkg_pv, (I32)strlen(subr_entry->called_subpkg_pv), 1);
             if (SvTYPE(pf_sv) == SVt_NULL) { /* log when first created */
                 sv_upgrade(pf_sv, SVt_PV);
-                if (trace_level >= 5)
+                if (trace_level >= 3)
logwarn("Noting that subs in package '%s' were called\n",
                         subr_entry->called_subpkg_pv);
             }
@@ -1909,7 +1961,7 @@
         sv_inc(AvARRAY(subr_call_av)[NYTP_SCi_CALL_COUNT]);
     }

-    if (trace_level >= 4)
+    if (trace_level >= 5)
logwarn("%2d <- %s %"NVff"s excl = %"NVff"s incl - %"NVff"s (%"NVff"-%"NVff"), oh %"NVff"-%"NVff"=%"NVff"t, d%d @%d:%d #%lu %p\n",
             subr_entry->subr_prof_depth,
             called_subname_pv,
@@ -2551,7 +2603,7 @@
     if (!profile_subs)
         subr_entry->already_counted++;

-    if (trace_level >= 3) {
+    if (trace_level >= 4) {
logwarn("%2d ->%4s %s::%s from %s::%s @%u:%u (d%d, oh %"NVff"t, sub %"NVff"s) #%lu\n",
             subr_entry->subr_prof_depth,
             (subr_entry->called_is_xs) ? subr_entry->called_is_xs : "sub",
@@ -2944,13 +2996,16 @@
 }


-/* Given a sub_name lookup the package name in pkg_fids_hv hash.
- * pp_subcall_profiler() creates undef entries for a package the
- * first time a sub in the package is called.
- * Return Nullsv if there's no package name or no correponding entry
- * else returns the SV.
+/* Given a fully-qualified sub_name lookup the package name portion in
+ * the pkg_fids_hv hash.  Return Nullsv if there's no package name or no
+ * correponding entry else returns the SV.
+ *
+ * pkg_fids_hv:
+ * pp_subcall_profiler() creates undef entries for a package
+ *      name the first time a sub in the package is called.
  * write_sub_line_ranges() updates the SV with the filename associated
- * with the package, or at least its best guess.
+ *      with the package, or at least its best guess.
+ *
  * As most callers get len via the hash API, they will have an I32, where
* "negative" length signifies UTF-8. As we're only dealing with looking for * ASCII here, it doesn't matter to use which encoding sub_name is in, but it
@@ -2989,22 +3044,6 @@
     return 1;
 }

-/* Returns a pointer to the ')' after the digits in the (?:re_)?eval prefix.
-   As the prefix length is known, this gives the length of the digits.  */
-
-static char *
-eval_prefix(char *filename, const char *prefix, STRLEN prefix_len) {
-    if (memEQ(filename, prefix, prefix_len)
-        && isdigit(filename[prefix_len])) {
-        char *s = filename + prefix_len + 1;
-
-        while (isdigit(*s))
-            ++s;
-        if (s[0] == ')' && s[1] == '[')
-            return s;
-    }
-    return NULL;
-}

 static void
 write_sub_line_ranges(pTHX)
@@ -3016,7 +3055,7 @@
     unsigned int fid;

     if (trace_level >= 2)
-        logwarn("~ writing sub line ranges\n");
+        logwarn("~ writing sub line ranges - prescan\n");

     /* Skim through PL_DBsub hash to build a package to filename hash
      * by associating the package part of the sub_name in the key
@@ -3048,27 +3087,47 @@
         if (file_lines_len > 4
&& filename[file_lines_len - 2] == '-' && filename[file_lines_len - 1] == '0' && filename[file_lines_len - 4] != ':' && filename[file_lines_len - 3] != '0')
-            continue;
+ continue; /* ignore filenames from %DB::sub that end in ":0-0" */

         first = strrchr(filename, ':');
         filename_len = (first) ? first - filename : 0;

- /* skip filenames for generated evals /\A\((?:re_)?eval \d+\)\[.*]\z/
-         */
-        if (filename_len > 9 && filename[filename_len - 1] == ']'
-            && (eval_prefix(filename, "(eval ", 6) ||
-                eval_prefix(filename, "(re_eval ", 9)))
-            continue;
-
         /* get sv for package-of-subname to filename mapping */
pkg_filename_sv = sub_pkg_filename_sv(aTHX_ sub_name, sub_name_len);

         if (!pkg_filename_sv) /* we don't know package */
             continue;

- /* already got a filename for this package XXX should allow multiple */
-        if (SvOK(pkg_filename_sv))
+ /* already got a cached filename for this package XXX should allow multiple */
+        if (SvOK(pkg_filename_sv)) {
+            STRLEN cached_len;
+            char *cached_filename = SvPV(pkg_filename_sv, cached_len);
+
+            /*
+             * if the cached filename is an eval and the current one isn't
+             * then we should cache the current one instead
+             */
+            if (filename_len > 0
+            &&  filename_is_eval(cached_filename, cached_len)
+            && !filename_is_eval(filename, filename_len)
+            ) {
+                if (trace_level >= 3)
+ logwarn("Sub %.*s package prompted from %.*s to %.*s\n",
+                        (int)sub_name_len, sub_name,
+                        (int)cached_len, cached_filename,
+                        (int)filename_len, filename);
+                sv_setpvn(pkg_filename_sv, filename, filename_len);
+                continue;
+            }
+
+ if (trace_level >= 3 && strnNE(SvPV_nolen(pkg_filename_sv), filename, filename_len)) { + /* eg utf8::SWASHNEW is already associated with .../utf8.pm not .../utf8_heavy.pl */ + logwarn("Package of sub %.*s is already associated with %s not %.*s\n",
+                    (int)sub_name_len, sub_name,
+ SvPV_nolen(pkg_filename_sv), (int)filename_len, filename);
+            }
             continue;
+        }

         /* ignore if filename is empty (eg xs) */
         if (!filename_len) {
@@ -3107,6 +3166,9 @@
         }
         sv_catpvs(sv, ":1-1");
     }
+
+    if (trace_level >= 2)
+        logwarn("~ writing sub line ranges\n");

     /* Iterate over PL_DBsub writing out fid and source line range of subs.
* If filename is missing (i.e., because it's an xsub so has no source file)
@@ -3274,10 +3336,13 @@
         AV *src_av = GvAV(gv_fetchfile_flags(e->key, e->key_len, 0));

         if ( !(e->fid_flags & NYTP_FIDf_HAS_SRC) ) {
+            char *hint = "";
             ++t_no_src;
-            if (src_av) /* sanity check */
- logwarn("fid %d has src but NYTP_FIDf_HAS_SRC not set! (%.*s)\n",
-                    e->id, e->key_len, e->key);
+            if (src_av && av_len(src_av) > -1) /* sanity check */
+                hint = " (NYTP_FIDf_HAS_SRC not set but src available!)";
+            if (trace_level >= 4 || *hint)
+                logwarn("fid %d has no src saved for %.*s%s\n",
+                    e->id, e->key_len, e->key, hint);
             continue;
         }
         if (!src_av) { /* sanity check */
=======================================
--- /trunk/lib/Devel/NYTProf/Data.pm    Mon May  3 13:49:06 2010
+++ /trunk/lib/Devel/NYTProf/Data.pm    Thu May 20 12:01:28 2010
@@ -691,7 +691,7 @@
 }


-=head2 file_line_range_of_subme
+=head2 file_line_range_of_sub

($file, $fid, $first, $last) = $profile->file_line_range_of_sub("main::foo");

=======================================
--- /trunk/lib/Devel/NYTProf/Reader.pm  Mon Apr 26 02:22:36 2010
+++ /trunk/lib/Devel/NYTProf/Reader.pm  Thu May 20 12:01:28 2010
@@ -470,16 +470,10 @@
     my $profile = $self->{profile};

my ($file, $fid, $first, $last, $fi) = $profile->file_line_range_of_sub($sub);
+    return "" unless $file;
     if (!$first) {
-        if (not defined $first) {
- warn("No file line range data for sub '$sub' (perhaps an xsub)\n") - unless our $url_for_sub_no_data_warn->{$sub}++; # warn just once
-            return "";
-        }
-        # probably xsub
-        # return no link if we don't have a file for this xsub
-        return "" unless $file;
-        # use sanitized subname as label
+        # use sanitized subname as label for xsubs
+        # XXX must match what nytprofhtml does for xsubs
         ($first = $sub) =~ s/\W/_/g;
     }
     return $self->url_for_file($fi, $first);

--
You've received this message because you are subscribed to
the Devel::NYTProf Development User group.

Group hosted at:  http://groups.google.com/group/develnytprof-dev
Project hosted at:  http://perl-devel-nytprof.googlecode.com
CPAN distribution:  http://search.cpan.org/dist/Devel-NYTProf

To post, email:  [email protected]
To unsubscribe, email:  [email protected]

Reply via email to