On Thu, Dec 17, 2009 at 01:38:43PM +0000, Tim Bunce wrote:
> On Thu, Dec 17, 2009 at 10:52:35AM +0000, Nicholas Clark wrote:

> > It moves the call to normalize_eval_seqn() after the callback runs.
> > With this change, it's possible to write a program to output a byte-for-byte
> > identical file via the callback interface.
> 
> I'm curious about why you might want to do that.

Ah, well. Because you can test whether you've read in and written out *one*
file correctly.

At which point it's not a huge step to read in multiple files, and write one
out...

Which I believe might be useful.

Appended is the summary, attached are all the commits. Should I push these?
(I'm using git-svn)

Nicholas Clark

diff --git a/MANIFEST b/MANIFEST
index fe496a2..096c366 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -12,6 +12,7 @@ benchmark.pl
 bin/nytprofcg
 bin/nytprofcsv
 bin/nytprofhtml
+bin/nytprofmerge
 demo/README
 demo/demo-code.pl
 demo/demo-run.pl
diff --git a/NYTProf.xs b/NYTProf.xs
index 5c9b9a5..2bad944 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -361,10 +361,10 @@ static unsigned int ticks_per_sec = 0;            /* 0 
forces error if not set *
 
 /* prototypes */
 static void output_header(pTHX);
-static void output_tag_int(unsigned char tag, unsigned int);
-#define     output_int(i)   output_tag_int(NYTP_TAG_NO_TAG, (unsigned int)(i))
-static void output_str(char *str, I32 len);
-static void output_nv(NV nv);
+static void output_tag_int(NYTP_file file, unsigned char tag, unsigned int);
+#define     output_int(fh, i)   output_tag_int(fh, NYTP_TAG_NO_TAG, (unsigned 
int)(i))
+static void output_str(NYTP_file file, char *str, I32 len);
+static void output_nv(NYTP_file file, NV nv);
 static unsigned int read_int(void);
 static SV *read_str(pTHX_ SV *sv);
 static unsigned int get_file_id(pTHX_ char*, STRLEN, int created_via);
@@ -971,9 +971,9 @@ output_header(pTHX)
     }
 #endif
         
-    output_tag_int(NYTP_TAG_PID_START, getpid());
-    output_int(getppid());
-    output_nv(gettimeofday_nv());
+    output_tag_int(out, NYTP_TAG_PID_START, getpid());
+    output_int(out, getppid());
+    output_nv(out, gettimeofday_nv());
 
     write_cached_fids();                          /* empty initially, 
non-empty after fork */
 
@@ -982,7 +982,7 @@ output_header(pTHX)
 
 
 static void
-output_str(char *str, I32 len) {    /* negative len signifies utf8 */
+output_str(NYTP_file file, char *str, I32 len) {    /* negative len signifies 
utf8 */
     unsigned char tag = NYTP_TAG_STRING;
     if (len < 0) {
         tag = NYTP_TAG_STRING_UTF8;
@@ -990,8 +990,8 @@ output_str(char *str, I32 len) {    /* negative len 
signifies utf8 */
     }
     if (trace_level >= 10)
         logwarn("output_str('%.*s', %d)\n", (int)len, str, (int)len);
-    output_tag_int(tag, len);
-    NYTP_write(out, str, len);
+    output_tag_int(file, tag, len);
+    NYTP_write(file, str, len);
 }
 
 
@@ -1131,12 +1131,12 @@ emit_fid (Hash_entry *fid_info)
         file_name = fid_info->key_abs;
         file_name_len = strlen(file_name);
     }
-    output_tag_int(NYTP_TAG_NEW_FID, fid_info->id);
-    output_int(fid_info->eval_fid);
-    output_int(fid_info->eval_line_num);
-    output_int(fid_info->fid_flags);
-    output_int(fid_info->file_size);
-    output_int(fid_info->file_mtime);
+    output_tag_int(out, NYTP_TAG_NEW_FID, fid_info->id);
+    output_int(out, fid_info->eval_fid);
+    output_int(out, fid_info->eval_line_num);
+    output_int(out, fid_info->fid_flags);
+    output_int(out, fid_info->file_size);
+    output_int(out, fid_info->file_mtime);
 
 #ifdef WIN32
     /* Make sure we only use forward slashes in filenames */
@@ -1147,13 +1147,13 @@ emit_fid (Hash_entry *fid_info)
             char ch = file_name[i];
             file_name_copy[i] = ch == '\\' ? '/' : ch;
         }
-        output_str(file_name_copy, (I32)file_name_len);
+        output_str(out, file_name_copy, (I32)file_name_len);
         Safefree(file_name_copy);
         return;
     }
 #endif
 
-    output_str(file_name, (I32)file_name_len);
+    output_str(out, file_name, (I32)file_name_len);
 }
 
 
@@ -1446,13 +1446,13 @@ get_file_id(pTHX_ char* file_name, STRLEN 
file_name_len, int created_via)
 
 /**
  * Output an integer in bytes, optionally preceded by a tag. Use the special 
tag
- * NYTP_TAG_NO_TAG to suppress the tag output. A wrapper macro output_int(i)
+ * NYTP_TAG_NO_TAG to suppress the tag output. A wrapper macro output_int(fh, 
i)
  * does this for you.
  * "In bytes" means output the number in binary, using the least number of 
bytes
  * possible.  All numbers are positive. Use sign slot as a marker
  */
 static void
-output_tag_int(unsigned char tag, unsigned int i)
+output_tag_int(NYTP_file file, unsigned char tag, unsigned int i)
 {
     U8 buffer[6];
     U8 *p = buffer;
@@ -1486,7 +1486,7 @@ output_tag_int(unsigned char tag, unsigned int i)
         *p++ = (U8)(i >> 8);
         *p++ = (U8)i;
     }
-    NYTP_write(out, buffer, p - buffer);
+    NYTP_write(file, buffer, p - buffer);
 }
 
 
@@ -1495,7 +1495,7 @@ output_uv_from_av(pTHX_ AV *av, int idx, UV default_uv)
 {
     SV **svp = av_fetch(av, idx, 0);
     UV uv = (!svp || !SvOK(*svp)) ? default_uv : SvUV(*svp);
-    output_int( uv );
+    output_int( out, uv );
     return uv;
 }
         
@@ -1506,9 +1506,9 @@ output_uv_from_av(pTHX_ AV *av, int idx, UV default_uv)
  * (Minor portbility issues are seen as less important than speed and space.)
  */
 static void
-output_nv(NV nv)
+output_nv(NYTP_file file, NV nv)
 {
-    NYTP_write(out, (unsigned char *)&nv, sizeof(NV));
+    NYTP_write(file, (unsigned char *)&nv, sizeof(NV));
 }
 
 
@@ -1517,7 +1517,7 @@ output_nv_from_av(pTHX_ AV *av, int idx, NV default_nv)
 {
     SV **svp = av_fetch(av, idx, 0);
     NV nv = (!svp || !SvOK(*svp)) ? default_nv : SvNV(*svp);
-    output_nv( nv );
+    output_nv( out, nv );
     return nv;
 }
 
