VirtualBox

Ignore:
Timestamp:
Apr 30, 2019 4:32:43 PM (6 years ago)
Author:
vboxsync
Message:

FsPerf: Working on adding a slave that runs on the host and so we can check whether the guest sees host changes as they occur. bugref:9172

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/utils/fs/FsPerf.cpp

    r78284 r78358  
    343343    { "--dir",              'd', RTGETOPT_REQ_STRING  },
    344344    { "--relative-dir",     'r', RTGETOPT_REQ_NOTHING },
     345    { "--comms-dir",        'c', RTGETOPT_REQ_STRING  },
     346    { "--comms-slave",      'C', RTGETOPT_REQ_NOTHING },
    345347    { "--seconds",          's', RTGETOPT_REQ_UINT32  },
    346348    { "--milliseconds",     'm', RTGETOPT_REQ_UINT64  },
     
    506508static size_t       g_cchDeepDir;
    507509
     510/** The length of g_szCommsDir. */
     511static size_t       g_cchCommsDir;
     512/** The length of g_szCommsSubDir. */
     513static size_t       g_cchCommsSubDir;
     514
    508515/** The test directory (absolute).  This will always have a trailing slash. */
    509516static char         g_szDir[FSPERF_MAX_PATH];
     
    515522static char         g_szDeepDir[FSPERF_MAX_PATH + _1K];
    516523
     524/** The communcations directory.  This will always have a trailing slash. */
     525static char         g_szCommsDir[FSPERF_MAX_PATH];
     526/** The communcations subdirectory used for the actual communication.  This will
     527 * always have a trailing slash. */
     528static char         g_szCommsSubDir[FSPERF_MAX_PATH];
    517529
    518530/**
     
    616628    g_szDeepDir[g_cchDeepDir + cchAppend] = '\0';
    617629    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 */
     645DECLINLINE(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 */
     661DECLINLINE(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 */
     681static 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 */
     720static 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 */
     749static 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 */
     832static 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
     850typedef 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
     866static 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
     881static 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
     900static 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
     910static int FsPerfSlaveHandleOpen(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
     911{
     912    RT_NOREF(pState, papszArgs, cArgs);
     913    return VINF_SUCCESS;
     914}
     915
     916
     917static 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 */
     945static 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 */
     994static 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 */
     1046static 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;
    6181112}
    6191113
     
    44414935    RTStrPrintf(szDefaultDir, sizeof(szDefaultDir), "fstestdir-%u" RTPATH_SLASH_STR, RTProcSelf());
    44424936
     4937    bool fCommsSlave = false;
     4938
    44434939    RTGETOPTUNION ValueUnion;
    44444940    RTGETOPTSTATE GetState;
     
    44484944        switch (rc)
    44494945        {
     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
    44504972            case 'd':
    44514973                pszDir = ValueUnion.psz;
     
    46665188        }
    46675189    }
     5190
     5191    /*
     5192     * If communcation slave, go do that and be done.
     5193     */
     5194    if (fCommsSlave)
     5195        return FsPerfCommsSlave();
    46685196
    46695197    /*
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette