File Coverage

blib/lib/PostgreSQL/PLPerl/NYTProf.pm
Criterion Covered Total %
statement 14 34 41.1
branch 0 10 0.0
condition 0 8 0.0
subroutine 6 10 60.0
pod 0 2 0.0
total 20 64 31.2


line stmt bran cond sub pod time code
1             package PostgreSQL::PLPerl::NYTProf;
2             BEGIN {
3 1     1   734 $PostgreSQL::PLPerl::NYTProf::VERSION = '1.002';
4             }
5              
6             # vim: ts=8 sw=4 expandtab:
7              
8             =head1 NAME
9              
10             PostgreSQL::PLPerl::NYTProf - Profile PostgreSQL PL/Perl functions with Devel::NYTProf
11              
12             =head1 VERSION
13              
14             version 1.002
15              
16             =head1 SYNOPSIS
17              
18             Load via the C environment variable:
19              
20             $ PERL5OPT='-MPostgreSQL::PLPerl::NYTProf' pg_ctl restart
21              
22             or load via your C file:
23              
24             custom_variable_classes = 'plperl'
25             plperl.on_init = 'use PostgreSQL::PLPerl::NYTProf;'
26              
27             and restart the server.
28              
29             Then run some PL/Perl code:
30              
31             $ psql -c "do 'sub w { } w() for 1..100_000' language plperl" template1
32              
33             which will create a nytprof.out.I file in the C<$PGDATA> directory,
34             where I is the process id of the postgres backend.
35              
36             Finally, run C to generate a report, for example:
37              
38             $ nytprofhtml --file $PGDATA/nytprof.out.54321 --open
39              
40             =head1 DESCRIPTION
41              
42             Profile PL/Perl functions inside PostgreSQL database with L.
43              
44             PostgreSQL 9.0 or later is required.
45              
46             =head1 ENABLING
47              
48             In order to use this module you need to arrange for it to be loaded when
49             PostgreSQL initializes a Perl interpreter.
50              
51             =head2 Quick Occasional Use
52              
53             The C environment variable can be used like this:
54              
55             $ PERL5OPT='-MPostgreSQL::PLPerl::NYTProf' pg_ctl restart
56              
57             This will be effective for any pg_ctl command that restarts the postmaster
58             process, so C will work but C won't.
59              
60             The profiler will remain enabled until the the postmaster process is restarted.
61              
62             =head2 Via postgres.conf
63              
64             You can simply add a C statement to your F file:
65              
66             plperl.on_init='use PostgreSQL::PLPerl::NYTProf;'
67              
68             though I'd recommend arranging for PostgreSQL to load a separate
69             F file from same directory as your F file:
70              
71             plperl.on_init='require "plperloninit.pl";'
72              
73             then you can put whatever Perl statements you want in that file:
74              
75             use PostgreSQL::PLPerl::NYTProf;
76              
77             When it's no longer needed just comment it out by prefixing with a C<#>.
78              
79             =head1 USAGE
80              
81             By default the NYTProf profile data files will be written into the database
82             directory, alongside your F, with the process id of the backend
83             appended to the name. For example F.
84              
85             You'll get one profile data file for each database connection. You can use the
86             L utility to merge multiple data files if needed.
87              
88             To generate a report from a data file, use a command like:
89              
90             nytprofhtml --file=$PGDATA/nytprof.out.54321 --open
91              
92             =head1 INTERPRETING REPORTS
93              
94             PL/Perl functions are given names in perl that include the OID of the PL/Perl
95             function. So a function created by C would appear
96             in the reports as something like C.
97              
98             =head1 PROFILE ON DEMAND
99              
100             The instructions above enable profiling for all database sessions that use PL/Perl.
101             Instead of profiling all sessions it can be useful to have the profiler loaded
102             into the server but only enable it for particular sessions.
103              
104             You can do this by loading setting the C environment variable to
105             include the "C" option. Then, to enable profiling for a particular
106             session you just need to call the C function. For example:
107              
108             do 'DB::enable_profile' language plperl;
109              
110             See L.
111              
112             The performance impact of loading but not enabling NYTProf should be I
113             low (though I've not tried measuring it). So, while I wouldn't recommend doing
114             that on a production instance, it would be fine on a development instance.
115              
116             =head1 LIMITATIONS
117              
118             =head2 Can't use plperl and plperlu at the same time
119              
120             Postgres uses separate Perl interpreters for the plperl and plperlu languages.
121             NYTProf is not multiplicity safe (as of version 4.05). It should just profile
122             whichever language was used first and ignore the other, but there may still be
123             problems in this situation. Let me know if you encounter any odd behaviour.
124              
125             =head2 PL/Perl functions with unusual names are __ANON__
126              
127             PL/Perl functions are created as anonymous subroutines in Perl.
128             PostgreSQL::PLPerl::NYTProf arranges for them to be given names.
129             The logic currently only works for names that match C.
130              
131             =head1 SEE ALSO
132              
133             L
134              
135             =head1 AUTHOR
136              
137             B, L and L
138              
139             =head1 COPYRIGHT AND LICENSE
140              
141             Copyright (C) 2009-2010 by Tim Bunce.
142              
143             This library is free software; you can redistribute it and/or modify
144             it under the same terms as Perl itself, either Perl version 5.8.8 or,
145             at your option, any later version of Perl 5 you may have available.
146              
147             =cut
148              
149 1     1   8 use strict;
  1         1  
  1         28  
150              
151 1     1   800 use Devel::NYTProf::Core;
  1         1604  
  1         431  
152              
153             # set some default options (can be overridden via NYTPROF env var)
154             DB::set_option("endatexit", 1); # for pg 8.4
155             DB::set_option("savesrc", 1);
156             DB::set_option("addpid", 1);
157             # file defaults to nytprof.out.$pid in $PGDATA directory
158              
159             my $trace = $ENV{PLPERL_NYTPROF_TRACE} || 0;
160             my @on_init;
161             my $mkfuncsrc = "PostgreSQL::InServer::mkfuncsrc";
162              
163             if (not -f 'postgres.conf') {
164             # there's no easy way to tell that we're being loaded into the
165             # postgres server when we're loaded via PERL5OPT. This'll do:
166             warn __PACKAGE__." not running in postgres server";
167             }
168             elsif (defined &{$mkfuncsrc}) {
169             # We were probably loaded via plperloninit.pl
170             fix_mkfuncsrc();
171             }
172             else {
173             # We were probably loaded via PERL5OPT='-M...' and so we're executing very
174             # early, before mkfuncsrc has even been defined.
175             # So we need to defer wrapping it until later.
176             # We do that by wrapping PostgreSQL::InServer::Util::bootstrap
177             # But that doesn't exist yet either. Happily it will do a INIT time
178             # so we arrange to wrap it then. Got that?
179             push @on_init, sub {
180             hook_after_sub("PostgreSQL::InServer::Util::bootstrap", \&fix_mkfuncsrc);
181             };
182             }
183              
184              
185             sub fix_mkfuncsrc {
186              
187             # wrap mkfuncsrc with code that edits the returned code string
188             # such that the code will give a name to the subroutine it defines.
189              
190             hook_after_sub("PostgreSQL::InServer::mkfuncsrc", sub {
191 0     0     my ($argref, $code) = @_;
192 0           my ($name, $imports, $prolog, $src) = @$argref;
193              
194             # $code = qq[ package main; sub { $BEGIN $prolog $src } ];
195             # XXX escape $name or extract from $code and use single quotes
196 0 0 0       $code =~ s/\b sub \s* {(.*)} \s* $/sub $name { $1 }; \\&$name/sx
197             or warn "Failed to edit sub name in $code"
198             if $name =~ /^\w+$/; # XXX just sane names for now
199              
200 0           return $code;
201 0     0 0   });
202             }
203              
204              
205             sub hook_after_sub {
206 0     0 0   my ($sub, $code, $force) = @_;
207              
208 0 0         warn "Wrapping $sub\n" if $trace;
209 0   0       my $orig_sub = (defined &{$sub}) && \&{$sub};
210 0 0 0       if (not $orig_sub and not $force) {
211 0           warn "hook_after_sub: $sub isn't defined\n";
212 0           return;
213             }
214              
215             my $wrapped = sub {
216 0 0   0     warn "Wrapped $sub(@_) called\n" if $trace;
217 0           my @ret;
218 0 0         if ($orig_sub) {
219             # XXX doesn't handle context
220             # XXX the 'package main;' here is a hack to make
221             # PostgreSQL::InServer::Util::bootstrap do the right thing
222 0           @ret = do { package main;
223             BEGIN {
224 1     1   68 $main::VERSION = '1.002';
225 0           } $orig_sub->(@_) };
226             }
227 0           return $code->( [ @_ ], @ret );
228 0           };
229              
230 1     1   7 no warnings 'redefine';
  1         1  
  1         41  
231 1     1   4 no strict;
  1         2  
  1         102  
232 0           *{$sub} = $wrapped;
  0            
233             }
234              
235              
236             # --- final initialization ---
237              
238             # give the 'application' a more user-friendly name
239             $0 = "PostgreSQL Session" if $0 eq '-e';
240              
241             eval q{ INIT { $_->() for @on_init }; 1 } or die
242             if @on_init;
243              
244             require Devel::NYTProf; # init profiler - DO THIS LAST
245              
246             __END__