diff options
Diffstat (limited to 'testscripts/slave-client')
-rwxr-xr-x | testscripts/slave-client | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/testscripts/slave-client b/testscripts/slave-client new file mode 100755 index 000000000..b1599cd2f --- /dev/null +++ b/testscripts/slave-client @@ -0,0 +1,414 @@ +#!/usr/bin/perl -w +#/**************************************************************************/ +#/* */ +#/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +#/* */ +#/* NXSCRIPTS, NX protocol compression and NX extensions to this software */ +#/* are copyright of NoMachine. Redistribution and use of the present */ +#/* software is allowed according to terms specified in the file LICENSE */ +#/* which comes in the source distribution. */ +#/* */ +#/* Check http://www.nomachine.com/licensing.html for applicability. */ +#/* */ +#/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +#/* */ +#/* All rights reserved. */ +#/* */ +#/**************************************************************************/ +# + +use strict; +use Getopt::Long; +use IO::Socket; +use Time::HiRes qw(gettimeofday tv_interval sleep ); +use IO::Socket::INET; + +my ($opt_batch, $opt_count, $opt_host, $opt_port, $opt_debug, $opt_blocksize, $opt_dump, $opt_delay); +my ($cmd_echo, $cmd_pingbench, $cmd_rand_read_bench, $cmd_fast_read_bench, $cmd_fast_write_bench); +my ($cmd_rand_write_bench, $cmd_fast_echo_bench, $cmd_rand_echo_bench, $cmd_all_bench); +my ($cmd_char_write_bench, $cmd_char_read_bench); +my ($cmd_help); + + +$opt_host = "127.0.0.1"; +$opt_count = 10000; +$opt_blocksize = 1024; +$opt_delay = 0; + +Getopt::Long::Configure ("bundling"); + +GetOptions( + "d|dump" => \$opt_dump, + "c|count=i" => \$opt_count, + "H|host=s" => \$opt_host, + "P|port=i" => \$opt_port, + "D|debug" => \$opt_debug, + "e|delay=f" => \$opt_delay, + "b|blocksize=i" => \$opt_blocksize, + "E|echo=s" => \$cmd_echo, + "pingbench" => \$cmd_pingbench, + "randreadbench" => \$cmd_rand_read_bench, + "fastreadbench" => \$cmd_fast_read_bench, + "fastwritebench" => \$cmd_fast_write_bench, + "randwritebench" => \$cmd_rand_write_bench, + "fastechobench" => \$cmd_fast_echo_bench, + "randechobench" => \$cmd_rand_echo_bench, + "charwritebench" => \$cmd_char_write_bench, + "charreadbench" => \$cmd_char_read_bench, + "a|allbench" => \$cmd_all_bench, + "h|help" => \$cmd_help, +) or die "Getopt failed"; + + +if ($cmd_help) { + print <<HELP; +Usage: $0 --port <port> [options] <command> +NX Slave Channel demo, benchmark and tester. + +Options: + -b, --blocksize=INT Block size for testing. 1024 bytes by default. + -c, --count=NUM Number of blocks or pings to issue. + -D, --debug Output protocol data for debugging + -d, --dump Dump benchmark data in tab separated format, for + graphing. + -e, --delay=FLOAT Delay between blocks or pings. None by default. + -H, --host=HOST Host to connect to. 'localhost' by default. + -P, --port=PORT Port to connect to. Mandatory. + +Benchmarks: + -a, --allbench Run all the benchmarks + --fastechobench Benchmark sending a single repeated character, + and receiving it back. + --fastreadbench Benchmark reading a single repeated character. + --fastwritebench Benchmark writing a single repeated character. + --pingbench Benchmark ping time. + --randechobench Benchmark sending random data, and receiving it + back + --randreadbench Benchmark reading random, incompressible data. + --randwritebench Benchmark writing random, incompressible data. + +Other commands: + -E, --echo=STR Send STR to the slave channel handler, and print + the response. + -h, --help Show this text + +Example: + Test basic connectivity: + $0 --port 42000 --echo "hi" + + Connect to port 42000 and run all the benchmarks: + $0 --port 42000 -a + +HELP +exit(0); +} + +if (!$opt_port) { + print STDERR "Syntax: $0 --port <port> <command> [arguments]\nUse $0 --help for more information.\n\n"; + exit(1); +} + + +my $socket = IO::Socket::INET->new(PeerAddr => $opt_host, PeerPort => $opt_port, Proto => 'tcp'); +if (!$socket) { + die "Can't connect to $opt_host:$opt_port: $!"; +} + +my @greeting = read_until_prompt(); +my $sl = StatusLine->new(); +my $random_fh; + +if ( $cmd_all_bench ) { + $cmd_pingbench = 1; + $cmd_rand_read_bench = 1; + $cmd_fast_read_bench = 1; + $cmd_fast_write_bench = 1; + $cmd_rand_write_bench = 1; + $cmd_fast_echo_bench = 1; + $cmd_rand_echo_bench = 1; + $cmd_all_bench = 1; + +} + +if ( $cmd_echo ) { + send_cmd("echo $cmd_echo"); + print read_until_prompt() . "\n"; +} + +if ( $cmd_pingbench ) { + my $t0 = [gettimeofday()]; + for(my $i=0;$i<$opt_count;$i++) { + send_cmd("echo $i"); + read_until_prompt(); + + my $elapsed = tv_interval($t0, [gettimeofday()]); + if ( $opt_dump ) { + print "$elapsed\t$i\n"; + } else { + $sl->set("Pinged " . ($i+1) . " times, ${elapsed}s elapsed, " . $opt_count / $elapsed . "/s"); + } + + sleep($opt_delay) if ($opt_delay>0); + } + + $sl->show_last(); + print STDERR "\n\n"; +} + +if ( $cmd_rand_read_bench ) { + read_bench("Random read", "randgen"); +} + +if ( $cmd_fast_read_bench ) { + read_bench("Fast read", "fastgen"); +} + +if ( $cmd_fast_write_bench ) { + write_bench("Fast write", "discard", sub { "x" x $opt_blocksize }, 0); +} + +if ( $cmd_rand_write_bench ) { + write_bench("Random write", "discard", \&get_random_bytes, 0); +} + +if ( $cmd_fast_echo_bench ) { + write_bench("Fast echo", "blkecho $opt_blocksize", sub { "x" x $opt_blocksize }, 1); +} + + +if ( $cmd_rand_echo_bench ) { + write_bench("Random echo", "blkecho $opt_blocksize", \&get_random_bytes, 1); +} + +if ( $cmd_char_write_bench ) { + for(my $i=0;$i<=255;$i++) { + write_bench("Fast write $i", "discard", sub { chr($i) x $opt_blocksize }, 0); + } +} + +if ( $cmd_char_read_bench ) { + for(my $i=0;$i<=255;$i++) { + read_bench("Fast read $i", "fastgen $i"); + } +} + +sub get_random_bytes { + if (!$random_fh) { + open($random_fh, '<', "/dev/urandom") or die "Can't open /dev/urandom: $!"; + } + + my $buf=""; + while(length($buf) < $opt_blocksize) { + my $tmp; + sysread($random_fh, $tmp, $opt_blocksize - length($buf)); + $buf .= $tmp; + } + + return $buf; +} + +sub read_bench { + my ($desc, $command) = @_; + init(); + send_cmd($command); + + my $t0 = [gettimeofday()]; + my $bytes = 0; + + while($bytes < $opt_count * $opt_blocksize) { + my $junk = ""; + while(length($junk) < $opt_blocksize) { + $junk .= read_sock($opt_blocksize - length($junk)); + } + + $bytes += length($junk); + + my $elapsed = tv_interval($t0, [gettimeofday()]); + if ( $opt_dump ) { + print "$elapsed\t$bytes\n"; + } else { + $sl->set("$desc $bytes bytes, ${elapsed}s elapsed, " . sprintf("%0.3f", ($bytes / $elapsed) / (1024*1024)) . " MB/s"); + } + + sleep($opt_delay) if ($opt_delay>0); + + } + + $sl->show_last(); + print STDERR "\n\n"; + +} + +sub write_bench { + my ($desc, $command, $generator, $do_read) = @_; + init(); + send_cmd($command); + + my $t0 = [gettimeofday()]; + my $bytes = 0; + + while($bytes < $opt_count * $opt_blocksize) { + + my $junk = $generator->(); + $bytes += length($junk); + + write_sock($junk); + + if ( $do_read ) { + my $readbuf = ""; + while(length($readbuf) < $opt_blocksize) { + $readbuf .= read_sock($opt_blocksize-length($readbuf)); + } + + if ( $junk ne $readbuf ) { + die "Agent returned different data! Sent:\n$junk\nReceived:\n$readbuf\n"; + } + } + + my $elapsed = tv_interval($t0, [gettimeofday()]); + if ( $opt_dump ) { + print "$elapsed\t$bytes\n"; + } else { + $sl->set("$desc $bytes bytes, ${elapsed}s elapsed, " . sprintf("%0.3f", ($bytes / $elapsed) / (1024*1024)) . " MB/s"); + } + + sleep($opt_delay) if ($opt_delay>0); + } + + $sl->show_last(); + print STDERR "\n\n"; + +} + + + +sub read_until_prompt { + my $buf; + my $tmp; + my @lines; + my $ret = ""; + + while(1) { + $buf .= read_sock(1024); + + while ( $buf =~ /^(.*?)\n/m ) { + dbg("LINE: '$1'\n"); + push @lines, $1; + $ret .= "$1\n"; + $buf =~ s/^(.*?)\n//m; + } + + dbg("BUF: '$buf'\n"); + if ( $buf =~ /^(Agent|Proxy|\?)> / ) { + dbg("PROMPT: '$buf'\n"); + return wantarray ? @lines : $ret; + } + } +} + +sub init { + if ( $socket ) { + close $socket; + } + + $socket = IO::Socket::INET->new(PeerAddr => $opt_host, PeerPort => $opt_port, Proto => 'tcp'); + + if (!$socket) { + die "Can't connect to $opt_host:$opt_port: $!"; + } + + my @greeting = read_until_prompt(); +} + +sub read_sock { + my ($len) = @_; + my $buf; + my $ret = sysread($socket, $buf, $len); + if (!defined $ret) { + die "Error reading $len bytes from socket: $!"; + } + + if ( $ret == 0 ) { + die "Socket unexpectedly closed when trying to read $len bytes"; + } + + dbg("READ: '$buf', length $ret\n"); + return $buf; +} + +sub write_sock { + my ($data) = @_; + dbg("SEND: '$data'\n"); + + my $written = 0; + my $total = length($data); + + while($written < $total) { + my $ret = syswrite($socket, $data, $total, $written); + + if (!$ret) { + die "Error writing '$data' to socket: $!"; + } + if ( $ret == 0 ) { + die "Socket closed when writing '$data' to socket"; + } + + $written += $ret; + } + +} + + +sub send_cmd { + my ($cmd) = @_; + write_sock("$cmd\n"); +} + +sub dbg { + my ($str) = @_; + if ( $opt_debug ) { + $str =~ s/[^[:print:]\r\n\t]/./g; + print STDERR $str; + } +} + + +package StatusLine; +use Time::HiRes qw(gettimeofday tv_interval ); + +sub new { + my $class = shift; + + my $self = { + prev_len => 0 + }; + + bless $self, $class; + return $self; +} + +sub set { + my ($self, $str) = @_; + + my $now = [gettimeofday()]; + $self->{last_str} = $str; + + if ( !defined $self->{prev_time} || tv_interval($self->{prev_time}, $now) >= 0.1 ) { + print STDERR "\r" . (" " x $self->{prev_len}) . "\r$str"; + $self->{prev_len} = length($str); + $self->{prev_time} = $now; + } +} + +sub clear { + my ($self) = @_; + undef $self->{prev_time}; + $self->set(""); +} + +sub show_last { + my ($self) = @_; + undef $self->{prev_time}; + $self->set( $self->{last_str} ) if ( $self->{last_str} ); +} + |