]> git.vpit.fr Git - perl/modules/Test-Valgrind.git/blob - lib/Test/Valgrind.pm
a3fae2fa5d14efa491a295531b59dec6a4c4b1c8
[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 - Generate suppressions, analyse and test any command with valgrind.
9
10 =head1 VERSION
11
12 Version 1.18
13
14 =cut
15
16 our $VERSION = '1.18';
17
18 =head1 SYNOPSIS
19
20     # From the command-line
21     perl -MTest::Valgrind leaky.pl
22
23     # From the command-line, snippet style
24     perl -MTest::Valgrind -e 'leaky()'
25
26     # In a test file
27     use Test::More;
28     eval 'use Test::Valgrind';
29     plan skip_all => 'Test::Valgrind is required to test your distribution with valgrind' if $@;
30     leaky();
31
32     # In all the test files of a directory
33     prove --exec 'perl -Iblib/lib -Iblib/arch -MTest::Valgrind' t/*.t
34
35 =head1 DESCRIPTION
36
37 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 for memory errors and leaks.
38 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>.
39 The actual run will then take place, and tests will be passed or failed according to the result of the analysis.
40
41 The complete API is much more versatile than this.
42 By declaring an appropriate L<Test::Valgrind::Command> class, you can run any executable (that is, not only Perl scripts) under valgrind, generate the corresponding suppressions on-the-fly and convert the analysis result to TAP output so that it can be incorporated into your project's testsuite.
43 If you're not interested in producing TAP, you can output the results in whatever format you like (for example HTML pages) by defining your own L<Test::Valgrind::Action> class.
44
45 Due to the nature of perl's memory allocator, this module can't track leaks of Perl objects.
46 This includes non-mortalized scalars and memory cycles.
47 However, it can track leaks of chunks of memory allocated in XS extensions with C<Newx> and friends or C<malloc>.
48 As such, it's complementary to the other very good leak detectors listed in the L</SEE ALSO> section.
49
50 =head1 METHODS
51
52 =head2 C<analyse>
53
54     Test::Valgrind->analyse(%options);
55
56 Run a C<valgrind> analysis configured by C<%options> :
57
58 =over 4
59
60 =item *
61
62 C<< command => $command >>
63
64 The L<Test::Valgrind::Command> object (or class name) to use.
65
66 Defaults to L<Test::Valgrind::Command::PerlScript>.
67
68 =item *
69
70 C<< tool => $tool >>
71
72 The L<Test::Valgrind::Tool> object (or class name) to use.
73
74 Defaults to L<Test::Valgrind::Tool::memcheck>.
75
76 =item *
77
78 C<< action => $action >>
79
80 The L<Test::Valgrind::Action> object (or class name) to use.
81
82 Defaults to L<Test::Valgrind::Action::Test>.
83
84 =item *
85
86 C<< file => $file >>
87
88 The file name of the script to analyse.
89
90 Ignored if you supply your own custom C<command>, but mandatory otherwise.
91
92 =item *
93
94 C<< callers => $number >>
95
96 Specify the maximum stack depth studied when valgrind encounters an error.
97 Raising this number improves granularity.
98
99 Ignored if you supply your own custom C<tool>, otherwise defaults to C<24> (the maximum allowed by C<valgrind>).
100
101 =item *
102
103 C<< diag => $bool >>
104
105 If true, print the output of the test script as diagnostics.
106
107 Ignored if you supply your own custom C<action>, otherwise defaults to false.
108
109 =item *
110
111 C<< regen_def_supp => $bool >>
112
113 If true, forcefully regenerate the default suppression file.
114
115 Defaults to false.
116
117 =item *
118
119 C<< no_def_supp => $bool >>
120
121 If true, do not use the default suppression file.
122
123 Defaults to false.
124
125 =item *
126
127 C<< allow_no_supp => $bool >>
128
129 If true, force running the analysis even if the suppression files do not refer to any C<perl>-related symbol.
130
131 Defaults to false.
132
133 =item *
134
135 C<< extra_supps => \@files >>
136
137 Also use suppressions from C<@files> besides C<perl>'s.
138
139 Defaults to empty.
140
141 =back
142
143 =cut
144
145 sub _croak {
146  require Carp;
147  Carp::croak(@_);
148 }
149
150 my %skippable_errors = (
151  session => [
152   'Empty valgrind candidates list',
153   'No appropriate valgrind executable could be found',
154  ],
155  action  => [ ],
156  tool    => [ ],
157  command => [ ],
158  run     => [
159   'No compatible suppressions available',
160  ],
161 );
162
163 my %filter_errors;
164
165 for my $obj (keys %skippable_errors) {
166  my @errors = @{$skippable_errors{$obj} || []};
167  if (@errors) {
168   my $rxp   = join '|', @errors;
169   $rxp      = qr/($rxp)\s+at.*/;
170   $filter_errors{$obj} = sub {
171    my ($err) = @_;
172    if ($err =~ /$rxp/) {
173     return ($1, 1);
174    } else {
175     return ($err, 0);
176    }
177   };
178  } else {
179   $filter_errors{$obj} = sub {
180    return ($_[0], 0);
181   };
182  }
183 }
184
185 sub _default_abort {
186  my ($err) = @_;
187
188  require Test::Builder;
189  my $tb   = Test::Builder->new;
190  my $plan = $tb->has_plan;
191  if (defined $plan) {
192   $tb->BAIL_OUT($err);
193   return 255;
194  } else {
195   $tb->skip_all($err);
196   return 0;
197  }
198 }
199
200 sub analyse {
201  shift;
202
203  my %args = @_;
204
205  my $instanceof = sub {
206   require Scalar::Util;
207   Scalar::Util::blessed($_[0]) && $_[0]->isa($_[1]);
208  };
209
210  my $tool = delete $args{tool};
211  unless ($tool->$instanceof('Test::Valgrind::Tool')) {
212   my $callers = delete $args{callers} || 24;
213   $callers = 24 if $callers <= 0;
214   require Test::Valgrind::Tool;
215   local $@;
216   $tool = eval {
217    Test::Valgrind::Tool->new(
218     tool    => $tool || 'memcheck',
219     callers => $callers,
220    );
221   };
222   unless ($tool) {
223    my ($err, $skippable) = $filter_errors{tool}->($@);
224    _croak($err) unless $skippable;
225    return _default_abort($err);
226   }
227  }
228
229  require Test::Valgrind::Session;
230  my $sess = eval {
231   Test::Valgrind::Session->new(
232    min_version => $tool->requires_version,
233    map { $_ => delete $args{$_} } qw<
234     regen_def_supp
235     no_def_supp
236     allow_no_supp
237     extra_supps
238    >
239   );
240  };
241  unless ($sess) {
242   my ($err, $skippable) = $filter_errors{session}->($@);
243   _croak($err) unless $skippable;
244   return _default_abort($err);
245  }
246
247  my $action = delete $args{action};
248  unless ($action->$instanceof('Test::Valgrind::Action')) {
249   require Test::Valgrind::Action;
250   local $@;
251   $action = eval {
252    Test::Valgrind::Action->new(
253     action => $action || 'Test',
254     diag   => delete $args{diag},
255    );
256   };
257   unless ($action) {
258    my ($err, $skippable) = $filter_errors{action}->($@);
259    _croak($err) unless $skippable;
260    return _default_abort($err);
261   }
262  }
263
264  my $cmd = delete $args{command};
265  unless ($cmd->$instanceof('Test::Valgrind::Command')) {
266   require Test::Valgrind::Command;
267   local $@;
268   $cmd = eval {
269    Test::Valgrind::Command->new(
270     command => $cmd || 'PerlScript',
271     file    => delete $args{file},
272     args    => [ '-MTest::Valgrind=run,1' ],
273    );
274   };
275   unless ($cmd) {
276    my ($err, $skippable) = $filter_errors{command}->($@);
277    _croak($err) unless $skippable;
278    $action->abort($sess, $err);
279    return $action->status($sess);
280   }
281  }
282
283  {
284   local $@;
285   eval {
286    $sess->run(
287     command => $cmd,
288     tool    => $tool,
289     action  => $action,
290    );
291    1
292   } or do {
293    my ($err, $skippable) = $filter_errors{run}->($@);
294    if ($skippable) {
295     $action->abort($sess, $err);
296     return $action->status($sess);
297    } else {
298     require Test::Valgrind::Report;
299     $action->report($sess, Test::Valgrind::Report->new_diag($@));
300    }
301   }
302  }
303
304  my $status = $sess->status;
305  $status = 255 unless defined $status;
306
307  return $status;
308 }
309
310 =head2 C<import>
311
312     use Test::Valgrind %options;
313
314 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 first caller context that looks like a script.
315 When the analysis ends, it exits with the status returned by the action (for the default TAP-generator action, it's the number of failed tests).
316
317 In the child process, it just C<return>s so that the calling code is actually run under C<valgrind>, albeit two side-effects :
318
319 =over 4
320
321 =item *
322
323 L<Perl::Destruct::Level> is loaded and the destruction level is set to C<3>.
324
325 =item *
326
327 Autoflush on C<STDOUT> is turned on.
328
329 =back
330
331 =cut
332
333 # We use as little modules as possible in run mode so that they don't pollute
334 # the analysis. Hence all the requires.
335
336 my $run;
337
338 sub import {
339  my $class = shift;
340  $class = ref($class) || $class;
341
342  _croak('Optional arguments must be passed as key => value pairs') if @_ % 2;
343  my %args = @_;
344
345  if (defined delete $args{run} or $run) {
346   require Perl::Destruct::Level;
347   Perl::Destruct::Level::set_destruct_level(3);
348   {
349    my $oldfh = select STDOUT;
350    $|++;
351    select $oldfh;
352   }
353   $run = 1;
354   return;
355  }
356
357  my $file = delete $args{file};
358  unless (defined $file) {
359   my ($next, $last_pm);
360   for (my $l = 0; 1; ++$l) {
361    $next = (caller $l)[1];
362    last unless defined $next;
363    next if $next =~ /^\s*\(\s*eval\s*\d*\s*\)\s*$/;
364    if ($next =~ /\.pmc?$/) {
365     $last_pm = $next;
366    } else {
367     $file = $next;
368     last;
369    }
370   }
371   $file = $last_pm unless defined $file;
372  }
373
374  unless (defined $file) {
375   require Test::Builder;
376   Test::Builder->new->diag('Couldn\'t find a valid source file');
377   return;
378  }
379
380  if ($file ne '-e') {
381   exit $class->analyse(
382    file => $file,
383    %args,
384   );
385  }
386
387  require File::Temp;
388  my $tmp = File::Temp->new;
389
390  require Filter::Util::Call;
391  Filter::Util::Call::filter_add(sub {
392   my $status = Filter::Util::Call::filter_read();
393   if ($status > 0) {
394    print $tmp $_;
395   } elsif ($status == 0) {
396    close $tmp;
397    my $code = $class->analyse(
398     file => $tmp->filename,
399     %args,
400    );
401    exit $code;
402   }
403   $status;
404  });
405 }
406
407 =head1 VARIABLES
408
409 =head2 C<$dl_unload>
410
411 When set to true, all dynamic extensions that were loaded during the analysis will be unloaded at C<END> time by L<DynaLoader/dl_unload_file>.
412
413 Since this obfuscates error stack traces, it's disabled by default.
414
415 =cut
416
417 our $dl_unload;
418
419 END {
420  if ($dl_unload and $run and eval { require DynaLoader; 1 }) {
421   my @rest;
422   DynaLoader::dl_unload_file($_) or push @rest, $_ for @DynaLoader::dl_librefs;
423   @DynaLoader::dl_librefs = @rest;
424  }
425 }
426
427 =head1 CAVEATS
428
429 Perl 5.8 is notorious for leaking like there's no tomorrow, so the suppressions are very likely not to be complete on it.
430 You also have a better chance to get more accurate results if your perl is built with debugging enabled.
431 Using the latest C<valgrind> available will also help.
432
433 This module is not really secure.
434 It's definitely not taint safe.
435 That shouldn't be a problem for test files.
436
437 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.
438
439 =head1 DEPENDENCIES
440
441 L<XML::Twig>, L<File::HomeDir>, L<Env::Sanctify>, L<Perl::Destruct::Level>.
442
443 =head1 SEE ALSO
444
445 All the C<Test::Valgrind::*> API, including L<Test::Valgrind::Command>, L<Test::Valgrind::Tool>, L<Test::Valgrind::Action> and L<Test::Valgrind::Session>.
446
447 The C<valgrind(1)> man page.
448
449 L<Test::LeakTrace>.
450
451 L<Devel::Leak>, L<Devel::LeakTrace>, L<Devel::LeakTrace::Fast>.
452
453 =head1 AUTHOR
454
455 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
456
457 You can contact me by mail or on C<irc.perl.org> (vincent).
458
459 =head1 BUGS
460
461 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>.
462 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
463
464 =head1 SUPPORT
465
466 You can find documentation for this module with the perldoc command.
467
468     perldoc Test::Valgrind
469
470 =head1 ACKNOWLEDGEMENTS
471
472 RafaE<euml>l Garcia-Suarez, for writing and instructing me about the existence of L<Perl::Destruct::Level> (Elizabeth Mattijsen is a close second).
473
474 H.Merijn Brand, for daring to test this thing.
475
476 David Cantrell, for providing shell access to one of his smokers where the tests were failing.
477
478 The Debian-perl team, for offering all the feedback they could regarding the build issues they met.
479
480 All you people that showed interest in this module, which motivated me into completely rewriting it.
481
482 =head1 COPYRIGHT & LICENSE
483
484 Copyright 2008,2009,2010,2011,2013,2015,2016 Vincent Pit, all rights reserved.
485
486 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
487
488 =cut
489
490 1; # End of Test::Valgrind