@ -25,7 +25,7 @@
*/
*/
# include <iostream>
# include <iostream>
# include < stdlib.h >
# include < c stdlib>
# include <pthread.h>
# include <pthread.h>
# include "srslte/common/log_filter.h"
# include "srslte/common/log_filter.h"
# include "srslte/common/logger_stdout.h"
# include "srslte/common/logger_stdout.h"
@ -34,10 +34,9 @@
# include "srslte/upper/rlc.h"
# include "srslte/upper/rlc.h"
# include <boost/program_options.hpp>
# include <boost/program_options.hpp>
# include <boost/program_options/parsers.hpp>
# include <boost/program_options/parsers.hpp>
# include < assert.h >
# include < c assert>
# include <srslte/upper/rlc_interface.h>
# include <srslte/upper/rlc_interface.h>
# define SDU_SIZE (1500)
# define LOG_HEX_LIMIT (-1)
# define LOG_HEX_LIMIT (-1)
using namespace std ;
using namespace std ;
@ -47,6 +46,7 @@ namespace bpo = boost::program_options;
typedef struct {
typedef struct {
std : : string mode ;
std : : string mode ;
uint32_t sdu_size ;
uint32_t test_duration_sec ;
uint32_t test_duration_sec ;
float error_rate ;
float error_rate ;
uint32_t sdu_gen_delay_usec ;
uint32_t sdu_gen_delay_usec ;
@ -55,8 +55,10 @@ typedef struct {
uint32_t log_level ;
uint32_t log_level ;
bool single_tx ;
bool single_tx ;
bool write_pcap ;
bool write_pcap ;
float opp_sdu_ratio ;
uint32_t avg_opp_size ;
bool random_opp ;
bool zero_seed ;
bool zero_seed ;
bool pedantic ;
} stress_test_args_t ;
} stress_test_args_t ;
void parse_args ( stress_test_args_t * args , int argc , char * argv [ ] ) {
void parse_args ( stress_test_args_t * args , int argc , char * argv [ ] ) {
@ -73,15 +75,18 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
common . add_options ( )
common . add_options ( )
( " mode " , bpo : : value < std : : string > ( & args - > mode ) - > default_value ( " AM " ) , " Whether to test RLC acknowledged or unacknowledged mode (AM/UM) " )
( " mode " , bpo : : value < std : : string > ( & args - > mode ) - > default_value ( " AM " ) , " Whether to test RLC acknowledged or unacknowledged mode (AM/UM) " )
( " duration " , bpo : : value < uint32_t > ( & args - > test_duration_sec ) - > default_value ( 5 ) , " Duration (sec) " )
( " duration " , bpo : : value < uint32_t > ( & args - > test_duration_sec ) - > default_value ( 5 ) , " Duration (sec) " )
( " sdu_size " , bpo : : value < uint32_t > ( & args - > sdu_size ) - > default_value ( 1500 ) , " Size of SDUs " )
( " avg_opp_size " , bpo : : value < uint32_t > ( & args - > avg_opp_size ) - > default_value ( 1505 ) , " Size of the MAC opportunity (if not random) " )
( " random_opp " , bpo : : value < bool > ( & args - > random_opp ) - > default_value ( true ) , " Whether to generate random MAC opportunities " )
( " sdu_gen_delay " , bpo : : value < uint32_t > ( & args - > sdu_gen_delay_usec ) - > default_value ( 0 ) , " SDU generation delay (usec) " )
( " sdu_gen_delay " , bpo : : value < uint32_t > ( & args - > sdu_gen_delay_usec ) - > default_value ( 0 ) , " SDU generation delay (usec) " )
( " pdu_tx_delay " , bpo : : value < uint32_t > ( & args - > pdu_tx_delay_usec ) - > default_value ( 0 ) , " Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec) " )
( " pdu_tx_delay " , bpo : : value < uint32_t > ( & args - > pdu_tx_delay_usec ) - > default_value ( 0 ) , " Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec) " )
( " error_rate " , bpo : : value < float > ( & args - > error_rate ) - > default_value ( 0.1 ) , " Rate at which RLC PDUs are dropped " )
( " error_rate " , bpo : : value < float > ( & args - > error_rate ) - > default_value ( 0.1 ) , " Rate at which RLC PDUs are dropped " )
( " opp_sdu_ratio " , bpo : : value < float > ( & args - > opp_sdu_ratio ) - > default_value ( 0.0 ) , " Ratio between MAC opportunity and SDU size (0==random) " )
( " reestablish " , bpo : : value < bool > ( & args - > reestablish ) - > default_value ( false ) , " Mimic RLC reestablish during execution " )
( " reestablish " , bpo : : value < bool > ( & args - > reestablish ) - > default_value ( false ) , " Mimic RLC reestablish during execution " )
( " loglevel " , bpo : : value < uint32_t > ( & args - > log_level ) - > default_value ( srslte : : LOG_LEVEL_DEBUG ) , " Log level (1=Error,2=Warning,3=Info,4=Debug) " )
( " loglevel " , bpo : : value < uint32_t > ( & args - > log_level ) - > default_value ( srslte : : LOG_LEVEL_DEBUG ) , " Log level (1=Error,2=Warning,3=Info,4=Debug) " )
( " singletx " , bpo : : value < bool > ( & args - > single_tx ) - > default_value ( false ) , " If set to true, only one node is generating data " )
( " singletx " , bpo : : value < bool > ( & args - > single_tx ) - > default_value ( false ) , " If set to true, only one node is generating data " )
( " pcap " , bpo : : value < bool > ( & args - > write_pcap ) - > default_value ( false ) , " Whether to write all RLC PDU to PCAP file " )
( " pcap " , bpo : : value < bool > ( & args - > write_pcap ) - > default_value ( false ) , " Whether to write all RLC PDU to PCAP file " )
( " zeroseed " , bpo : : value < bool > ( & args - > zero_seed ) - > default_value ( false ) , " Whether to initialize random seed to zero " ) ;
( " zeroseed " , bpo : : value < bool > ( & args - > zero_seed ) - > default_value ( false ) , " Whether to initialize random seed to zero " )
( " pedantic " , bpo : : value < bool > ( & args - > pedantic ) - > default_value ( true ) , " Whether to perform strict SDU size checking at receiver " ) ;
// these options are allowed on the command line
// these options are allowed on the command line
bpo : : options_description cmdline_options ;
bpo : : options_description cmdline_options ;
@ -93,7 +98,7 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
bpo : : notify ( vm ) ;
bpo : : notify ( vm ) ;
// help option was given - print usage and exit
// help option was given - print usage and exit
if ( vm . count ( " help " ) ) {
if ( vm . count ( " help " ) > 0 ) {
cout < < " Usage: " < < argv [ 0 ] < < " [OPTIONS] config_file " < < endl < < endl ;
cout < < " Usage: " < < argv [ 0 ] < < " [OPTIONS] config_file " < < endl < < endl ;
cout < < common < < endl < < general < < endl ;
cout < < common < < endl < < general < < endl ;
exit ( 0 ) ;
exit ( 0 ) ;
@ -110,19 +115,17 @@ class mac_dummy
, public thread
, public thread
{
{
public :
public :
mac_dummy ( rlc_interface_mac * rlc1_ , rlc_interface_mac * rlc2_ , float fail_rate_ , float opp_sdu_ratio_ , int32_t pdu_tx_delay_usec _, uint32_t lcid_ , rlc_pcap * pcap_ = NULL )
mac_dummy ( rlc_interface_mac * rlc1_ , rlc_interface_mac * rlc2_ , stress_test_args_t args _, uint32_t lcid_ , rlc_pcap * pcap_ = NULL )
: timers ( 8 )
: timers ( 8 )
, run_enable ( true )
, run_enable ( true )
, rlc1 ( rlc1_ )
, rlc1 ( rlc1_ )
, rlc2 ( rlc2_ )
, rlc2 ( rlc2_ )
, fail_rate ( fail_rate_ )
, args ( args_ )
, opp_sdu_ratio ( opp_sdu_ratio_ )
, pdu_tx_delay_usec ( pdu_tx_delay_usec_ )
, pcap ( pcap_ )
, pcap ( pcap_ )
, lcid ( lcid_ )
, lcid ( lcid_ )
, log ( " MAC " )
, log ( " MAC " )
{
{
log . set_level ( srslte : : LOG_LEVEL_ERROR ) ;
log . set_level ( static_cast < LOG_LEVEL_ENUM > ( args . log_level ) ) ;
log . set_hex_limit ( LOG_HEX_LIMIT ) ;
log . set_hex_limit ( LOG_HEX_LIMIT ) ;
}
}
@ -155,14 +158,19 @@ private:
exit ( - 1 ) ;
exit ( - 1 ) ;
}
}
float r = opp_sdu_ratio ? opp_sdu_ratio : ( float ) rand ( ) / RAND_MAX ;
float factor = 1.0 ;
int opp_size = r * SDU_SIZE ;
if ( args . random_opp ) {
factor = 0.5 + static_cast < float > ( rand ( ) ) / RAND_MAX ;
}
int opp_size = args . avg_opp_size * factor ;
uint32_t buf_state = tx_rlc - > get_buffer_state ( lcid ) ;
uint32_t buf_state = tx_rlc - > get_buffer_state ( lcid ) ;
if ( buf_state ) {
if ( buf_state > 0 ) {
int read = tx_rlc - > read_pdu ( lcid , pdu - > msg , opp_size ) ;
int read = tx_rlc - > read_pdu ( lcid , pdu - > msg , opp_size ) ;
pdu - > N_bytes = read ;
pdu - > N_bytes = read ;
if ( pdu_tx_delay_usec ) usleep ( pdu_tx_delay_usec ) ;
if ( args . pdu_tx_delay_usec > 0 ) {
if ( ( ( float ) rand ( ) / RAND_MAX > fail_rate ) & & read > 0 ) {
usleep ( args . pdu_tx_delay_usec ) ;
}
if ( ( ( float ) rand ( ) / RAND_MAX > args . error_rate ) & & read > 0 ) {
rx_rlc - > write_pdu ( lcid , pdu - > msg , pdu - > N_bytes ) ;
rx_rlc - > write_pdu ( lcid , pdu - > msg , pdu - > N_bytes ) ;
if ( is_dl ) {
if ( is_dl ) {
pcap - > write_dl_am_ccch ( pdu - > msg , pdu - > N_bytes ) ;
pcap - > write_dl_am_ccch ( pdu - > msg , pdu - > N_bytes ) ;
@ -170,7 +178,7 @@ private:
pcap - > write_ul_am_ccch ( pdu - > msg , pdu - > N_bytes ) ;
pcap - > write_ul_am_ccch ( pdu - > msg , pdu - > N_bytes ) ;
}
}
} else {
} else {
log . info _hex( pdu - > msg , pdu - > N_bytes , " Dropping RLC PDU (%d B) \n " , pdu - > N_bytes ) ;
log . warning _hex( pdu - > msg , pdu - > N_bytes , " Dropping RLC PDU (%d B) \n " , pdu - > N_bytes ) ;
}
}
}
}
byte_buffer_pool : : get_instance ( ) - > deallocate ( pdu ) ;
byte_buffer_pool : : get_instance ( ) - > deallocate ( pdu ) ;
@ -194,9 +202,7 @@ private:
rlc_interface_mac * rlc2 ;
rlc_interface_mac * rlc2 ;
srslte : : timers timers ;
srslte : : timers timers ;
bool run_enable ;
bool run_enable ;
float fail_rate ;
stress_test_args_t args ;
float opp_sdu_ratio ;
uint32_t pdu_tx_delay_usec ;
rlc_pcap * pcap ;
rlc_pcap * pcap ;
uint32_t lcid ;
uint32_t lcid ;
srslte : : log_filter log ;
srslte : : log_filter log ;
@ -209,13 +215,17 @@ class rlc_tester
, public thread
, public thread
{
{
public :
public :
rlc_tester ( rlc_interface_pdcp * rlc_ , std : : string name_ , uint32_t sdu_gen_delay_usec_ , uint32_t lcid_ ) {
rlc_tester ( rlc_interface_pdcp * rlc_ , std : : string name_ , stress_test_args_t args_ , uint32_t lcid_ )
rlc = rlc_ ;
: log ( " Testr " )
run_enable = true ;
, rlc ( rlc_ )
rx_pdus = 0 ;
, run_enable ( true )
name = name_ ;
, rx_pdus ( )
sdu_gen_delay_usec = sdu_gen_delay_usec_ ;
, name ( name_ )
lcid = lcid_ ;
, args ( args_ )
, lcid ( lcid_ )
{
log . set_level ( srslte : : LOG_LEVEL_ERROR ) ;
log . set_hex_limit ( LOG_HEX_LIMIT ) ;
}
}
void stop ( )
void stop ( )
@ -228,13 +238,14 @@ public:
void write_pdu ( uint32_t rx_lcid , byte_buffer_t * sdu )
void write_pdu ( uint32_t rx_lcid , byte_buffer_t * sdu )
{
{
assert ( rx_lcid = = lcid ) ;
assert ( rx_lcid = = lcid ) ;
if ( sdu - > N_bytes ! = SDU_SIZE ) {
if ( sdu - > N_bytes ! = args . sdu_size ) {
srslte : : log_filter log1 ( " Testr " ) ; ;
log . error_hex ( sdu - > msg , sdu - > N_bytes , " Received SDU with size %d, expected %d. \n " , sdu - > N_bytes , args . sdu_size ) ;
log1 . set_level ( srslte : : LOG_LEVEL_ERROR ) ;
// exit if in pedantic mode or SDU is not a multiple of the expected size
log1 . set_hex_limit ( sdu - > N_bytes ) ;
if ( args . pedantic | | sdu - > N_bytes % args . sdu_size ! = 0 ) {
log1 . error_hex ( sdu - > msg , sdu - > N_bytes , " Received PDU with size %d, expected %d. Exiting. \n " , sdu - > N_bytes , SDU_SIZE ) ;
exit ( - 1 ) ;
exit ( - 1 ) ;
}
}
}
byte_buffer_pool : : get_instance ( ) - > deallocate ( sdu ) ;
byte_buffer_pool : : get_instance ( ) - > deallocate ( sdu ) ;
rx_pdus + + ;
rx_pdus + + ;
}
}
@ -254,29 +265,32 @@ private:
uint8_t sn = 0 ;
uint8_t sn = 0 ;
while ( run_enable ) {
while ( run_enable ) {
byte_buffer_t * pdu = byte_buffer_pool : : get_instance ( ) - > allocate ( " rlc_tester::run_thread " ) ;
byte_buffer_t * pdu = byte_buffer_pool : : get_instance ( ) - > allocate ( " rlc_tester::run_thread " ) ;
if ( ! pdu ) {
if ( pdu = = NULL ) {
printf ( " Error: Could not allocate PDU in rlc_tester::run_thread \n \n \n " ) ;
printf ( " Error: Could not allocate PDU in rlc_tester::run_thread \n \n \n " ) ;
// backoff for a bit
// backoff for a bit
usleep ( 1000 ) ;
usleep ( 1000 ) ;
continue ;
continue ;
}
}
for ( uint32_t i = 0 ; i < SDU_SIZE ; i + + ) {
for ( uint32_t i = 0 ; i < args. sdu_size ; i + + ) {
pdu - > msg [ i ] = sn ;
pdu - > msg [ i ] = sn ;
}
}
sn + + ;
sn + + ;
pdu - > N_bytes = SDU_SIZE ;
pdu - > N_bytes = args. sdu_size ;
rlc - > write_sdu ( lcid , pdu ) ;
rlc - > write_sdu ( lcid , pdu ) ;
if ( sdu_gen_delay_usec ) usleep ( sdu_gen_delay_usec ) ;
if ( args . sdu_gen_delay_usec > 0 ) {
usleep ( args . sdu_gen_delay_usec ) ;
}
}
}
}
}
bool run_enable ;
bool run_enable ;
long rx_pdus ;
uint64_t rx_pdus ;
uint32_t lcid ;
uint32_t lcid ;
srslte : : log_filter log ;
std : : string name ;
std : : string name ;
uint32_t sdu_gen_delay_usec ;
stress_test_args_t args ;
rlc_interface_pdcp * rlc ;
rlc_interface_pdcp * rlc ;
} ;
} ;
@ -285,8 +299,8 @@ void stress_test(stress_test_args_t args)
{
{
srslte : : log_filter log1 ( " RLC_1 " ) ;
srslte : : log_filter log1 ( " RLC_1 " ) ;
srslte : : log_filter log2 ( " RLC_2 " ) ;
srslte : : log_filter log2 ( " RLC_2 " ) ;
log1 . set_level ( ( LOG_LEVEL_ENUM ) args . log_level ) ;
log1 . set_level ( static_cast < LOG_LEVEL_ENUM > ( args . log_level ) ) ;
log2 . set_level ( ( LOG_LEVEL_ENUM ) args . log_level ) ;
log2 . set_level ( static_cast < LOG_LEVEL_ENUM > ( args . log_level ) ) ;
log1 . set_hex_limit ( LOG_HEX_LIMIT ) ;
log1 . set_hex_limit ( LOG_HEX_LIMIT ) ;
log2 . set_hex_limit ( LOG_HEX_LIMIT ) ;
log2 . set_hex_limit ( LOG_HEX_LIMIT ) ;
rlc_pcap pcap ;
rlc_pcap pcap ;
@ -326,9 +340,9 @@ void stress_test(stress_test_args_t args)
rlc rlc1 ;
rlc rlc1 ;
rlc rlc2 ;
rlc rlc2 ;
rlc_tester tester1 ( & rlc1 , " tester1 " , args .sdu_gen_delay_usec , lcid ) ;
rlc_tester tester1 ( & rlc1 , " tester1 " , args , lcid ) ;
rlc_tester tester2 ( & rlc2 , " tester2 " , args .sdu_gen_delay_usec , lcid ) ;
rlc_tester tester2 ( & rlc2 , " tester2 " , args , lcid ) ;
mac_dummy mac ( & rlc1 , & rlc2 , args .error_rate , args . opp_sdu_ratio , args . pdu_tx_delay_usec , lcid , & pcap ) ;
mac_dummy mac ( & rlc1 , & rlc2 , args , lcid , & pcap ) ;
ue_interface ue ;
ue_interface ue ;
rlc1 . init ( & tester1 , & tester1 , & ue , & log1 , & mac , 0 ) ;
rlc1 . init ( & tester1 , & tester1 , & ue , & log1 , & mac , 0 ) ;
@ -346,6 +360,10 @@ void stress_test(stress_test_args_t args)
}
}
mac . start ( ) ;
mac . start ( ) ;
if ( args . test_duration_sec < 1 ) {
args . test_duration_sec = 1 ;
}
for ( uint32_t i = 0 ; i < args . test_duration_sec ; i + + ) {
for ( uint32_t i = 0 ; i < args . test_duration_sec ; i + + ) {
// if enabled, mimic reestablishment every second
// if enabled, mimic reestablishment every second
if ( args . reestablish ) {
if ( args . reestablish ) {
@ -355,39 +373,47 @@ void stress_test(stress_test_args_t args)
usleep ( 1e6 ) ;
usleep ( 1e6 ) ;
}
}
printf ( " Test finished, tearing down .. \n " ) ;
// Stop RLC instances first to release blocking writers
// Stop RLC instances first to release blocking writers
rlc1 . stop ( ) ;
rlc1 . stop ( ) ;
rlc2 . stop ( ) ;
rlc2 . stop ( ) ;
printf ( " RLC entities stopped. \n " ) ;
// Stop upper layer writers
tester1 . stop ( ) ;
tester1 . stop ( ) ;
tester2 . stop ( ) ;
tester2 . stop ( ) ;
printf ( " Writers stopped. \n " ) ;
mac . stop ( ) ;
mac . stop ( ) ;
if ( args . write_pcap ) {
if ( args . write_pcap ) {
pcap . close ( ) ;
pcap . close ( ) ;
}
}
rlc_metrics_t metrics ;
rlc_metrics_t metrics = { } ;
rlc1 . get_metrics ( metrics ) ;
rlc1 . get_metrics ( metrics ) ;
printf ( " RLC1 received %d SDUs in %ds (%.2f PDU /s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n " ,
printf ( " RLC1 received %d SDUs in %ds (%.2f /s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n " ,
tester1 . get_nof_rx_pdus ( ) ,
tester1 . get_nof_rx_pdus ( ) ,
args . test_duration_sec ,
args . test_duration_sec ,
( float ) tester1 . get_nof_rx_pdus ( ) / args . test_duration_sec ,
static_cast < double > ( tester1 . get_nof_rx_pdus ( ) / args . test_duration_sec ) ,
metrics . dl_tput_mbps [ lcid ] ,
metrics . dl_tput_mbps [ lcid ] ,
metrics . ul_tput_mbps [ lcid ] ) ;
metrics . ul_tput_mbps [ lcid ] ) ;
rlc2 . get_metrics ( metrics ) ;
rlc2 . get_metrics ( metrics ) ;
printf ( " RLC2 received %d SDUs in %ds (%.2f PDU /s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n " ,
printf ( " RLC2 received %d SDUs in %ds (%.2f /s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n " ,
tester2 . get_nof_rx_pdus ( ) ,
tester2 . get_nof_rx_pdus ( ) ,
args . test_duration_sec ,
args . test_duration_sec ,
( float ) tester2 . get_nof_rx_pdus ( ) / args . test_duration_sec ,
static_cast < double > ( tester2 . get_nof_rx_pdus ( ) / args . test_duration_sec ) ,
metrics . dl_tput_mbps [ lcid ] ,
metrics . dl_tput_mbps [ lcid ] ,
metrics . ul_tput_mbps [ lcid ] ) ;
metrics . ul_tput_mbps [ lcid ] ) ;
}
}
int main ( int argc , char * * argv ) {
int main ( int argc , char * * argv ) {
stress_test_args_t args ;
stress_test_args_t args = { } ;
parse_args ( & args , argc , argv ) ;
parse_args ( & args , argc , argv ) ;
if ( args . zero_seed ) {
if ( args . zero_seed ) {