Author: gisle.aas
Date: Mon Oct 27 03:02:40 2008
New Revision: 558

Added:
    trunk/lib/Devel/NYTProf/Data/
    trunk/lib/Devel/NYTProf/Data/Raw.pm
Modified:
    trunk/MANIFEST
    trunk/NYTProf.xs

Log:
Add Devel::NYTProf::Data::Raw module

This provides an interface for reading the raw profile data files.
I just found this handy for understanding the file format and debugging
it.  It also opens up ways for creating alternative ways to summarize the
data from Perl.

Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST      (original)
+++ trunk/MANIFEST      Mon Oct 27 03:02:40 2008
@@ -13,6 +13,7 @@
  lib/Devel/NYTProf/Apache.pm
  lib/Devel/NYTProf/Core.pm
  lib/Devel/NYTProf/Data.pm
+lib/Devel/NYTProf/Data/Raw.pm
  lib/Devel/NYTProf/Test.pm
  lib/Devel/NYTProf.pm
  lib/Devel/NYTProf/Reader.pm

Modified: trunk/NYTProf.xs
==============================================================================
--- trunk/NYTProf.xs    (original)
+++ trunk/NYTProf.xs    Mon Oct 27 03:02:40 2008
@@ -265,7 +265,7 @@
  static void write_cached_fids();
  static void write_sub_line_ranges(pTHX);
  static void write_sub_callers(pTHX);
-static HV *load_profile_data_from_stream();
+static HV *load_profile_data_from_stream(SV* cb, bool cb_seq);
  static AV *store_profile_line_entry(pTHX_ SV *rvav, unsigned int line_num,
                                    NV time, int count, unsigned int fid);

@@ -2721,9 +2721,10 @@
   * data for each line of the string eval.
   */
  static HV*
-load_profile_data_from_stream()
+load_profile_data_from_stream(SV *cb, bool cb_seq)
  {
      dTHX;
+    dSP;
      int file_major, file_minor;

      unsigned long input_chunk_seqn = 0L;
@@ -2765,6 +2766,16 @@
          croak("Profile format version %d.%d not supported by %s %s",
              file_major, file_minor, __FILE__, XS_VERSION);

+    if (cb) {
+        PUSHMARK(SP);
+       if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+       XPUSHs(sv_2mortal(newSVpvs("VERSION")));
+       XPUSHs(sv_2mortal(newSViv(file_major)));
+       XPUSHs(sv_2mortal(newSViv(file_minor)));
+       PUTBACK;
+       call_sv(cb, G_DISCARD);
+    }
+
      while (1) {
        /* Loop "forever" until EOF. We can only check the EOF flag *after* we
           attempt a read.  */
@@ -2784,6 +2795,15 @@
          switch (c) {
              case NYTP_TAG_DISCOUNT:
              {
+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("DISCOUNT")));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  if (statement_discount)
                      warn("multiple statement discount after %u:%d\n",  
last_file_num, last_line_num);
                  ++statement_discount;
@@ -2803,6 +2823,27 @@
                  unsigned int file_num = read_int();
                  unsigned int line_num = read_int();

+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(c == NYTP_TAG_TIME_BLOCK ?
+                                     newSVpvs("TIME_BLOCK") : 
newSVpvs("TIME_LINE")
+                    ));
+                   XPUSHs(sv_2mortal(newSViv(eval_file_num)));
+                   XPUSHs(sv_2mortal(newSViv(eval_line_num)));
+                   XPUSHs(sv_2mortal(newSViv(ticks)));
+                   XPUSHs(sv_2mortal(newSViv(file_num)));
+                   XPUSHs(sv_2mortal(newSViv(line_num)));
+
+                   if (c == NYTP_TAG_TIME_BLOCK) {
+                       XPUSHs(sv_2mortal(newSViv(read_int())));  /* 
block_line_num */
+                       XPUSHs(sv_2mortal(newSViv(read_int())));  /* 
sub_line_num */
+                   }
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  seconds  = (NV)ticks / ticks_per_sec;

                  fid_info_rvav = *av_fetch(fid_fileinfo_av, file_num, 1);
@@ -2878,6 +2919,23 @@
                  unsigned int file_mtime    = read_int();

                  filename_sv = read_str(aTHX_ NULL);
+
+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("NEW_FID")));
+                   XPUSHs(sv_2mortal(newSViv(file_num)));
+                   XPUSHs(sv_2mortal(newSViv(eval_file_num)));
+                   XPUSHs(sv_2mortal(newSViv(eval_line_num)));
+                   XPUSHs(sv_2mortal(newSViv(fid_flags)));
+                   XPUSHs(sv_2mortal(newSViv(file_size)));
+                   XPUSHs(sv_2mortal(newSViv(file_mtime)));
+                   XPUSHs(sv_2mortal(filename_sv));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  if (trace_level >= 2) {
                      warn("Fid %2u is %s (eval %u:%u) 0x%x sz%u mt%u\n",
                          file_num, SvPV_nolen(filename_sv), eval_file_num,  
eval_line_num,
@@ -2916,6 +2974,18 @@
                  SV *src = read_str(aTHX_ NULL);
                  AV *file_av;

+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("SRC_LINE")));
+                   XPUSHs(sv_2mortal(newSVuv(file_num)));
+                   XPUSHs(sv_2mortal(newSVuv(line_num)));
+                   XPUSHs(sv_2mortal(src));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  /* first line in the file seen */
                  if (!av_exists(fid_filecontents_av, file_num)) {
                      file_av = newAV();
@@ -2940,6 +3010,20 @@
                  unsigned int first_line = read_int();
                  unsigned int last_line  = read_int();
                  SV *subname_sv = read_str(aTHX_ tmp_str_sv);
+
+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("SUB_LINE_RANGE")));
+                   XPUSHs(sv_2mortal(newSVuv(fid)));
+                   XPUSHs(sv_2mortal(newSVuv(first_line)));
+                   XPUSHs(sv_2mortal(newSVuv(last_line)));
+                   XPUSHs(sv_2mortal(newSVsv(subname_sv)));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  if (trace_level >= 2)
                      warn("Sub %s fid %u lines %u..%u\n",
                          SvPV_nolen(subname_sv), fid, first_line,  
last_line);
@@ -2976,6 +3060,25 @@
                  UV rec_depth       = (file_minor >= 1) ? read_int() : 0;
                  subname_sv = read_str(aTHX_ tmp_str_sv);

+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("SUB_CALLERS")));
+                   XPUSHs(sv_2mortal(newSVuv(fid)));
+                   XPUSHs(sv_2mortal(newSVuv(line)));
+                   XPUSHs(sv_2mortal(newSVuv(count)));
+                   XPUSHs(sv_2mortal(newSVnv(incl_time)));
+                   XPUSHs(sv_2mortal(newSVnv(excl_time)));
+                   XPUSHs(sv_2mortal(newSVnv(ucpu_time)));
+                   XPUSHs(sv_2mortal(newSVnv(scpu_time)));
+                   XPUSHs(sv_2mortal(newSVnv(reci_time)));
+                   XPUSHs(sv_2mortal(newSViv(rec_depth)));
+                   XPUSHs(sv_2mortal(newSVsv(subname_sv)));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  if (trace_level >= 3)
                      warn("Sub %s called by fid %u line %u: count %d,  
incl %f, excl %f, ucpu %f scpu %f\n",
                          SvPV_nolen(subname_sv), fid, line, count,  
incl_time, excl_time, ucpu_time, scpu_time);
@@ -3042,6 +3145,19 @@
                  int len = my_snprintf(text, sizeof(text), "%d", pid);
                  profiler_start_time = (file_minor >= 1) ? read_nv() : 0;

+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("PID_START")));
+                   XPUSHs(sv_2mortal(newSVuv(pid)));
+                   XPUSHs(sv_2mortal(newSVuv(ppid)));
+                   if (file_minor >= 1)
+                       XPUSHs(sv_2mortal(newSVuv(profiler_start_time)));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  (void)hv_store(live_pids_hv, text, len, newSVuv(ppid), 0);
                  if (trace_level)
                      warn("Start of profile data for pid %s  
(ppid %d, %"IVdf" pids live) at %"NVff"\n",
@@ -3059,6 +3175,18 @@
                  int len = my_snprintf(text, sizeof(text), "%d", pid);
                  profiler_end_time = (file_minor >= 1) ? read_nv() : 0;

+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("PID_END")));
+                   XPUSHs(sv_2mortal(newSVuv(pid)));
+                   if (file_minor >= 1)
+                       XPUSHs(sv_2mortal(newSVuv(profiler_end_time)));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  if (!hv_delete(live_pids_hv, text, len, 0))
                      warn("Inconsistent pids in profile data (pid %d not  
introduced)",
                          pid);
