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