Author: tim.bunce
Date: Tue Jul 7 15:54:53 2009
New Revision: 811
Modified:
trunk/Changes
trunk/Makefile.PL
trunk/NYTProf.xs
trunk/lib/Devel/NYTProf/Data.pm
trunk/lib/Devel/NYTProf/ReadStream.pm
trunk/t/22-readstream.t
trunk/t/lib/NYTProfTest.pm
Log:
Added recording of calling sub to subroutine profiler.
(Not yet added to reporting data structures.)
Bumped file format version number and removed old minor version fudges.
Noted use of XCPT_* macros as a good fix for the 'xsub or sysop call is
lost if
returned via an exception' issue. (Though I've no plans to do that anytime
soon.)
Fixed setting of $. in callback interface to invoke magic.
Rewrote t/22-readstream.t to remove dependency on a (now outdated) frozen
profile file.
In some senses the tests aren't as detailed as they were. Patches welcome.
Modified: trunk/Changes
==============================================================================
--- trunk/Changes (original)
+++ trunk/Changes Tue Jul 7 15:54:53 2009
@@ -8,6 +8,8 @@
XXX sysops needs docs and more ops
+ Note: The file format has changed. Old files can't be read.
+
Fixed (rare) overflow bug for 32bit perls.
Fixed discarding of (rare) negative intervals.
Fixed risk of infinite recursion if trace enabled and
@@ -15,6 +17,10 @@
Fixed recursion depth measurement.
Changed colors on report pages to be less saturated.
+
+ Added recording the name of the calling subroutine to enable proper
+ linking of call trees. Previously only the calling file and line
+ were recorded.
Added interactive treemap view of package and subroutine times.
Left-click to zoom in (drill-down) one level, right-click to zoom out.
Modified: trunk/Makefile.PL
==============================================================================
--- trunk/Makefile.PL (original)
+++ trunk/Makefile.PL Tue Jul 7 15:54:53 2009
@@ -186,7 +186,7 @@
q{
prove:: pure_all
- time nice prove -j 9 --shuffle
+ time nice prove -b -j 9 --shuffle
svnmanifest::
svn list -R ....@head | sort | grep -v '/$$' > MANIFEST
Modified: trunk/NYTProf.xs
==============================================================================
--- trunk/NYTProf.xs (original)
+++ trunk/NYTProf.xs Tue Jul 7 15:54:53 2009
@@ -869,7 +869,7 @@
assert(out != NULL);
/* File header with "magic" string, with file major and minor version
*/
- NYTP_printf(out, "NYTProf %d %d\n", 2, 1);
+ NYTP_printf(out, "NYTProf %d %d\n", 3, 0);
/* Human readable comments and attributes follow
* comments start with '#', end with '\n', and are discarded
* attributes start with ':', a word, '=', then the value, then '\n'
@@ -919,6 +919,8 @@
tag = NYTP_TAG_STRING_UTF8;
len = -len;
}
+ if (trace_level >= 10)
+ logwarn("output_str('%.*s', %d)\n", len, str, len);
output_tag_int(tag, len);
NYTP_write(out, str, len);
}
@@ -2072,7 +2074,7 @@
SV *subname_sv;
AV *sub_av;
CV *sub_cv;
- int call_depth;
+ int cv_depth;
NV initial_overhead_ticks;
NV initial_subr_secs;
UV seqn;
@@ -2124,11 +2126,11 @@
SvPV_nolen(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->call_depth,
+ (int)subr_entry->cv_depth,
subr_entry->calling_fid, subr_entry->calling_line,
subr_entry->seqn);
/* only count inclusive time for the outer-most calls */
- if (subr_entry->call_depth <= 1) {
+ if (subr_entry->cv_depth <= 1) {
sv_setnv(incl_time_sv, SvNV(incl_time_sv)+incl_subr_sec);
}
else {
@@ -2137,9 +2139,9 @@
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 call_depth-1 */
- if (!SvOK(max_depth_sv) || subr_entry->call_depth-1 >
SvIV(max_depth_sv))
- sv_setiv(max_depth_sv, subr_entry->call_depth-1);
+ /* 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);
}
sv_setnv(excl_time_sv, SvNV(excl_time_sv)+excl_subr_sec);
@@ -2283,7 +2285,7 @@
subr_entry->subname_sv = &PL_sv_undef;
subr_entry->sub_av = NULL;
subr_entry->sub_cv = NULL;
- subr_entry->call_depth = 0;
+ subr_entry->cv_depth = 0;
SETERRNO(saved_errno, 0);
}
@@ -2302,26 +2304,24 @@
* xsub calls back into perl code which then croaks. In that case the
* entersub to perl, and any calls made by the perl code, will get
recorded
* but the xsub call itself won't)
+ * A good fix would be to use setjmp/longjmp to catch and rethrow the
+ * exception via the XCPT_* macros defined in XSUB.h. See
+ *
http://search.cpan.org/~nwclark/perl/pod/perlguts.pod#Exception_Handling
*/
op = run_original_op(op_type);
if (profile_sub_call) {
int saved_errno = errno;
- /* get line, file, and fid for statement *before* the call */
-
- /* XXX could use same closest_cop as DB_stmt() but it doesn't seem
- * to be needed here. Line is 0 only when call is from embedded
- * C code like mod_perl (at least in my testing so far)
- */
- char fid_line_key[50];
+ 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 *subname_sv = newSV(0);
- char *subname_pv;
- SV *sv_tmp;
+ SV *called_subname_sv = newSV(0);
+ char *called_subname_pv;
char *stash_name = NULL;
CV *cv;
char *is_xs;
+ SV *sv_tmp;
if (is_sysop) {
/* pretend builtins are xsubs in the same package
@@ -2329,16 +2329,15 @@
*/
const char *sysop_name = OP_NAME_safe(PL_op);
cv = NULL;
- is_xs = "sysop";
+ is_xs = "sop";
if (profile_sysops == 1) { /* 1 == put sysops into 1 package */
stash_name = "CORE";
- sv_setpvf(subname_sv, "%s::%s", stash_name, sysop_name);
+ sv_setpvf(called_subname_sv, "%s::%s", stash_name,
sysop_name);
}
else { /* 2 == put sysops into multiple
packages */
stash_name = CopSTASHPV(PL_curcop);
- sv_setpvf(subname_sv, "%s::CORE:%s", stash_name,
sysop_name);
+ sv_setpvf(called_subname_sv, "%s::CORE:%s", stash_name,
sysop_name);
}
- subname_pv = SvPV_nolen(subname_sv);
}
else {
if (op != next_op) { /* have entered a sub */
@@ -2349,7 +2348,7 @@
else { /* have returned from XS so use sub_sv
for name */
/* determine the original fully qualified name for sub */
/* CV or NULL */
- cv = (CV *)resolve_sub(aTHX_ sub_sv, subname_sv);
+ cv = (CV *)resolve_sub(aTHX_ sub_sv, called_subname_sv);
is_xs = "xsub";
}
@@ -2361,7 +2360,7 @@
* package, so we dig to find the original package
*/
stash_name = HvNAME(GvSTASH(gv));
- sv_setpvf(subname_sv, "%s::%s", stash_name,
GvNAME(gv));
+ sv_setpvf(called_subname_sv, "%s::%s", stash_name,
GvNAME(gv));
}
else if (trace_level) {
logwarn("I'm confused about CV %p\n", cv);
@@ -2371,7 +2370,7 @@
}
}
- if (!SvOK(subname_sv)) {
+ if (!SvOK(called_subname_sv)) {
if (!cv) {
/* should never get here as pp_entersub would have
croaked */
@@ -2379,12 +2378,12 @@
logwarn("unknown entersub %s '%s'\n", what,
SvPV_nolen(sub_sv));
if (trace_level)
sv_dump(sub_sv);
- sv_setpvf(subname_sv, "(unknown %s %s)", what,
SvPV_nolen(sub_sv));
+ sv_setpvf(called_subname_sv, "(unknown %s %s)", what,
SvPV_nolen(sub_sv));
}
else {
/* unnamed CV, e.g. seen in mod_perl/Class::MOP. XXX
do better? */
stash_name = HvNAME(CvSTASH(cv));
- sv_setpvf(subname_sv, "%s::__UNKNOWN__[0x%p]",
+ sv_setpvf(called_subname_sv, "%s::__UNKNOWN__[0x%p]",
(stash_name)?stash_name:"__UNKNOWN__", cv);
if (trace_level) {
logwarn("unknown entersub %s assumed to be anon
cv '%s'\n", (is_xs) ? is_xs : "sub", SvPV_nolen(sub_sv));
@@ -2393,26 +2392,15 @@
}
}
}
- subname_pv = SvPV_nolen(subname_sv);
+ called_subname_pv = SvPV_nolen(called_subname_sv);
/* ignore our own DB::_INIT sub - only shows up with 5.8.9+ &
5.10.1+ */
- if (is_xs && *subname_pv == 'D' && strEQ(subname_pv, "DB::_INIT"))
+ if (is_xs && *called_subname_pv == 'D' &&
strEQ(called_subname_pv, "DB::_INIT"))
goto skip_sub_profile;
- /* { called_subname => { "fid:line" => [ count, incl_time ] } } */
- sv_tmp = *hv_fetch(sub_callers_hv, subname_pv,
- (I32)SvCUR(subname_sv), 1);
-
- /* XXX fid:line can be ambiguous, e.g sub foo { return sub { ... }
}
- * We could add subname_sv to the [ count, incl_time ] array
- * and check it on each call. To improve performance we could also
- * add the op and so avoid the string compare if the op's are the
same.
- * If there's a call with a different subname_sv value, then we
- * could interpose a hash to hold per-subname values:
- * old => { "fid:line" => [ count, incl_time, "sub1"
] }
- * new => { "fid:line" => { "sub1"=>[ count, incl_time
], "sub2"=>[...] } }
- * or => { "fid:line" => { OP =>[ count, incl_time, "sub1"
], "sub2"=>[...] } }
- */
+ /* { called_subname => { "calling_subname[fid:line]" => [ count,
incl_time, ... ] } } */
+ sv_tmp = *hv_fetch(sub_callers_hv, called_subname_pv,
+ (I32)SvCUR(called_subname_sv), 1);
if (!SvROK(sv_tmp)) { /* autoviv hash ref - is first call of this
subname from anywhere */
HV *hv = newHV();
@@ -2422,53 +2410,61 @@
/* create dummy item with fid=0 & line=0 to act as flag to
indicate xs */
AV *av = new_sub_call_info_av(aTHX);
av_store(av, NYTP_SCi_CALL_COUNT, newSVuv(0));
- sv_setsv(*hv_fetch(hv, "0:0", 3, 1), newRV_noinc((SV
*)av));
+ sv_setsv(*hv_fetch(hv, "[0:0]", 5, 1), newRV_noinc((SV
*)av));
- if ((cv && SvTYPE(cv) == SVt_PVCV) || (is_xs && 's' ==
*is_xs)) {
+ if ((cv && SvTYPE(cv) == SVt_PVCV) || ('s' == *is_xs)) {
/* We just use an empty string as the filename for
xsubs
* because CvFILE() isn't reliable on perl 5.8.[78]
* and the name of the .c file isn't very useful
anyway.
* The reader can try to associate the xsubs with the
* corresonding .pm file using the package part of the
subname.
*/
- SV *sv = *hv_fetch(GvHV(PL_DBsub), subname_pv,
(I32)SvCUR(subname_sv), 1);
+ SV *sv = *hv_fetch(GvHV(PL_DBsub), called_subname_pv,
(I32)SvCUR(called_subname_sv), 1);
sv_setpv(sv, ":0-0"); /* empty file name */
if (trace_level >= 2)
- logwarn("Adding fake DBsub entry for '%s' xsub\n",
subname_pv);
+ logwarn("Adding fake DBsub entry for '%s' xsub\n",
called_subname_pv);
}
}
}
- fid_line_key_len = sprintf(fid_line_key, "%u:%d",
+ 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);
+ if (fid_line_key_len >= sizeof(fid_line_key))
+ croak("panic: NYTProf buffer overflow on %s\n", fid_line_key);
/* drill-down to array of sub call information for this
fid_line_key */
sv_tmp = *hv_fetch((HV*)SvRV(sv_tmp), fid_line_key,
fid_line_key_len, 1);
- if (!SvROK(sv_tmp)) { /* first call from this fid:line - autoviv
array ref */
+ if (!SvROK(sv_tmp)) { /* first call from this subname[fid:line] -
autoviv array ref */
AV *av = new_sub_call_info_av(aTHX);
sv_setsv(sv_tmp, newRV_noinc((SV *)av));
subr_entry->sub_av = av;
- if (stash_name) /* note that a sub in this package was called
*/
- (void)hv_fetch(pkg_fids_hv, stash_name,
(I32)strlen(stash_name), 1);
+ if (stash_name) { /* note that a sub in this package was
called */
+ SV *pf_sv = *hv_fetch(pkg_fids_hv, stash_name,
(I32)strlen(stash_name), 1);
+ if (!SvOK(pf_sv)) { /* log when first created */
+ if (trace_level >= 5)
+ logwarn("Noting that subs in package '%s' were
called\n",
+ stash_name);
+ sv_setsv(pf_sv, &PL_sv_no);
+ }
+ }
}
else {
subr_entry->sub_av = (AV *)SvRV(sv_tmp);
sv_inc(AvARRAY(subr_entry->sub_av)[0]); /* ++call count */
}
- /* record call_depth, adjust for xs since, in that case, we
+ /* record cv_depth, adjust for xs since, in that case, we
* have already left the sub, unlike the non-xs case. */
- subr_entry->call_depth = (cv) ? CvDEPTH(cv)+(is_xs?1:0) : 1;
+ subr_entry->cv_depth = (cv) ? CvDEPTH(cv)+(is_xs?1:0) : 1;
if (trace_level >= 2) {
- subr_entry_t *se_caller = subr_entry_caller(subr_entry);
- fprintf(stderr, " ->%s %s from %s %d:%d (d%d, oh %"NVff"t,
sub %"NVff"s) #%lu\n",
- (is_xs) ? is_xs : " sub", subname_pv,
- (se_caller) ? SvPV_nolen(se_caller->subname_sv) : "",
- subr_entry->calling_fid, subr_entry->calling_line,
- subr_entry->call_depth,
+ 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->initial_overhead_ticks,
subr_entry->initial_subr_secs,
subr_entry->seqn
@@ -2476,7 +2472,7 @@
}
if (profile_subs) {
- subr_entry->subname_sv = subname_sv;
+ subr_entry->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);
@@ -2487,7 +2483,7 @@
}
}
else {
- sv_free(subname_sv);
+ sv_free(called_subname_sv);
}
skip_sub_profile:
SETERRNO(saved_errno, 0);
@@ -2890,14 +2886,21 @@
/* get sv for package-of-subname to filename mapping */
SV *pkg_filename_sv = sub_pkg_filename_sv(aTHX_ sub_name);
- /* ignore if package is not of interest, or filename is empty (xs)
*/
- if (!pkg_filename_sv || !filename_len)
+ if (!pkg_filename_sv) /* we don't know package */
continue;
- /* ignore if we've already got a filename for this package XXX
should allow multiple */
- if (SvOK(pkg_filename_sv))
+ /* already got a filename for this package XXX should allow
multiple */
+ if (SvTRUE(pkg_filename_sv))
continue;
+ /* ignore if filename is empty (eg xs) */
+ if (!filename_len) {
+ if (trace_level >= 3)
+ logwarn("Sub %.*s has no filename associated (%s)\n",
+ sub_name_len, sub_name, filename);
+ continue;
+ }
+
/* associate the filename with the package */
sv_setpvn(pkg_filename_sv, filename, filename_len);
@@ -2933,7 +2936,7 @@
if (!filename_len) { /* no filename, so presumably a fake entry
for xsub */
/* do we know a filename that contains subs in the same
package */
SV *pkg_filename_sv = sub_pkg_filename_sv(aTHX_ sub_name);
- if (pkg_filename_sv && SvOK(pkg_filename_sv)) {
+ if (pkg_filename_sv && SvTRUE(pkg_filename_sv)) {
filename = SvPV(pkg_filename_sv, filename_len);
if (trace_level >= 2)
logwarn("Sub %s is xsub, we'll associate it with
filename %.*s\n",
@@ -2944,7 +2947,8 @@
fid = get_file_id(aTHX_ filename, filename_len, 0);
if (!fid) {
if (trace_level >= 4)
- logwarn("Sub %s not profiled\n", sub_name);
+ logwarn("Sub %s has no fid assigned (for file '%.*s')\n",
+ sub_name, filename_len, filename);
continue; /* no point in writing subs in files we've not
profiled */
}
@@ -2963,8 +2967,8 @@
static void
write_sub_callers(pTHX)
{
- char *sub_name;
- I32 sub_name_len;
+ char *called_subname;
+ I32 called_subname_len;
SV *fid_line_rvhv;
if (!sub_callers_hv)
@@ -2973,21 +2977,35 @@
logwarn("writing sub callers\n");
hv_iterinit(sub_callers_hv);
- while (NULL != (fid_line_rvhv = hv_iternextsv(sub_callers_hv,
&sub_name, &sub_name_len))) {
+ 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 *fid_line_string;
- I32 fid_line_len;
+ char *calling_subname;
+ I32 calling_subname_len;
SV *sv;
- /* iterate over callers to this sub ({ "fid:line" => [ ... ] }) */
+ /* I32 level, PerlIO *file, SV *sv, I32 nest, I32 maxnest, bool
dumpops, STRLEN pvlim */
+ if (0) do_sv_dump(0, Perl_debug_log, fid_line_rvhv, 0, 5, 0, 100);
+
+ /* iterate over callers to this sub ({ "subname[fid:line]" =>
[ ... ] }) */
hv_iterinit(fid_lines_hv);
- while (NULL != (sv = hv_iternextsv(fid_lines_hv, &fid_line_string,
&fid_line_len))) {
+ while (NULL != (sv = hv_iternextsv(fid_lines_hv, &calling_subname,
&calling_subname_len))) {
NV sc[NYTP_SCi_elements];
AV *av = (AV *)SvRV(sv);
int trace = (trace_level >= 3);
unsigned int fid = 0, line = 0;
- (void)sscanf(fid_line_string, "%u:%u", &fid, &line);
+ char *fid_line_delim = "[";
+ char *fid_line_start = rninstr(calling_subname,
calling_subname+calling_subname_len, fid_line_delim, fid_line_delim+1);
+ if (!fid_line_start) {
+ logwarn("bad fid_lines_hv key '%s'\n", calling_subname);
+ continue;
+ }
+ if (2 != sscanf(fid_line_start+1, "%u:%u", &fid, &line)) {
+ logwarn("bad fid_lines_hv format '%s'\n", calling_subname);
+ continue;
+ }
+ /* trim length to effectively hide the [fid:line] suffix */
+ calling_subname_len = fid_line_start-calling_subname;
output_tag_int(NYTP_TAG_SUB_CALLERS, fid);
output_int(line);
@@ -2998,17 +3016,20 @@
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(sub_name, sub_name_len);
+ output_str(called_subname, called_subname_len);
+ output_str(calling_subname, calling_subname_len);
/* sanity check - early warning */
if (sc[NYTP_SCi_INCL_RTIME] < 0.0 || sc[NYTP_SCi_EXCL_RTIME] <
0.0) {
- logwarn("%s call has negative time!\n", sub_name);
+ logwarn("%s call has negative time for call from %u:%d!\n",
+ called_subname, fid, line);
trace = 1;
}
if (trace)
- logwarn("%s called by %u:%u: count %"NVff" (i%"NVff"s
e%"NVff"s u%"NVff"s s%"NVff"s, d%"NVff" ri%"NVff"s)\n",
- sub_name, fid, line, sc[NYTP_SCi_CALL_COUNT],
+ 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],
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]);
@@ -3299,7 +3320,8 @@
AV* fid_block_time_av = NULL;
AV* fid_sub_time_av = NULL;
HV* sub_subinfo_hv = newHV();
- SV *tmp_str_sv = newSVpvn("",0);
+ SV *tmp_str1_sv = newSVpvn("",0);
+ SV *tmp_str2_sv = newSVpvn("",0);
HV *file_info_stash = gv_stashpv("Devel::NYTProf::FileInfo",
GV_ADDWARN);
/* these times don't reflect profile_enable & profile_disable calls */
@@ -3325,11 +3347,11 @@
if (2 != fscanf(in->file, "NYTProf %d %d\n", &file_major,
&file_minor)) {
croak("Profile format error while parsing header");
}
- if (file_major != 2)
+ if (file_major != 3)
croak("Profile format version %d.%d not supported by %s %s",
file_major, file_minor, __FILE__, XS_VERSION);
- if (cb) {
+ if (cb && SvROK(cb)) {
input_chunk_seqn_sv = save_scalar(gv_fetchpv(".", GV_ADD, SVt_IV));
sv_setuv(input_chunk_seqn_sv, input_chunk_seqn);
@@ -3344,17 +3366,17 @@
for (i = 0; i < C_ARRAY_LENGTH(cb_args); i++)
cb_args[i] = sv_newmortal();
-
PUSHMARK(SP);
-
i = 0;
sv_setpvs(cb_args[i], "VERSION"); XPUSHs(cb_args[i++]);
sv_setiv(cb_args[i], file_major); XPUSHs(cb_args[i++]);
sv_setiv(cb_args[i], file_minor); XPUSHs(cb_args[i++]);
-
PUTBACK;
call_sv(cb, G_DISCARD);
}
+ else {
+ cb = Nullsv;
+ }
while (1) {
/* Loop "forever" until EOF. We can only check the EOF flag
*after* we
@@ -3370,7 +3392,7 @@
input_chunk_seqn++;
if (cb) {
- sv_setuv(input_chunk_seqn_sv, input_chunk_seqn);
+ sv_setuv_mg(input_chunk_seqn_sv, input_chunk_seqn);
}
if (trace_level >= 6)
@@ -3635,7 +3657,7 @@
unsigned int first_line = read_int();
unsigned int last_line = read_int();
int skip_subinfo_store = 0;
- SV *subname_sv = normalize_eval_seqn(aTHX_ read_str(aTHX_
tmp_str_sv));
+ SV *subname_sv = normalize_eval_seqn(aTHX_ read_str(aTHX_
tmp_str1_sv));
STRLEN subname_len;
char *subname_pv;
@@ -3699,6 +3721,7 @@
char text[MAXPATHLEN*2];
SV *sv;
SV *subname_sv;
+ SV *calling_subname_sv = Nullsv;
AV *subinfo_av;
int len;
unsigned int fid = read_int();
@@ -3708,9 +3731,10 @@
NV excl_time = read_nv();
NV ucpu_time = read_nv();
NV scpu_time = read_nv();
- NV reci_time = (file_minor >= 1) ? read_nv() : 0;
- UV rec_depth = (file_minor >= 1) ? read_int() : 0;
- subname_sv = normalize_eval_seqn(aTHX_ read_str(aTHX_
tmp_str_sv));
+ NV reci_time = read_nv();
+ UV rec_depth = read_int();
+ subname_sv = normalize_eval_seqn(aTHX_ read_str(aTHX_
tmp_str1_sv));
+ calling_subname_sv = normalize_eval_seqn(aTHX_
read_str(aTHX_ tmp_str2_sv));
if (cb) {
PUSHMARK(SP);
@@ -3735,8 +3759,9 @@
}
if (trace_level >= 3)
- logwarn("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);
+ logwarn("Sub %s called by %s %u:%u: count %d, incl %f,
excl %f, ucpu %f scpu %f\n",
+ SvPV_nolen(subname_sv),
SvPV_nolen(calling_subname_sv), fid, line,
+ count, incl_time, excl_time, ucpu_time, scpu_time);
subinfo_av = lookup_subinfo_av(aTHX_ subname_sv,
sub_subinfo_hv);
@@ -3818,7 +3843,7 @@
unsigned int pid = read_int();
unsigned int ppid = read_int();
int len = sprintf(text, "%d", pid);
- profiler_start_time = (file_minor >= 1) ? read_nv() : 0;
+ profiler_start_time = read_nv();
if (cb) {
PUSHMARK(SP);
@@ -3827,9 +3852,7 @@
sv_setpvs(cb_args[i], "PID_START");
XPUSHs(cb_args[i++]);
sv_setuv(cb_args[i], pid);
XPUSHs(cb_args[i++]);
sv_setuv(cb_args[i], ppid);
XPUSHs(cb_args[i++]);
- if (file_minor >= 1) {
- sv_setnv(cb_args[i], profiler_start_time);
XPUSHs(cb_args[i++]);
- }
+ sv_setnv(cb_args[i], profiler_start_time);
XPUSHs(cb_args[i++]);
PUTBACK;
call_sv(cb, G_DISCARD);
@@ -3851,7 +3874,7 @@
char text[MAXPATHLEN*2];
unsigned int pid = read_int();
int len = sprintf(text, "%d", pid);
- profiler_end_time = (file_minor >= 1) ? read_nv() : 0;
+ profiler_end_time = read_nv();
if (cb) {
PUSHMARK(SP);
@@ -3859,9 +3882,7 @@
i = 0;
sv_setpvs(cb_args[i], "PID_END");
XPUSHs(cb_args[i++]);
sv_setuv(cb_args[i], pid);
XPUSHs(cb_args[i++]);
- if (file_minor >= 1) {
- sv_setnv(cb_args[i], profiler_end_time);
XPUSHs(cb_args[i++]);
- }
+ sv_setnv(cb_args[i], profiler_end_time);
XPUSHs(cb_args[i++]);
PUTBACK;
call_sv(cb, G_DISCARD);
@@ -3973,9 +3994,16 @@
}
}
+ if (HvKEYS(live_pids_hv)) {
+ logwarn("profile data possibly truncated, no terminator
for %"IVdf" pids\n",
+ HvKEYS(live_pids_hv));
+ }
+ sv_free((SV*)live_pids_hv);
+ sv_free(tmp_str1_sv);
+ sv_free(tmp_str2_sv);
+
if (cb) {
SvREFCNT_dec(profile_modes);
- SvREFCNT_dec(live_pids_hv);
SvREFCNT_dec(attr_hv);
SvREFCNT_dec(fid_fileinfo_av);
SvREFCNT_dec(fid_srclines_av);
@@ -3983,17 +4011,9 @@
SvREFCNT_dec(fid_block_time_av);
SvREFCNT_dec(fid_sub_time_av);
SvREFCNT_dec(sub_subinfo_hv);
- SvREFCNT_dec(tmp_str_sv);
return newHV(); /* dummy */
}
-
- if (HvKEYS(live_pids_hv)) {
- logwarn("profile data possibly truncated, no terminator
for %"IVdf" pids\n",
- HvKEYS(live_pids_hv));
- }
- sv_free((SV*)live_pids_hv);
- sv_free(tmp_str_sv);
if (statement_discount) /* discard unused statement_discount */
total_stmts_discounted -= statement_discount;
Modified: trunk/lib/Devel/NYTProf/Data.pm
==============================================================================
--- trunk/lib/Devel/NYTProf/Data.pm (original)
+++ trunk/lib/Devel/NYTProf/Data.pm Tue Jul 7 15:54:53 2009
@@ -68,14 +68,16 @@
sub new {
my $class = shift;
- my $args = shift || { filename => 'nytprof.out' };
+ my $args = shift || { };
- my $file = $args->{filename}
- or croak "No filename specified";
+ my $file = $args->{filename} ||= 'nytprof.out';
print "Reading $file\n" unless $args->{quiet};
- my $profile = load_profile_data_from_file($file);
+ my $profile = load_profile_data_from_file(
+ $file,
+ $args->{callback},
+ );
bless $profile => $class;
my $fid_fileinfo = $profile->{fid_fileinfo};
Modified: trunk/lib/Devel/NYTProf/ReadStream.pm
==============================================================================
--- trunk/lib/Devel/NYTProf/ReadStream.pm (original)
+++ trunk/lib/Devel/NYTProf/ReadStream.pm Tue Jul 7 15:54:53 2009
@@ -10,14 +10,14 @@
for_chunks
);
-use Devel::NYTProf::Core;
+use Devel::NYTProf::Data;
sub for_chunks (&%) {
my($cb, %opts) = @_;
- Devel::NYTProf::Data::load_profile_data_from_file(
- $opts{filename} || 'nytprof.out',
- $cb,
- );
+ Devel::NYTProf::Data->new( {
+ %opts,
+ callback => $cb,
+ });
}
1;
@@ -67,19 +67,12 @@
callback.
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>.
-
-=back
+pairs after the callback. The contents of %opts are passed to
+L<Devel::NYTProf::Data/new>.
The function is prototyped as C<(&%)> which means that it can be invoked
with a
-bare block representing the callback function. In that case there
-should be no comma before any options. Example:
+bare block representing the callback function. In that case there should
be no
+comma before any options. Example:
for_chunk { say $_[0] } filename => "myprof.out";
Modified: trunk/t/22-readstream.t
==============================================================================
--- trunk/t/22-readstream.t (original)
+++ trunk/t/22-readstream.t Tue Jul 7 15:54:53 2009
@@ -1,34 +1,53 @@
-use Test::More tests => 8;
+use Test::More tests => 18;
use strict;
+
+use lib qw(t/lib);
+use Config;
+use NYTProfTest;
+
use Devel::NYTProf::ReadStream qw(for_chunks);
(my $base = __FILE__) =~ s/\.t$//;
-my @arr;
-eval {
- for_chunks {
- push(@arr, [$., @_]);
- } filename => "$base-v20.out";
-};
-SKIP: {
- if ($@) {
- skip "No zlib support", 8 if $@ && $@ =~ /compression is not supported/;
- skip "Unusual NV size", 8 if $@ && $@ =~ /Profile data created by
incompatible perl config/;
- die $@;
- }
-
- is_deeply([0..51], [map shift(@$_), @arr], "chunk seq");
-
- # some samples
- is_deeply($arr[0], ["VERSION", 2, 0], "version");
- is_deeply($arr[3], ["ATTRIBUTE", "xs_version", "2.05"], "attr");
- is_deeply($arr[10], ["START_DEFLATE"], "deflate");
- is_deeply($arr[11], ["PID_START", 1710, 13983], "pid start");
- is_deeply($arr[12], ["NEW_FID", 1, 0, 0, 2, 0,
0, "/Users/gisle/p/Devel-NYTProf/t/test01.p"], "fid");
- is_deeply($arr[14], ["TIME_BLOCK", 0, 0, 76, 1, 7, 7, 7], "time");
- is_deeply($arr[15], ["DISCOUNT"], "discount");
-
- #use Data::Dump; ddx \...@arr;
+# generate an nytprof out file
+my $out = 'nytprof_readstream.out';
+$ENV{NYTPROF} = "file=$out";
+unlink $out;
+
+run_command(q{perl -d:NYTProf -e "sub A { };" -e "1;" -e "A()"});
+
+my %prof;
+my @seqn;
+
+for_chunks {
+ push @seqn, "$.";
+ my $tag = shift;
+ push @{ $prof{$tag} }, [ @_ ];
+ if (1) { chomp @_; note("$. $tag @_"); }
+} filename => $out;
+
+ok scalar @seqn, 'should have read chunks';
+is_deeply(\...@seqn, [...@seqn-1], "chunk seq");
+
+#use Data::Dumper; warn Dumper \%prof;
+
+is_deeply $prof{VERSION}, [ [ 3, 0 ] ];
+
+# check for expected tags
+# (but not START_DEFLATE as that'll be missing if there's no zlib)
+for my $tag (qw(
+ COMMENT ATTRIBUTE DISCOUNT SRC_LINE TIME_BLOCK
+ SUB_LINE_RANGE SUB_CALLERS
+ PID_START PID_END NEW_FID
+)) {
+ is ref $prof{$tag}[0], 'ARRAY', $tag;
}
+# check some attributes
+my %attr = map { $_->[0] => $_->[1] } @{ $prof{ATTRIBUTE} };
+cmp_ok $attr{ticks_per_sec}, '>=', 1_000_000, 'ticks_per_sec';
+is $attr{application}, '-e', 'application';
+is $attr{nv_size}, $Config{nvsize}, 'nv_size';
+cmp_ok $attr{xs_version}, '>=', 2.1, 'xs_version';
+cmp_ok $attr{basetime}, '>=', $^T, 'basetime';
Modified: trunk/t/lib/NYTProfTest.pm
==============================================================================
--- trunk/t/lib/NYTProfTest.pm (original)
+++ trunk/t/lib/NYTProfTest.pm Tue Jul 7 15:54:53 2009
@@ -14,10 +14,12 @@
use base qw(Exporter);
our @EXPORT = qw(
run_test_group
+ run_command
do_foreach_env_combination
profile_this_code
);
+use Devel::NYTProf::Data;
use Devel::NYTProf::Reader;
use Devel::NYTProf::Util qw(strip_prefix_from_paths html_safe_filename);
@@ -525,7 +527,10 @@
croak "Neither src_file or src_code was provided";
}
- my $profile = Devel::NYTProf::Data->new( { filename => $out_file } );
+ my $profile = Devel::NYTProf::Data->new( {
+ filename => $out_file,
+ callback => $opts{for_chunks},
+ } );
unlink $out_file;
--~--~---------~--~----~------------~-------~--~----~
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]
-~----------~----~----~----~------~----~------~--~---