NAME
Devel::Probe - Quick & dirty code probes for Perl
VERSION
Version 0.000006
SYNOPSIS
use Devel::Probe;
...
Devel::Probe::trigger(sub {
my ($file, $line, $argument) = @_;
# probe logic
});
Devel::Probe::config(\%config);
...
Devel::Probe::enable();
...
Devel::Probe::disable();
DESCRIPTION
Use this module to allow the possibility of creating probes for some lines in your code. These probes can be used to build things like debuggers, fault injection tests, or production observability tooling.
The probing code is installed when you import the module, but it is disabled. In these conditions, the probe code is light enough that it should cause no impact at all in your CPU usage; an impact might be noticed when you enable the module and configure some probes, particularly depending on the frequency with which those probes will be triggered, and how heavy the trigger callback turns out to be.
FUNCTIONS
trigger(\&coderef)
Specify a piece of Perl code that will be called for every probe that triggers.
config(\%config)
Specify a configuration for the module, including what lines in your code will cause probes to be triggered. This call will always disable the module as a first action, so you always need to explicitly enable it again, either from the configuration itself or in a further call to
enable()
.add_probe($file, $line, $type, $callback_argument)
Manually add a probe. This is what gets called from
config()
when adding probes; please see the CONFIGURATION example for more information.enable()
/disable()
/is_enabled()
Dynamically activate and deactivate probing, and check this status. When disabled,
Devel::Probe
will have minimal overhead, and probes will fire again as soon asenable()
is called.install()
/remove()
/is_installed()
Install or remove the probe handling code, and check this status. When you import the module,
install()
is called automatically for you.When uninstalled,
Devel::Probe
will have zero overhead, and all probe state is cleared. Probes will not fire again untilinstall()
is called and both trigger and probes are redefined.clear()
Remove all probes.
dump()
Return all probes as a hash.
CONFIGURATION
An example configuration hash looks like this:
my %config = (
actions => [
{ action => 'disable' },
{ action => 'clear' },
{ action => 'define' ... },
{ action => 'enable' },
],
);
Possible actions are:
disable
: disable probing.clear
: clear current list of probes.enable
: enable probing.define
: define a new probe. A full define action looks like:my %define = ( action => 'define', type => PROBE_TYPE, file => 'file_name', lines => [ 10, 245, 333 ], argument => $my_callback_argument, );
The type field is optional and its default value is
Devel::Probe::ONCE
. Possible values are:Devel::Probe::ONCE
: the probe will trigger once and then will be destroyed right after that. This default makes it more difficult to overwhelm your system with too much probing, unless you explicitly request a different type of probe.Devel::Probe::PERMANENT
: the probe will trigger every time that line of code is executed.
The
argument
field is optional and its default value is undefined. Possible values are any Perl scalar. If present, it will be passed to thetrigger
callback as the third argument.
EXAMPLE
This will invoke the callback defined with the call to trigger()
, the first time line 21 executes, taking advantage of PadWalker
to dump the local variables. After that first execution, that particular probe will not be triggered anymore. For line 22, every time that line is executed the probe will be triggered.
# line 1
use 5.18.0;
use Data::Dumper qw(Dumper);
use PadWalker qw(peek_my);
use Devel::Probe;
Devel::Probe::trigger(sub {
my ($file, $line) = @_;
say Dumper(peek_my(1)); # 1 to jump up one level in the stack;
});
my %config = (
actions => [
{ action => 'define', file => __FILE__, lines => [ 22 ] },
{ action => 'define', file => __FILE__, type => Devel::Probe::PERMANENT, lines => [ 23 ] },
],
);
Devel::Probe::config(\%config);
Devel::Probe::enable();
my $count;
while (1) {
$count++; # line 22
my $something_inside_the_loop = $count * 2; # line 23
sleep 5;
}
Devel::Probe::disable();
As another example, you can pass a custom argument to the trigger callback:
# line 1
use 5.18.0;
use PadWalker qw(peek_my);
use Devel::Probe;
Devel::Probe::trigger(sub {
my ($file, $line, $interesting_var_name) = @_;
say "$interesting_var_name: " . ${ peek_my(1)->{$interesting_var_name} };
});
my %config = (
actions => [
{ action => 'enable' },
{ action => 'define',
file => __FILE__,
type => Devel::Probe::PERMANENT,
lines => [ 26 ],
argument => '$squared'
},
],
);
Devel::Probe::config(\%config);
my $count = 0;
my $squared = 0;
while (1) {
$count++;
$squared = $count * $count; # line 26
sleep 5;
}
SUGGESTIONS
For files found directly by the Perl interpreter, the file name in the probe definition will usually be a relative path name; for files that are found through the PERL5LIB environment variable, the file name in the probe definition will usually be a full path name.
One typical use case would be to have a signal handler associated with a specific signal, which when triggered would disable the module, read the configuration from a given place, reconfigure the module accordingly and then enable it. Similarly, this kind of control can be implemented using remote endpoints to deal with reconfiguring, disabling and enabling the module.
TODO
Probes are stored in a hash of file names; per file name, there is a hash of line numbers (with the probe type as a value). It is likely this can be made more performant with a better data structure, but that needs profiling.
AUTHORS
Gonzalo Diethelm
gonzus AT cpan DOT org
Ben Tyler
btyler AT cpan DOT org