@@ -1855,13 +1855,13 @@ DB_stmt(pTHX_ COP *cop, OP *op)
 
     if (last_executed_fid) {
 
-        output_tag_int((unsigned char)((profile_blocks)
+        output_tag_int(out, (unsigned char)((profile_blocks)
                         ? NYTP_TAG_TIME_BLOCK : NYTP_TAG_TIME_LINE), elapsed);
-        output_int(last_executed_fid);
-        output_int(last_executed_line);
+        output_int(out, last_executed_fid);
+        output_int(out, last_executed_line);
         if (profile_blocks) {
-            output_int(last_block_line);
-            output_int(last_sub_line);
+            output_int(out, last_block_line);
+            output_int(out, last_sub_line);
         }
         if (trace_level >= 4)
             logwarn("Wrote %d:%-4d %2ld ticks (%u, %u)\n", last_executed_fid,
@@ -2123,8 +2123,8 @@ close_output_file(pTHX) {
     /* mark end of profile data for last_pid pid
      * which is the pid that this file relates to
      */
-    output_tag_int(NYTP_TAG_PID_END, last_pid);
-    output_nv(gettimeofday_nv());
+    output_tag_int(out, NYTP_TAG_PID_END, last_pid);
+    output_nv(out, gettimeofday_nv());
 
     if (-1 == NYTP_close(out, 0))
         logwarn("Error closing profile data file: %s\n", strerror(errno));
@@ -3503,11 +3503,11 @@ write_sub_line_ranges(pTHX)
             logwarn("Sub %s fid %u lines %lu..%lu\n",
                 sub_name, fid, (unsigned long)first_line, (unsigned 
long)last_line);
 
-        output_tag_int(NYTP_TAG_SUB_INFO, fid);
-        output_str(sub_name, sub_name_len);
-        output_int(first_line);
-        output_int(last_line);
-        output_int(0);  /* how many extra items follow */
+        output_tag_int(out, NYTP_TAG_SUB_INFO, fid);
+        output_str(out, sub_name, sub_name_len);
+        output_int(out, first_line);
+        output_int(out, last_line);
+        output_int(out, 0);  /* how many extra items follow */
     }
 }
 
@@ -3559,9 +3559,9 @@ write_sub_callers(pTHX)
             /* trim length to effectively hide the [fid:line] suffix */
             caller_subname_len = fid_line_start-caller_subname;
 
-            output_tag_int(NYTP_TAG_SUB_CALLERS, fid);
-            output_int(line);
-            output_str(caller_subname, caller_subname_len);
+            output_tag_int(out, NYTP_TAG_SUB_CALLERS, fid);
+            output_int(out, line);
+            output_str(out, caller_subname, caller_subname_len);
             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);
             sc[NYTP_SCi_EXCL_RTIME] = output_nv_from_av(aTHX_ av, 
NYTP_SCi_EXCL_RTIME, 0.0);
@@ -3569,7 +3569,7 @@ write_sub_callers(pTHX)
             sc[NYTP_SCi_INCL_STIME] = output_nv_from_av(aTHX_ av, 
NYTP_SCi_INCL_STIME, 0.0);
             sc[NYTP_SCi_RECI_RTIME] = output_nv_from_av(aTHX_ av, 
NYTP_SCi_RECI_RTIME, 0.0);
             sc[NYTP_SCi_REC_DEPTH]  = output_uv_from_av(aTHX_ av, 
NYTP_SCi_REC_DEPTH , 0) * 1.0;
-            output_str(called_subname, called_subname_len);
+            output_str(out, called_subname, called_subname_len);
 
             /* sanity check - early warning */
             if (sc[NYTP_SCi_INCL_RTIME] < 0.0 || sc[NYTP_SCi_EXCL_RTIME] < 
0.0) {
@@ -3659,9 +3659,9 @@ write_src_of_files(pTHX)
             char *src = (svp) ? SvPV(*svp, len) : "";
             /* outputting the tag and fid for each (non empty) line
              * is a little inefficient, but not enough to worry about */
-            output_tag_int(NYTP_TAG_SRC_LINE, e->id);
-            output_int(line);
-            output_str(src, (I32)len);    /* includes newline */
+            output_tag_int(out, NYTP_TAG_SRC_LINE, e->id);
+            output_int(out, line);
+            output_str(out, src, (I32)len);    /* includes newline */
             if (trace_level >= 5) {
                 logwarn("fid %d src line %d: %s%s", e->id, line, src,
                     (*src && src[strlen(src)-1]=='\n') ? "" : "\n");
@@ -4494,7 +4494,7 @@ load_profile_data_from_stream(SV *cb)
                         HvKEYS(live_pids_hv), profiler_end_time);
 
                 store_attrib_sv(aTHX_ attr_hv, "profiler_end_time", 
newSVnv(profiler_end_time));
-                profiler_duration = profiler_end_time - profiler_start_time;
+                profiler_duration += profiler_end_time - profiler_start_time;
                 store_attrib_sv(aTHX_ attr_hv, "profiler_duration", 
newSVnv(profiler_duration));
 
                 break;
@@ -4853,3 +4853,108 @@ SV* cb;
     NYTP_close(in, 0);
     OUTPUT:
     RETVAL
+
+MODULE = Devel::NYTProf     PACKAGE = Devel::NYTProf::FileHandle
+
+PROTOTYPES: DISABLE
+
+void
+open(pathname, mode)
+char *pathname
+char *mode
+    PREINIT:
+        NYTP_file fh = NYTP_open(pathname, mode);
+        SV *object;
+    PPCODE:
+        if(!fh)
+            XSRETURN(0);
+        object = newSV(0);
+        sv_usepvn(object, (char *) fh, sizeof(NYTP_file_t));
+        ST(0) = sv_bless(sv_2mortal(newRV_noinc(object)), 
gv_stashpvs("Devel::NYTProf::FileHandle", GV_ADD));
+        XSRETURN(1);
+
+int
+DESTROY(handle)
+SV *handle
+    ALIAS:
+        close = 1
+    PREINIT:
+        SV *guts;
+    CODE:
+        if (ix == ix) {
+            /* Unused argument.  */
+        }
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        guts = SvRV(handle);
+        RETVAL = NYTP_close((NYTP_file)SvPVX(guts), 0);
+        SvPV_set(guts, NULL);
+        SvLEN_set(guts, 0);
+    OUTPUT:
+        RETVAL
+
+size_t
+write(handle, string)
+SV *handle
+SV *string
+    PREINIT:
+        STRLEN len;
+       char *p;
+        NYTP_file fh;
+    CODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+       p = SvPVbyte(string, len);
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        RETVAL = NYTP_write(fh, p, len);
+    OUTPUT:
+        RETVAL
+
+void
+output_int(handle, ...)
+SV *handle
+    PREINIT:
+        NYTP_file fh;
+        SV **last = sp + items;
+    PPCODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        ++sp; /* A pointer to the function is first item on the stack.
+                 It's not included in items  */
+        while(sp++ < last)
+            output_int(fh, SvUV(*sp));
+        XSRETURN(0);
+
+void
+output_nv(handle, ...)
+SV *handle
+NV value
+    PREINIT:
+        NYTP_file fh;
+        SV **last = sp + items;
+    PPCODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        ++sp; /* A pointer to the function is first item on the stack.
+                 It's not included in items  */
+        while(sp++ < last)
+            output_nv(fh, SvNV(*sp));
+        XSRETURN(0);
+
+
+void
+output_str(handle, value)
+SV *handle
+SV *value
+    PREINIT:
+        STRLEN len;
+       char *p;
+        NYTP_file fh;
+    CODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        p = SvPV(value, len);
+       output_str(fh, p, SvUTF8(value) ? -(I32)len : (I32) len);
diff --git a/bin/nytprofmerge b/bin/nytprofmerge
new file mode 100755
index 0000000..c15b0fe
--- /dev/null
+++ b/bin/nytprofmerge
@@ -0,0 +1,205 @@
+#!/usr/bin/perl
+##########################################################
+# This script is part of the Devel::NYTProf distribution
+#
+# Copyright, contact and other information can be found
+# at the bottom of this file, or by going to:
+# http://search.cpan.org/dist/Devel-NYTProf/
+#
+##########################################################
+# $Id$
+##########################################################
+
+use warnings;
+use strict;
+use Devel::NYTProf::ReadStream qw(for_chunks);
+
+use Getopt::Long;
+use Carp;
+
+my %opt = (
+          out  => 'nytprof-merged.out',
+         );
+
+GetOptions(\%opt, qw/out|o=s help|h/)
+    or do {
+        usage();
+        exit 1;
+    };
+
+if (defined($opt{help})) {
+    usage();
+    exit;
+}
+
+sub usage {
+    print <<END
+usage: [perl] nytprofmerge [opts]
+ --out <dir>,   -o <dir>   Place merged file [default: ./nytprof-merged.out]
+ --help,        -h         Print this message
+
+This script of part of the Devel::NYTProf distribution.
+See http://search.cpan.org/dist/Devel-NYTProf/ for details and copyright.
+END
+}
+
+my $out = Devel::NYTProf::FileHandle::open($opt{out}, "wb");
+
+my $next_fid = 1;
+my %file_to_fid;
+my %fids = (0 => 0);
+
+my $version;
+my %seen_subs;
+
+my %callers;
+
+sub _time_block_or_line {
+    my ($tag, undef, undef, $ticks, $fid, $line, $block_line, $sub_line) = @_;
+    my $is_line = $tag eq 'TIME_LINE';
+    $out->write($is_line ? '+' : '*');
+    $out->output_int($ticks, $fid, $line);
+    if (!$is_line) {
+       $out->output_int($block_line);
+       $out->output_int($sub_line);
+    }
+}
+
+# Effectively, this is a global variable. Sorry.
+my $input;
+
+my %dispatcher =
+    (
+     VERSION => sub {
+        my (undef, $major, $minor) = @_;
+        my $this_version = "$major $minor";
+        if($version) {
+            die "Incompatible version '$this_version' in $input, expected 
'$version'"
+                unless $this_version eq $version;
+        } else {
+            $version = $this_version;
+            $out->write("NYTProf $version\n");
+        }
+     },
+     COMMENT => sub {
+        my (undef, $text) = @_;
+        $out->write("#$text");
+     },
+     ATTRIBUTE => sub {
+        my (undef, $key, $value) = @_;
+        $out->write(":$key=$value\n");
+     },
+
+     START_DEFLATE => sub {
+     },
+
+     PID_START => sub {
+        my (undef, $pid, $parent, $time) = @_;
+        $out->write('P');
+        $out->output_int($pid, $parent);
+        $out->output_nv($time);
+     },
+     PID_END => sub {
+        my (undef, $pid, $time) = @_;
+        $out->write('p');
+        $out->output_int($pid);
+        $out->output_nv($time);
+     },
+
+     NEW_FID => sub {
+        my (undef, $fid, $eval_fid, $eval_line, $flags, $size, $mtime, $name) 
= @_;
+        my ($new_fid, $new_eval_fid);
+        if($eval_fid) {
+            $new_eval_fid = $fids{$eval_fid};
+            confess("unknown eval_fid $eval_fid") unless defined $new_eval_fid;
+            $new_fid = $next_fid++;
+            $fids{$fid} = $new_fid;
+        } else {
+            $new_eval_fid = $eval_fid;
+            $new_fid = $file_to_fid{$name};
+            return if defined $new_fid;
+
+            $new_fid = $next_fid++;
+            $fids{$fid} = $new_fid;
+            $file_to_fid{$name} = $fid;
+        }
+        $out->write('@');
+        $out->output_int($new_fid, $new_eval_fid, $eval_line, $flags, $size, 
$mtime);
+        $out->output_str($name);
+     },
+     TIME_BLOCK => \&_time_block_or_line,
+     TIME_LINE => \&_time_block_or_line,
+
+     DISCOUNT => sub {
+        $out->write('-');
+     },
+     SUB_INFO => sub {
+        my (undef, $fid, $first_line, $last_line, $name) = @_;
+        if(!$seen_subs{"$fid,$name"}++) {
+            $out->write('s');
+            $out->output_int($fids{$fid});
+            $out->output_str($name);
+            $out->output_int($first_line, $last_line, 0);
+        }
+     },
+     SUB_CALLERS => sub {
+        my (undef, $fid, $line, $count, $incl_time, $excl_time, $ucpu_time, 
$scpu_time, $reci_time, $rec_depth, $called, $caller) = @_;
+        $fid = $fids{$fid};
+
+        if ($callers{"$fid,$line"}{$called}{$caller}) {
+            my $sum = $callers{"$fid,$line"}{$called}{$caller};
+            $sum->{count} += $count;
+            $sum->{incl} += $incl_time;
+            $sum->{excl} += $excl_time;
+            $sum->{ucpu} += $ucpu_time;
+            $sum->{scpu} += $scpu_time;
+            $sum->{reci} += $reci_time;
+            $sum->{depth} = $rec_depth if $rec_depth > $sum->{depth};
+        } else {
+            # New;
+            $callers{"$fid,$line"}{$called}{$caller} =
+                {
+                 depth => $rec_depth,
+                 count => $count,
+                 incl => $incl_time,
+                 excl => $excl_time,
+                 ucpu => $ucpu_time,
+                 scpu => $scpu_time,
+                 reci => $reci_time,
+                };
+        }
+     },
+     SRC_LINE => sub {
+        my (undef, $fid, $line, $text) = @_;
+        $out->write('S');
+        $out->output_int($fids{$fid}, $line);
+        $out->output_str($text);
+     },
+    );
+
+foreach $input (@ARGV) {
+    for_chunks {
+       my $tag = $_[0];
+
+       my $sub = $dispatcher{$tag};
+       die "Unknown tag '$tag'" unless defined $sub;
+       &$sub(@_);
+    } filename => $input;
+}
+
+# Deterministic order is useful for testing.
+foreach my $fid_line (sort keys %callers) {
+    my ($fid, $line) = split ',', $fid_line;
+    foreach my $called (sort keys %{$callers{$fid_line}}) {
+       foreach my $caller (sort keys %{$callers{$fid_line}{$called}}) {
+           my $sum = $callers{$fid_line}{$called}{$caller};
+           $out->write('c');
+           $out->output_int($fid, $line);
+           $out->output_str($caller);
+           $out->output_int($sum->{count});
+           $out->output_nv(@{$sum}{qw(incl excl ucpu scpu reci)});
+           $out->output_int($sum->{depth});
+           $out->output_str($called);
+       }
+    }
+}

-- 
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]
>From b28874e4271cdf5e20da2a1557863064ae6e6a52 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Wed, 16 Dec 2009 13:32:02 +0000
Subject: [PATCH] Create a package Devel::NYTProf::FileHandle which can open and close NYYProf's
 file handles.

---
 NYTProf.xs |   39 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 39 insertions(+), 0 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index 5c9b9a5..2c13fc1 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -4853,3 +4853,42 @@ SV* cb;
     NYTP_close(in, 0);
     OUTPUT:
     RETVAL
+
+MODULE = Devel::NYTProf     PACKAGE = Devel::NYTProf::FileHandle
+
+PROTOTYPES: DISABLE
+
+void
+open(pathname, mode)
+char *pathname
+char *mode
+    PREINIT:
+        NYTP_file fh = NYTP_open(pathname, mode);
+        SV *object;
+    PPCODE:
+        if(!fh)
+            XSRETURN(0);
+        object = newSV(0);
+        sv_usepvn(object, (char *) fh, sizeof(NYTP_file_t));
+        ST(0) = sv_bless(sv_2mortal(newRV_noinc(object)), gv_stashpvs("Devel::NYTProf::FileHandle", GV_ADD));
+        XSRETURN(1);
+
+int
+DESTROY(handle)
+SV *handle
+    ALIAS:
+        close = 1
+    PREINIT:
+        SV *guts;
+    CODE:
+        if (ix == ix) {
+            /* Unused argument.  */
+        }
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        guts = SvRV(handle);
+        RETVAL = NYTP_close((NYTP_file)SvPVX(guts), 0);
+        SvPV_set(guts, NULL);
+        SvLEN_set(guts, 0);
+    OUTPUT:
+        RETVAL
-- 
1.6.0

>From f602c348a0faa90d6cbd3fdce690cb679ccbd3fa Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Wed, 16 Dec 2009 16:11:25 +0000
Subject: [PATCH] Refactor output_int() and output_tag_int() to take an explict file handle.

---
 NYTProf.xs |   58 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index 2c13fc1..91461c2 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -361,8 +361,8 @@ static unsigned int ticks_per_sec = 0;            /* 0 forces error if not set *
 
 /* prototypes */
 static void output_header(pTHX);
-static void output_tag_int(unsigned char tag, unsigned int);
-#define     output_int(i)   output_tag_int(NYTP_TAG_NO_TAG, (unsigned int)(i))
+static void output_tag_int(NYTP_file file, unsigned char tag, unsigned int);
+#define     output_int(fh, i)   output_tag_int(fh, NYTP_TAG_NO_TAG, (unsigned int)(i))
 static void output_str(char *str, I32 len);
 static void output_nv(NV nv);
 static unsigned int read_int(void);
@@ -971,8 +971,8 @@ output_header(pTHX)
     }
 #endif
         
-    output_tag_int(NYTP_TAG_PID_START, getpid());
-    output_int(getppid());
+    output_tag_int(out, NYTP_TAG_PID_START, getpid());
+    output_int(out, getppid());
     output_nv(gettimeofday_nv());
 
     write_cached_fids();                          /* empty initially, non-empty after fork */
@@ -990,7 +990,7 @@ output_str(char *str, I32 len) {    /* negative len signifies utf8 */
     }
     if (trace_level >= 10)
         logwarn("output_str('%.*s', %d)\n", (int)len, str, (int)len);
-    output_tag_int(tag, len);
+    output_tag_int(out, tag, len);
     NYTP_write(out, str, len);
 }
 
