]> git.vpit.fr Git - perl/modules/Test-Valgrind.git/blob - lib/Test/Valgrind.pm
Factor the agnostic part of Test::Valgrind->import out to a new ->analyse
[perl/modules/Test-Valgrind.git] / lib / Test / Valgrind.pm
1 package Test::Valgrind;
2
3 use strict;
4 use warnings;
5
6 =head1 NAME
7
8 Test::Valgrind - Test Perl code through valgrind.
9
10 =head1 VERSION
11
12 Version 1.00
13
14 =cut
15
16 our $VERSION = '1.00';
17
18 =head1 SYNOPSIS
19
20     # From the command-line
21     perl -MTest::Valgrind leaky.pl
22
23     # In a test file
24     use Test::More;
25     eval 'use Test::Valgrind';
26     plan skip_all => 'Test::Valgrind is required to test your distribution with valgrind' if $@;
27     ...
28
29     # In all the test files of a directory
30     prove --exec 'perl -Iblib/lib -Iblib/arch -MTest::Valgrind' t/*.t
31
32 =head1 DESCRIPTION
33
34 This module is a front-end to the C<Test::Valgrind::*> API that lets you run Perl code through the C<memcheck> tool of the C<valgrind> memory debugger, to test it for memory errors and leaks.
35 If they aren't available yet, it will first generate suppressions for the current C<perl> interpreter and store them in the portable flavour of F<~/.perl/Test-Valgrind/suppressions/$VERSION>.
36 The actual run will then take place, and tests will be passed or failed according to the result of the analysis.
37
38 Due to the nature of perl's memory allocator, this module can't track leaks of Perl objects.
39 This includes non-mortalized scalars and memory cycles. However, it can track leaks of chunks of memory allocated in XS extensions with C<Newx> and friends or C<malloc>.
40 As such, it's complementary to the other very good leak detectors listed in the L</SEE ALSO> section.
41
42 =head1 METHODS
43
44 =head2 C<analyse [ %options ]>
45
46 Run a C<valgrind> analysis configured by C<%options> :
47
48 =over 4
49
50 =item *
51
52 C<< command => $command >>
53
54 The L<Test::Valgrind::Command> object (or class name) to use.
55
56 Defaults to L<Test::Valgrind::Command::PerlScript>.
57
58 =item *
59
60 C<< tool => $tool >>
61
62 The L<Test::Valgrind::Tool> object (or class name) to use.
63
64 Defaults to L<Test::Valgrind::Tool::memcheck>.
65
66 =item *
67
68 C<< action => $action >>
69
70 The L<Test::Valgrind::Action> object (or class name) to use.
71
72 Defaults to L<Test::Valgrind::Action::Test>.
73
74 =item *
75
76 C<< file => $file >>
77
78 The file name of the script to analyse.
79
80 Ignored if you supply your own custom C<command>, but mandatory otherwise.
81
82 =item *
83
84 C<< callers => $number >>
85
86 Specify the maximum stack depth studied when valgrind encounters an error.
87 Raising this number improves granularity.
88
89 Ignored if you supply your own custom C<tool>, otherwise defaults to C<12>.
90
91 =item *
92
93 C<< diag => $bool >>
94
95 If true, print the output of the test script as diagnostics.
96
97 Ignored if you supply your own custom C<action>, otherwise defaults to false.
98
99 =item *
100
101 C<< extra_supps => \@files >>
102
103 Also use suppressions from C<@files> besides C<perl>'s.
104
105 Defaults to empty.
106
107 =item *
108
109 C<< no_def_supp => $bool >>
110
111 If true, do not use the default suppression file.
112
113 Defaults to false.
114
115 =back
116
117 =cut
118
119 sub analyse {
120  shift;
121
122  my %args = @_;
123
124  my $instanceof = sub {
125   require Scalar::Util;
126   Scalar::Util::blessed($_[0]) && $_[0]->isa($_[1]);
127  };
128
129  my $cmd = delete $args{command};
130  unless ($cmd->$instanceof('Test::Valgrind::Command')) {
131   require Test::Valgrind::Command;
132   $cmd = Test::Valgrind::Command->new(
133    command => $cmd || 'PerlScript',
134    file    => delete $args{file},
135    args    => [ '-MTest::Valgrind=run,1' ],
136   );
137  }
138
139  my $tool = delete $args{tool};
140  unless ($tool->$instanceof('Test::Valgrind::Tool')) {
141   require Test::Valgrind::Tool;
142   $tool = Test::Valgrind::Tool->new(
143    tool     => $tool || 'memcheck',
144    callers  => delete $args{callers},
145   );
146  }
147
148  my $action = delete $args{action};
149  unless ($action->$instanceof('Test::Valgrind::Action')) {
150   require Test::Valgrind::Action;
151   $action = Test::Valgrind::Action->new(
152    action => $action || 'Test',
153    diag   => delete $args{diag},
154   );
155  }
156
157  require Test::Valgrind::Session;
158  my $sess = eval {
159   Test::Valgrind::Session->new(
160    min_version => $tool->requires_version,
161    map { $_ => delete $args{$_} } qw/extra_supps no_def_supp/
162   );
163  };
164  unless ($sess) {
165   $action->abort($sess, $@);
166   return $action->status($sess);
167  }
168
169  eval {
170   $sess->run(
171    command => $cmd,
172    tool    => $tool,
173    action  => $action,
174   );
175  };
176  if ($@) {
177   require Test::Valgrind::Report;
178   $action->report($sess, Test::Valgrind::Report->new_diag($@));
179  }
180
181  my $status = $sess->status;
182  $status = 255 unless defined $status;
183
184  return $status;
185 }
186
187 =head2 C<import [ %options ]>
188
189 In the parent process, L</import> calls L</analyse> with the arguments it received itself - except that if no C<file> option was supplied, it tries to pick the highest caller context that looks like a script.
190 When the analyse finishes, it exists with the status that was returned.
191
192 In the child process, it just C<return>s so that the calling code is actually run under C<valgrind>.
193
194 =cut
195
196 # We use as little modules as possible in run mode so that they don't pollute
197 # the analysis. Hence all the requires.
198
199 my $run;
200
201 sub import {
202  my $class = shift;
203  $class = ref($class) || $class;
204
205  if (@_ % 2) {
206   require Carp;
207   Carp::croak('Optional arguments must be passed as key => value pairs');
208  }
209  my %args = @_;
210
211  if (defined delete $args{run} or $run) {
212   require Perl::Destruct::Level;
213   Perl::Destruct::Level::set_destruct_level(3);
214   {
215    my $oldfh = select STDOUT;
216    $|++;
217    select $oldfh;
218   }
219   $run = 1;
220   return;
221  }
222
223  my ($file, $pm, $next);
224  my $l = 0;
225  while ($l < 1000) {
226   $next = (caller $l++)[1];
227   last unless defined $next;
228   next if $next eq '-e' or $next =~ /^\s*\(\s*eval\s*\d*\s*\)\s*$/ or !-f $next;
229   if ($next =~ /\.pm$/) {
230    $pm   = $next;
231   } else {
232    $file = $next;
233   }
234  }
235  unless (defined($file) or defined($file = $pm)) {
236   require Test::Builder;
237   Test::Builder->new->diag('Couldn\'t find a valid source file');
238   return;
239  }
240
241  exit $class->analyse(
242   file => $file,
243   %args,
244  );
245 }
246
247 END {
248  if ($run and eval { require DynaLoader; 1 }) {
249   my @rest;
250   DynaLoader::dl_unload_file($_) or push @rest, $_ for @DynaLoader::dl_librefs;
251   @DynaLoader::dl_librefs = @rest;
252  }
253 }
254
255 =head1 CAVEATS
256
257 You can't use this module to test code given by the C<-e> command-line switch.
258
259 Perl 5.8 is notorious for leaking like there's no tomorrow, so the suppressions are very likely not to be very accurate on it. Anyhow, results will most likely be better if your perl is built with debugging enabled. Using the latest C<valgrind> available will also help.
260
261 This module is not really secure. It's definitely not taint safe. That shouldn't be a problem for test files.
262
263 What your tests output to C<STDOUT> and C<STDERR> is eaten unless you pass the C<diag> option, in which case it will be reprinted as diagnostics.
264
265 =head1 DEPENDENCIES
266
267 Valgrind 3.1.0 (L<http://valgrind.org>).
268
269 L<XML::Twig>, L<version>, L<File::HomeDir>, L<Env::Sanctify>, L<Perl::Destruct::Level>.
270
271 =head1 SEE ALSO
272
273 All the C<Test::Valgrind::*> API, including L<Test::Valgrind::Command>, L<Test::Valgrind::Tool>, L<Test::Valgrind::Action> and L<Test::Valgrind::Session>.
274
275 L<Test::LeakTrace>.
276
277 L<Devel::Leak>, L<Devel::LeakTrace>, L<Devel::LeakTrace::Fast>.
278
279 =head1 AUTHOR
280
281 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
282
283 You can contact me by mail or on C<irc.perl.org> (vincent).
284
285 =head1 BUGS
286
287 Please report any bugs or feature requests to C<bug-test-valgrind at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Valgrind>.
288 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
289
290 =head1 SUPPORT
291
292 You can find documentation for this module with the perldoc command.
293
294     perldoc Test::Valgrind
295
296 =head1 ACKNOWLEDGEMENTS
297
298 RafaĆ«l Garcia-Suarez, for writing and instructing me about the existence of L<Perl::Destruct::Level> (Elizabeth Mattijsen is a close second).
299
300 H.Merijn Brand, for daring to test this thing.
301
302 All you people that showed interest in this module, which motivated me into completely rewriting it.
303
304 =head1 COPYRIGHT & LICENSE
305
306 Copyright 2008-2009 Vincent Pit, all rights reserved.
307
308 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
309
310 =cut
311
312 1; # End of Test::Valgrind