Changeset 70632 in vbox
- Timestamp:
- Jan 18, 2018 3:20:49 PM (7 years ago)
- svn:sync-xref-src-repo-rev:
- 120356
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/utils/serial/SerialTest.cpp
r69985 r70632 5 5 6 6 /* 7 * Copyright (C) 2017 Oracle Corporation7 * Copyright (C) 2017-2018 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 31 31 #include <iprt/err.h> 32 32 #include <iprt/getopt.h> 33 #include <iprt/mem.h> 33 34 #include <iprt/path.h> 34 35 #include <iprt/param.h> … … 50 51 *********************************************************************************************************************************/ 51 52 52 53 /** Pointer to the serial test data instance. */ 54 typedef struct SERIALTEST *PSERIALTEST; 55 56 /** 57 * Test callback function. 58 * 59 * @returns IPRT status code. 60 * @param pSerialTest The serial test instance data. 61 */ 62 typedef DECLCALLBACK(int) FNSERIALTESTRUN(PSERIALTEST pSerialTest); 63 /** Pointer to the serial test callback. */ 64 typedef FNSERIALTESTRUN *PFNSERIALTESTRUN; 65 66 67 /** 68 * The serial test instance data. 69 */ 70 typedef struct SERIALTEST 71 { 72 /** The assigned test handle. */ 73 RTTEST hTest; 74 /** The assigned serial port. */ 75 RTSERIALPORT hSerialPort; 76 /** The currently active config. */ 77 PCRTSERIALPORTCFG pSerialCfg; 78 } SERIALTEST; 79 80 81 /** 82 * Test descriptor. 83 */ 84 typedef struct SERIALTESTDESC 85 { 86 /** Test ID. */ 87 const char *pszId; 88 /** Test description. */ 89 const char *pszDesc; 90 /** Test run callback. */ 91 PFNSERIALTESTRUN pfnRun; 92 } SERIALTESTDESC; 93 /** Pointer to a test descriptor. */ 94 typedef SERIALTESTDESC *PSERIALTESTDESC; 95 /** Pointer to a constant test descriptor. */ 96 typedef const SERIALTESTDESC *PCSERIALTESTDESC; 97 98 99 /** 100 * TX/RX buffer containing a simple counter. 101 */ 102 typedef struct SERIALTESTTXRXBUFCNT 103 { 104 /** The current counter value. */ 105 uint32_t iCnt; 106 /** Number of bytes left to receive/transmit. */ 107 size_t cbTxRxLeft; 108 /** The offset into the buffer to receive to/send from. */ 109 uint32_t offBuf; 110 /** Maximum size to send/receive before processing is needed again. */ 111 size_t cbTxRxMax; 112 /** The data buffer. */ 113 uint8_t abBuf[_1K]; 114 } SERIALTESTTXRXBUFCNT; 115 /** Pointer to a TX/RX buffer. */ 116 typedef SERIALTESTTXRXBUFCNT *PSERIALTESTTXRXBUFCNT; 53 117 54 118 /********************************************************************************************************************************* … … 66 130 {"--stopbits", 's', RTGETOPT_REQ_STRING }, 67 131 {"--loopbackdevice", 'l', RTGETOPT_REQ_STRING }, 132 {"--tests", 't', RTGETOPT_REQ_STRING }, 133 {"--txbytes", 'x', RTGETOPT_REQ_UINT32 }, 68 134 {"--help", 'h', RTGETOPT_REQ_NOTHING} 69 135 }; 70 136 137 138 static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest); 139 140 /** Implemented tests. */ 141 static const SERIALTESTDESC g_aSerialTests[] = 142 { 143 {"readwrite", "Simple Read/Write test", serialTestRunReadWrite } 144 }; 145 71 146 /** The test handle. */ 72 static RTTEST g_hTest ;147 static RTTEST g_hTest = NIL_RTTEST; 73 148 /** The serial port handle. */ 74 static RTSERIALPORT g_hSerialPort = NIL_RTSERIALPORT;149 static RTSERIALPORT g_hSerialPort = NIL_RTSERIALPORT; 75 150 /** The loopback serial port handle if configured. */ 76 151 static RTSERIALPORT g_hSerialPortLoopback = NIL_RTSERIALPORT; 152 /** Number of bytes to transmit for read/write tests. */ 153 static size_t g_cbTx = _1M; 77 154 /** The config used. */ 78 155 static RTSERIALPORTCFG g_SerialPortCfg = … … 89 166 90 167 91 92 /** 93 * Runs the selected serial tests with the given configuration. 168 /** 169 * Initializes a TX buffer. 170 * 171 * @returns nothing. 172 * @param pSerBuf The serial buffer to initialize. 173 * @param cbTx Maximum number of bytes to transmit. 174 */ 175 static void serialTestTxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbTx) 176 { 177 pSerBuf->iCnt = 0; 178 pSerBuf->offBuf = 0; 179 pSerBuf->cbTxRxMax = 0; 180 pSerBuf->cbTxRxLeft = cbTx; 181 RT_ZERO(pSerBuf->abBuf); 182 } 183 184 185 /** 186 * Initializes a RX buffer. 187 * 188 * @returns nothing. 189 * @param pSerBuf The serial buffer to initialize. 190 * @param cbRx Maximum number of bytes to receive. 191 */ 192 static void serialTestRxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbRx) 193 { 194 pSerBuf->iCnt = 0; 195 pSerBuf->offBuf = 0; 196 pSerBuf->cbTxRxMax = sizeof(pSerBuf->abBuf); 197 pSerBuf->cbTxRxLeft = cbRx; 198 RT_ZERO(pSerBuf->abBuf); 199 } 200 201 202 /** 203 * Prepares the given TX buffer with data for sending it out. 204 * 205 * @returns nothing. 206 * @param pSerBuf The TX buffer pointer. 207 */ 208 static void serialTestTxBufPrepare(PSERIALTESTTXRXBUFCNT pSerBuf) 209 { 210 /* Move the data to the front to make room at the end to fill. */ 211 if (pSerBuf->offBuf) 212 { 213 memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[pSerBuf->offBuf], sizeof(pSerBuf->abBuf) - pSerBuf->offBuf); 214 pSerBuf->offBuf = 0; 215 } 216 217 /* Fill up with data. */ 218 uint32_t offData = 0; 219 while (pSerBuf->cbTxRxMax + sizeof(uint32_t) <= sizeof(pSerBuf->abBuf)) 220 { 221 pSerBuf->iCnt++; 222 *(uint32_t *)&pSerBuf->abBuf[pSerBuf->offBuf + offData] = pSerBuf->iCnt; 223 pSerBuf->cbTxRxMax += sizeof(uint32_t); 224 offData += sizeof(uint32_t); 225 } 226 } 227 228 229 /** 230 * Sends a new batch of data from the TX buffer preapring new data if required. 94 231 * 95 232 * @returns IPRT status code. 96 */ 97 static int serialTestRun(void) 98 { 99 return VERR_NOT_IMPLEMENTED; 233 * @param hSerialPort The serial port handle to send the data to. 234 * @param pSerBuf The TX buffer pointer. 235 */ 236 static int serialTestTxBufSend(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf) 237 { 238 int rc = VINF_SUCCESS; 239 240 if (pSerBuf->cbTxRxLeft) 241 { 242 if (!pSerBuf->cbTxRxMax) 243 serialTestTxBufPrepare(pSerBuf); 244 245 size_t cbToWrite = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft); 246 size_t cbWritten = 0; 247 rc = RTSerialPortWriteNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToWrite, &cbWritten); 248 if (RT_SUCCESS(rc)) 249 { 250 pSerBuf->cbTxRxMax -= cbWritten; 251 pSerBuf->offBuf += cbWritten; 252 pSerBuf->cbTxRxLeft -= cbWritten; 253 } 254 } 255 256 return rc; 257 } 258 259 260 /** 261 * Receives dat from the given serial port into the supplied RX buffer and does some validity checking. 262 * 263 * @returns IPRT status code. 264 * @param hSerialPort The serial port handle to receive data from. 265 * @param pSerBuf The RX buffer pointer. 266 */ 267 static int serialTestRxBufRecv(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf) 268 { 269 int rc = VINF_SUCCESS; 270 271 if (pSerBuf->cbTxRxLeft) 272 { 273 size_t cbToRead = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft); 274 size_t cbRead = 0; 275 rc = RTSerialPortReadNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToRead, &cbRead); 276 if (RT_SUCCESS(rc)) 277 { 278 pSerBuf->offBuf += cbRead; 279 pSerBuf->cbTxRxMax -= cbRead; 280 pSerBuf->cbTxRxLeft -= cbRead; 281 } 282 } 283 284 return rc; 285 } 286 287 288 /** 289 * Verifies the data in the given RX buffer for correct transmission. 290 * 291 * @returns nothing. 292 * @param hTest The test handle to report errors to. 293 * @param pSerBuf The RX buffer pointer. 294 * @param iCntTx The current TX counter value the RX buffer should never get ahead of. 295 */ 296 static void serialTestRxBufVerify(RTTEST hTest, PSERIALTESTTXRXBUFCNT pSerBuf, uint32_t iCntTx) 297 { 298 uint32_t offRx = 0; 299 300 while (offRx + sizeof(uint32_t) < pSerBuf->offBuf) 301 { 302 uint32_t u32Val = *(uint32_t *)&pSerBuf->abBuf[offRx]; 303 offRx += sizeof(uint32_t); 304 305 if (RT_UNLIKELY(u32Val != ++pSerBuf->iCnt)) 306 RTTestFailed(hTest, "Data corruption/loss detected, expected counter value %u got %u\n", 307 pSerBuf->iCnt, u32Val); 308 } 309 310 if (RT_UNLIKELY(pSerBuf->iCnt > iCntTx)) 311 RTTestFailed(hTest, "Overtook the send buffer, expected maximum counter value %u got %u\n", 312 iCntTx, pSerBuf->iCnt); 313 314 /* Remove processed data from the buffer and move the rest to the front. */ 315 if (offRx) 316 { 317 memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[offRx], sizeof(pSerBuf->abBuf) - offRx); 318 pSerBuf->offBuf -= offRx; 319 pSerBuf->cbTxRxMax += offRx; 320 } 321 } 322 323 324 /** 325 * Runs a simple read/write test. 326 * 327 * @returns IPRT status code. 328 * @param pSerialTest The serial test configuration. 329 */ 330 static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest) 331 { 332 uint64_t tsStart = RTTimeMilliTS(); 333 SERIALTESTTXRXBUFCNT SerBufTx; 334 SERIALTESTTXRXBUFCNT SerBufRx; 335 336 serialTestTxBufInit(&SerBufTx, g_cbTx); 337 serialTestRxBufInit(&SerBufRx, g_cbTx); 338 339 int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx); 340 while ( RT_SUCCESS(rc) 341 && ( SerBufTx.cbTxRxLeft 342 || SerBufRx.cbTxRxLeft)) 343 { 344 uint32_t fEvts = 0; 345 uint32_t fEvtsQuery = 0; 346 if (SerBufTx.cbTxRxLeft) 347 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX; 348 if (SerBufRx.cbTxRxLeft) 349 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX; 350 351 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT); 352 if (RT_FAILURE(rc)) 353 break; 354 355 if (fEvts & RTSERIALPORT_EVT_F_DATA_RX) 356 { 357 rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx); 358 if (RT_FAILURE(rc)) 359 break; 360 361 serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, SerBufTx.iCnt); 362 } 363 if ( RT_SUCCESS(rc) 364 && (fEvts & RTSERIALPORT_EVT_F_DATA_TX)) 365 rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx); 366 } 367 368 uint64_t tsRuntime = RTTimeMilliTS() - tsStart; 369 tsRuntime /= 1000; /* Seconds */ 370 RTTestValue(pSerialTest->hTest, "Throughput", g_cbTx / tsRuntime, RTTESTUNIT_BYTES_PER_SEC); 371 372 return rc; 373 } 374 375 376 /** 377 * Returns an array of test descriptors get from the given string. 378 * 379 * @returns Pointer to the array of test descriptors. 380 * @param pszTests The string containing the tests separated with ':'. 381 */ 382 static PSERIALTESTDESC serialTestSelectFromCmdLine(const char *pszTests) 383 { 384 size_t cTests = 1; 385 386 const char *pszNext = strchr(pszTests, ':'); 387 while (pszNext) 388 { 389 pszNext++; 390 cTests++; 391 pszNext = strchr(pszNext, ':'); 392 } 393 394 PSERIALTESTDESC paTests = (PSERIALTESTDESC)RTMemAllocZ((cTests + 1) * sizeof(SERIALTESTDESC)); 395 if (RT_LIKELY(paTests)) 396 { 397 uint32_t iTest = 0; 398 399 pszNext = strchr(pszTests, ':'); 400 while (pszNext) 401 { 402 bool fFound = false; 403 404 pszNext++; /* Skip : character. */ 405 406 for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++) 407 { 408 if (!RTStrNICmp(pszTests, g_aSerialTests[i].pszId, pszNext - pszTests - 1)) 409 { 410 memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC)); 411 fFound = true; 412 break; 413 } 414 } 415 416 if (RT_UNLIKELY(!fFound)) 417 { 418 RTPrintf("Testcase \"%.*s\" not known\n", pszNext - pszTests - 1, pszTests); 419 RTMemFree(paTests); 420 return NULL; 421 } 422 423 pszTests = pszNext; 424 pszNext = strchr(pszTests, ':'); 425 } 426 427 /* Fill last descriptor. */ 428 bool fFound = false; 429 for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++) 430 { 431 if (!RTStrICmp(pszTests, g_aSerialTests[i].pszId)) 432 { 433 memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC)); 434 fFound = true; 435 break; 436 } 437 } 438 439 if (RT_UNLIKELY(!fFound)) 440 { 441 RTPrintf("Testcase \"%s\" not known\n", pszTests); 442 RTMemFree(paTests); 443 paTests = NULL; 444 } 445 } 446 else 447 RTPrintf("Failed to allocate test descriptors for %u selected tests\n", cTests); 448 449 return paTests; 100 450 } 101 451 … … 138 488 case 'l': 139 489 pszHelp = "Use the given serial port device as the loopback device"; 490 break; 491 case 't': 492 pszHelp = "The tests to run separated by ':'"; 493 break; 494 case 'x': 495 pszHelp = "Number of bytes to transmit during read/write tests"; 140 496 break; 141 497 default: … … 164 520 const char *pszDevice = NULL; 165 521 const char *pszDeviceLoopback = NULL; 522 PSERIALTESTDESC paTests = NULL; 166 523 167 524 RTGETOPTUNION ValueUnion; … … 229 586 } 230 587 break; 588 case 't': 589 paTests = serialTestSelectFromCmdLine(ValueUnion.psz); 590 if (!paTests) 591 return RTEXITCODE_FAILURE; 592 break; 593 case 'x': 594 g_cbTx = ValueUnion.u32; 595 break; 231 596 default: 232 597 return RTGetOptPrintError(rc, &ValueUnion); 233 598 } 599 } 600 601 if (!paTests) 602 { 603 /* Select all. */ 604 paTests = (PSERIALTESTDESC)RTMemAllocZ((RT_ELEMENTS(g_aSerialTests) + 1) * sizeof(SERIALTESTDESC)); 605 if (RT_UNLIKELY(!paTests)) 606 { 607 RTPrintf("Failed to allocate memory for test descriptors\n"); 608 return RTEXITCODE_FAILURE; 609 } 610 memcpy(paTests, &g_aSerialTests[0], RT_ELEMENTS(g_aSerialTests) * sizeof(SERIALTESTDESC)); 234 611 } 235 612 … … 273 650 274 651 if (RT_SUCCESS(rc)) 275 rc = serialTestRun(); 652 { 653 SERIALTEST Test; 654 PSERIALTESTDESC pTest = &paTests[0]; 655 656 Test.hTest = g_hTest; 657 Test.hSerialPort = g_hSerialPort; 658 Test.pSerialCfg = &g_SerialPortCfg; 659 660 while (pTest->pszId) 661 { 662 RTTestSub(g_hTest, pTest->pszDesc); 663 rc = pTest->pfnRun(&Test); 664 if (RT_FAILURE(rc)) 665 RTTestFailed(g_hTest, "Running test \"%s\" failed with %Rrc\n", pTest->pszId, rc); 666 667 RTTestSubDone(g_hTest); 668 pTest++; 669 } 670 } 276 671 } 277 672 else … … 287 682 RTTestFailed(g_hTest, "No device given on command line\n"); 288 683 684 RTMemFree(paTests); 289 685 RTEXITCODE rcExit = RTTestSummaryAndDestroy(g_hTest); 290 686 return rcExit;
Note:
See TracChangeset
for help on using the changeset viewer.