B-Tap
view release on metacpan or search on metacpan
lib/Devel/CodeObserver.pm view on Meta::CPAN
use Data::Dumper ();
sub new {
my $class = shift;
my %args = @_==1 ? %{$_[0]} : @_;
bless {%args}, $class;
}
sub null {
my $op = shift;
return class($op) eq "NULL";
}
sub call {
my ($class,$code) = @_;
my $cv = B::svref_2object($code);
my @tap_results;
my $root = $cv->ROOT;
# local $B::overlay = {};
if (not null $root) {
op_walk {
if (need_hook($_)) {
my @buf = ($_);
tap($_, $cv->ROOT, \@buf);
push @tap_results, \@buf;
}
} $cv->ROOT;
}
if (0) {
require B::Concise;
my $walker = B::Concise::compile('', '', $code);
$walker->();
}
my $retval = $code->();
return (
$retval,
Devel::CallTrace::Result->new(
code => $code,
tap_results => [grep { @$_ > 1 } @tap_results],
)
);
}
sub need_hook {
my $op = shift;
return 1 if $op->name eq 'entersub';
return 1 if $op->name eq 'padsv';
return 1 if $op->name eq 'aelem';
return 1 if $op->name eq 'helem';
return 1 if $op->name eq 'null' && ppname($op->targ) eq 'pp_rv2sv';
return 0;
}
package Devel::CallTrace::Result;
use Try::Tiny;
use constant { DEBUG => 0 };
sub new {
my $class = shift;
my %args = @_==1 ? %{$_[0]} : @_;
bless {%args}, $class;
}
sub dump_pairs {
my ($self) = @_;
my $tap_results = $self->{tap_results};
my $code = $self->{code};
# We should load B::Deparse lazily. Because loading B::Deparse is really slow.
# It's really big module.
#
# And so, this module is mainly used for testing. And this part is only required if
# the test case was failed. I make faster the passed test case.
require B::Deparse;
my @pairs;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;
for my $result (@$tap_results) {
my $op = shift @$result;
for my $value (@$result) {
# take first argument if the value is scalar.
try {
# Suppress warnings for: sub { expect(\@p)->to_be(['a']) }
local $SIG{__WARN__} = sub { };
my $deparse = B::Deparse->new();
$deparse->{curcv} = B::svref_2object($code);
push @pairs, [
$deparse->deparse($op),
Data::Dumper::Dumper($value->[1])
];
} catch {
DEBUG && warn "[Devel::CodeObserver] [BUG]: $_";
push @WARNINGS, "[Devel::CodeObserver] [BUG]: $_";
};
}
}
return \@pairs;
}
1;
__END__
=head1 NAME
Devel::CodeObserver - Code tracer
=head1 SYNOPSIS
my $tracer = Devel::CodeObserver->new();
my ($retval, $trace_data) = $tracer->call(sub { $dat->{foo}{bar} eq 200 });
=head1 DESCRIPTION
( run in 1.460 second using v1.01-cache-2.11-cpan-39bf76dae61 )