rlc_stress_test: make pedantic operation the default

among cleaning up the the stress test, it does:
* remove optional pedantic flag and make it the default to catch resegmentation issues
* use random SDU and MAC grant size the default
* use factory functions to create RLC configs (make maxretx configurable)
* remove reestablishment and print error when maxRetx is triggered
* fix NR PCAP usage and removes PCAP ifdef
master
Andre Puschmann 4 years ago
parent 1a59f2d5ba
commit 5e345df439

@ -26,34 +26,29 @@
#define LOG_HEX_LIMIT (-1) #define LOG_HEX_LIMIT (-1)
#define PCAP 0
#define PCAP_CRNTI (0x1001) #define PCAP_CRNTI (0x1001)
#define PCAP_TTI (666) #define PCAP_TTI (666)
#if PCAP #include "srslte/common/mac_pcap.h"
#include "srslte/common/mac_nr_pcap.h" #include "srslte/mac/mac_sch_pdu_nr.h"
#include "srslte/mac/mac_nr_pdu.h" static std::unique_ptr<srslte::mac_pcap> pcap_handle = nullptr;
static std::unique_ptr<srslte::mac_nr_pcap> pcap_handle = nullptr;
#endif
int write_pdu_to_pcap(const bool is_dl, const uint32_t lcid, const uint8_t* payload, const uint32_t len) int write_pdu_to_pcap(const bool is_dl, const uint32_t lcid, const uint8_t* payload, const uint32_t len)
{ {
#if PCAP
if (pcap_handle) { if (pcap_handle) {
srslte::byte_buffer_t tx_buffer; srslte::byte_buffer_t tx_buffer;
srslte::mac_nr_sch_pdu tx_pdu; srslte::mac_sch_pdu_nr tx_pdu;
tx_pdu.init_tx(&tx_buffer, len + 10); tx_pdu.init_tx(&tx_buffer, len + 10);
tx_pdu.add_sdu(lcid, payload, len); tx_pdu.add_sdu(lcid, payload, len);
tx_pdu.pack(); tx_pdu.pack();
if (is_dl) { if (is_dl) {
pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI);
} else { } else {
pcap_handle->write_ul_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); pcap_handle->write_ul_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI);
} }
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
#endif
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
@ -62,25 +57,27 @@ using namespace srsue;
using namespace srslte; using namespace srslte;
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
#define MIN_SDU_SIZE (5)
#define MAX_SDU_SIZE (1500)
typedef struct { typedef struct {
std::string rat; std::string rat;
std::string mode; std::string mode;
uint32_t sdu_size; int32_t sdu_size;
uint32_t test_duration_sec; uint32_t test_duration_sec;
float pdu_drop_rate; float pdu_drop_rate;
float pdu_cut_rate; float pdu_cut_rate;
float pdu_duplicate_rate; float pdu_duplicate_rate;
uint32_t sdu_gen_delay_usec; uint32_t sdu_gen_delay_usec;
uint32_t pdu_tx_delay_usec; uint32_t pdu_tx_delay_usec;
bool reestablish;
uint32_t log_level; uint32_t log_level;
bool single_tx; bool single_tx;
bool write_pcap; bool write_pcap;
uint32_t avg_opp_size; uint32_t avg_opp_size;
bool random_opp; bool random_opp;
bool zero_seed; bool zero_seed;
bool pedantic_sdu_check;
uint32_t nof_pdu_tti; uint32_t nof_pdu_tti;
uint32_t max_retx;
} 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[])
@ -98,20 +95,19 @@ void parse_args(stress_test_args_t* args, int argc, char* argv[])
("rat", bpo::value<std::string>(&args->rat)->default_value("LTE"), "The RLC version to use (LTE/NR)") ("rat", bpo::value<std::string>(&args->rat)->default_value("LTE"), "The RLC version to use (LTE/NR)")
("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") ("sdu_size", bpo::value<int32_t>(&args->sdu_size)->default_value(-1), "Size of SDUs (-1 means random)")
("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") ("random_opp", bpo::value<bool>(&args->random_opp)->default_value(true), "Whether to generate random MAC opportunities")
("avg_opp_size", bpo::value<uint32_t>(&args->avg_opp_size)->default_value(1505), "Size of the MAC opportunity (if not random)")
("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)")
("pdu_drop_rate", bpo::value<float>(&args->pdu_drop_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") ("pdu_drop_rate", bpo::value<float>(&args->pdu_drop_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped")
("pdu_cut_rate", bpo::value<float>(&args->pdu_cut_rate)->default_value(0.0), "Rate at which RLC PDUs are chopped in length") ("pdu_cut_rate", bpo::value<float>(&args->pdu_cut_rate)->default_value(0.0), "Rate at which RLC PDUs are chopped in length")
("pdu_duplicate_rate", bpo::value<float>(&args->pdu_duplicate_rate)->default_value(0.0), "Rate at which RLC PDUs are duplicated") ("pdu_duplicate_rate", bpo::value<float>(&args->pdu_duplicate_rate)->default_value(0.0), "Rate at which RLC PDUs are duplicated")
("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_sdu_check)->default_value(false), "Whether to check SDU length and exit on error") ("max_retx", bpo::value<uint32_t>(&args->max_retx)->default_value(8), "Maximum number of RLC retransmission attempts")
("nof_pdu_tti", bpo::value<uint32_t>(&args->nof_pdu_tti)->default_value(1), "Number of PDUs processed in a TTI"); ("nof_pdu_tti", bpo::value<uint32_t>(&args->nof_pdu_tti)->default_value(1), "Number of PDUs processed in a TTI");
// clang-format on // clang-format on
@ -135,9 +131,14 @@ void parse_args(stress_test_args_t* args, int argc, char* argv[])
args->log_level = 4; args->log_level = 4;
printf("Set log level to %d (%s)\n", args->log_level, srslte::log_level_text[args->log_level]); printf("Set log level to %d (%s)\n", args->log_level, srslte::log_level_text[args->log_level]);
} }
// convert mode to upper case
for (auto& c : args->mode) {
c = toupper(c);
}
} }
class mac_dummy : public thread class mac_dummy : public srslte::thread
{ {
public: public:
mac_dummy(rlc_interface_mac* rlc1_, mac_dummy(rlc_interface_mac* rlc1_,
@ -145,7 +146,8 @@ public:
stress_test_args_t args_, stress_test_args_t args_,
uint32_t lcid_, uint32_t lcid_,
timer_handler* timers_, timer_handler* timers_,
rlc_pcap* pcap_ = NULL) : rlc_pcap* pcap_,
uint32_t seed_) :
run_enable(true), run_enable(true),
rlc1(rlc1_), rlc1(rlc1_),
rlc2(rlc2_), rlc2(rlc2_),
@ -156,8 +158,7 @@ public:
logger(srslog::fetch_basic_logger("MAC", false)), logger(srslog::fetch_basic_logger("MAC", false)),
thread("MAC_DUMMY"), thread("MAC_DUMMY"),
real_dist(0.0, 1.0), real_dist(0.0, 1.0),
mt19937(1234), mt19937(seed_)
pool(byte_buffer_pool::get_instance())
{ {
logger.set_level(static_cast<srslog::basic_levels>(args.log_level)); logger.set_level(static_cast<srslog::basic_levels>(args.log_level));
logger.set_hex_dump_max_size(LOG_HEX_LIMIT); logger.set_hex_dump_max_size(LOG_HEX_LIMIT);
@ -209,7 +210,7 @@ private:
{ {
// Sleep if necessary // Sleep if necessary
if (args.pdu_tx_delay_usec > 0) { if (args.pdu_tx_delay_usec > 0) {
usleep(args.pdu_tx_delay_usec); std::this_thread::sleep_for(std::chrono::microseconds(args.pdu_tx_delay_usec));
} }
auto it = pdu_list.begin(); // PDU iterator auto it = pdu_list.begin(); // PDU iterator
@ -293,13 +294,13 @@ private:
} }
} }
rlc_interface_mac* rlc1; rlc_interface_mac* rlc1 = nullptr;
rlc_interface_mac* rlc2; rlc_interface_mac* rlc2 = nullptr;
bool run_enable; bool run_enable = false;
stress_test_args_t args; stress_test_args_t args = {};
rlc_pcap* pcap; rlc_pcap* pcap = nullptr;
uint32_t lcid; uint32_t lcid = 0;
srslog::basic_logger& logger; srslog::basic_logger& logger;
srslte::timer_handler* timers = nullptr; srslte::timer_handler* timers = nullptr;
@ -307,22 +308,25 @@ private:
std::mt19937 mt19937; std::mt19937 mt19937;
std::uniform_real_distribution<float> real_dist; std::uniform_real_distribution<float> real_dist;
byte_buffer_pool* pool = nullptr;
}; };
class rlc_tester : public pdcp_interface_rlc, public rrc_interface_rlc, public thread class rlc_tester : public pdcp_interface_rlc, public rrc_interface_rlc, public srslte::thread
{ {
public: public:
rlc_tester(rlc_interface_pdcp* rlc_, std::string name_, stress_test_args_t args_, uint32_t lcid_) : rlc_tester(rlc_interface_pdcp* rlc_pdcp_,
std::string name_,
stress_test_args_t args_,
uint32_t lcid_,
uint32_t seed_) :
log("TEST"), log("TEST"),
logger(srslog::fetch_basic_logger("TEST", false)), logger(srslog::fetch_basic_logger("TEST", false)),
rlc(rlc_), rlc_pdcp(rlc_pdcp_),
run_enable(true),
rx_pdus(),
name(name_), name(name_),
args(args_), args(args_),
lcid(lcid_), lcid(lcid_),
thread("RLC_TESTER") thread("RLC_TESTER"),
int_dist(MIN_SDU_SIZE, MAX_SDU_SIZE),
mt19937(seed_)
{ {
logger.set_level(srslog::basic_levels::error); logger.set_level(srslog::basic_levels::error);
logger.set_hex_dump_max_size(LOG_HEX_LIMIT); logger.set_hex_dump_max_size(LOG_HEX_LIMIT);
@ -338,13 +342,26 @@ public:
void write_pdu(uint32_t rx_lcid, unique_byte_buffer_t sdu) void write_pdu(uint32_t rx_lcid, unique_byte_buffer_t sdu)
{ {
assert(rx_lcid == lcid); assert(rx_lcid == lcid);
if (sdu->N_bytes != args.sdu_size) { if (args.mode != "AM") {
logger.error(sdu->msg, sdu->N_bytes, "Received SDU with size %d, expected %d.", sdu->N_bytes, args.sdu_size); // Only AM will guarantee to deliver SDUs, take first byte as reference for other modes
if (args.pedantic_sdu_check) { next_expected_sdu = sdu->msg[0];
}
// check SDU content (consider faster alternative)
for (uint32_t i = 0; i < sdu->N_bytes; ++i) {
if (sdu->msg[i] != next_expected_sdu) {
logger.error(sdu->msg,
sdu->N_bytes,
"Received malformed SDU with size %d, expected data 0x%X",
sdu->N_bytes,
next_expected_sdu);
fprintf(stderr, "Received malformed SDU with size %d\n", sdu->N_bytes);
fprintf(stdout, "Received malformed SDU with size %d\n", sdu->N_bytes);
std::this_thread::sleep_for(std::chrono::seconds(1)); // give some time to flush logs
exit(-1); exit(-1);
} }
} }
next_expected_sdu += 1;
rx_pdus++; rx_pdus++;
} }
void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {}
@ -355,7 +372,13 @@ public:
void notify_failure(uint32_t lcid, const std::vector<uint32_t>& pdcp_sns) {} void notify_failure(uint32_t lcid, const std::vector<uint32_t>& pdcp_sns) {}
// RRC interface // RRC interface
void max_retx_attempted() {} void max_retx_attempted()
{
logger.error(
"Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate.");
std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1);
}
std::string get_rb_name(uint32_t rx_lcid) { return std::string("DRB1"); } std::string get_rb_name(uint32_t rx_lcid) { return std::string("DRB1"); }
int get_nof_rx_pdus() { return rx_pdus; } int get_nof_rx_pdus() { return rx_pdus; }
@ -365,10 +388,10 @@ private:
void run_thread() void run_thread()
{ {
uint32_t pdcp_sn = 0; uint32_t pdcp_sn = 0;
byte_buffer_pool* pool = byte_buffer_pool::get_instance(); uint32_t sdu_size = 0;
while (run_enable) { while (run_enable) {
// SDU queue is full, don't assign PDCP SN // SDU queue is full, don't assign PDCP SN
if (rlc->sdu_queue_is_full(lcid)) { if (rlc_pdcp->sdu_queue_is_full(lcid)) {
continue; continue;
} }
@ -376,33 +399,49 @@ private:
if (pdu == NULL) { 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); std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue; continue;
} }
pdu->md.pdcp_sn = pdcp_sn; pdu->md.pdcp_sn = pdcp_sn;
for (uint32_t i = 0; i < args.sdu_size; i++) {
// random or fixed SDU size
if (args.sdu_size < 1) {
sdu_size = int_dist(mt19937);
} else {
sdu_size = args.sdu_size;
}
for (uint32_t i = 0; i < sdu_size; i++) {
pdu->msg[i] = pdcp_sn & 0xFF; pdu->msg[i] = pdcp_sn & 0xFF;
} }
pdu->N_bytes = args.sdu_size; pdu->N_bytes = sdu_size;
rlc->write_sdu(lcid, std::move(pdu));
logger.info(pdu->msg, pdu->N_bytes, "Generated SDU with %d B", pdu->N_bytes);
rlc_pdcp->write_sdu(lcid, std::move(pdu));
pdcp_sn = (pdcp_sn + 1) % max_pdcp_sn; pdcp_sn = (pdcp_sn + 1) % max_pdcp_sn;
if (args.sdu_gen_delay_usec > 0) { if (args.sdu_gen_delay_usec > 0) {
usleep(args.sdu_gen_delay_usec); std::this_thread::sleep_for(std::chrono::microseconds(args.sdu_gen_delay_usec));
} }
} }
} }
bool run_enable; bool run_enable = true;
uint64_t rx_pdus; /// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs
uint32_t lcid; uint8_t next_expected_sdu = 0;
uint64_t rx_pdus = 0;
uint32_t lcid = 0;
srslte::log_filter log; srslte::log_filter log;
srslog::basic_logger& logger; srslog::basic_logger& logger;
std::string name; std::string name;
stress_test_args_t args; stress_test_args_t args = {};
rlc_interface_pdcp* rlc_pdcp = nullptr; // used by run_thread to push PDCP SDUs to RLC
rlc_interface_pdcp* rlc; std::mt19937 mt19937;
std::uniform_int_distribution<> int_dist;
}; };
void stress_test(stress_test_args_t args) void stress_test(stress_test_args_t args)
@ -421,22 +460,11 @@ void stress_test(stress_test_args_t args)
if (args.rat == "LTE") { if (args.rat == "LTE") {
if (args.mode == "AM") { if (args.mode == "AM") {
// config RLC AM bearer // config RLC AM bearer
cnfg_.rlc_mode = rlc_mode_t::am; cnfg_ = rlc_config_t::default_rlc_am_config();
cnfg_.am.max_retx_thresh = 4; cnfg_.am.max_retx_thresh = args.max_retx;
cnfg_.am.poll_byte = 25 * 1000;
cnfg_.am.poll_pdu = 4;
cnfg_.am.t_poll_retx = 5;
cnfg_.am.t_reordering = 5;
cnfg_.am.t_status_prohibit = 5;
} else if (args.mode == "UM") { } else if (args.mode == "UM") {
// config UM bearer // config UM bearer
cnfg_.rlc_mode = rlc_mode_t::um; cnfg_ = rlc_config_t::default_rlc_um_config();
cnfg_.um.t_reordering = 5;
cnfg_.um.rx_mod = 32;
cnfg_.um.rx_sn_field_length = rlc_umd_sn_size_t::size5bits;
cnfg_.um.rx_window_size = 16;
cnfg_.um.tx_sn_field_length = rlc_umd_sn_size_t::size5bits;
cnfg_.um.tx_mod = 32;
} else if (args.mode == "TM") { } else if (args.mode == "TM") {
// use default LCID in TM // use default LCID in TM
lcid = 0; lcid = 0;
@ -445,12 +473,9 @@ void stress_test(stress_test_args_t args)
exit(-1); exit(-1);
} }
#if PCAP
if (args.write_pcap) { if (args.write_pcap) {
pcap.open("rlc_stress_test.pcap", cnfg_); pcap.open("rlc_stress_test.pcap", cnfg_);
} }
#endif
} else if (args.rat == "NR") { } else if (args.rat == "NR") {
if (args.mode == "UM") { if (args.mode == "UM") {
cnfg_ = rlc_config_t::default_rlc_um_nr_config(6); cnfg_ = rlc_config_t::default_rlc_um_nr_config(6);
@ -459,25 +484,30 @@ void stress_test(stress_test_args_t args)
exit(-1); exit(-1);
} }
#if PCAP
if (args.write_pcap) { if (args.write_pcap) {
pcap_handle = std::unique_ptr<srslte::mac_nr_pcap>(new srslte::mac_nr_pcap()); pcap_handle = std::unique_ptr<srslte::mac_pcap>(new srslte::mac_pcap());
pcap_handle->open("rlc_stress_test_nr.pcap"); pcap_handle->open("rlc_stress_test_nr.pcap");
} }
#endif
} else { } else {
cout << "Unsupported RAT mode " << args.rat << ", exiting." << endl; cout << "Unsupported RAT mode " << args.rat << ", exiting." << endl;
exit(-1); exit(-1);
} }
// generate random seed if needed
uint32_t seed = 0;
if (not args.zero_seed) {
std::random_device rd;
seed = rd();
}
srslte::timer_handler timers(8); srslte::timer_handler timers(8);
rlc rlc1(log1.id().c_str()); rlc rlc1(log1.id().c_str());
rlc rlc2(log2.id().c_str()); rlc rlc2(log2.id().c_str());
rlc_tester tester1(&rlc1, "tester1", args, lcid); rlc_tester tester1(&rlc1, "tester1", args, lcid, seed);
rlc_tester tester2(&rlc2, "tester2", args, lcid); rlc_tester tester2(&rlc2, "tester2", args, lcid, seed);
mac_dummy mac(&rlc1, &rlc2, args, lcid, &timers, &pcap); mac_dummy mac(&rlc1, &rlc2, args, lcid, &timers, &pcap, seed);
rlc1.init(&tester1, &tester1, &timers, 0); rlc1.init(&tester1, &tester1, &timers, 0);
rlc2.init(&tester2, &tester2, &timers, 0); rlc2.init(&tester2, &tester2, &timers, 0);
@ -494,18 +524,8 @@ void stress_test(stress_test_args_t args)
} }
mac.start(); mac.start();
if (args.test_duration_sec < 1) { // wait until test is over
args.test_duration_sec = 1; std::this_thread::sleep_for(std::chrono::seconds(args.test_duration_sec));
}
for (uint32_t i = 0; i < args.test_duration_sec; i++) {
// if enabled, mimic reestablishment every second
if (args.reestablish) {
rlc1.reestablish();
rlc2.reestablish();
}
usleep(1e6);
}
printf("Test finished, tearing down ..\n"); printf("Test finished, tearing down ..\n");
@ -558,12 +578,6 @@ int main(int argc, char** argv)
srslog::init(); srslog::init();
if (args.zero_seed) {
srand(0);
} else {
srand(time(NULL));
}
stress_test(args); stress_test(args);
exit(0); exit(0);

Loading…
Cancel
Save