| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Test2::Tools::MemoryCycle; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
191485
|
use strict; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
25
|
|
|
4
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
17
|
|
|
5
|
1
|
|
|
1
|
|
19
|
use 5.008004; |
|
|
1
|
|
|
|
|
3
|
|
|
6
|
1
|
|
|
1
|
|
426
|
use Devel::Cycle qw( find_cycle ); |
|
|
1
|
|
|
|
|
2730
|
|
|
|
1
|
|
|
|
|
4
|
|
|
7
|
1
|
|
|
1
|
|
132
|
use Test2::API qw( context ); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
36
|
|
|
8
|
1
|
|
|
1
|
|
4
|
use Exporter qw( import ); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
350
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# ABSTRACT: Check for memory leaks and circular memory references |
|
11
|
|
|
|
|
|
|
our $VERSION = '0.01'; # VERSION |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our @EXPORT = qw( memory_cycle_ok ); |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# Adapted from Test::Memory::Cycle for Test2::API |
|
17
|
|
|
|
|
|
|
sub memory_cycle_ok { |
|
18
|
3
|
|
|
3
|
1
|
37890
|
my $ref = shift; |
|
19
|
3
|
|
|
|
|
6
|
my $msg = shift; |
|
20
|
|
|
|
|
|
|
|
|
21
|
3
|
|
100
|
|
|
18
|
$msg ||= 'no memory cycle'; |
|
22
|
|
|
|
|
|
|
|
|
23
|
3
|
|
|
|
|
5
|
my $cycle_no = 0; |
|
24
|
3
|
|
|
|
|
5
|
my @diags; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# Callback function that is called once for each memory cycle found. |
|
27
|
|
|
|
|
|
|
my $callback = sub { |
|
28
|
1
|
|
|
1
|
|
134
|
my $path = shift; |
|
29
|
1
|
|
|
|
|
1
|
$cycle_no++; |
|
30
|
1
|
|
|
|
|
18
|
push( @diags, "Cycle #$cycle_no" ); |
|
31
|
1
|
|
|
|
|
5
|
foreach (@$path) { |
|
32
|
3
|
|
|
|
|
5
|
my ($type,$index,$ref,$value) = @$_; |
|
33
|
|
|
|
|
|
|
|
|
34
|
3
|
|
|
|
|
5
|
my $str = 'Unknown! This should never happen!'; |
|
35
|
3
|
|
|
|
|
6
|
my $refdisp = _ref_shortname( $ref ); |
|
36
|
3
|
|
|
|
|
5
|
my $valuedisp = _ref_shortname( $value ); |
|
37
|
|
|
|
|
|
|
|
|
38
|
3
|
100
|
|
|
|
9
|
$str = sprintf( ' %s => %s', $refdisp, $valuedisp ) if $type eq 'SCALAR'; |
|
39
|
3
|
50
|
|
|
|
5
|
$str = sprintf( ' %s => %s', "${refdisp}->[$index]", $valuedisp ) if $type eq 'ARRAY'; |
|
40
|
3
|
100
|
|
|
|
7
|
$str = sprintf( ' %s => %s', "${refdisp}->{$index}", $valuedisp ) if $type eq 'HASH'; |
|
41
|
3
|
100
|
|
|
|
7
|
$str = sprintf( ' closure %s => %s', "${refdisp}, $index", $valuedisp ) if $type eq 'CODE'; |
|
42
|
|
|
|
|
|
|
|
|
43
|
3
|
|
|
|
|
7
|
push( @diags, $str ); |
|
44
|
|
|
|
|
|
|
} |
|
45
|
3
|
|
|
|
|
12
|
}; |
|
46
|
|
|
|
|
|
|
|
|
47
|
3
|
|
|
|
|
9
|
find_cycle( $ref, $callback ); |
|
48
|
3
|
|
|
|
|
137
|
my $ok = !$cycle_no; |
|
49
|
|
|
|
|
|
|
|
|
50
|
3
|
|
|
|
|
7
|
my $ctx = context(); |
|
51
|
3
|
100
|
|
|
|
231
|
if($ok) { |
|
52
|
2
|
|
|
|
|
11
|
$ctx->pass_and_release($msg); |
|
53
|
|
|
|
|
|
|
} else { |
|
54
|
1
|
|
|
|
|
4
|
$ctx->fail_and_release($msg, @diags); |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
|
|
57
|
3
|
|
|
|
|
475
|
return $ok; |
|
58
|
|
|
|
|
|
|
} # memory_cycle_ok |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my %shortnames; |
|
61
|
|
|
|
|
|
|
my $new_shortname = "A"; |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub _ref_shortname { |
|
64
|
6
|
|
|
6
|
|
8
|
my $ref = shift; |
|
65
|
6
|
|
|
|
|
9
|
my $refstr = "$ref"; |
|
66
|
6
|
|
|
|
|
8
|
my $refdisp = $shortnames{ $refstr }; |
|
67
|
6
|
100
|
|
|
|
11
|
if ( !$refdisp ) { |
|
68
|
3
|
|
|
|
|
7
|
my $sigil = ref($ref) . " "; |
|
69
|
3
|
100
|
|
|
|
6
|
$sigil = '%' if $sigil eq "HASH "; |
|
70
|
3
|
50
|
|
|
|
6
|
$sigil = '@' if $sigil eq "ARRAY "; |
|
71
|
3
|
100
|
|
|
|
4
|
$sigil = '$' if $sigil eq "REF "; |
|
72
|
3
|
100
|
|
|
|
15
|
$sigil = '&' if $sigil eq "CODE "; |
|
73
|
3
|
|
|
|
|
8
|
$refdisp = $shortnames{ $refstr } = $sigil . $new_shortname++; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
|
|
|
|
|
|
|
|
76
|
6
|
|
|
|
|
10
|
return $refdisp; |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
1; |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
__END__ |