@@ -1131,12 +1131,12 @@ emit_fid (Hash_entry *fid_info)
         file_name = fid_info->key_abs;
         file_name_len = strlen(file_name);
     }
-    output_tag_int(NYTP_TAG_NEW_FID, fid_info->id);
-    output_int(fid_info->eval_fid);
-    output_int(fid_info->eval_line_num);
-    output_int(fid_info->fid_flags);
-    output_int(fid_info->file_size);
-    output_int(fid_info->file_mtime);
+    output_tag_int(out, NYTP_TAG_NEW_FID, fid_info->id);
+    output_int(out, fid_info->eval_fid);
+    output_int(out, fid_info->eval_line_num);
+    output_int(out, fid_info->fid_flags);
+    output_int(out, fid_info->file_size);
+    output_int(out, fid_info->file_mtime);
 
 #ifdef WIN32
     /* Make sure we only use forward slashes in filenames */
@@ -1446,13 +1446,13 @@ get_file_id(pTHX_ char* file_name, STRLEN file_name_len, int created_via)
 
 /**
  * Output an integer in bytes, optionally preceded by a tag. Use the special tag
- * NYTP_TAG_NO_TAG to suppress the tag output. A wrapper macro output_int(i)
+ * NYTP_TAG_NO_TAG to suppress the tag output. A wrapper macro output_int(fh, i)
  * does this for you.
  * "In bytes" means output the number in binary, using the least number of bytes
  * possible.  All numbers are positive. Use sign slot as a marker
  */
 static void
-output_tag_int(unsigned char tag, unsigned int i)
+output_tag_int(NYTP_file file, unsigned char tag, unsigned int i)
 {
     U8 buffer[6];
     U8 *p = buffer;
@@ -1486,7 +1486,7 @@ output_tag_int(unsigned char tag, unsigned int i)
         *p++ = (U8)(i >> 8);
         *p++ = (U8)i;
     }
-    NYTP_write(out, buffer, p - buffer);
+    NYTP_write(file, buffer, p - buffer);
 }
 
 
@@ -1495,7 +1495,7 @@ output_uv_from_av(pTHX_ AV *av, int idx, UV default_uv)
 {
     SV **svp = av_fetch(av, idx, 0);
     UV uv = (!svp || !SvOK(*svp)) ? default_uv : SvUV(*svp);
-    output_int( uv );
+    output_int( out, uv );
     return uv;
 }
         
@@ -1855,13 +1855,13 @@ DB_stmt(pTHX_ COP *cop, OP *op)
 
     if (last_executed_fid) {
 
-        output_tag_int((unsigned char)((profile_blocks)
+        output_tag_int(out, (unsigned char)((profile_blocks)
                         ? NYTP_TAG_TIME_BLOCK : NYTP_TAG_TIME_LINE), elapsed);
-        output_int(last_executed_fid);
-        output_int(last_executed_line);
+        output_int(out, last_executed_fid);
+        output_int(out, last_executed_line);
         if (profile_blocks) {
-            output_int(last_block_line);
-            output_int(last_sub_line);
+            output_int(out, last_block_line);
+            output_int(out, last_sub_line);
         }
         if (trace_level >= 4)
             logwarn("Wrote %d:%-4d %2ld ticks (%u, %u)\n", last_executed_fid,
@@ -2123,7 +2123,7 @@ close_output_file(pTHX) {
     /* mark end of profile data for last_pid pid
      * which is the pid that this file relates to
      */
-    output_tag_int(NYTP_TAG_PID_END, last_pid);
+    output_tag_int(out, NYTP_TAG_PID_END, last_pid);
     output_nv(gettimeofday_nv());
 
     if (-1 == NYTP_close(out, 0))
@@ -3503,11 +3503,11 @@ write_sub_line_ranges(pTHX)
             logwarn("Sub %s fid %u lines %lu..%lu\n",
                 sub_name, fid, (unsigned long)first_line, (unsigned long)last_line);
 
-        output_tag_int(NYTP_TAG_SUB_INFO, fid);
+        output_tag_int(out, NYTP_TAG_SUB_INFO, fid);
         output_str(sub_name, sub_name_len);
-        output_int(first_line);
-        output_int(last_line);
-        output_int(0);  /* how many extra items follow */
+        output_int(out, first_line);
+        output_int(out, last_line);
+        output_int(out, 0);  /* how many extra items follow */
     }
 }
 
@@ -3559,8 +3559,8 @@ write_sub_callers(pTHX)
             /* trim length to effectively hide the [fid:line] suffix */
             caller_subname_len = fid_line_start-caller_subname;
 
-            output_tag_int(NYTP_TAG_SUB_CALLERS, fid);
-            output_int(line);
+            output_tag_int(out, NYTP_TAG_SUB_CALLERS, fid);
+            output_int(out, line);
             output_str(caller_subname, caller_subname_len);
             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);
@@ -3659,8 +3659,8 @@ write_src_of_files(pTHX)
             char *src = (svp) ? SvPV(*svp, len) : "";
             /* outputting the tag and fid for each (non empty) line
              * is a little inefficient, but not enough to worry about */
-            output_tag_int(NYTP_TAG_SRC_LINE, e->id);
-            output_int(line);
+            output_tag_int(out, NYTP_TAG_SRC_LINE, e->id);
+            output_int(out, line);
             output_str(src, (I32)len);    /* includes newline */
             if (trace_level >= 5) {
                 logwarn("fid %d src line %d: %s%s", e->id, line, src,
-- 
1.6.0

>From 59baad18f78045ffafdfa5435f590813f3d25f1e Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Wed, 16 Dec 2009 16:16:31 +0000
Subject: [PATCH] Refactor output_nv() to take an explict file handle.

---
 NYTProf.xs |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index 91461c2..457fb73 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -364,7 +364,7 @@ static void output_header(pTHX);
 static void output_tag_int(NYTP_file file, unsigned char tag, unsigned int);
 #define     output_int(fh, i)   output_tag_int(fh, NYTP_TAG_NO_TAG, (unsigned int)(i))
 static void output_str(char *str, I32 len);
-static void output_nv(NV nv);
+static void output_nv(NYTP_file file, NV nv);
 static unsigned int read_int(void);
 static SV *read_str(pTHX_ SV *sv);
 static unsigned int get_file_id(pTHX_ char*, STRLEN, int created_via);
@@ -973,7 +973,7 @@ output_header(pTHX)
         
     output_tag_int(out, NYTP_TAG_PID_START, getpid());
     output_int(out, getppid());
-    output_nv(gettimeofday_nv());
+    output_nv(out, gettimeofday_nv());
 
     write_cached_fids();                          /* empty initially, non-empty after fork */
 
@@ -1506,9 +1506,9 @@ output_uv_from_av(pTHX_ AV *av, int idx, UV default_uv)
  * (Minor portbility issues are seen as less important than speed and space.)
  */
 static void
-output_nv(NV nv)
+output_nv(NYTP_file file, NV nv)
 {
-    NYTP_write(out, (unsigned char *)&nv, sizeof(NV));
+    NYTP_write(file, (unsigned char *)&nv, sizeof(NV));
 }
 
 
@@ -1517,7 +1517,7 @@ output_nv_from_av(pTHX_ AV *av, int idx, NV default_nv)
 {
     SV **svp = av_fetch(av, idx, 0);
     NV nv = (!svp || !SvOK(*svp)) ? default_nv : SvNV(*svp);
-    output_nv( nv );
+    output_nv( out, nv );
     return nv;
 }
 
@@ -2124,7 +2124,7 @@ close_output_file(pTHX) {
      * which is the pid that this file relates to
      */
     output_tag_int(out, NYTP_TAG_PID_END, last_pid);
-    output_nv(gettimeofday_nv());
+    output_nv(out, gettimeofday_nv());
 
     if (-1 == NYTP_close(out, 0))
         logwarn("Error closing profile data file: %s\n", strerror(errno));
-- 
1.6.0

>From 126cca93496d090f45d0f74cba2d95a4721fb968 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Wed, 16 Dec 2009 16:18:59 +0000
Subject: [PATCH] Refactor output_str() to take an explict file handle.

---
 NYTProf.xs |   20 ++++++++++----------
 1 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index 457fb73..26e6d22 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -363,7 +363,7 @@ static unsigned int ticks_per_sec = 0;            /* 0 forces error if not set *
 static void output_header(pTHX);
 static void output_tag_int(NYTP_file file, unsigned char tag, unsigned int);
 #define     output_int(fh, i)   output_tag_int(fh, NYTP_TAG_NO_TAG, (unsigned int)(i))
-static void output_str(char *str, I32 len);
+static void output_str(NYTP_file file, char *str, I32 len);
 static void output_nv(NYTP_file file, NV nv);
 static unsigned int read_int(void);
 static SV *read_str(pTHX_ SV *sv);
@@ -982,7 +982,7 @@ output_header(pTHX)
 
 
 static void
-output_str(char *str, I32 len) {    /* negative len signifies utf8 */
+output_str(NYTP_file file, char *str, I32 len) {    /* negative len signifies utf8 */
     unsigned char tag = NYTP_TAG_STRING;
     if (len < 0) {
         tag = NYTP_TAG_STRING_UTF8;
@@ -990,8 +990,8 @@ output_str(char *str, I32 len) {    /* negative len signifies utf8 */
     }
     if (trace_level >= 10)
         logwarn("output_str('%.*s', %d)\n", (int)len, str, (int)len);
-    output_tag_int(out, tag, len);
-    NYTP_write(out, str, len);
+    output_tag_int(file, tag, len);
+    NYTP_write(file, str, len);
 }
 
 
@@ -1147,13 +1147,13 @@ emit_fid (Hash_entry *fid_info)
             char ch = file_name[i];
             file_name_copy[i] = ch == '\\' ? '/' : ch;
         }
-        output_str(file_name_copy, (I32)file_name_len);
+        output_str(out, file_name_copy, (I32)file_name_len);
         Safefree(file_name_copy);
         return;
     }
 #endif
 
-    output_str(file_name, (I32)file_name_len);
+    output_str(out, file_name, (I32)file_name_len);
 }
 
 
@@ -3504,7 +3504,7 @@ write_sub_line_ranges(pTHX)
                 sub_name, fid, (unsigned long)first_line, (unsigned long)last_line);
 
         output_tag_int(out, NYTP_TAG_SUB_INFO, fid);
-        output_str(sub_name, sub_name_len);
+        output_str(out, sub_name, sub_name_len);
         output_int(out, first_line);
         output_int(out, last_line);
         output_int(out, 0);  /* how many extra items follow */
@@ -3561,7 +3561,7 @@ write_sub_callers(pTHX)
 
             output_tag_int(out, NYTP_TAG_SUB_CALLERS, fid);
             output_int(out, line);
-            output_str(caller_subname, caller_subname_len);
+            output_str(out, caller_subname, caller_subname_len);
             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);
             sc[NYTP_SCi_EXCL_RTIME] = output_nv_from_av(aTHX_ av, NYTP_SCi_EXCL_RTIME, 0.0);
@@ -3569,7 +3569,7 @@ write_sub_callers(pTHX)
             sc[NYTP_SCi_INCL_STIME] = output_nv_from_av(aTHX_ av, NYTP_SCi_INCL_STIME, 0.0);
             sc[NYTP_SCi_RECI_RTIME] = output_nv_from_av(aTHX_ av, NYTP_SCi_RECI_RTIME, 0.0);
             sc[NYTP_SCi_REC_DEPTH]  = output_uv_from_av(aTHX_ av, NYTP_SCi_REC_DEPTH , 0) * 1.0;
-            output_str(called_subname, called_subname_len);
+            output_str(out, called_subname, called_subname_len);
 
             /* sanity check - early warning */
             if (sc[NYTP_SCi_INCL_RTIME] < 0.0 || sc[NYTP_SCi_EXCL_RTIME] < 0.0) {
@@ -3661,7 +3661,7 @@ write_src_of_files(pTHX)
              * is a little inefficient, but not enough to worry about */
             output_tag_int(out, NYTP_TAG_SRC_LINE, e->id);
             output_int(out, line);
-            output_str(src, (I32)len);    /* includes newline */
+            output_str(out, src, (I32)len);    /* includes newline */
             if (trace_level >= 5) {
                 logwarn("fid %d src line %d: %s%s", e->id, line, src,
                     (*src && src[strlen(src)-1]=='\n') ? "" : "\n");
-- 
1.6.0

>From 929ae42738461e568cd2045d196a3a9f02cfc7a5 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Wed, 16 Dec 2009 17:53:53 +0000
Subject: [PATCH] Add methods write(), output_int(), output_nv() and output_str() to
 Devel::NYTProf::FileHandle. It's now possible to write out a profile file from
 Perl.

---
 NYTProf.xs |   57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 57 insertions(+), 0 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index 26e6d22..c39ad96 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -4892,3 +4892,60 @@ SV *handle
         SvLEN_set(guts, 0);
     OUTPUT:
         RETVAL
+
+size_t
+write(handle, string)
+SV *handle
+SV *string
+    PREINIT:
+        STRLEN len;
+	char *p;
+        NYTP_file fh;
+    CODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+	p = SvPVbyte(string, len);
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        RETVAL = NYTP_write(fh, p, len);
+    OUTPUT:
+        RETVAL
+
+void
+output_int(handle, value)
+SV *handle
+unsigned int value
+    PREINIT:
+        NYTP_file fh;
+    CODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        output_int(fh, value);
+
+void
+output_nv(handle, value)
+SV *handle
+NV value
+    PREINIT:
+        NYTP_file fh;
+    CODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        output_nv(fh, value);
+
+
+void
+output_str(handle, value)
+SV *handle
+SV *value
+    PREINIT:
+        STRLEN len;
+	char *p;
+        NYTP_file fh;
+    CODE:
+        if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
+            croak("handle is not a Devel::NYTProf::FileHandle");
+        fh = (NYTP_file)SvPVX(SvRV(handle));
+        p = SvPV(value, len);
+	output_str(fh, p, SvUTF8(value) ? -(I32)len : (I32) len);
-- 
1.6.0

>From 6391daffa999ca6d8d659cf49026c7fff5cf145a Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Thu, 17 Dec 2009 09:59:45 +0000
Subject: [PATCH] Refactor Devel::NYTProf::ReadStream::output_iv() to take a list of values.

---
 NYTProf.xs |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index c39ad96..b5bd19b 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -4911,16 +4911,20 @@ SV *string
         RETVAL
 
 void
-output_int(handle, value)
+output_int(handle, ...)
 SV *handle
-unsigned int value
     PREINIT:
         NYTP_file fh;
-    CODE:
+        SV **last = sp + items;
+    PPCODE:
         if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
             croak("handle is not a Devel::NYTProf::FileHandle");
         fh = (NYTP_file)SvPVX(SvRV(handle));
-        output_int(fh, value);
+        ++sp; /* A pointer to the function is first item on the stack.
+                 It's not included in items  */
+        while(sp++ < last)
+            output_int(fh, SvUV(*sp));
+        XSRETURN(0);
 
 void
 output_nv(handle, value)
-- 
1.6.0

>From 1cfb80197db59a7463c62bad1a98b154b63dce17 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Thu, 17 Dec 2009 10:19:02 +0000
Subject: [PATCH] Refactor Devel::NYTProf::ReadStream::output_nv() to take a list of values.

Unlike output_iv, this isn't much of a performance win, but given the
similarity of the two XSubs, it's trivial to make this change.
---
 NYTProf.xs |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index b5bd19b..ba58e52 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -4927,16 +4927,21 @@ SV *handle
         XSRETURN(0);
 
 void
-output_nv(handle, value)
+output_nv(handle, ...)
 SV *handle
 NV value
     PREINIT:
         NYTP_file fh;
-    CODE:
+        SV **last = sp + items;
+    PPCODE:
         if(!sv_isa(handle, "Devel::NYTProf::FileHandle"))
             croak("handle is not a Devel::NYTProf::FileHandle");
         fh = (NYTP_file)SvPVX(SvRV(handle));
-        output_nv(fh, value);
+        ++sp; /* A pointer to the function is first item on the stack.
+                 It's not included in items  */
+        while(sp++ < last)
+            output_nv(fh, SvNV(*sp));
+        XSRETURN(0);
 
 
 void
-- 
1.6.0

>From c63d1813b4cde7203ced4ba7d59f387326e070e7 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Thu, 17 Dec 2009 14:18:25 +0000
Subject: [PATCH] A tool for merging NYTProf profile files. This prototype can't merge yet.

---
 MANIFEST         |    1 +
 bin/nytprofmerge |  135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 136 insertions(+), 0 deletions(-)
 create mode 100755 bin/nytprofmerge

diff --git a/MANIFEST b/MANIFEST
index fe496a2..096c366 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -12,6 +12,7 @@ benchmark.pl
 bin/nytprofcg
 bin/nytprofcsv
 bin/nytprofhtml
+bin/nytprofmerge
 demo/README
 demo/demo-code.pl
 demo/demo-run.pl
diff --git a/bin/nytprofmerge b/bin/nytprofmerge
new file mode 100755
index 0000000..b55b389
--- /dev/null
+++ b/bin/nytprofmerge
@@ -0,0 +1,135 @@
+#!/usr/bin/perl
+##########################################################
+# This script is part of the Devel::NYTProf distribution
+#
+# Copyright, contact and other information can be found
+# at the bottom of this file, or by going to:
+# http://search.cpan.org/dist/Devel-NYTProf/
+#
+##########################################################
+# $Id$
+##########################################################
+
+use warnings;
+use strict;
+use Devel::NYTProf::ReadStream qw(for_chunks);
+
+use Getopt::Long;
+use Carp;
+
+my %opt = (
+	   out  => 'nytprof-merged.out',
+	  );
+
+GetOptions(\%opt, qw/out|o=s help|h/)
+    or do {
+        usage();
+        exit 1;
+    };
+
+if (defined($opt{help})) {
+    usage();
+    exit;
+}
+
+sub usage {
+    print <<END
+usage: [perl] nytprofmerge [opts]
+ --out <dir>,   -o <dir>   Place merged file [default: ./nytprof-merged.out]
+ --help,        -h         Print this message
+
+This script of part of the Devel::NYTProf distribution.
+See http://search.cpan.org/dist/Devel-NYTProf/ for details and copyright.
+END
+}
+
+my $out = Devel::NYTProf::FileHandle::open($opt{out}, "wb");
+
+sub _time_block_or_line {
+    my ($tag, undef, undef, $ticks, $fid, $line, $block_line, $sub_line) = @_;
+    my $is_line = $tag eq 'TIME_LINE';
+    $out->write($is_line ? '+' : '*');
+    $out->output_int($ticks, $fid, $line);
+    if (!$is_line) {
+	$out->output_int($block_line);
+	$out->output_int($sub_line);
+    }
+}
+
+my %dispatcher =
+    (
+     VERSION => sub {
+	 my (undef, $major, $minor) = @_;
+	 $out->write("NYTProf $major $minor\n");
+     },
+     COMMENT => sub {
+	 my (undef, $text) = @_;
+	 $out->write("#$text");
+     },
+     ATTRIBUTE => sub {
+	 my (undef, $key, $value) = @_;
+	 $out->write(":$key=$value\n");
+     },
+
+     START_DEFLATE => sub {
+     },
+
+     PID_START => sub {
+	 my (undef, $pid, $parent, $time) = @_;
+	 $out->write('P');
+	 $out->output_int($pid, $parent);
+	 $out->output_nv($time);
+     },
+     PID_END => sub {
+	 my (undef, $pid, $time) = @_;
+	 $out->write('p');
+	 $out->output_int($pid);
+	 $out->output_nv($time);
+     },
+
+     NEW_FID => sub {
+	 my (undef, $fid, $eval_fid, $eval_line, $flags, $size, $mtime, $name) = @_;
+	 $out->write('@');
+	 $out->output_int($fid, $eval_fid, $eval_line, $flags, $size, $mtime);
+	 $out->output_str($name);
+     },
+     TIME_BLOCK => \&_time_block_or_line,
+     TIME_LINE => \&_time_block_or_line,
+
+     DISCOUNT => sub {
+	 $out->write('-');
+     },
+     SUB_INFO => sub {
+	 my (undef, $fid, $first_line, $last_line, $name) = @_;
+	 $out->write('s');
+	 $out->output_int($fid);
+	 $out->output_str($name);
+	 $out->output_int($first_line, $last_line, 0);
+     },
+     SUB_CALLERS => sub {
+	 my (undef, $fid, $line, $count, $incl_time, $excl_time, $ucpu_time, $scpu_time, $reci_time, $rec_depth, $called, $caller) = @_;
+	 $out->write('c');
+	 $out->output_int($fid, $line);
+	 $out->output_str($caller);
+	 $out->output_int($count);
+	 $out->output_nv($incl_time, $excl_time, $ucpu_time, $scpu_time, $reci_time);
+	 $out->output_int($rec_depth);
+	 $out->output_str($called);
+     },
+     SRC_LINE => sub {
+	 my (undef, $fid, $line, $text) = @_;
+	 $out->write('S');
+	 $out->output_int($fid, $line);
+	 $out->output_str($text);
+     },
+    );
+
+my $input = shift @ARGV;
+
+for_chunks {
+    my $tag = $_[0];
+
+    my $sub = $dispatcher{$tag};
+    die "Unknown tag '$tag'" unless defined $sub;
+    &$sub(@_);
+} filename => $input;
-- 
1.6.0

>From 91e01e76af6a7e9bcc223b810acececef5f2c551 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Thu, 17 Dec 2009 15:49:26 +0000
Subject: [PATCH] An initial implementation of FID remapping.

---
 bin/nytprofmerge |   27 +++++++++++++++++++++++----
 1 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/bin/nytprofmerge b/bin/nytprofmerge
index b55b389..fa6ed73 100755
--- a/bin/nytprofmerge
+++ b/bin/nytprofmerge
@@ -45,6 +45,10 @@ END
 
 my $out = Devel::NYTProf::FileHandle::open($opt{out}, "wb");
 
+my $next_fid = 1;
+my %file_to_fid;
+my %fids = (0 => 0);
+
 sub _time_block_or_line {
     my ($tag, undef, undef, $ticks, $fid, $line, $block_line, $sub_line) = @_;
     my $is_line = $tag eq 'TIME_LINE';
@@ -89,8 +93,23 @@ my %dispatcher =
 
      NEW_FID => sub {
 	 my (undef, $fid, $eval_fid, $eval_line, $flags, $size, $mtime, $name) = @_;
+	 my ($new_fid, $new_eval_fid);
+	 if($eval_fid) {
+	     $new_eval_fid = $fids{$eval_fid};
+	     confess("unknown eval_fid $eval_fid") unless defined $new_eval_fid;
+	     $new_fid = $next_fid++;
+	     $fids{$fid} = $new_fid;
+	 } else {
+	     $new_eval_fid = $eval_fid;
+	     $new_fid = $file_to_fid{$name};
+	     unless(defined $new_fid) {
+		 $new_fid = $next_fid++;
+		 $fids{$fid} = $new_fid;
+		 $file_to_fid{$name} = $fid;
+	     }
+	 }
 	 $out->write('@');
-	 $out->output_int($fid, $eval_fid, $eval_line, $flags, $size, $mtime);
+	 $out->output_int($new_fid, $new_eval_fid, $eval_line, $flags, $size, $mtime);
 	 $out->output_str($name);
      },
      TIME_BLOCK => \&_time_block_or_line,
@@ -102,14 +121,14 @@ my %dispatcher =
      SUB_INFO => sub {
 	 my (undef, $fid, $first_line, $last_line, $name) = @_;
 	 $out->write('s');
-	 $out->output_int($fid);
+	 $out->output_int($fids{$fid});
 	 $out->output_str($name);
 	 $out->output_int($first_line, $last_line, 0);
      },
      SUB_CALLERS => sub {
 	 my (undef, $fid, $line, $count, $incl_time, $excl_time, $ucpu_time, $scpu_time, $reci_time, $rec_depth, $called, $caller) = @_;
 	 $out->write('c');
-	 $out->output_int($fid, $line);
+	 $out->output_int($fids{$fid}, $line);
 	 $out->output_str($caller);
 	 $out->output_int($count);
 	 $out->output_nv($incl_time, $excl_time, $ucpu_time, $scpu_time, $reci_time);
@@ -119,7 +138,7 @@ my %dispatcher =
      SRC_LINE => sub {
 	 my (undef, $fid, $line, $text) = @_;
 	 $out->write('S');
-	 $out->output_int($fid, $line);
+	 $out->output_int($fids{$fid}, $line);
 	 $out->output_str($text);
      },
     );
-- 
1.6.0

>From e378d429bebc1ea0a2b1e3686d4be4cc4299faa0 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Thu, 17 Dec 2009 16:57:54 +0000
Subject: [PATCH] profiler_duracion needs to sum over all PIDs run, not just the last PID seen.

---
 NYTProf.xs |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/NYTProf.xs b/NYTProf.xs
index ba58e52..2bad944 100644
--- a/NYTProf.xs
+++ b/NYTProf.xs
@@ -4494,7 +4494,7 @@ load_profile_data_from_stream(SV *cb)
                         HvKEYS(live_pids_hv), profiler_end_time);
 
                 store_attrib_sv(aTHX_ attr_hv, "profiler_end_time", newSVnv(profiler_end_time));
-                profiler_duration = profiler_end_time - profiler_start_time;
+                profiler_duration += profiler_end_time - profiler_start_time;
                 store_attrib_sv(aTHX_ attr_hv, "profiler_duration", newSVnv(profiler_duration));
 
                 break;
-- 
1.6.0

>From f6264238296b138ec6951a17ad4521df1b3cf94e Mon Sep 17 00:00:00 2001
From: Nicholas Clark <[email protected]>
Date: Thu, 17 Dec 2009 16:58:35 +0000
Subject: [PATCH] Merge 2 (or more) NYTProf output files. Unpolished - error checking, sanity
 checking and tests not present yet.

---
 bin/nytprofmerge |   99 +++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 75 insertions(+), 24 deletions(-)

diff --git a/bin/nytprofmerge b/bin/nytprofmerge
index fa6ed73..c15b0fe 100755
--- a/bin/nytprofmerge
+++ b/bin/nytprofmerge
@@ -49,6 +49,11 @@ my $next_fid = 1;
 my %file_to_fid;
 my %fids = (0 => 0);
 
+my $version;
+my %seen_subs;
+
+my %callers;
+
 sub _time_block_or_line {
     my ($tag, undef, undef, $ticks, $fid, $line, $block_line, $sub_line) = @_;
     my $is_line = $tag eq 'TIME_LINE';
@@ -60,11 +65,21 @@ sub _time_block_or_line {
     }
 }
 
+# Effectively, this is a global variable. Sorry.
+my $input;
+
 my %dispatcher =
     (
      VERSION => sub {
 	 my (undef, $major, $minor) = @_;
-	 $out->write("NYTProf $major $minor\n");
+	 my $this_version = "$major $minor";
+	 if($version) {
+	     die "Incompatible version '$this_version' in $input, expected '$version'"
+		 unless $this_version eq $version;
+	 } else {
+	     $version = $this_version;
+	     $out->write("NYTProf $version\n");
+	 }
      },
      COMMENT => sub {
 	 my (undef, $text) = @_;
@@ -102,11 +117,11 @@ my %dispatcher =
 	 } else {
 	     $new_eval_fid = $eval_fid;
 	     $new_fid = $file_to_fid{$name};
-	     unless(defined $new_fid) {
-		 $new_fid = $next_fid++;
-		 $fids{$fid} = $new_fid;
-		 $file_to_fid{$name} = $fid;
-	     }
+	     return if defined $new_fid;
+
+	     $new_fid = $next_fid++;
+	     $fids{$fid} = $new_fid;
+	     $file_to_fid{$name} = $fid;
 	 }
 	 $out->write('@');
 	 $out->output_int($new_fid, $new_eval_fid, $eval_line, $flags, $size, $mtime);
@@ -120,20 +135,39 @@ my %dispatcher =
      },
      SUB_INFO => sub {
 	 my (undef, $fid, $first_line, $last_line, $name) = @_;
-	 $out->write('s');
-	 $out->output_int($fids{$fid});
-	 $out->output_str($name);
-	 $out->output_int($first_line, $last_line, 0);
+	 if(!$seen_subs{"$fid,$name"}++) {
+	     $out->write('s');
+	     $out->output_int($fids{$fid});
+	     $out->output_str($name);
+	     $out->output_int($first_line, $last_line, 0);
+	 }
      },
      SUB_CALLERS => sub {
 	 my (undef, $fid, $line, $count, $incl_time, $excl_time, $ucpu_time, $scpu_time, $reci_time, $rec_depth, $called, $caller) = @_;
-	 $out->write('c');
-	 $out->output_int($fids{$fid}, $line);
-	 $out->output_str($caller);
-	 $out->output_int($count);
-	 $out->output_nv($incl_time, $excl_time, $ucpu_time, $scpu_time, $reci_time);
-	 $out->output_int($rec_depth);
-	 $out->output_str($called);
+	 $fid = $fids{$fid};
+
+	 if ($callers{"$fid,$line"}{$called}{$caller}) {
+	     my $sum = $callers{"$fid,$line"}{$called}{$caller};
+	     $sum->{count} += $count;
+	     $sum->{incl} += $incl_time;
+	     $sum->{excl} += $excl_time;
+	     $sum->{ucpu} += $ucpu_time;
+	     $sum->{scpu} += $scpu_time;
+	     $sum->{reci} += $reci_time;
+	     $sum->{depth} = $rec_depth if $rec_depth > $sum->{depth};
+	 } else {
+	     # New;
+	     $callers{"$fid,$line"}{$called}{$caller} =
+		 {
+		  depth => $rec_depth,
+		  count => $count,
+		  incl => $incl_time,
+		  excl => $excl_time,
+		  ucpu => $ucpu_time,
+		  scpu => $scpu_time,
+		  reci => $reci_time,
+		 };
+	 }
      },
      SRC_LINE => sub {
 	 my (undef, $fid, $line, $text) = @_;
@@ -143,12 +177,29 @@ my %dispatcher =
      },
     );
 
-my $input = shift @ARGV;
+foreach $input (@ARGV) {
+    for_chunks {
+	my $tag = $_[0];
 
-for_chunks {
-    my $tag = $_[0];
+	my $sub = $dispatcher{$tag};
+	die "Unknown tag '$tag'" unless defined $sub;
+	&$sub(@_);
+    } filename => $input;
+}
 
-    my $sub = $dispatcher{$tag};
-    die "Unknown tag '$tag'" unless defined $sub;
-    &$sub(@_);
-} filename => $input;
+# Deterministic order is useful for testing.
+foreach my $fid_line (sort keys %callers) {
+    my ($fid, $line) = split ',', $fid_line;
+    foreach my $called (sort keys %{$callers{$fid_line}}) {
+	foreach my $caller (sort keys %{$callers{$fid_line}{$called}}) {
+	    my $sum = $callers{$fid_line}{$called}{$caller};
+	    $out->write('c');
+	    $out->output_int($fid, $line);
+	    $out->output_str($caller);
+	    $out->output_int($sum->{count});
+	    $out->output_nv(@{$sum}{qw(incl excl ucpu scpu reci)});
+	    $out->output_int($sum->{depth});
+	    $out->output_str($called);
+	}
+    }
+}
-- 
1.6.0

Reply via email to