@@ -3089,6 +3217,17 @@
                  }
                  *value++ = '\0';
                  value_sv = newSVpvn(value, end-value);
+
+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("ATTRIBUTE")));
+                   XPUSHs(sv_2mortal(newSVpv(text, 0)));
+                   XPUSHs(sv_2mortal(newSVsv(value_sv)));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+               }
+
                  store_attrib_sv(aTHX_ attr_hv, text, value_sv);
                  if ('t' == *text && strEQ(text, "ticks_per_sec")) {
                      ticks_per_sec = (unsigned int)SvUV(value_sv);
@@ -3108,6 +3247,17 @@
                  if (NULL == NYTP_gets(in, text, sizeof(text)))
                      /* probably EOF */
                      croak("Profile format error reading comment");
+
+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("COMMENT")));
+                   XPUSHs(sv_2mortal(newSVpv(text, 0)));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+                   break;
+               }
+
                  if (trace_level >= 1)
                      warn("# %s", text);           /* includes \n */
                  break;
@@ -3116,6 +3266,13 @@
            case NYTP_TAG_START_DEFLATE:
            {
  #ifdef HAS_ZLIB
+               if (cb) {
+                   PUSHMARK(SP);
+                   if (cb_seq) XPUSHs(sv_2mortal(newSViv(input_chunk_seqn)));
+                   XPUSHs(sv_2mortal(newSVpvs("START_DEFLATE")));
+                   PUTBACK;
+                   call_sv(cb, G_DISCARD);
+               }
                NYTP_start_inflate(in);
  #else
                  croak("File uses compression but compression is not  
supported by this build of NYTProf");
@@ -3129,6 +3286,22 @@
          }
      }

