Author: tim.bunce
Date: Fri Jul 10 14:42:51 2009
New Revision: 815

Modified:
    trunk/NYTProf.xs

Log:
Finally got robust calling subroutine name working in a way that doesn't
run into problems with exceptions from xsubs and other odd cases.
(The subr_entry structs are no longer linked into a chain.)
Currently allocates/frees and SV per call, but that can be fixed later.
Assorted variable renamings.


Modified: trunk/NYTProf.xs
==============================================================================
--- trunk/NYTProf.xs    (original)
+++ trunk/NYTProf.xs    Fri Jul 10 14:42:51 2009
@@ -914,9 +914,7 @@
  static void
  output_str(char *str, I32 len) {    /* negative len signifies utf8 */
      unsigned char tag = NYTP_TAG_STRING;
-    if (!len)
-        len = (I32)strlen(str);
-    else if (len < 0) {
+    if (len < 0) {
          tag = NYTP_TAG_STRING_UTF8;
          len = -len;
      }
@@ -1206,7 +1204,7 @@

      /* inserted new entry */
      if (1 != hash_op(entry, &found, (bool)(created_via ? 1 : 0))) {
-        if (trace_level >= 4) {
+        if (trace_level >= 5) {
              if (found)
                   logwarn("fid %d: %.*s\n",  found->id, found->key_len,  
found->key);
              else logwarn("fid -: %.*s not profiled\n",  entry.key_len,   
entry.key);
@@ -1465,7 +1463,7 @@

  /* based on S_dopoptosub_at() from perl pp_ctl.c */
  static int
-dopopcx_at(pTHX_ PERL_CONTEXT *cxstk, I32 startingblock, UV stop_at)
+dopopcx_at(pTHX_ PERL_CONTEXT *cxstk, I32 startingblock, UV cx_type_mask)
  {
      I32 i;
      register PERL_CONTEXT *cx;
@@ -1473,7 +1471,7 @@
          UV type_bit;
          cx = &cxstk[i];
          type_bit = 1 << CxTYPE(cx);
-        if (type_bit & stop_at)
+        if (type_bit & cx_type_mask)
              return i;
      }
      return i;                                     /* == -1 */
@@ -1562,9 +1560,18 @@
  }


+/* Walk up the context stack calling callback
+ * return first context that callback returns true for
+ * else return null.
+ * UV cx_type_mask is a bit flag that specifies what kinds of contexts the
+ * callback should be called for: (cx_type_mask & (1 << CxTYPE(cx)))
+ * Use ~0 to stop at all contexts.
+ * The callback is called with the context pointer and a pointer to
+ * a copy of the UV cx_type_mask argument (so it can change it on the fly).
+ */
  static PERL_CONTEXT *
-visit_contexts(pTHX_ UV stop_at, int (*callback)(pTHX_ PERL_CONTEXT *cx,
-UV *stop_at_ptr))
+visit_contexts(pTHX_ UV cx_type_mask, int (*callback)(pTHX_ PERL_CONTEXT  
*cx,
+UV *cx_type_mask_ptr))
  {
      /* modelled on pp_caller() in pp_ctl.c */
      register I32 cxix = cxstack_ix;
@@ -1585,7 +1592,7 @@
                      (int)top_si->si_type, top_si, top_si->si_prev,  
ccstack, top_si->si_cxstack);
              top_si  = top_si->si_prev;
              ccstack = top_si->si_cxstack;
-            cxix = dopopcx_at(aTHX_ ccstack, top_si->si_cxix, stop_at);
+            cxix = dopopcx_at(aTHX_ ccstack, top_si->si_cxix,  
cx_type_mask);
          }
          if (cxix < 0 || (cxix == 0 && !top_si->si_prev)) {
              /* cxix==0 && !top_si->si_prev => top-level BLOCK */
@@ -1597,10 +1604,10 @@
          if (trace_level >= 5)
              logwarn("visit_context: %s cxix %d (si_prev %p)\n",
                  block_type[CxTYPE(cx)], (int)cxix, top_si->si_prev);
-        if (callback(aTHX_ cx, &stop_at))
+        if (callback(aTHX_ cx, &cx_type_mask))
              return cx;
          /* no joy, look further */
-        cxix = dopopcx_at(aTHX_ ccstack, cxix - 1, stop_at);
+        cxix = dopopcx_at(aTHX_ ccstack, cxix - 1, cx_type_mask);
      }
      return NULL;                                  /* not reached */
  }
@@ -1623,10 +1630,10 @@


  static int
-_check_context(pTHX_ PERL_CONTEXT *cx, UV *stop_at_ptr)
+_check_context(pTHX_ PERL_CONTEXT *cx, UV *cx_type_mask_ptr)
  {
      COP *near_cop;
-    PERL_UNUSED_ARG(stop_at_ptr);
+    PERL_UNUSED_ARG(cx_type_mask_ptr);

      if (CxTYPE(cx) == CXt_SUB) {
          if (PL_debstash && CvSTASH(cx->blk_sub.cv) == PL_debstash)
@@ -2070,38 +2077,35 @@
  typedef struct subr_entry_st subr_entry_t;
  struct subr_entry_st {
      time_of_day_t initial_call_time;
-    unsigned int calling_fid;
-    int calling_line;
-    SV *subname_sv;
+    NV            initial_overhead_ticks;
+    NV            initial_subr_secs;
+    unsigned int  caller_fid;
+    int           caller_line;
+    CV           *caller_cv;
+    int           called_cv_depth;
+    SV *called_subname_sv;
      AV *sub_av;
      CV *sub_cv;
-    int cv_depth;
-    NV initial_overhead_ticks;
-    NV initial_subr_secs;
-    UV seqn;
-    int caller_ix;
+    UV subr_call_seqn;
      /* ensure all items are initialized in pp_subcall_profiler */
  };
-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)
  {
      int saved_errno = errno;
-    AV *av = subr_entry->sub_av;
-    SV *subname_sv = subr_entry->subname_sv;
-    SV *incl_time_sv = *av_fetch(av, NYTP_SCi_INCL_RTIME, 1);
-    SV *excl_time_sv = *av_fetch(av, NYTP_SCi_EXCL_RTIME, 1);
+    AV *av         = subr_entry->sub_av;
+    NV  overhead_ticks, called_sub_secs;
+    SV *incl_time_sv, *excl_time_sv;
+    NV  incl_subr_sec, excl_subr_sec;
+
+    incl_time_sv = *av_fetch(av, NYTP_SCi_INCL_RTIME, 1);
+    excl_time_sv = *av_fetch(av, NYTP_SCi_EXCL_RTIME, 1);
      /* statement overheads we've accumulated since we entered the sub */
-    NV overhead_ticks = cumulative_overhead_ticks -  
subr_entry->initial_overhead_ticks;
+    overhead_ticks = cumulative_overhead_ticks -  
subr_entry->initial_overhead_ticks;
      /* seconds spent in subroutines called by this subroutine */
-    NV called_sub_secs = (cumulative_subr_secs -  
subr_entry->initial_subr_secs);
-    NV incl_subr_sec;
-    NV excl_subr_sec;
+    called_sub_secs = (cumulative_subr_secs -  
subr_entry->initial_subr_secs);

      if (profile_zero) {
          incl_subr_sec = 0.0;
@@ -2124,14 +2128,15 @@

      if (trace_level >= 3)
          logwarn(" <-     %s %"NVff"s excl = %"NVff"s incl - %"NVff"s  
(%g-%g), oh %g-%g=%gt, d%d @%d:%d #%lu\n",
-            SvPV_nolen(subname_sv), excl_subr_sec, incl_subr_sec,  
called_sub_secs,
+            SvPV_nolen(subr_entry->called_subname_sv),
+            excl_subr_sec, incl_subr_sec, called_sub_secs,
              cumulative_subr_secs, subr_entry->initial_subr_secs,
              cumulative_overhead_ticks, subr_entry->initial_overhead_ticks,  
overhead_ticks,
-            (int)subr_entry->cv_depth,
-            subr_entry->calling_fid, subr_entry->calling_line,  
subr_entry->seqn);
+            (int)subr_entry->called_cv_depth,
+            subr_entry->caller_fid, subr_entry->caller_line,  
subr_entry->subr_call_seqn);

      /* only count inclusive time for the outer-most calls */
-    if (subr_entry->cv_depth <= 1) {
+    if (subr_entry->called_cv_depth <= 1) {
          sv_setnv(incl_time_sv, SvNV(incl_time_sv)+incl_subr_sec);
      }
      else {
@@ -2140,15 +2145,14 @@
          SV *reci_time_sv = *av_fetch(av, NYTP_SCi_RECI_RTIME, 1);
          SV *max_depth_sv = *av_fetch(av, NYTP_SCi_REC_DEPTH, 1);
          sv_setnv(reci_time_sv, (SvOK(reci_time_sv)) ?  
SvNV(reci_time_sv)+incl_subr_sec : incl_subr_sec);
-        /* we track recursion depth here, which is cv_depth-1 */
-        if (!SvOK(max_depth_sv) || subr_entry->cv_depth-1 >  
SvIV(max_depth_sv))
-            sv_setiv(max_depth_sv, subr_entry->cv_depth-1);
+        /* we track recursion depth here, which is called_cv_depth-1 */
+        if (!SvOK(max_depth_sv) || subr_entry->called_cv_depth-1 >  
SvIV(max_depth_sv))
+            sv_setiv(max_depth_sv, subr_entry->called_cv_depth-1);
      }
      sv_setnv(excl_time_sv, SvNV(excl_time_sv)+excl_subr_sec);

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

      cumulative_subr_secs += excl_subr_sec;
      SETERRNO(saved_errno, 0);
@@ -2229,6 +2233,35 @@
  }


+
+static CV*
+current_cv(pTHX_ I32 ix, PERL_SI *si)
+{
+    /* returning the current cv */
+    /* logic based on perl's S_deb_curcv in dump.c */
+    PERL_CONTEXT *cx;
+    if (!si)
+        si = PL_curstackinfo;
+    cx = &si->si_cxstack[ix];
+
+    /* the common case of finding the caller on the same stack */
+    if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)
+        return cx->blk_sub.cv;
+    else if (CxTYPE(cx) == CXt_EVAL && !CxTRYBLOCK(cx))
+        return current_cv(aTHX_ ix - 1, si); /* recurse up stack */
+    else if (ix == 0 && si->si_type == PERLSI_MAIN)
+        return PL_main_cv;
+    else if (ix > 0)
+        return current_cv(aTHX_ ix - 1, si); /* recurse up stack */
+
+    /* caller isn't on the same stack so we'll walk the stacks as well */
+    if (si->si_type != PERLSI_MAIN) {
+        return current_cv(aTHX_ si->si_prev->si_cxix, si->si_prev);
+    }
+    return Nullcv;
+}
+
+
  static OP *
  pp_entersub_profiler(pTHX)
  {
@@ -2262,6 +2295,14 @@
          if (!profile_stmts)
              reinit_if_forked(aTHX);

+        if (trace_level >= 99) {
+            logwarn("entering sub\n");
+            /* crude, but the only way to deal with the miriad logic at the
+             * start of pp_entersub (which ought to be available as  
separate sub)
+             */
+            sv_dump(*PL_stack_sp);
+        }
+
          /* allocate struct to save stack (very efficient) */
          /* XXX "warning: cast from pointer to integer of different size"  
with use64bitall=define */
          save_ix = SSNEWa(sizeof(*subr_entry), MEM_ALIGNBYTES);
@@ -2270,23 +2311,21 @@
          get_time_of_day(subr_entry->initial_call_time);
          subr_entry->initial_overhead_ticks = cumulative_overhead_ticks;
          subr_entry->initial_subr_secs      = cumulative_subr_secs;
-        subr_entry->seqn                   = ++cumulative_subr_seqn;
+        subr_entry->subr_call_seqn         = ++cumulative_subr_seqn;

          file = OutCopFILE(prev_cop);
-        subr_entry->calling_fid = (file == last_executed_fileptr)
+        subr_entry->caller_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);
+        subr_entry->caller_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;
+        subr_entry->caller_cv = current_cv(aTHX_ cxstack_ix, NULL);

          /* sub name related items */
-        subr_entry->subname_sv = &PL_sv_undef;
+        subr_entry->called_subname_sv = &PL_sv_undef;
          subr_entry->sub_av = NULL;
          subr_entry->sub_cv = NULL;
-        subr_entry->cv_depth = 0;
+        subr_entry->called_cv_depth = 0;

          SETERRNO(saved_errno, 0);
      }
@@ -2314,7 +2353,6 @@
      if (profile_sub_call) {
          int saved_errno = errno;

-        subr_entry_t *se_caller = subr_entry_caller(subr_entry);
          char fid_line_key[350]; /* XXX fast but limiting */
          int fid_line_key_len;
          SV *called_subname_sv = newSV(0);
@@ -2323,6 +2361,24 @@
          CV *cv;
          char *is_xs;
          SV *sv_tmp;
+        char *caller_pv;
+        SV *caller_sv = NULL;
+
+        if (subr_entry->caller_cv == PL_main_cv)
+            caller_pv = "MAIN";
+        else {
+            caller_sv = newSV(0); /* XXX add cache/stack thing for these  
SVs */
+            GV *gv = CvGV(subr_entry->caller_cv);
+            if (gv) {
+                gv_efullname3(caller_sv, CvGV(subr_entry->caller_cv),  
Nullch);
+            }
+            else {
+                sv_dump(subr_entry->caller_cv);
+                sv_setpv(caller_sv, "XXXNULLGV");
+            }
+            caller_pv = SvPV_nolen(caller_sv);
+            sv_2mortal(caller_sv);
+        }

          if (is_sysop) {
              /* pretend builtins are xsubs in the same package
@@ -2399,7 +2455,7 @@
          if (is_xs && *called_subname_pv == 'D' &&  
strEQ(called_subname_pv, "DB::_INIT"))
              goto skip_sub_profile;

-        /* { called_subname => { "calling_subname[fid:line]" => [ count,  
incl_time, ... ] } } */
+        /* { called_subname => { "caller_subname[fid:line]" => [ count,  
incl_time, ... ] } } */
          sv_tmp = *hv_fetch(sub_callers_hv, called_subname_pv,
              (I32)SvCUR(called_subname_sv), 1);

@@ -2429,8 +2485,7 @@
          }

          fid_line_key_len = sprintf(fid_line_key, "%s[%u:%d]",
-            (se_caller) ? SvPV_nolen(se_caller->subname_sv) : "",
-            subr_entry->calling_fid, subr_entry->calling_line);
+            caller_pv, subr_entry->caller_fid, subr_entry->caller_line);
          if (fid_line_key_len >= sizeof(fid_line_key))
              croak("panic: NYTProf buffer overflow on %s\n", fid_line_key);

@@ -2456,24 +2511,23 @@
              subr_entry->sub_av = (AV *)SvRV(sv_tmp);
              sv_inc(AvARRAY(subr_entry->sub_av)[0]); /* ++call count */
          }
-
-        /* record cv_depth, adjust for xs since, in that case, we
+        /* record called_cv_depth, adjust for xs since, in that case, we
           * have already left the sub, unlike the non-xs case.        */
-        subr_entry->cv_depth = (cv) ? CvDEPTH(cv)+(is_xs?1:0) : 1;
+        subr_entry->called_cv_depth = (cv) ? CvDEPTH(cv)+(is_xs?1:0) : 1;

          if (trace_level >= 2) {
              logwarn(" ->%4s %s from %s (d%d, oh %"NVff"t, sub %"NVff"s)  
#%lu\n",
                  (is_xs) ? is_xs : "sub", called_subname_pv,
                  fid_line_key,
-                subr_entry->cv_depth,
+                subr_entry->called_cv_depth,
                  subr_entry->initial_overhead_ticks,
                  subr_entry->initial_subr_secs,
-                subr_entry->seqn
+                subr_entry->subr_call_seqn
              );
          }

          if (profile_subs) {
-            subr_entry->subname_sv = called_subname_sv;
+            subr_entry->called_subname_sv = called_subname_sv;
              if (is_xs) {
                  /* acculumate now time we've just spent in the xs sub */
                  incr_sub_inclusive_time(aTHX_ subr_entry);
@@ -2980,8 +3034,8 @@
      hv_iterinit(sub_callers_hv);
      while (NULL != (fid_line_rvhv = hv_iternextsv(sub_callers_hv,  
&called_subname, &called_subname_len))) {
          HV *fid_lines_hv = (HV*)SvRV(fid_line_rvhv);
-        char *calling_subname;
-        I32 calling_subname_len;
+        char *caller_subname;
+        I32 caller_subname_len;
          SV *sv;

          /* I32 level, PerlIO *file, SV *sv, I32 nest, I32 maxnest, bool  
dumpops, STRLEN pvlim */
@@ -2989,27 +3043,27 @@

          /* iterate over callers to this sub ({ "subname[fid:line]" =>  
[ ... ] })  */
          hv_iterinit(fid_lines_hv);
-        while (NULL != (sv = hv_iternextsv(fid_lines_hv, &calling_subname,  
&calling_subname_len))) {
+        while (NULL != (sv = hv_iternextsv(fid_lines_hv, &caller_subname,  
&caller_subname_len))) {
              NV sc[NYTP_SCi_elements];
              AV *av = (AV *)SvRV(sv);
              int trace = (trace_level >= 3);

              unsigned int fid = 0, line = 0;
              char *fid_line_delim = "[";
-            char *fid_line_start = rninstr(calling_subname,  
calling_subname+calling_subname_len, fid_line_delim, fid_line_delim+1);
+            char *fid_line_start = rninstr(caller_subname,  
caller_subname+caller_subname_len, fid_line_delim, fid_line_delim+1);
              if (!fid_line_start) {
-                logwarn("bad fid_lines_hv key '%s'\n", calling_subname);
+                logwarn("bad fid_lines_hv key '%s'\n", caller_subname);
                  continue;
              }
              if (2 != sscanf(fid_line_start+1, "%u:%u", &fid, &line)) {
-                logwarn("bad fid_lines_hv format '%s'\n", calling_subname);
+                logwarn("bad fid_lines_hv format '%s'\n", caller_subname);
                  continue;
              }
              /* trim length to effectively hide the [fid:line] suffix */
-            calling_subname_len = fid_line_start-calling_subname;
+            caller_subname_len = fid_line_start-caller_subname;

              output_tag_int(NYTP_TAG_SUB_CALLERS, fid);
-            output_str(calling_subname, calling_subname_len);
+            output_str(caller_subname, caller_subname_len);
              output_int(line);
              sc[NYTP_SCi_CALL_COUNT] = output_uv_from_av(aTHX_ av,  
NYTP_SCi_CALL_COUNT, 0) * 1.0;
              sc[NYTP_SCi_INCL_RTIME] = output_nv_from_av(aTHX_ av,  
NYTP_SCi_INCL_RTIME, 0.0);
@@ -3030,7 +3084,7 @@
              if (trace)
                  logwarn("%s called by %.*s at %u:%u: count %"NVff"  
(i%"NVff"s e%"NVff"s u%"NVff"s s%"NVff"s, d%"NVff" ri%"NVff"s)\n",
                      called_subname,
-                    calling_subname_len, calling_subname, fid, line,  
sc[NYTP_SCi_CALL_COUNT],
+                    caller_subname_len, caller_subname, fid, line,  
sc[NYTP_SCi_CALL_COUNT],
                      sc[NYTP_SCi_INCL_RTIME], sc[NYTP_SCi_EXCL_RTIME],
                      sc[NYTP_SCi_INCL_UTIME], sc[NYTP_SCi_INCL_STIME],
                      sc[NYTP_SCi_REC_DEPTH], sc[NYTP_SCi_RECI_RTIME]);
@@ -3724,7 +3778,7 @@
                  AV *subinfo_av;
                  int len;
                  unsigned int fid   = read_int();
-                SV *calling_subname_sv = normalize_eval_seqn(aTHX_  
read_str(aTHX_ tmp_str2_sv));
+                SV *caller_subname_sv = normalize_eval_seqn(aTHX_  
read_str(aTHX_ tmp_str2_sv));
                  unsigned int line  = read_int();
                  unsigned int count = read_int();
                  NV incl_time       = read_nv();
@@ -3759,7 +3813,7 @@

                  if (trace_level >= 3)
                      logwarn("Sub %s called by %s %u:%u: count %d, incl %f,  
excl %f, ucpu %f scpu %f\n",
-                        SvPV_nolen(called_subname_sv),  
SvPV_nolen(calling_subname_sv), fid, line,
+                        SvPV_nolen(called_subname_sv),  
SvPV_nolen(caller_subname_sv), fid, line,
                          count, incl_time, excl_time, ucpu_time, scpu_time);

                  subinfo_av = lookup_subinfo_av(aTHX_ called_subname_sv,  
sub_subinfo_hv);
@@ -3806,7 +3860,7 @@
                      sv = *av_fetch(av, NYTP_SCi_CALLING_SUB, 1);
                      if (!SvROK(sv))               /* autoviv */
                          sv_setsv(sv, newRV_noinc((SV*)newHV()));
-                    hv_fetch_ent((HV *)SvRV(sv), calling_subname_sv, 1, 0);
+                    hv_fetch_ent((HV *)SvRV(sv), caller_subname_sv, 1, 0);

                      /* add sub call to NYTP_FIDi_SUBS_CALLED hash of fid  
making the call */
                      /* => { line => { subname => [ ... ] } } */

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