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
This commit is contained in:
Andre Puschmann 2021-03-04 12:15:23 +01:00
parent 1a59f2d5ba
commit 5e345df439
1 changed files with 109 additions and 95 deletions

View File

@ -26,34 +26,29 @@
#define LOG_HEX_LIMIT (-1)
#define PCAP 0
#define PCAP_CRNTI (0x1001)
#define PCAP_TTI (666)
#if PCAP
#include "srslte/common/mac_nr_pcap.h"
#include "srslte/mac/mac_nr_pdu.h"
static std::unique_ptr<srslte::mac_nr_pcap> pcap_handle = nullptr;
#endif
#include "srslte/common/mac_pcap.h"
#include "srslte/mac/mac_sch_pdu_nr.h"
static std::unique_ptr<srslte::mac_pcap> pcap_handle = nullptr;
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) {
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.add_sdu(lcid, payload, len);
tx_pdu.pack();
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 {
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;
}
#endif
return SRSLTE_ERROR;
}
@ -62,25 +57,27 @@ using namespace srsue;
using namespace srslte;
namespace bpo = boost::program_options;
#define MIN_SDU_SIZE (5)
#define MAX_SDU_SIZE (1500)
typedef struct {
std::string rat;
std::string mode;
uint32_t sdu_size;
int32_t sdu_size;
uint32_t test_duration_sec;
float pdu_drop_rate;
float pdu_cut_rate;
float pdu_duplicate_rate;
uint32_t sdu_gen_delay_usec;
uint32_t pdu_tx_delay_usec;
bool reestablish;
uint32_t log_level;
bool single_tx;
bool write_pcap;
uint32_t avg_opp_size;
bool random_opp;
bool zero_seed;
bool pedantic_sdu_check;
uint32_t nof_pdu_tti;
uint32_t max_retx;
} stress_test_args_t;
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)")
("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)")
("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)")
("sdu_size", bpo::value<int32_t>(&args->sdu_size)->default_value(-1), "Size of SDUs (-1 means random)")
("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)")
("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_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")
("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)")
("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")
("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");
// clang-format on
@ -135,9 +131,14 @@ void parse_args(stress_test_args_t* args, int argc, char* argv[])
args->log_level = 4;
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:
mac_dummy(rlc_interface_mac* rlc1_,
@ -145,7 +146,8 @@ public:
stress_test_args_t args_,
uint32_t lcid_,
timer_handler* timers_,
rlc_pcap* pcap_ = NULL) :
rlc_pcap* pcap_,
uint32_t seed_) :
run_enable(true),
rlc1(rlc1_),
rlc2(rlc2_),
@ -156,8 +158,7 @@ public:
logger(srslog::fetch_basic_logger("MAC", false)),
thread("MAC_DUMMY"),
real_dist(0.0, 1.0),
mt19937(1234),
pool(byte_buffer_pool::get_instance())
mt19937(seed_)
{
logger.set_level(static_cast<srslog::basic_levels>(args.log_level));
logger.set_hex_dump_max_size(LOG_HEX_LIMIT);
@ -209,7 +210,7 @@ private:
{
// Sleep if necessary
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
@ -293,13 +294,13 @@ private:
}
}
rlc_interface_mac* rlc1;
rlc_interface_mac* rlc2;
rlc_interface_mac* rlc1 = nullptr;
rlc_interface_mac* rlc2 = nullptr;
bool run_enable;
stress_test_args_t args;
rlc_pcap* pcap;
uint32_t lcid;
bool run_enable = false;
stress_test_args_t args = {};
rlc_pcap* pcap = nullptr;
uint32_t lcid = 0;
srslog::basic_logger& logger;
srslte::timer_handler* timers = nullptr;
@ -307,22 +308,25 @@ private:
std::mt19937 mt19937;
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:
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"),
logger(srslog::fetch_basic_logger("TEST", false)),
rlc(rlc_),
run_enable(true),
rx_pdus(),
rlc_pdcp(rlc_pdcp_),
name(name_),
args(args_),
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_hex_dump_max_size(LOG_HEX_LIMIT);
@ -338,13 +342,26 @@ public:
void write_pdu(uint32_t rx_lcid, unique_byte_buffer_t sdu)
{
assert(rx_lcid == lcid);
if (sdu->N_bytes != args.sdu_size) {
logger.error(sdu->msg, sdu->N_bytes, "Received SDU with size %d, expected %d.", sdu->N_bytes, args.sdu_size);
if (args.pedantic_sdu_check) {
if (args.mode != "AM") {
// Only AM will guarantee to deliver SDUs, take first byte as reference for other modes
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);
}
}
next_expected_sdu += 1;
rx_pdus++;
}
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) {}
// 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"); }
int get_nof_rx_pdus() { return rx_pdus; }
@ -365,10 +388,10 @@ private:
void run_thread()
{
uint32_t pdcp_sn = 0;
byte_buffer_pool* pool = byte_buffer_pool::get_instance();
uint32_t sdu_size = 0;
while (run_enable) {
// 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;
}
@ -376,33 +399,49 @@ private:
if (pdu == NULL) {
printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n");
// backoff for a bit
usleep(1000);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
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->N_bytes = args.sdu_size;
rlc->write_sdu(lcid, std::move(pdu));
pdu->N_bytes = sdu_size;
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;
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;
uint64_t rx_pdus;
uint32_t lcid;
bool run_enable = true;
/// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs
uint8_t next_expected_sdu = 0;
uint64_t rx_pdus = 0;
uint32_t lcid = 0;
srslte::log_filter log;
srslog::basic_logger& logger;
std::string name;
stress_test_args_t args;
stress_test_args_t args = {};
rlc_interface_pdcp* rlc;
rlc_interface_pdcp* rlc_pdcp = nullptr; // used by run_thread to push PDCP SDUs to RLC
std::mt19937 mt19937;
std::uniform_int_distribution<> int_dist;
};
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.mode == "AM") {
// config RLC AM bearer
cnfg_.rlc_mode = rlc_mode_t::am;
cnfg_.am.max_retx_thresh = 4;
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;
cnfg_ = rlc_config_t::default_rlc_am_config();
cnfg_.am.max_retx_thresh = args.max_retx;
} else if (args.mode == "UM") {
// config UM bearer
cnfg_.rlc_mode = rlc_mode_t::um;
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;
cnfg_ = rlc_config_t::default_rlc_um_config();
} else if (args.mode == "TM") {
// use default LCID in TM
lcid = 0;
@ -445,12 +473,9 @@ void stress_test(stress_test_args_t args)
exit(-1);
}
#if PCAP
if (args.write_pcap) {
pcap.open("rlc_stress_test.pcap", cnfg_);
}
#endif
} else if (args.rat == "NR") {
if (args.mode == "UM") {
cnfg_ = rlc_config_t::default_rlc_um_nr_config(6);
@ -459,25 +484,30 @@ void stress_test(stress_test_args_t args)
exit(-1);
}
#if 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");
}
#endif
} else {
cout << "Unsupported RAT mode " << args.rat << ", exiting." << endl;
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);
rlc rlc1(log1.id().c_str());
rlc rlc2(log2.id().c_str());
rlc_tester tester1(&rlc1, "tester1", args, lcid);
rlc_tester tester2(&rlc2, "tester2", args, lcid);
mac_dummy mac(&rlc1, &rlc2, args, lcid, &timers, &pcap);
rlc_tester tester1(&rlc1, "tester1", args, lcid, seed);
rlc_tester tester2(&rlc2, "tester2", args, lcid, seed);
mac_dummy mac(&rlc1, &rlc2, args, lcid, &timers, &pcap, seed);
rlc1.init(&tester1, &tester1, &timers, 0);
rlc2.init(&tester2, &tester2, &timers, 0);
@ -494,18 +524,8 @@ void stress_test(stress_test_args_t args)
}
mac.start();
if (args.test_duration_sec < 1) {
args.test_duration_sec = 1;
}
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);
}
// wait until test is over
std::this_thread::sleep_for(std::chrono::seconds(args.test_duration_sec));
printf("Test finished, tearing down ..\n");
@ -558,12 +578,6 @@ int main(int argc, char** argv)
srslog::init();
if (args.zero_seed) {
srand(0);
} else {
srand(time(NULL));
}
stress_test(args);
exit(0);