diff --git a/scripts/mm-delay-graph b/scripts/mm-delay-graph index 03b56ade..7afcf0d8 100755 --- a/scripts/mm-delay-graph +++ b/scripts/mm-delay-graph @@ -115,7 +115,7 @@ my @signal_delay_samples = sort { $a <=> $b } keys %signal_delay; for ( my $ts = $signal_delay_samples[ -1 ]; $ts >= $signal_delay_samples[ 0 ]; $ts-- ) { if ( not defined $signal_delay{ $ts } ) { - $signal_delay{ $ts } = $signal_delay{ $ts + 1 } + 1; + $signal_delay{ $ts } = $signal_delay{ $ts + 1 }; } } @@ -135,7 +135,7 @@ my $graph_max = $signal_delays[-1] * 1.2; # make graph open GNUPLOT, q{| gnuplot} or die; -my $lower_limit = min( 30, int $pp50 ); +my $lower_limit = 0; print GNUPLOT < ) { # parse and validate line my ( $timestamp, $event_type, $num_bytes, $delay ) = split /\s+/, $_; - + if ($event_type eq q{d}){ + $num_bytes = $delay; + $delay = 0; + } if ( not defined $num_bytes ) { die q{Format: timestamp event_type num_bytes [delay]}; } @@ -110,6 +113,9 @@ LINE: while ( <> ) { (defined $signal_delay{ $timestamp - $delay }) ? $signal_delay{ $timestamp - $delay } : POSIX::DBL_MAX ); + } elsif ( $event_type eq q{d} ) { + $capacity{ $bin } -= $num_bits; + $capacity_sum -= $num_bits; } else { die qq{Unknown event type: $event_type}; } @@ -163,7 +169,7 @@ my @signal_delay_samples = sort { $a <=> $b } keys %signal_delay; for ( my $ts = $signal_delay_samples[ -1 ]; $ts >= $signal_delay_samples[ 0 ]; $ts-- ) { if ( not defined $signal_delay{ $ts } ) { - $signal_delay{ $ts } = $signal_delay{ $ts + 1 } + 1; + $signal_delay{ $ts } = $signal_delay{ $ts + 1 }; } } @@ -196,7 +202,7 @@ set xlabel "time (s)" set ylabel "throughput (Mbits/s)" set key center outside top horizontal set style fill solid 0.2 noborder -set terminal svg size 1024,560 fixed fname 'Arial' rounded solid mouse standalone name "Throughput" +set terminal png size 1024,560 set output "/dev/stdout" END diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am index f945df74..7e8900a7 100644 --- a/src/frontend/Makefile.am +++ b/src/frontend/Makefile.am @@ -6,6 +6,11 @@ mm_delay_SOURCES = delayshell.cc delay_queue.hh delay_queue.cc mm_delay_LDADD = -lrt ../util/libutil.a ../packet/libpacket.a mm_delay_LDFLAGS = -pthread +bin_PROGRAMS += mm-file-delay +mm_file_delay_SOURCES = filedelayshell.cc file_delay_queue.hh file_delay_queue.cc +mm_file_delay_LDADD = -lrt ../util/libutil.a ../packet/libpacket.a +mm_file_delay_LDFLAGS = -pthread + bin_PROGRAMS += mm-loss mm_loss_SOURCES = lossshell.cc loss_queue.hh loss_queue.cc mm_loss_LDADD = -lrt ../util/libutil.a ../packet/libpacket.a @@ -49,6 +54,8 @@ libmod_deepcgi_la_CPPFLAGS = # empty install-exec-hook: chown root $(DESTDIR)$(bindir)/mm-delay chmod u+s $(DESTDIR)$(bindir)/mm-delay + chown root $(DESTDIR)$(bindir)/mm-file-delay + chmod u+s $(DESTDIR)$(bindir)/mm-file-delay chown root $(DESTDIR)$(bindir)/mm-loss chmod u+s $(DESTDIR)$(bindir)/mm-loss chown root $(DESTDIR)$(bindir)/mm-onoff diff --git a/src/frontend/file_delay_queue.cc b/src/frontend/file_delay_queue.cc new file mode 100644 index 00000000..44e7e17c --- /dev/null +++ b/src/frontend/file_delay_queue.cc @@ -0,0 +1,59 @@ +/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include +#include +#include +#include "file_delay_queue.hh" +#include "timestamp.hh" + +using namespace std; + +FileDelayQueue::FileDelayQueue(const std::string & delay_file_name, uint64_t time_res_ms) : delays_(), time_res_ms_(time_res_ms), packet_queue_() +{ + ifstream delay_file(delay_file_name); + uint64_t delay; + + if (not delay_file.good()) { + throw runtime_error(delay_file_name + ": error while opening for reading."); + } + + /* Read file, line-by-line. */ + string line; + while (delay_file.good() and getline(delay_file, line)) + { + delay = stoi(line); + delays_.push_back(delay); + } +} + +void FileDelayQueue::read_packet( const string & contents ) +{ + uint64_t packet_timestamp = timestamp(); + uint64_t delay_index = packet_timestamp/time_res_ms_; + uint64_t delay = delays_[delay_index % delays_.size()]; + packet_queue_.emplace(packet_timestamp + delay, contents ); +} + +void FileDelayQueue::write_packets( FileDescriptor & fd ) +{ + while ( (!packet_queue_.empty()) + && (packet_queue_.front().first <= timestamp()) ) { + fd.write( packet_queue_.front().second ); + packet_queue_.pop(); + } +} + +unsigned int FileDelayQueue::wait_time( void ) const +{ + if ( packet_queue_.empty() ) { + return numeric_limits::max(); + } + + const auto now = timestamp(); + + if ( packet_queue_.front().first <= now ) { + return 0; + } else { + return packet_queue_.front().first - now; + } +} diff --git a/src/frontend/file_delay_queue.hh b/src/frontend/file_delay_queue.hh new file mode 100644 index 00000000..7a5490ec --- /dev/null +++ b/src/frontend/file_delay_queue.hh @@ -0,0 +1,34 @@ +/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef FILE_DELAY_QUEUE_HH +#define FILE_DELAY_QUEUE_HH + +#include +#include +#include +#include "file_descriptor.hh" + +class FileDelayQueue +{ +private: + std::vector< uint64_t > delays_; + uint64_t time_res_ms_; + std::queue< std::pair > packet_queue_; + /* release timestamp, contents */ + +public: + + FileDelayQueue(const std::string & delay_file_name, uint64_t time_res_ms); + + void read_packet( const std::string & contents ); + + void write_packets( FileDescriptor & fd ); + + unsigned int wait_time( void ) const; + + bool pending_output( void ) const { return wait_time() <= 0; } + + static bool finished( void ) { return false; } +}; + +#endif /* FILE_DELAY_QUEUE_HH */ diff --git a/src/frontend/filedelayshell.cc b/src/frontend/filedelayshell.cc new file mode 100644 index 00000000..86883d8c --- /dev/null +++ b/src/frontend/filedelayshell.cc @@ -0,0 +1,48 @@ +/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include +#include + +#include "file_delay_queue.hh" +#include "util.hh" +#include "ezio.hh" +#include "packetshell.cc" + +using namespace std; + +int main( int argc, char *argv[] ) +{ + try { + /* clear environment while running as root */ + char ** const user_environment = environ; + environ = nullptr; + + check_requirements( argc, argv ); + + if ( argc < 3 ) { + throw runtime_error( "Usage: " + string( argv[ 0 ] ) + " delay_file_name time_resolution_ms [command...]" ); + } + + string delay_file = argv[1]; + uint64_t time_res_ms = std::stoi(argv[2]); + + vector< string > command; + + if ( argc == 3 ) { + command.push_back( shell_path() ); + } else { + for ( int i = 3; i < argc; i++ ) { + command.push_back( argv[ i ] ); + } + } + + PacketShell file_delay_shell_app( "file-delay", user_environment ); + + file_delay_shell_app.start_uplink( "[delay " + delay_file + "]", command, delay_file, time_res_ms); + file_delay_shell_app.start_downlink( delay_file, time_res_ms ); + return file_delay_shell_app.wait_for_exit(); + } catch ( const exception & e ) { + print_exception( e ); + return EXIT_FAILURE; + } +} diff --git a/src/frontend/mm-file-delay b/src/frontend/mm-file-delay new file mode 100755 index 00000000..a52706df Binary files /dev/null and b/src/frontend/mm-file-delay differ