- Timestamp:
- Apr 30, 2019 4:32:43 PM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/utils/fs/FsPerf.cpp
r78284 r78358 343 343 { "--dir", 'd', RTGETOPT_REQ_STRING }, 344 344 { "--relative-dir", 'r', RTGETOPT_REQ_NOTHING }, 345 { "--comms-dir", 'c', RTGETOPT_REQ_STRING }, 346 { "--comms-slave", 'C', RTGETOPT_REQ_NOTHING }, 345 347 { "--seconds", 's', RTGETOPT_REQ_UINT32 }, 346 348 { "--milliseconds", 'm', RTGETOPT_REQ_UINT64 }, … … 506 508 static size_t g_cchDeepDir; 507 509 510 /** The length of g_szCommsDir. */ 511 static size_t g_cchCommsDir; 512 /** The length of g_szCommsSubDir. */ 513 static size_t g_cchCommsSubDir; 514 508 515 /** The test directory (absolute). This will always have a trailing slash. */ 509 516 static char g_szDir[FSPERF_MAX_PATH]; … … 515 522 static char g_szDeepDir[FSPERF_MAX_PATH + _1K]; 516 523 524 /** The communcations directory. This will always have a trailing slash. */ 525 static char g_szCommsDir[FSPERF_MAX_PATH]; 526 /** The communcations subdirectory used for the actual communication. This will 527 * always have a trailing slash. */ 528 static char g_szCommsSubDir[FSPERF_MAX_PATH]; 517 529 518 530 /** … … 616 628 g_szDeepDir[g_cchDeepDir + cchAppend] = '\0'; 617 629 return &g_szDeepDir[0]; 630 } 631 632 633 634 /********************************************************************************************************************************* 635 * Slave FsPerf Instance Interaction. * 636 *********************************************************************************************************************************/ 637 638 /** 639 * Construct a path relative to the comms directory. 640 * 641 * @returns g_szCommsDir. 642 * @param pszAppend What to append. 643 * @param cchAppend How much to append. 644 */ 645 DECLINLINE(char *) InCommsDir(const char *pszAppend, size_t cchAppend) 646 { 647 Assert(g_szCommsDir[g_cchCommsDir - 1] == RTPATH_SLASH); 648 memcpy(&g_szCommsDir[g_cchCommsDir], pszAppend, cchAppend); 649 g_szCommsDir[g_cchCommsDir + cchAppend] = '\0'; 650 return &g_szCommsDir[0]; 651 } 652 653 654 /** 655 * Construct a path relative to the comms sub-directory. 656 * 657 * @returns g_szCommsSubDir. 658 * @param pszAppend What to append. 659 * @param cchAppend How much to append. 660 */ 661 DECLINLINE(char *) InCommsSubDir(const char *pszAppend, size_t cchAppend) 662 { 663 Assert(g_szCommsSubDir[g_cchCommsSubDir - 1] == RTPATH_SLASH); 664 memcpy(&g_szCommsSubDir[g_cchCommsSubDir], pszAppend, cchAppend); 665 g_szCommsSubDir[g_cchCommsSubDir + cchAppend] = '\0'; 666 return &g_szCommsSubDir[0]; 667 } 668 669 670 /** 671 * Creates a file under g_szCommsDir with the given content. 672 * 673 * Will modify g_szCommsDir to contain the given filename. 674 * 675 * @returns IPRT status code (fully bitched). 676 * @param pszFilename The filename. 677 * @param cchFilename The length of the filename. 678 * @param pszContent The file content. 679 * @param cchContent The length of the file content. 680 */ 681 static int FsPerfCommsWriteFile(const char *pszFilename, size_t cchFilename, const char *pszContent, size_t cchContent) 682 { 683 RTFILE hFile; 684 int rc = RTFileOpen(&hFile, InCommsDir(pszFilename, cchFilename), 685 RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE_REPLACE); 686 if (RT_SUCCESS(rc)) 687 { 688 rc = RTFileWrite(hFile, pszContent, cchContent, NULL); 689 if (RT_FAILURE(rc)) 690 RTMsgError("Error writing %#zx bytes to '%s': %Rrc", cchContent, g_szCommsDir, rc); 691 692 int rc2 = RTFileClose(hFile); 693 if (RT_FAILURE(rc2)) 694 { 695 RTMsgError("Error closing to '%s': %Rrc", g_szCommsDir, rc); 696 rc = rc2; 697 } 698 if (RT_FAILURE(rc)) 699 RTFileDelete(g_szCommsDir); 700 } 701 else 702 RTMsgError("Failed to create '%s': %Rrc", g_szCommsDir, rc); 703 return rc; 704 } 705 706 707 /** 708 * Creates a file under g_szCommsDir with the given content, then renames it 709 * into g_szCommsSubDir. 710 * 711 * Will modify g_szCommsSubDir to contain the final filename and g_szCommsDir to 712 * hold the temporary one. 713 * 714 * @returns IPRT status code (fully bitched). 715 * @param pszFilename The filename. 716 * @param cchFilename The length of the filename. 717 * @param pszContent The file content. 718 * @param cchContent The length of the file content. 719 */ 720 static int FsPerfCommsWriteFileAndRename(const char *pszFilename, size_t cchFilename, const char *pszContent, size_t cchContent) 721 { 722 int rc = FsPerfCommsWriteFile(pszFilename, cchFilename, pszContent, cchContent); 723 if (RT_SUCCESS(rc)) 724 { 725 rc = RTFileRename(g_szCommsDir, InCommsSubDir(pszFilename, cchFilename), RTPATHRENAME_FLAGS_REPLACE); 726 if (RT_FAILURE(rc)) 727 { 728 RTMsgError("Error renaming '%s' to '%s': %Rrc", g_szCommsDir, g_szCommsSubDir, rc); 729 RTFileDelete(g_szCommsDir); 730 } 731 } 732 return rc; 733 } 734 735 736 /** 737 * Reads the given file from the comms subdir, ensuring that it is terminated by 738 * an EOF (0x1d) character. 739 * 740 * @returns IPRT status code. 741 * @retval VERR_TRY_AGAIN if the file is incomplete. 742 * @retval VERR_FILE_TOO_BIG if the file is considered too big. 743 * @retval VERR_FILE_NOT_FOUND if not found. 744 * 745 * @param iSeqNo The sequence number. 746 * @param pszSuffix The filename suffix. 747 * @param ppszContent Where to return the content. 748 */ 749 static int FsPerfCommsReadFile(uint32_t iSeqNo, const char *pszSuffix, char **ppszContent) 750 { 751 *ppszContent = NULL; 752 753 RTStrPrintf(&g_szCommsSubDir[g_cchCommsSubDir], sizeof(g_szCommsSubDir) - g_cchCommsSubDir, "%u%s", iSeqNo, pszSuffix); 754 RTFILE hFile; 755 int rc = RTFileOpen(&hFile, g_szCommsSubDir, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN); 756 if (RT_SUCCESS(rc)) 757 { 758 size_t cbUsed = 0; 759 size_t cbAlloc = 16; /// @todo _1K 760 char *pszBuf = (char *)RTMemAllocZ(cbAlloc); 761 for (;;) 762 { 763 /* Do buffer resizing. */ 764 size_t cbMaxRead = cbAlloc - cbUsed - 1; 765 if (cbMaxRead < 8) 766 { 767 if (cbAlloc < _1M) 768 { 769 cbAlloc *= 2; 770 void *pvRealloced = RTMemRealloc(pszBuf, cbAlloc); 771 if (!pvRealloced) 772 { 773 rc = VERR_NO_MEMORY; 774 break; 775 } 776 pszBuf = (char *)pvRealloced; 777 RT_BZERO(&pszBuf[cbAlloc / 2], cbAlloc); 778 cbMaxRead = cbAlloc - cbUsed - 1; 779 } 780 else 781 { 782 RTMsgError("File '%s' is too big - giving up at 1MB", g_szCommsSubDir); 783 rc = VERR_FILE_TOO_BIG; 784 break; 785 } 786 } 787 788 /* Do the reading. */ 789 size_t cbActual = 0; 790 rc = RTFileRead(hFile, &pszBuf[cbUsed], cbMaxRead, &cbActual); 791 if (RT_FAILURE(rc)) 792 { 793 RTMsgError("Failed to read '%s': %Rrc", g_szCommsSubDir, rc); 794 break; 795 } 796 797 /* EOF? */ 798 if (cbActual < cbMaxRead) 799 break; 800 } 801 802 RTFileClose(hFile); 803 804 /* 805 * Check if the file ends with the EOF marker. 806 */ 807 if ( RT_SUCCESS(rc) 808 && ( cbUsed == 0 809 || pszBuf[cbUsed - 1] != 0x1a)) 810 rc = VERR_TRY_AGAIN; 811 812 /* 813 * Return or free the content we've read. 814 */ 815 if (RT_SUCCESS(rc)) 816 *ppszContent = pszBuf; 817 else 818 RTMemFree(pszBuf); 819 } 820 else if (rc != VERR_FILE_NOT_FOUND && rc != VERR_SHARING_VIOLATION) 821 RTMsgError("Failed to open '%s': %Rrc", g_szCommsSubDir, rc); 822 return rc; 823 } 824 825 826 /** 827 * FsPerfCommsReadFile + renaming from the comms subdir to the comms dir. 828 * 829 * g_szCommsSubDir holds the original filename and g_szCommsDir the final 830 * filename on success. 831 */ 832 static int FsPerfCommsReadFileAndRename(uint32_t iSeqNo, const char *pszSuffix, const char *pszRenameSuffix, char **ppszContent) 833 { 834 RTStrPrintf(&g_szCommsDir[g_cchCommsDir], sizeof(g_szCommsDir) - g_cchCommsDir, "%u%s", iSeqNo, pszRenameSuffix); 835 int rc = FsPerfCommsReadFile(iSeqNo, pszSuffix, ppszContent); 836 if (RT_SUCCESS(rc)) 837 { 838 rc = RTFileRename(g_szCommsSubDir, g_szCommsDir, RTPATHRENAME_FLAGS_REPLACE); 839 if (RT_FAILURE(rc)) 840 { 841 RTMsgError("Error renaming '%s' to '%s': %Rrc", g_szCommsSubDir, g_szCommsDir, rc); 842 RTMemFree(*ppszContent); 843 *ppszContent = NULL; 844 } 845 } 846 return rc; 847 } 848 849 850 typedef struct FSPERFCOMMSSLAVESTATE 851 { 852 uint32_t iSeqNo; 853 bool fTerminate; 854 RTEXITCODE rcExit; 855 RTFILE ahFiles[8]; 856 char *apszFilenames[8]; 857 /** The current line number. */ 858 uint32_t iLineNo; 859 /** The current line content. */ 860 const char *pszLine; 861 /** Where to return extra error info text. */ 862 RTERRINFOSTATIC ErrInfo; 863 } FSPERFCOMMSSLAVESTATE; 864 865 866 static void FsPerfSlaveStateInit(FSPERFCOMMSSLAVESTATE *pState) 867 { 868 pState->iSeqNo = 0; 869 pState->fTerminate = false; 870 pState->rcExit = RTEXITCODE_SUCCESS; 871 unsigned i = RT_ELEMENTS(pState->ahFiles); 872 while (i-- > 0) 873 { 874 pState->ahFiles[i] = NIL_RTFILE; 875 pState->apszFilenames[i] = NULL; 876 } 877 RTErrInfoInitStatic(&pState->ErrInfo); 878 } 879 880 881 static void FsPerfSlaveStateCleanup(FSPERFCOMMSSLAVESTATE *pState) 882 { 883 unsigned i = RT_ELEMENTS(pState->ahFiles); 884 while (i-- > 0) 885 { 886 if (pState->ahFiles[i] != NIL_RTFILE) 887 { 888 RTFileClose(pState->ahFiles[i]); 889 pState->ahFiles[i] = NIL_RTFILE; 890 } 891 if (pState->apszFilenames[i] != NULL) 892 { 893 RTStrFree(pState->apszFilenames[i]); 894 pState->apszFilenames[i] = NULL; 895 } 896 } 897 } 898 899 900 static int FsPerfSlaveSyntax(FSPERFCOMMSSLAVESTATE *pState, const char *pszError, ...) 901 { 902 va_list va; 903 va_start(va, pszError); 904 RTErrInfoSetF(&pState->ErrInfo.Core, VERR_PARSE_ERROR, "line %u: syntax error: %N", pState->iLineNo, pszError, &va); 905 va_end(va); 906 return VERR_PARSE_ERROR; 907 } 908 909 910 static int FsPerfSlaveHandleOpen(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs) 911 { 912 RT_NOREF(pState, papszArgs, cArgs); 913 return VINF_SUCCESS; 914 } 915 916 917 static int FsPerfSlaveHandleClose(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs) 918 { 919 /* 920 * Parse parameters. 921 */ 922 if (cArgs != 1 + 1) 923 return FsPerfSlaveSyntax(pState, "The 'close' command takes 1 argument, not %u", cArgs); 924 uint32_t idxFile; 925 int rc = RTStrToUInt32Full(papszArgs[1], 0, &idxFile); 926 if (RT_FAILURE(rc)) 927 return FsPerfSlaveSyntax(pState, "Invalid 'close' argument #1 (%Rrc): %s", rc, papszArgs[1]); 928 if (idxFile >= RT_ELEMENTS(pState->ahFiles)) 929 return FsPerfSlaveSyntax(pState, "The 'close' argument idxFile is out of range: %u", idxFile); 930 931 /* 932 * Do it. 933 */ 934 rc = RTFileClose(pState->ahFiles[idxFile]); 935 if (RT_SUCCESS(rc)) 936 pState->ahFiles[idxFile] = NIL_RTFILE; 937 938 return rc; 939 } 940 941 942 /** 943 * Executes a script line. 944 */ 945 static int FsPerfSlaveExecuteLine(FSPERFCOMMSSLAVESTATE *pState, char *pszLine) 946 { 947 /* 948 * Parse the command line using bourne shell quoting style. 949 */ 950 char **papszArgs; 951 int cArgs; 952 int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL); 953 if (RT_FAILURE(rc)) 954 return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "Failed to parse line %u: %s", pState->iLineNo, pszLine); 955 if (cArgs <= 0) 956 { 957 RTGetOptArgvFree(papszArgs); 958 return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "No command found on line %u: %s", pState->iLineNo, pszLine); 959 } 960 961 /* 962 * Execute the command. 963 */ 964 static const struct 965 { 966 const char *pszCmd; 967 size_t cchCmd; 968 int (*pfnHandler)(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs); 969 } s_aHandlers[] = 970 { 971 { RT_STR_TUPLE("open"), FsPerfSlaveHandleOpen }, 972 { RT_STR_TUPLE("close"), FsPerfSlaveHandleClose }, 973 }; 974 const char * const pszCmd = papszArgs[0]; 975 size_t const cchCmd = strlen(pszCmd); 976 for (size_t i = 0; i < RT_ELEMENTS(s_aHandlers); i++) 977 if ( s_aHandlers[i].cchCmd == cchCmd 978 && memcmp(pszCmd, s_aHandlers[i].pszCmd, cchCmd) == 0) 979 { 980 rc = s_aHandlers[i].pfnHandler(pState, papszArgs, cArgs); 981 RTGetOptArgvFree(papszArgs); 982 return rc; 983 } 984 985 rc = RTErrInfoSetF(&pState->ErrInfo.Core, VERR_NOT_FOUND, "Command on line %u not found: %s", pState->iLineNo, pszLine); 986 RTGetOptArgvFree(papszArgs); 987 return rc; 988 } 989 990 991 /** 992 * Executes a script. 993 */ 994 static int FsPerfSlaveExecuteScript(FSPERFCOMMSSLAVESTATE *pState, char *pszContent) 995 { 996 /* 997 * Validate the encoding. 998 */ 999 int rc = RTStrValidateEncoding(pszContent); 1000 if (RT_FAILURE(rc)) 1001 return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "Invalid UTF-8 encoding"); 1002 1003 /* 1004 * Work the script content line by line. 1005 */ 1006 pState->iLineNo = 0; 1007 while (*pszContent != 0x1d && *pszContent != '\0') 1008 { 1009 pState->iLineNo++; 1010 1011 /* Figure the current line and move pszContent ahead: */ 1012 char *pszLine = RTStrStripL(pszContent); 1013 char *pszEol = strchr(pszLine, '\n'); 1014 if (pszEol) 1015 pszContent = pszEol + 1; 1016 else 1017 { 1018 pszEol = strchr(pszLine, 0x1d); 1019 AssertStmt(pszEol, pszEol = strchr(pszLine, '\0')); 1020 pszContent = pszEol; 1021 } 1022 1023 /* Terminate and strip it: */ 1024 *pszEol = '\0'; 1025 pszLine = RTStrStrip(pszLine); 1026 1027 /* Skip empty lines and comment lines: */ 1028 if (*pszLine == '\0' || *pszLine == '#') 1029 continue; 1030 1031 /* Execute the line: */ 1032 pState->pszLine = pszLine; 1033 rc = FsPerfSlaveExecuteLine(pState, pszLine); 1034 if (RT_FAILURE(rc)) 1035 break; 1036 } 1037 return rc; 1038 } 1039 1040 1041 /** 1042 * Communication slave. 1043 * 1044 * @returns exit code. 1045 */ 1046 static int FsPerfCommsSlave(void) 1047 { 1048 /* 1049 * Make sure we've got a directory and create it and it's subdir. 1050 */ 1051 if (g_cchCommsDir == 0) 1052 return RTMsgError("no communcation directory was specified (-C)"); 1053 1054 int rc = RTDirCreateFullPath(g_szCommsSubDir, 0775); 1055 if (RT_FAILURE(rc)) 1056 return RTMsgError("Failed to create '%s': %Rrc", g_szCommsSubDir, rc); 1057 1058 /* 1059 * Signal that we're here. 1060 */ 1061 char szTmp[_4K]; 1062 rc = FsPerfCommsWriteFile(RT_STR_TUPLE("slave.pid"), szTmp, RTStrPrintf(szTmp, sizeof(szTmp), "%u\x1d", RTProcSelf())); 1063 if (RT_FAILURE(rc)) 1064 return RTEXITCODE_FAILURE; 1065 1066 /* 1067 * Processing loop. 1068 */ 1069 FSPERFCOMMSSLAVESTATE State; 1070 FsPerfSlaveStateInit(&State); 1071 while (!State.fTerminate) 1072 { 1073 /* 1074 * Try read the next command script. 1075 */ 1076 char *pszContent = NULL; 1077 rc = FsPerfCommsReadFileAndRename(State.iSeqNo, "-order.send", "-order.ack", &pszContent); 1078 if (RT_SUCCESS(rc)) 1079 { 1080 /* 1081 * Execute it. 1082 */ 1083 RTErrInfoInitStatic(&State.ErrInfo); 1084 rc = FsPerfSlaveExecuteScript(&State, pszContent); 1085 1086 /* 1087 * Write the result. 1088 */ 1089 char szResult[64]; 1090 size_t cchResult = RTStrPrintf(szResult, sizeof(szResult), "%u-order.done", State.iSeqNo); 1091 size_t cchTmp = RTStrPrintf(szTmp, sizeof(szTmp), "%d\n%s\x1d", 1092 rc, RTErrInfoIsSet(&State.ErrInfo.Core) ? State.ErrInfo.Core.pszMsg : ""); 1093 FsPerfCommsWriteFile(szResult, cchResult, szTmp, cchTmp); 1094 State.iSeqNo++; 1095 } 1096 1097 /* 1098 * Wait a little and check again. 1099 */ 1100 if (rc == VERR_TRY_AGAIN || rc == VERR_SHARING_VIOLATION) 1101 RTThreadSleep(8); 1102 else 1103 RTThreadSleep(64); 1104 } 1105 1106 /* 1107 * Remove the we're here indicator and quit. 1108 */ 1109 RTFileDelete(InCommsDir(RT_STR_TUPLE("slave.pid"))); 1110 FsPerfSlaveStateCleanup(&State); 1111 return State.rcExit; 618 1112 } 619 1113 … … 4441 4935 RTStrPrintf(szDefaultDir, sizeof(szDefaultDir), "fstestdir-%u" RTPATH_SLASH_STR, RTProcSelf()); 4442 4936 4937 bool fCommsSlave = false; 4938 4443 4939 RTGETOPTUNION ValueUnion; 4444 4940 RTGETOPTSTATE GetState; … … 4448 4944 switch (rc) 4449 4945 { 4946 case 'c': 4947 if (!g_fRelativeDir) 4948 rc = RTPathAbs(ValueUnion.psz, g_szCommsDir, sizeof(g_szCommsDir) - 128); 4949 else 4950 rc = RTStrCopy(g_szCommsDir, sizeof(g_szCommsDir) - 128, ValueUnion.psz); 4951 if (RT_FAILURE(rc)) 4952 { 4953 RTTestFailed(g_hTest, "%s(%s) failed: %Rrc\n", g_fRelativeDir ? "RTStrCopy" : "RTAbsPath", pszDir, rc); 4954 return RTTestSummaryAndDestroy(g_hTest); 4955 } 4956 RTPathEnsureTrailingSeparator(g_szCommsDir, sizeof(g_szCommsDir)); 4957 g_cchCommsDir = strlen(g_szCommsDir); 4958 4959 rc = RTPathAppend(g_szCommsSubDir, sizeof(g_szCommsSubDir) - 128, "comms" RTPATH_SLASH_STR); 4960 if (RT_FAILURE(rc)) 4961 { 4962 RTTestFailed(g_hTest, "RTPathAppend(%s,,'comms/') failed: %Rrc\n", g_szCommsDir, rc); 4963 return RTTestSummaryAndDestroy(g_hTest); 4964 } 4965 g_cchCommsSubDir = strlen(g_szCommsSubDir); 4966 break; 4967 4968 case 'C': 4969 fCommsSlave = true; 4970 break; 4971 4450 4972 case 'd': 4451 4973 pszDir = ValueUnion.psz; … … 4666 5188 } 4667 5189 } 5190 5191 /* 5192 * If communcation slave, go do that and be done. 5193 */ 5194 if (fCommsSlave) 5195 return FsPerfCommsSlave(); 4668 5196 4669 5197 /*
Note:
See TracChangeset
for help on using the changeset viewer.