Author: tim.bunce
Date: Sun Jul  5 15:31:26 2009
New Revision: 804

Modified:
    trunk/NYTProf.xs
    trunk/lib/Devel/NYTProf.pm
    trunk/lib/Devel/NYTProf/Core.pm

Log:
Switched to using context stack offsets instead of pointers to track current
sub enter and chain of outer callers. A step on the way to at least  
detecting
xsubs & builtins that exit via exceptions.
Record calling fid and line before invoking the op.
Added docs describing how the subroutine profiler works.


Modified: trunk/NYTProf.xs
==============================================================================
--- trunk/NYTProf.xs    (original)
+++ trunk/NYTProf.xs    Sun Jul  5 15:31:26 2009
@@ -1206,7 +1206,7 @@
          if (trace_level >= 4) {
              if (found)
                   logwarn("fid %d: %.*s\n",  found->id, found->key_len,  
found->key);
-            else logwarn("fid -: %.*s HAS NO FID\n",    entry.key_len,   
entry.key);
+            else logwarn("fid -: %.*s not profiled\n",  entry.key_len,   
entry.key);
          }
          return (found) ? found->id : 0;
      }
@@ -2067,7 +2067,7 @@
  typedef struct subr_entry_st subr_entry_t;
  struct subr_entry_st {
      time_of_day_t initial_call_time;
-    int calling_fid;
+    unsigned int calling_fid;
      int calling_line;
      SV *subname_sv;
      AV *sub_av;
@@ -2076,9 +2076,14 @@
      NV initial_overhead_ticks;
      NV initial_subr_secs;
      UV seqn;
-    subr_entry_t *caller;
+    int caller_ix;
+    /* ensure all items are initialized in pp_subcall_profiler */
  };
-static subr_entry_t *subr_entry_latest;
+static int subr_entry_latest_ix;
+
+/* return the subr_entry_t of the caller of the specified subr_entry_t */
+#define subr_entry_caller(subr_entry) ((subr_entry &&  
subr_entry->caller_ix) \
+    ? SSPTR(subr_entry->caller_ix, subr_entry_t *) : NULL)

  static void
  incr_sub_inclusive_time(pTHX_ subr_entry_t *subr_entry)
@@ -2140,7 +2145,7 @@

      if (subr_entry->subname_sv)
          sv_free(subr_entry->subname_sv);
-    subr_entry_latest = subr_entry->caller;
+    subr_entry_latest_ix = subr_entry->caller_ix;

      cumulative_subr_secs += excl_subr_sec;
      SETERRNO(saved_errno, 0);
@@ -2263,9 +2268,15 @@
          subr_entry->initial_subr_secs      = cumulative_subr_secs;
          subr_entry->seqn                   = ++cumulative_subr_seqn;

-        /* link in as head of the chain */
-        subr_entry->caller = subr_entry_latest;
-        subr_entry_latest = subr_entry;
+        char *file = OutCopFILE(prev_cop);
+        subr_entry->calling_fid = (file == last_executed_fileptr)
+            ? last_executed_fid
+            : get_file_id(aTHX_ file, strlen(file), NYTP_FIDf_VIA_SUB);
+        subr_entry->calling_line = CopLINE(prev_cop);
+
+        /* link in as head of the chain, but use offset not pointer */
+        subr_entry->caller_ix = subr_entry_latest_ix;
+        subr_entry_latest_ix = save_ix;

          /* sub name related items */
          subr_entry->subname_sv = &PL_sv_undef;
@@ -2298,13 +2309,10 @@

          /* get line, file, and fid for statement *before* the call */

-        char *file = OutCopFILE(prev_cop);
-        unsigned int fid;
          /* XXX could use same closest_cop as DB_stmt() but it doesn't seem
           * to be needed here. Line is 0 only when call is from embedded
           * C code like mod_perl (at least in my testing so far)
           */
-        int line = CopLINE(prev_cop);
          char fid_line_key[50];
          int fid_line_key_len;
          SV *subname_sv = newSV(0);
@@ -2388,13 +2396,6 @@
          if (is_xs && *subname_pv == 'D' && strEQ(subname_pv, "DB::_INIT"))
              goto skip_sub_profile;

-        fid = (file == last_executed_fileptr)
-            ? last_executed_fid
-            : get_file_id(aTHX_ file, strlen(file), NYTP_FIDf_VIA_SUB);
-        fid_line_key_len = sprintf(fid_line_key, "%u:%d", fid, line);
-        subr_entry->calling_fid  = fid;
-        subr_entry->calling_line = line;
-
          /* { called_subname => { "fid:line" => [ count, incl_time ] } } */
          sv_tmp = *hv_fetch(sub_callers_hv, subname_pv,
              (I32)SvCUR(subname_sv), 1);
@@ -2435,6 +2436,9 @@
              }
          }

