| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package HTTP::LoadGen; |
|
2
|
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
48894
|
use 5.008008; |
|
|
3
|
|
|
|
|
20
|
|
|
|
3
|
|
|
|
|
506
|
|
|
4
|
3
|
|
|
3
|
|
22
|
use strict; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
161
|
|
|
5
|
3
|
|
|
3
|
|
20
|
no warnings qw/uninitialized/; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
193
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
sub tlscache (); |
|
8
|
|
|
|
|
|
|
sub conncache (); |
|
9
|
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
2791
|
use HTTP::LoadGen::Run; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
BEGIN{ HTTP::LoadGen::Run::_dbg->import } |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use Coro; |
|
14
|
|
|
|
|
|
|
use Coro::Semaphore (); |
|
15
|
|
|
|
|
|
|
use Coro::Specific (); |
|
16
|
|
|
|
|
|
|
use Coro::Timer (); |
|
17
|
|
|
|
|
|
|
use Coro::Handle; |
|
18
|
|
|
|
|
|
|
use AnyEvent; |
|
19
|
|
|
|
|
|
|
use AnyEvent::TLS; |
|
20
|
|
|
|
|
|
|
use Exporter (); |
|
21
|
|
|
|
|
|
|
use Scalar::Util (); |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
{our $VERSION = '0.07';} |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
BEGIN { |
|
26
|
|
|
|
|
|
|
our %EXPORT_TAGS= |
|
27
|
|
|
|
|
|
|
( |
|
28
|
|
|
|
|
|
|
common=>[qw!loadgen threadnr done userdata options rng rnd delay |
|
29
|
|
|
|
|
|
|
register_iterator get_iterator follow_3XX!], |
|
30
|
|
|
|
|
|
|
const=>\@HTTP::LoadGen::Run::EXPORT, |
|
31
|
|
|
|
|
|
|
); |
|
32
|
|
|
|
|
|
|
my %seen; |
|
33
|
|
|
|
|
|
|
foreach my $v (values %EXPORT_TAGS) { |
|
34
|
|
|
|
|
|
|
undef @seen{@$v} if @$v; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
our @EXPORT_OK=@{$EXPORT_TAGS{all}=[keys %seen]}; |
|
37
|
|
|
|
|
|
|
} |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
use constant { |
|
40
|
|
|
|
|
|
|
TD_USER=>0, |
|
41
|
|
|
|
|
|
|
TD_RNG=>1, |
|
42
|
|
|
|
|
|
|
TD_THREADNR=>2, |
|
43
|
|
|
|
|
|
|
TD_DONE=>3, |
|
44
|
|
|
|
|
|
|
TD_CONN_CACHE=>4, |
|
45
|
|
|
|
|
|
|
TD_TLS_CACHE=>5, |
|
46
|
|
|
|
|
|
|
}; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
my $td; # thread specific data |
|
49
|
|
|
|
|
|
|
our $o; # the global config hash |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub rnd; # predeclaration |
|
52
|
|
|
|
|
|
|
sub import { |
|
53
|
|
|
|
|
|
|
my $name=shift; |
|
54
|
|
|
|
|
|
|
local *export=\&Exporter::export; |
|
55
|
|
|
|
|
|
|
Exporter::export_to_level $name, 1, $name, map { |
|
56
|
|
|
|
|
|
|
my $what=$_; local $_; |
|
57
|
|
|
|
|
|
|
if($what eq '-rand') { |
|
58
|
|
|
|
|
|
|
*CORE::GLOBAL::rand=\&rnd; |
|
59
|
|
|
|
|
|
|
(); |
|
60
|
|
|
|
|
|
|
} elsif($what eq ':all') { |
|
61
|
|
|
|
|
|
|
our %EXPORT_TAGS; |
|
62
|
|
|
|
|
|
|
unless( exists $EXPORT_TAGS{sb} ) { |
|
63
|
|
|
|
|
|
|
require HTTP::LoadGen::ScoreBoard; |
|
64
|
|
|
|
|
|
|
require HTTP::LoadGen::Logger; |
|
65
|
|
|
|
|
|
|
HTTP::LoadGen::ScoreBoard->import |
|
66
|
|
|
|
|
|
|
(@HTTP::LoadGen::ScoreBoard::EXPORT_OK); |
|
67
|
|
|
|
|
|
|
*get_logger=\&HTTP::LoadGen::Logger::get; |
|
68
|
|
|
|
|
|
|
$EXPORT_TAGS{sb}=\@HTTP::LoadGen::ScoreBoard::EXPORT_OK; |
|
69
|
|
|
|
|
|
|
$EXPORT_TAGS{log}=[qw!get_logger!]; |
|
70
|
|
|
|
|
|
|
my %seen; |
|
71
|
|
|
|
|
|
|
foreach my $v (values %EXPORT_TAGS) { |
|
72
|
|
|
|
|
|
|
undef @seen{@$v} if @$v; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
our @EXPORT_OK=@{$EXPORT_TAGS{all}=[keys %seen]}; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
$what; |
|
77
|
|
|
|
|
|
|
} else { |
|
78
|
|
|
|
|
|
|
$what; |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
} @_; |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub create_proc { |
|
84
|
|
|
|
|
|
|
my ($how_many, $init, $handler, $exit)=@_; |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
AnyEvent::detect; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
my @watcher; |
|
89
|
|
|
|
|
|
|
my %status; |
|
90
|
|
|
|
|
|
|
my $sem=Coro::Semaphore->new; |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
pipe my($r, $w); |
|
93
|
|
|
|
|
|
|
pipe my($r2, $w2); |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
for( my $i=0; $i<$how_many; $i++ ) { |
|
96
|
|
|
|
|
|
|
my $pid; |
|
97
|
|
|
|
|
|
|
select undef, undef, undef, 0.1 until defined ($pid=fork); |
|
98
|
|
|
|
|
|
|
unless($pid) { |
|
99
|
|
|
|
|
|
|
close $r; |
|
100
|
|
|
|
|
|
|
close $w2; |
|
101
|
|
|
|
|
|
|
$r2=unblock $r2; |
|
102
|
|
|
|
|
|
|
$init->($i) if $init; |
|
103
|
|
|
|
|
|
|
close $w; # signal parent |
|
104
|
|
|
|
|
|
|
$r2->readable; # wait for start signal |
|
105
|
|
|
|
|
|
|
undef $r2; |
|
106
|
|
|
|
|
|
|
my $rc=$handler->($i); |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
exit $exit->($i, $rc) if $exit; |
|
109
|
|
|
|
|
|
|
exit $rc; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
push @watcher, AE::child $pid, sub { |
|
112
|
|
|
|
|
|
|
$status{$_[0]}=[($_[1]>>8)&0xff, $_[1]&0x7f, $_[1]&0x80]; |
|
113
|
|
|
|
|
|
|
$sem->up; |
|
114
|
|
|
|
|
|
|
}; |
|
115
|
|
|
|
|
|
|
$sem->adjust(-1); |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
close $w; |
|
119
|
|
|
|
|
|
|
unblock($r)->readable; # wait for children to finish ChildInit |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
return [$w2, $sem, \@watcher, \%status]; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub start_proc { |
|
125
|
|
|
|
|
|
|
my ($arr)=@_; |
|
126
|
|
|
|
|
|
|
close $arr->[0]; |
|
127
|
|
|
|
|
|
|
$arr->[1]->down; |
|
128
|
|
|
|
|
|
|
return $arr->[3]; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub _start_thr { |
|
132
|
|
|
|
|
|
|
my ($threadnr, $sem, $handler)=@_; |
|
133
|
|
|
|
|
|
|
$sem->adjust(-1); |
|
134
|
|
|
|
|
|
|
async { |
|
135
|
|
|
|
|
|
|
$handler->(@_); |
|
136
|
|
|
|
|
|
|
$sem->up; |
|
137
|
|
|
|
|
|
|
} $threadnr; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub ramp_up { |
|
141
|
|
|
|
|
|
|
my ($procnr, $nproc, $start, $max, $duration, $handler)=@_; |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
$duration=300 if $duration<=0; |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# begin with $start (system total) threads and start over a period |
|
146
|
|
|
|
|
|
|
# of $duration seconds up to $max threads. |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $sem=Coro::Semaphore->new(1); |
|
149
|
|
|
|
|
|
|
my $initial_sleep=($nproc + $procnr - $start % $nproc) % $nproc + 1; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
my $i=$procnr; |
|
152
|
|
|
|
|
|
|
for(; $i<$start; $i+=$nproc ) { |
|
153
|
|
|
|
|
|
|
_start_thr $i, $sem, $handler; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
return $sem if $i>=$max; |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
my $interval=$duration/($max-$start); |
|
159
|
|
|
|
|
|
|
$initial_sleep*=$interval; |
|
160
|
|
|
|
|
|
|
$interval*=$nproc; |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
my $cb=Coro::rouse_cb; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
my $tm; |
|
165
|
|
|
|
|
|
|
$tm=AE::timer $initial_sleep, $interval, sub { |
|
166
|
|
|
|
|
|
|
_start_thr $i, $sem, $handler; |
|
167
|
|
|
|
|
|
|
$i+=$nproc; |
|
168
|
|
|
|
|
|
|
unless ($i<$max) { |
|
169
|
|
|
|
|
|
|
undef $tm; |
|
170
|
|
|
|
|
|
|
$cb->(); |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
}; |
|
173
|
|
|
|
|
|
|
Coro::rouse_wait; |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
return $sem; |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub tlscache () {$$td->[TD_TLS_CACHE]} |
|
179
|
|
|
|
|
|
|
sub conncache () {$$td->[TD_CONN_CACHE]} |
|
180
|
|
|
|
|
|
|
sub threadnr () {$$td->[TD_THREADNR]} |
|
181
|
|
|
|
|
|
|
sub done () : lvalue {$$td->[TD_DONE]} |
|
182
|
|
|
|
|
|
|
sub userdata () : lvalue {$$td->[TD_USER]} |
|
183
|
|
|
|
|
|
|
sub options () {$o} |
|
184
|
|
|
|
|
|
|
sub rng () : lvalue {$$td->[TD_RNG]} |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub rnd (;$) { |
|
187
|
|
|
|
|
|
|
my $rng=rng; |
|
188
|
|
|
|
|
|
|
(ref $rng eq 'CODE' ? $rng->($_[0]||1) : |
|
189
|
|
|
|
|
|
|
ref $rng ? $rng->rand($_[0]||1) : |
|
190
|
|
|
|
|
|
|
CORE::rand $_[0]); |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub delay { |
|
194
|
|
|
|
|
|
|
my ($prefix, $param)=@_; |
|
195
|
|
|
|
|
|
|
return if delete $param->{'skip'.$prefix.'delay'}; |
|
196
|
|
|
|
|
|
|
return unless exists $param->{$prefix.'delay'}; |
|
197
|
|
|
|
|
|
|
my $sec=$param->{$prefix.'delay'}; |
|
198
|
|
|
|
|
|
|
if( exists $param->{$prefix.'jitter'} ) { |
|
199
|
|
|
|
|
|
|
my $jitter=$param->{$prefix.'jitter'}; |
|
200
|
|
|
|
|
|
|
$sec+=-$jitter+rnd(2*$jitter); |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
#D warn "\u${prefix}Delay: $sec sec\n"; |
|
203
|
|
|
|
|
|
|
Coro::Timer::sleep $sec if $sec>0; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
my (%services, %known_iterators); |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub register_iterator { |
|
209
|
|
|
|
|
|
|
my $code=pop; |
|
210
|
|
|
|
|
|
|
if( Scalar::Util::reftype $code eq 'CODE' ) { |
|
211
|
|
|
|
|
|
|
@known_iterators{@_}=($code)x(+@_); |
|
212
|
|
|
|
|
|
|
} else { |
|
213
|
|
|
|
|
|
|
die "CODE reference expected"; |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub get_iterator { |
|
218
|
|
|
|
|
|
|
my ($name)=@_; |
|
219
|
|
|
|
|
|
|
exists $known_iterators{$name} and return $known_iterators{$name}; |
|
220
|
|
|
|
|
|
|
return $known_iterators{''}; |
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
{ |
|
224
|
|
|
|
|
|
|
my %keep=('user-agent'=>1, 'referer'=>1); |
|
225
|
|
|
|
|
|
|
sub follow_3XX { |
|
226
|
|
|
|
|
|
|
my ($rc, $el)=@_; |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# we are stricter here than most browsers because we do not follow |
|
229
|
|
|
|
|
|
|
# partial URLs. |
|
230
|
|
|
|
|
|
|
if( $rc->[RC_STATUS]=~/^3/ and |
|
231
|
|
|
|
|
|
|
exists $rc->[RC_HEADERS]->{location} and |
|
232
|
|
|
|
|
|
|
$rc->[RC_HEADERS]->{location}->[0]=~m!^(https?):// # scheme |
|
233
|
|
|
|
|
|
|
([^:/]+) # host |
|
234
|
|
|
|
|
|
|
(:[0-9]+)? # optional port |
|
235
|
|
|
|
|
|
|
(.*)!ix ) { # uri |
|
236
|
|
|
|
|
|
|
# follow location |
|
237
|
|
|
|
|
|
|
my $scheme=lc($1); |
|
238
|
|
|
|
|
|
|
my $host=$2; |
|
239
|
|
|
|
|
|
|
my $port=$3||$services{$scheme}; |
|
240
|
|
|
|
|
|
|
my $uri=$4||'/'; |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
my @h; |
|
243
|
|
|
|
|
|
|
if( exists $el->[RQ_PARAM]->{headers} ) { |
|
244
|
|
|
|
|
|
|
my $hdr=$el->[RQ_PARAM]->{headers}; |
|
245
|
|
|
|
|
|
|
for (my $i=0; $i<@$hdr; $i+=2) { |
|
246
|
|
|
|
|
|
|
push @h, $hdr->[$i], $hdr->[$i+1] if exists $keep{lc $hdr->[$i]}; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
return ['GET', $scheme, $host, $port, $uri, |
|
251
|
|
|
|
|
|
|
{keepalive=>KEEPALIVE, followed=>1, headers=>\@h}]; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
BEGIN { |
|
257
|
|
|
|
|
|
|
%services=(http=>80, https=>443); |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
register_iterator '', default=>sub { |
|
260
|
|
|
|
|
|
|
my $urls=options->{URLList}; |
|
261
|
|
|
|
|
|
|
my $nurls=@$urls; |
|
262
|
|
|
|
|
|
|
my $i=0; |
|
263
|
|
|
|
|
|
|
return sub { |
|
264
|
|
|
|
|
|
|
return if $i>=$nurls; |
|
265
|
|
|
|
|
|
|
return $urls->[$i++]; |
|
266
|
|
|
|
|
|
|
}; |
|
267
|
|
|
|
|
|
|
}; |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
register_iterator random_start=>sub { |
|
270
|
|
|
|
|
|
|
my $urls=options->{URLList}; |
|
271
|
|
|
|
|
|
|
my $nurls=@$urls; |
|
272
|
|
|
|
|
|
|
my ($i, $off)=(0, int rnd $nurls); |
|
273
|
|
|
|
|
|
|
return sub { |
|
274
|
|
|
|
|
|
|
return if $i>=$nurls; |
|
275
|
|
|
|
|
|
|
return $urls->[($off+$i++) % $nurls]; |
|
276
|
|
|
|
|
|
|
}; |
|
277
|
|
|
|
|
|
|
}; |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
register_iterator follow=>sub { |
|
280
|
|
|
|
|
|
|
my %save_delay; |
|
281
|
|
|
|
|
|
|
my $it=@_ ? $_[0] : get_iterator('')->(); |
|
282
|
|
|
|
|
|
|
return sub { |
|
283
|
|
|
|
|
|
|
my ($rc, $el)=@_; |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
my $next=follow_3XX $rc, $el; |
|
286
|
|
|
|
|
|
|
return $next if $next; |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
delay 'post', \%save_delay; |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# get next request |
|
291
|
|
|
|
|
|
|
$next=$it->($rc, $el); |
|
292
|
|
|
|
|
|
|
return unless $next;; |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# save postdelay |
|
295
|
|
|
|
|
|
|
if( exists $next->[RQ_PARAM]->{postdelay} ) { |
|
296
|
|
|
|
|
|
|
$save_delay{postdelay}=$next->[RQ_PARAM]->{postdelay}; |
|
297
|
|
|
|
|
|
|
$save_delay{postjitter}=$next->[RQ_PARAM]->{postjitter} |
|
298
|
|
|
|
|
|
|
if exists $next->[RQ_PARAM]->{postjitter}; |
|
299
|
|
|
|
|
|
|
$next->[RQ_PARAM]->{skippostdelay}=1; |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
return $next; |
|
303
|
|
|
|
|
|
|
}; |
|
304
|
|
|
|
|
|
|
}; |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
register_iterator random_start_follow=>sub { |
|
307
|
|
|
|
|
|
|
@_=get_iterator('random_start')->(); |
|
308
|
|
|
|
|
|
|
goto &{get_iterator 'follow'}; |
|
309
|
|
|
|
|
|
|
}; |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub loadgen { |
|
313
|
|
|
|
|
|
|
local $o=+{@_==1 ? %{$_[0]} : @_}; |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
my $nproc=($o->{NWorker}||=1); |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
die "'URLList' or 'InitURLs' invalid" |
|
318
|
|
|
|
|
|
|
unless (exists $o->{InitURLs} && |
|
319
|
|
|
|
|
|
|
Scalar::Util::reftype $o->{InitURLs} eq 'CODE' or |
|
320
|
|
|
|
|
|
|
exists $o->{URLList} && (!exists $o->{InitURLs} || |
|
321
|
|
|
|
|
|
|
exists $known_iterators{$o->{InitURLs}})); |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
my $init=sub { |
|
324
|
|
|
|
|
|
|
my ($procnr)=@_; |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
$td=Coro::Specific->new(); # thread specific data |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
AnyEvent::TLS::init; |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
HTTP::LoadGen::Run::dnscache=$o->{dnscache} if exists $o->{dnscache}; |
|
331
|
|
|
|
|
|
|
$o->{ProcInit}->($procnr) if exists $o->{ProcInit}; |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
$o->{before}=sub { |
|
334
|
|
|
|
|
|
|
my ($el)=@_; |
|
335
|
|
|
|
|
|
|
delay 'pre', $el->[5]; |
|
336
|
|
|
|
|
|
|
$o->{ReqStart}->($el) if exists $o->{ReqStart}; |
|
337
|
|
|
|
|
|
|
}; |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
$o->{after}=sub { |
|
340
|
|
|
|
|
|
|
my ($rc, $el, $connh)=@_; |
|
341
|
|
|
|
|
|
|
$o->{ReqDone}->($rc, $el, $connh) if exists $o->{ReqDone}; |
|
342
|
|
|
|
|
|
|
return 1 if done; |
|
343
|
|
|
|
|
|
|
delay 'post', $el->[5]; |
|
344
|
|
|
|
|
|
|
return; |
|
345
|
|
|
|
|
|
|
}; |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
if( exists $o->{InitURLs} ) { |
|
348
|
|
|
|
|
|
|
$o->{InitURLs}=$known_iterators{$o->{InitURLs}} unless ref $o->{InitURLs}; |
|
349
|
|
|
|
|
|
|
} else { |
|
350
|
|
|
|
|
|
|
$o->{InitURLs}=$known_iterators{''}; |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
}; |
|
353
|
|
|
|
|
|
|
my $exit; |
|
354
|
|
|
|
|
|
|
$exit=$o->{ProcExit} if exists $o->{ProcExit}; |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
$o->{ParentInit}->() if exists $o->{ParentInit}; |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
start_proc create_proc $nproc, $init, sub { |
|
359
|
|
|
|
|
|
|
my ($procnr)=@_; |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
ramp_up($procnr, $nproc, $o->{RampUpStart}||$nproc, |
|
362
|
|
|
|
|
|
|
$o->{RampUpMax}||$nproc, $o->{RampUpDuration}||300, sub { |
|
363
|
|
|
|
|
|
|
my ($threadnr)=@_; |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
my $data=[]; |
|
366
|
|
|
|
|
|
|
$$td=$data; |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
$data->[TD_CONN_CACHE]={}; |
|
369
|
|
|
|
|
|
|
$data->[TD_TLS_CACHE]={}; |
|
370
|
|
|
|
|
|
|
$data->[TD_THREADNR]=$threadnr; |
|
371
|
|
|
|
|
|
|
$data->[TD_USER]=$o->{ThreadInit}->() if exists $o->{ThreadInit}; |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
HTTP::LoadGen::Run::run_urllist $o; |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
$o->{ThreadExit}->() if exists $o->{ThreadExit}; |
|
376
|
|
|
|
|
|
|
})->down; |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
return 0; |
|
379
|
|
|
|
|
|
|
}, $exit; |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
$o->{ParentExit}->() if exists $o->{ParentExit}; |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
1; |