dip - Dynamic instrumentation like DTrace, using aspects
# run a dip script from a file; pass perl switches after the '--' $ dip -s toolkit/count-new.dip -- -S myapp.pl # run an inline dip script $ dip -e 'our %c; before { count("constructor", ARGS(1), ustack(5)); $c{total}++ } call "URI::new"' test.pl # a more complex dip script $ cat quant-requests.dip # quantize request handling time, separated by request method/URI around { my $ts_start = [gettimeofday]; $_->proceed; quantize [ 'all', [ ARGS(1)->method, ARGS(1)->request_uri ] ] => 10**6*tv_interval($ts_start); } call 'Dancer::Handler::handle_request'; $ dip -s request-quant.dip test.pl ... GET / value ------------------ Distribution ------------------ count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 95 4096 |@@ 4 8192 | 0 16384 |@ 1 32768 | 0 GET /login value ------------------ Distribution ------------------ count 512 | 0 1024 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 70 2048 |@@@@@@@@@@@@@@@ 30 4096 | 0 # The next example relies on Aspect::Library::Profiler, so # if something goes wrong, you need to look in the Aspect modules. $ dip -e 'aspect Profiler => call qr/^Person::set_/' myapp.pl
This is the documentation for the dip module. If you are looking for the documentation on the dip program, use perldoc dip or man 1 dip.
dip
perldoc dip
man 1 dip
dip is a dynamic instrumentation framework for troubleshooting Perl programs in real time. dip can provide fine-grained information, such as a log of the arguments with which a specific function is being called.
Conceptually, dip sits on top of Aspect and uses pointcuts and advice - to use Aspect-oriented programming jargon - to define dynamic instrumentation. These instruments are applied to the program from the outside, without having to change the program code at all. While most dip scripts will consist of aspect-oriented instrumentation, they can also use the full power of Perl.
dip aims to bring some of the power of DTrace to perl. Therefore it is useful to stick to DTrace terminology. dip pointcuts resemble DTrace "probes"; dip advice resembles DTrace "actions".
Whenever the condition for a probe is met, the associated action is executed; the probe "fires". A typical probe might fire when a certain function is entered or exited. The probe's action may analyze the run-time situation by accessing the call stack and context variables and evaluating expressions; it can then print out or log some information, record it in a database, or modify variables - an action is, after all, pure Perl code. Using variables allows probes to pass information to each other, allowing them to cooperatively analyze the correlation of different events. For example, a probe that fires when a function is entered could record the current time; another probe that fires when that function is exited could record how much time the function took.
Because of the nature of Aspect-oriented programming in Perl, you only pay for what you use. When probes are defined, all existing possible locations for running the action are examined, and the probe is only activated for those locations that match the probe's condition.
At the end of your program run, during END time, all aggregators - see below - will dump their results. Also any other hashes you have written to in your dip scripts will be dumped if they are declared as our variables.
END
our
For example, if you simply wanted to know which kinds of objects have been instantiated at least once, you could use:
our %c; before { $c{total}++ } call qr/::new$/
and then %c will be dumped.
%c
dip provides aggregating functions that help in understanding a set of data. You can keep counts of occurrences, or quantize data, much like with DTrace.
The quantize aggregating function generates a power-of-two distribution - see its documentation.
quantize
Remembers the dip script given on the command-line so we can run it in instrument(). Complains if there was no dip script. The --delay option is passed in this way as well.
instrument()
--delay
Evaluates the dip script we remembered in import() using _eval_code(). Dies if there was a problem evaluating it.
import()
_eval_code()
Normally this function will be called automatically during INIT time, but you can delay by giving the --delay option to dip; you would use this if your program loads other code at runtime - using do(), for example - that needs to be instrumented as well. In that case you have manually activate the instrumentation using:
INIT
do()
$dip::dip && $dip::dip->();
Convenience function that takes a filename and evaluates the contents of the file using _eval_code(). This is what dip -s uses. For example:
dip -s
dip -s myscript.dip myapp.pl
is more or less turned into:
dip -e 'run q!$file!' myapp.pl
Returns a concise stack trace. Takes an argument of how many levels deep the stack trace should be; the default is 20 levels. Stack frames that point to a package name in the Aspect:: or dip namespace are omitted.
Aspect::
Example: count how many times a XML::LibXML::NodeList object is created, and keep a separate counter for each place it is created from, remembering three stack frames for each place:
XML::LibXML::NodeList
before { count "constructor", ARGS(0), ustack(3) } call 'XML::LibXML::NodeList::new'
Returns what Carp's cluck() would return, again with Aspect:: and dip namespaces omitted.
cluck()
Returns what Carp's longmess() would return, again with Aspect:: and dip namespaces omitted.
longmess()
This aggregator function takes a counter name and a value and keeps a count of how often this value was seen for this counter.
You can pass several values; they will be concatenated using newlines. See the example for ustack().
ustack()
Example: For each class, count how many objects are created. Also keep a total count.
before { count("constructor", ARGS(0)); $c{total}++ } call qr/::new$/
Convenience method to dump a variable like Data::Dumper does.
Example: Show all requests a Dancer web application handles:
before { dump_var ARGS(1) } call 'Dancer::Handler::handle_request'
Convenience function to right-trim a string.
Convenience function that, if given a string - for example, a package name -, just returns the string, but if given an object, it returns that object's class.
Useful if objects you want to instrument are sometimes created by calling new() on existing objects:
new()
before { count("constructor", rref ARGS(0)) } call qr/::new$/
Convenience function to access the arguments of a function that you are instrumenting. ARGS(0), for example, returns the first argument. You can use several argument indices; in this case the indicated function arguments will be stringified and concatenated with a space.
ARGS(0)
ARGS(0) is equivalent to $_->{args}[0]; ARGS(1,2) is equivalent to join ' ' => ARGS(0), ARGS(1) - see Aspect for the kind of context information that is passed to advice code.
$_->{args}[0]
ARGS(1,2)
join ' ' => ARGS(0), ARGS(1)
For example:
# print SQL statements as they are prepared by DBI before { print ARGS(1) } call qr/DBI::.*::prepare/
This aggregator function takes a name, or an reference to a list of names, and a value. For each name, it keeps track of a power-of-two frequency distribution of the values of the specified expressions. Increments the value in the highest power-of-two bucket that is less than the specified expression.
If a name is an array reference itself, the array elements are joined by single spaces. So you can write:
quantize [ 'all', [ ARGS(1)->method, ARGS(1)->request_uri ] ] => ...
Suppose ARGS(1) is an HTTP requst, then this builds two distributions, one called all, and another that consists of the method and URI of the request, for example GET /login.
ARGS(1)
all
GET /login
The gettimeofday() function from Time::HiRes is available to dip scripts.
gettimeofday()
The tv_interval() function from Time::HiRes is available to dip scripts.
tv_interval()
Color constants from Term::ANSIColor are available to dip scripts. For example:
before { say RED, ARGS(1), RESET } call qr/DBI::.*::prepare/
prints each DBI query in red text as it is prepared.
Is called for advice given on the command line and dip scripts evaluated by run().
run()
The following code is prepended to the code:
use strict; use warnings; use 5.10.0;
so that dip scripts are properly checked and say() is available.
say()
This is a helper function used by the dip program to pass options to dip scripts.
When calling the dip program, you can pass values to the instrumentation code using the --define command-line option. This option can be given several times and each time expects an argument of the form key=value. These arguments are available to the instrumentation code in %opt.
--define
key=value
%opt
Example:
$ dip count-uri-new-with-ustack.dip --define depth=5
Would work with this instrumentation code:
my $depth = $opt{depth} // 5; before { count constructor => ustack($depth) } call 'URI::new' & cflow qr/Dancer/;
If the --verbose option was given in the dip program invocation, that option will be in %opt as well.
--verbose
dip scripts are just Perl code and as such can use any helper module. For example, you might use the following code at the beginning of your dip scripts:
use strict; use warnings;
The p() function from Data::Printer can be useful to dip scripts.
p()
# Print a stack trace every time the name is changed, # except when reading from the database. use DDP; before { print longmess(p $_->{args}[1]) if $_->{args}[1] } call "MyObj::name" & !cflow("MyObj::read")
The ONCE() function provided by once can be used to run advice only the first time the relevant join point is encountered. For example:
ONCE()
# Print Dancer's route registry, but only once, since it's # not going to change. use once; use DDP; before { ONCE { p(Dancer::App->current->registry) } } call "Dancer::Handler::handle_request"
The following person is the author of all the files provided in this distribution unless explicitly noted otherwise.
Marcel Gruenauer <marcel@cpan.org>, http://perlservices.at
The following copyright notice applies to all the files provided in this distribution, including binary files, unless explicitly noted otherwise.
This software is copyright (c) 2011 by Marcel Gruenauer.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
To install dip, copy and paste the appropriate command in to your terminal.
cpanm
cpanm dip
CPAN shell
perl -MCPAN -e shell install dip
For more information on module installation, please visit the detailed CPAN module installation guide.