+    if (cb) {
+       SvREFCNT_dec(profile_modes);
+       SvREFCNT_dec(live_pids_hv);
+       SvREFCNT_dec(attr_hv);
+       SvREFCNT_dec(fid_fileinfo_av);
+       SvREFCNT_dec(fid_filecontents_av);
+       SvREFCNT_dec(fid_line_time_av);
+       SvREFCNT_dec(fid_block_time_av);
+       SvREFCNT_dec(fid_sub_time_av);
+       SvREFCNT_dec(sub_subinfo_hv);
+       SvREFCNT_dec(sub_callers_hv);
+       SvREFCNT_dec(tmp_str_sv);
+
+       return newHV(); /* dummy */
+    }
+
      if (HvKEYS(live_pids_hv)) {
          warn("profile data possibly truncated, no terminator for %"IVdf"  
pids",
              HvKEYS(live_pids_hv));
@@ -3276,8 +3449,10 @@
  PROTOTYPES: DISABLE

  HV*
-load_profile_data_from_file(file)
+load_profile_data_from_file(file,cb=NULL,cb_seq=0)
  char *file;
+SV* cb;
+bool cb_seq;
      CODE:
      if (trace_level)
          warn("reading profile data from file %s\n", file);
@@ -3285,7 +3460,7 @@
      if (in == NULL) {
          croak("Failed to open input '%s': %s", file, strerror(errno));
      }
-    RETVAL = load_profile_data_from_stream();
+    RETVAL = load_profile_data_from_stream(cb, cb_seq);
      NYTP_close(in, 0);
      OUTPUT:
      RETVAL

Added: trunk/lib/Devel/NYTProf/Data/Raw.pm
==============================================================================
--- (empty file)
+++ trunk/lib/Devel/NYTProf/Data/Raw.pm Mon Oct 27 03:02:40 2008
@@ -0,0 +1,173 @@
+package Devel::NYTProf::Data::Raw;
+
+use warnings;
+use strict;
+
+our $VERSION = '2.06';
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+     for_chunks
+);
+
+use Devel::NYTProf::Core;
+
+sub for_chunks (&%) {
+    my($cb, %opts) = @_;
+    Devel::NYTProf::Data::load_profile_data_from_file(
+       $opts{filename} || 'nytprof.out',
+       $cb, $opts{sequence_numbers},
+    );
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Devel::NYTProf::Data::Raw - Reader of Devel::NYTProf data files
+
+=head1 SYNOPSIS
+
+  use Devel::NYTProf::Data::Raw qw(for_chunks);
+  for_chunks {
+      my $tag = shift;
+      print "$tag\n";
+      # examine @_
+      ....
+  }
+
+  # quickly dump content of a file
+  use Data::Dump;
+  for_chunks(\&dd);
+
+=head1 DESCRIPTION
+
+This module provide a low level interface for reading Devel::NYTProf
+data files.  Currently the module only provide a single function:
+
+=over
+
+=item for_chunks( \&callback, %opts )
+
+This function will read the F<nytprof.out> file and invoke the
+callback function for each chunk in the file.  The first argument
+passed to the callback is the chunk tag.  The rest of the arguments
+passed depend on the tag.  See L</"Chunks"> for the details.
+
+The return value of the callback function is ignored.  The
+for_chunks() function will croak if the file isn't readable.
+
+The behaviour of the function can be modified by passing key/value
+pairs after the callback.  Currently recognized are:
+
+=over
+
+=item filename => $path
+
+The path to the data file to read.  Defaults to F<nytprof.out>.
+
+=item sequence_numbers => $bool
+
+If TRUE pass the chunk sequence number as the first argument (ahead of
+the chunk tag) to the callback function.  This is mostly useful for
+testing the internal workings of this module, as this should just be a
+counter that starts at 0 and increments by 1 for each call to the
+callback function.
+
+=back
+
+=back
+
+=head2 Chunks
+
+The F<nytprof.out> file contains a sequence of tagged chunks that are
+streamed out as the profiled program runs.  This documents how the
+chunks appear when presented to the callback function of the
+for_chunks() function for the version 2.0 and 2.1 version of the file  
format.
+
+=over
+
+=item VERSION => $major, $minor
+
+The first chunk in the file declare what version of the file format
+this is.
+
+=item COMMENT => $text
+
+This chunk is just some textual content that can be ignored.
+
+=item ATTRIBUTE => $key, $value
+
+This chunk is repeated at the beginning of the file and used to declare
+various facts about the profiling run.
+
+=item START_DEFLATE
+
+This chunk just say that from now on all chunks have been compressed
+in the file.
+
+=item PID_START => $pid, $parent_pid (v2.0)
+
+=item PID_START => $pid, $parent_pid, $start_time (v2.1)
+
+Still a mystery to me.
+
+=item NEW_FID => $fid, $eval_fid, $eval_line, $flags, $size, $mtime, $name
+
+Files are represented by integers called $fid and this chunk declare
+the mapping between these numbers and file names.
+
+=item TIME_BLOCK => $eval_fid, $eval_line, $ticks, $fid, $line,  
$block_line, $sub_line
+
+=item TIME_LINE => $eval_fid, $eval_line, $ticks, $fid, $line
+
+A TIME_BLOCK or TIME_LINE chunk is output each time the execution of
+the program leaves a line.
+
+=item DISCOUNT
+
+Still a mystery to me.
+
+=item SUB_LINE_RANGE => $fid, $beg, $end, $name
+
+At the end of the run the profiler will output chunks that report on
+the subroutines encountered.
+
+=item SUB_CALLERS => $fid, $line, $count, $incl_time, $excl_time,  
$ucpu_time, $scpu_time, $reci_time, $rec_depth, $name
+
+At the end of the run the profiler will output chunks that report on
+where subroutines were called from.
+
+=item SRC_LINE => $fid, $line, $text
+
+Not used (???)
+
+=item PID_END => $pid (v2.0)
+
+=item PID_END => $pid, $end_time (v2.1)
+
+Still a mystery to me.
+
+=back
+
+=head1 SEE ALSO
+
+L<Devel::NYTProf>
+
+=head1 AUTHOR
+
+B<Gisle Aas>
+
+=head1 COPYRIGHT AND LICENSE
+
+ Copyright (C) 2008 by Adam Kaplan and The New York Times Company.
+ Copyright (C) 2008 by Tim Bunce, Ireland.
+ Copyright (C) 2008 by Gisle Aas
+
+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,
+at your option, any later version of Perl 5 you may have available.
+
+=cut

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