+        fid_line_key_len = sprintf(fid_line_key, "%u:%d",
+            subr_entry->calling_fid, subr_entry->calling_line);
+
          /* drill-down to array of sub call information for this  
fid_line_key */
          sv_tmp = *hv_fetch((HV*)SvRV(sv_tmp), fid_line_key,  
fid_line_key_len, 1);
          if (!SvROK(sv_tmp)) { /* first call from this fid:line - autoviv  
array ref */
@@ -2455,16 +2459,18 @@
           * have already left the sub, unlike the non-xs case.        */
          subr_entry->call_depth = (cv) ? CvDEPTH(cv)+(is_xs?1:0) : 1;

-        if (trace_level >= 2)
+        if (trace_level >= 2) {
+            subr_entry_t *se_caller = subr_entry_caller(subr_entry);
              fprintf(stderr, " ->%s %s from %s %d:%d (d%d, oh %"NVff"t,  
sub %"NVff"s) #%lu\n",
                  (is_xs) ? is_xs : " sub", subname_pv,
-                (subr_entry->caller) ?  
SvPV_nolen(subr_entry->caller->subname_sv) : "",
-                fid, line,
+                (se_caller) ? SvPV_nolen(se_caller->subname_sv) : "",
+                subr_entry->calling_fid, subr_entry->calling_line,
                  subr_entry->call_depth,
                  subr_entry->initial_overhead_ticks,
                  subr_entry->initial_subr_secs,
                  subr_entry->seqn
              );
+        }

          if (profile_subs) {
              subr_entry->subname_sv = subname_sv;

Modified: trunk/lib/Devel/NYTProf.pm
==============================================================================
--- trunk/lib/Devel/NYTProf.pm  (original)
+++ trunk/lib/Devel/NYTProf.pm  Sun Jul  5 15:31:26 2009
@@ -793,6 +793,6 @@
  added zip compression. Jan Dubois contributed Windows support.

  Adam's work is sponsored by The New York Times Co.  
L<http://open.nytimes.com>.
-Tim's work was partly sponsored by Shopzilla. L<http://www.shopzilla.com>.
+Tim's work was partly sponsored by Shopzilla L<http://www.shopzilla.com>  
during 2008.

  =cut

Modified: trunk/lib/Devel/NYTProf/Core.pm
==============================================================================
--- trunk/lib/Devel/NYTProf/Core.pm     (original)
+++ trunk/lib/Devel/NYTProf/Core.pm     Sun Jul  5 15:31:26 2009
@@ -39,16 +39,100 @@
  This module is not meant to be used directly.
  See L<Devel::NYTProf>, L<Devel::NYTProf::Data>, and  
L<Devel::NYTProf::Reader>.

+While it's not meant to be used directly, it is a handy place to document  
some
+internals.
+
+=head1 SUBROUTINE PROFILER
+
+The subroutine profiler intercepts the C<entersub> opcode which perl uses  
to
+invoke a subroutine, both XS subs (henceforth xsubs) and pure perl subs.
+
+The following sections outline the way the subroutine profiler works:
+
+=head2 Before the subroutine call
+
+The profiler records the current time, the current value of
+cumulative_subr_secs (as initial_subr_secs), and the current
+cumulative_overhead_ticks (as initial_overhead_ticks).
+
+The statement profiler measures time at the start and end of processing for
+each statement (so time spent in the profiler, writing to the file for  
example,
+is excluded.) It accumulates the measured overhead into the
+cumulative_overhead_ticks variable.
+
+In a similar way, the subroutine profiler measures the I<exclusive> time  
spent
+in subroutines and accumulates it into the cumulative_subr_secs global.
+
+=head2 Make the subroutine call
+
+The call is made by executing the original perl internal code for the
+C<entersub> opcode.
+
+=head3 Calling a perl subroutine
+
+If the sub being called is a perl sub then when the entersub opcode  
returns,
+back into the subroutine profiler, the subroutine has been 'entered' but  
the
+first opcode of the subroutine hasn't been executed yet.
+Crucially though, a new scope has been entered by the entersub opcode.
+
+The subroutine profiler then pushes a destructor onto the context stack.
+The destructor is effectively I<inside> the sub, like a C<local>, and so  
will be
+triggered when the subroutine exits by I<any> means.  When the destructor  
is
+invoked it calls a function which completes the measurement of the time  
spent
+in the sub.
+
+In this way the profiling of perl subroutines is very accurate and robust.
+
+=head3 Calling an xsub
+
+If the sub being called is an xsub, then control doesn't return from the
+entersub opcode until the xsub has returned. The profiler detects this and
+calls the function which completes the measurement of the time spent in the
+xsub.
+
+So far so good, but there's a problem. What if the xsub doesn't return  
normally
+but throws an exception instead?
+
+In that case (currently) the profiler acts as if the xsub was never called.
+Time spent inside the xsub will be allocated to the calling sub.
+
+=head2 Completing the measurement
+
+The function which completes the timing of a subroutine call does the  
following:
+
+It calculates the time spent in the statement profiler:
+
+    overhead_ticks  = cumulative_overhead_ticks - initial_overhead_ticks
+
+and subtracts that from the total time spent 'inside' the subroutine:
+
+    incl_subr_sec = (time now - time call was made) - overhead_ticks
+
+That gives us an accurate I<inclusive> time. To get the I<exclusive> time
+it calculates the time spent in subroutines called from the subroutine call
+we're measuring:
+
+    called_sub_secs = cumulative_subr_secs - initial_subr_secs
+
+and subtracts that from the incl_subr_sec:
+
+    excl_subr_sec = incl_subr_sec - called_sub_secs
+
+To make that easier to follow, consider a call to a sub that calls no  
others.
+In that case cumulative_subr_secs remains unchanged during the call, so
+called_sub_secs is zero, and excl_subr_sec is the same as incl_subr_sec.
+
+Finally, it adds the exclusive time to the cumulative exclusive time:
+
+    cumulative_subr_secs += excl_subr_sec
+
  =head1 AUTHOR

-B<Adam Kaplan>, C<< <akaplan at nytimes.com> >>
  B<Tim Bunce>, L<http://www.tim.bunce.name> and L<http://blog.timbunce.org>
-B<Steve Peters>, C<< <steve at fisharerojo.org> >>

  =head1 COPYRIGHT AND LICENSE

-  Copyright (C) 2008 by Adam Kaplan and The New York Times Company.
-  Copyright (C) 2008 by Tim Bunce.
+  Copyright (C) 2008, 2009 by Tim Bunce.

  This library is free software; you can redistribute it and/or modify
  it under the same terms as Perl itself, either Perl version 5.8.8 or,

--~--~---------~--~----~------------~-------~--~----~
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