line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Devel::TimeStats; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.04'; |
4
|
|
|
|
|
|
|
|
5
|
3
|
|
|
3
|
|
68505
|
use Moo; |
|
3
|
|
|
|
|
67425
|
|
|
3
|
|
|
|
|
20
|
|
6
|
3
|
|
|
3
|
|
10063
|
use namespace::autoclean; |
|
3
|
|
|
|
|
65101
|
|
|
3
|
|
|
|
|
20
|
|
7
|
3
|
|
|
3
|
|
5309
|
use Time::HiRes qw/gettimeofday tv_interval/; |
|
3
|
|
|
|
|
5568
|
|
|
3
|
|
|
|
|
30
|
|
8
|
3
|
|
|
3
|
|
6332
|
use Text::UnicodeTable::Simple; |
|
3
|
|
|
|
|
148084
|
|
|
3
|
|
|
|
|
147
|
|
9
|
3
|
|
|
3
|
|
3524
|
use Term::ExtendedColor qw(:all); |
|
3
|
|
|
|
|
7606
|
|
|
3
|
|
|
|
|
939
|
|
10
|
3
|
|
|
3
|
|
2324
|
use Tree::Simple qw/use_weak_refs/; |
|
3
|
|
|
|
|
6285
|
|
|
3
|
|
|
|
|
25
|
|
11
|
3
|
|
|
3
|
|
3372
|
use Tree::Simple::Visitor::FindByUID; |
|
3
|
|
|
|
|
8328
|
|
|
3
|
|
|
|
|
4448
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
has enable => (is => 'rw', required => 1, default => sub{ 1 }); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
has tree => ( |
16
|
|
|
|
|
|
|
is => 'ro', |
17
|
|
|
|
|
|
|
required => 1, |
18
|
|
|
|
|
|
|
default => sub{ Tree::Simple->new({t => [gettimeofday]}) }, |
19
|
|
|
|
|
|
|
handles => [qw/ accept traverse /], |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
has stack => ( |
22
|
|
|
|
|
|
|
is => 'ro', |
23
|
|
|
|
|
|
|
required => 1, |
24
|
|
|
|
|
|
|
lazy => 1, |
25
|
|
|
|
|
|
|
default => sub { [ shift->tree ] } |
26
|
|
|
|
|
|
|
); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
has color_map => ( |
29
|
|
|
|
|
|
|
is => 'ro', |
30
|
|
|
|
|
|
|
isa => sub{ ref $_ eq 'HASH' }, |
31
|
|
|
|
|
|
|
default => sub{{ |
32
|
|
|
|
|
|
|
'0.01' => 'yellow3', |
33
|
|
|
|
|
|
|
'0.05' => 'yellow1', |
34
|
|
|
|
|
|
|
'0.1' => 'red3', |
35
|
|
|
|
|
|
|
'0.5' => 'red1', |
36
|
|
|
|
|
|
|
}} |
37
|
|
|
|
|
|
|
); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
has percentage_decimal_precision => (is => 'ro', required => 1, default => sub { 0 } ); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub profile { |
43
|
28
|
|
|
28
|
1
|
3766
|
my $self = shift; |
44
|
|
|
|
|
|
|
|
45
|
28
|
100
|
|
|
|
118
|
return unless $self->enable; |
46
|
|
|
|
|
|
|
|
47
|
27
|
|
|
|
|
34
|
my %params; |
48
|
27
|
100
|
|
|
|
84
|
if (@_ <= 1) { |
|
|
50
|
|
|
|
|
|
49
|
6
|
|
50
|
|
|
24
|
$params{comment} = shift || ""; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
elsif (@_ % 2 != 0) { |
52
|
0
|
|
|
|
|
0
|
die "profile() requires a single comment parameter or a list of name-value pairs; found " |
53
|
|
|
|
|
|
|
. (scalar @_) . " values: " . join(", ", @_); |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
else { |
56
|
21
|
|
|
|
|
71
|
(%params) = @_; |
57
|
21
|
|
100
|
|
|
120
|
$params{comment} ||= ""; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
27
|
|
|
|
|
33
|
my $parent; |
61
|
|
|
|
|
|
|
my $prev; |
62
|
27
|
|
|
|
|
60
|
my $t = [ gettimeofday ]; |
63
|
27
|
|
|
|
|
769
|
my $stack = $self->stack; |
64
|
|
|
|
|
|
|
|
65
|
27
|
100
|
|
|
|
270
|
if ($params{end}) { |
66
|
|
|
|
|
|
|
# parent is on stack; search for matching block and splice out |
67
|
8
|
|
|
|
|
37
|
for (my $i = $#{$stack}; $i > 0; $i--) { |
|
8
|
|
|
|
|
37
|
|
68
|
12
|
100
|
|
|
|
70
|
if ($stack->[$i]->getNodeValue->{action} eq $params{end}) { |
69
|
8
|
|
|
|
|
49
|
my ($node) = splice(@{$stack}, $i, 1); |
|
8
|
|
|
|
|
19
|
|
70
|
|
|
|
|
|
|
# Adjust elapsed on partner node |
71
|
8
|
|
|
|
|
22
|
my $v = $node->getNodeValue; |
72
|
8
|
|
|
|
|
56
|
$v->{elapsed} = tv_interval($v->{t}, $t); |
73
|
8
|
|
|
|
|
81
|
return $node->getUID; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
# if partner not found, fall through to treat as non-closing call |
77
|
|
|
|
|
|
|
} |
78
|
19
|
100
|
|
|
|
45
|
if ($params{parent}) { |
79
|
|
|
|
|
|
|
# parent is explicitly defined |
80
|
1
|
|
|
|
|
6
|
$prev = $parent = $self->_get_uid($params{parent}); |
81
|
|
|
|
|
|
|
} |
82
|
19
|
100
|
|
|
|
67
|
if (!$parent) { |
83
|
|
|
|
|
|
|
# Find previous node, which is either previous sibling or parent, for ref time. |
84
|
18
|
50
|
|
|
|
83
|
$prev = $parent = $stack->[-1] or return undef; |
85
|
18
|
|
|
|
|
69
|
my $n = $parent->getChildCount; |
86
|
18
|
100
|
|
|
|
153
|
$prev = $parent->getChild($n - 1) if $n > 0; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
19
|
|
100
|
|
|
140
|
my $node = Tree::Simple->new({ |
90
|
|
|
|
|
|
|
action => $params{begin} || "", |
91
|
|
|
|
|
|
|
t => $t, |
92
|
|
|
|
|
|
|
elapsed => tv_interval($prev->getNodeValue->{t}, $t), |
93
|
|
|
|
|
|
|
comment => $params{comment}, |
94
|
|
|
|
|
|
|
}); |
95
|
19
|
100
|
|
|
|
918
|
$node->setUID($params{uid}) if $params{uid}; |
96
|
|
|
|
|
|
|
|
97
|
19
|
|
|
|
|
61
|
$parent->addChild($node); |
98
|
19
|
100
|
|
|
|
1778
|
push(@{$stack}, $node) if $params{begin}; |
|
9
|
|
|
|
|
20
|
|
99
|
|
|
|
|
|
|
|
100
|
19
|
|
|
|
|
55
|
return $node->getUID; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub created { |
104
|
1
|
|
|
1
|
1
|
829
|
return @{ shift->{tree}->getNodeValue->{t} }; |
|
1
|
|
|
|
|
6
|
|
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub elapsed { |
108
|
1
|
|
|
1
|
1
|
3315
|
return tv_interval(shift->{tree}->getNodeValue->{t}); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub report { |
112
|
4
|
|
|
4
|
1
|
39
|
my $self = shift; |
113
|
|
|
|
|
|
|
|
114
|
4
|
|
|
|
|
8
|
my $total_duration = 0; |
115
|
4
|
|
|
|
|
19
|
$total_duration += $_->getNodeValue->{elapsed} for $self->tree->getAllChildren; |
116
|
|
|
|
|
|
|
|
117
|
4
|
|
|
|
|
88
|
my $t = Text::UnicodeTable::Simple->new(ansi_color => 1); |
118
|
4
|
|
|
|
|
108
|
$t->set_header(qw/ Action Time % /); |
119
|
|
|
|
|
|
|
|
120
|
4
|
|
|
|
|
604
|
my @results; |
121
|
|
|
|
|
|
|
$self->traverse( |
122
|
|
|
|
|
|
|
sub { |
123
|
19
|
|
|
19
|
|
1690
|
my $action = shift; |
124
|
19
|
|
|
|
|
54
|
my $stat = $action->getNodeValue; |
125
|
19
|
100
|
100
|
|
|
94
|
my @r = ( $action->getDepth, |
|
|
100
|
100
|
|
|
|
|
|
|
100
|
|
|
|
|
|
126
|
|
|
|
|
|
|
($stat->{action} || "") . |
127
|
|
|
|
|
|
|
($stat->{action} && $stat->{comment} ? " " : "") . ($stat->{comment} ? '- ' . $stat->{comment} : ""), |
128
|
|
|
|
|
|
|
$stat->{elapsed}, |
129
|
|
|
|
|
|
|
$stat->{action} ? 1 : 0, |
130
|
|
|
|
|
|
|
($stat->{elapsed} * 100) / $total_duration |
131
|
|
|
|
|
|
|
); |
132
|
|
|
|
|
|
|
# Trim down any times >= 10 to avoid ugly Text::Simple line wrapping |
133
|
19
|
|
|
|
|
399
|
my $elapsed = substr(sprintf("%f", $stat->{elapsed}), 0, 8) . "s"; |
134
|
|
|
|
|
|
|
|
135
|
19
|
|
|
|
|
27
|
my $color = ''; |
136
|
19
|
|
|
|
|
23
|
foreach my $key (sort { $a <=> $b } keys %{$self->color_map}) { |
|
95
|
|
|
|
|
184
|
|
|
19
|
|
|
|
|
101
|
|
137
|
76
|
100
|
|
|
|
267
|
$color = $self->color_map->{$key} if $stat->{elapsed} >= $key; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# format % |
141
|
19
|
|
|
|
|
163
|
my $share = sprintf "%2.".$self->percentage_decimal_precision."f%%", $r[4]; |
142
|
|
|
|
|
|
|
|
143
|
19
|
|
|
|
|
23
|
my @rows; |
144
|
19
|
50
|
|
|
|
72
|
for my $value (( q{ } x $r[0] ) . $r[1], defined $r[2] ? $elapsed : '??', $share) { |
145
|
57
|
|
|
|
|
2000
|
push @rows, fg('bold', fg($color, $value)); |
146
|
|
|
|
|
|
|
} |
147
|
19
|
|
|
|
|
914
|
$t->add_row(@rows); |
148
|
|
|
|
|
|
|
|
149
|
19
|
|
|
|
|
2312
|
push(@results, \@r); |
150
|
|
|
|
|
|
|
} |
151
|
4
|
|
|
|
|
84
|
); |
152
|
4
|
100
|
|
|
|
177
|
return wantarray ? @results : $t->draw; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub _get_uid { |
156
|
1
|
|
|
1
|
|
2
|
my ($self, $uid) = @_; |
157
|
|
|
|
|
|
|
|
158
|
1
|
|
|
|
|
11
|
my $visitor = Tree::Simple::Visitor::FindByUID->new; |
159
|
1
|
|
|
|
|
44
|
$visitor->searchForUID($uid); |
160
|
1
|
|
|
|
|
12
|
$self->accept($visitor); |
161
|
1
|
|
|
|
|
1111
|
return $visitor->getResult; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub addChild { |
165
|
1
|
|
|
1
|
1
|
86
|
my $self = shift; |
166
|
1
|
|
|
|
|
2
|
my $node = $_[ 0 ]; |
167
|
|
|
|
|
|
|
|
168
|
1
|
|
|
|
|
5
|
my $stat = $node->getNodeValue; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# do we need to fake $stat->{ t } ? |
171
|
1
|
50
|
|
|
|
9
|
if( $stat->{ elapsed } ) { |
172
|
|
|
|
|
|
|
# remove the "s" from elapsed time |
173
|
1
|
|
|
|
|
6
|
$stat->{ elapsed } =~ s{s$}{}; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
1
|
|
|
|
|
22
|
$self->tree->addChild( @_ ); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub setNodeValue { |
180
|
1
|
|
|
1
|
1
|
81
|
my $self = shift; |
181
|
1
|
|
|
|
|
3
|
my $stat = $_[ 0 ]; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# do we need to fake $stat->{ t } ? |
184
|
1
|
50
|
|
|
|
6
|
if( $stat->{ elapsed } ) { |
185
|
|
|
|
|
|
|
# remove the "s" from elapsed time |
186
|
1
|
|
|
|
|
7
|
$stat->{ elapsed } =~ s{s$}{}; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
1
|
|
|
|
|
9
|
$self->tree->setNodeValue( @_ ); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub getNodeValue { |
193
|
1
|
|
|
1
|
1
|
76
|
my $self = shift; |
194
|
1
|
|
|
|
|
8
|
$self->tree->getNodeValue( @_ )->{ t }; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
1; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
__END__ |