| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- // rlrun.c
- //
- // executable regression worker for rl.c
- #include "rl.h"
- #define TMP_PREFIX "ex" // 2 characters
- #define POGO_PGD "rlpogo.pgd"
- // In the RL environment running Pogo tests, some warnings in the optimization
- // compile should be errors, since we do the instrumentation and optimization
- // compiles back-to-back.
- // 4951 "'%s' has been edited since profile data was collected, function profile data not used"
- // 4952 "'%s' : no profile data found in program database '%s'"
- // 4953 "Inlinee '%s' has been edited since profile data was collected, profile data not used"
- // 4961 "No profile data was merged into '%s', profile-guided optimizations disabled"
- // 4962 "Profile-guided optimizations disabled because profile data became inconsistent"
- // 4963 "'%s' : no profile data found; different compiler options were used in instrumented build"
- static const char *PogoForceErrors = "-we4951 -we4952 -we4953 -we4961 -we4962 -we4963";
- //
- // Global variables set before worker threads start, and only accessed
- // (not set) by the worker threads.
- //
- // sets of options to iterate over
- const char *OptFlags[MAXOPTIONS + 1], *PogoOptFlags[MAXOPTIONS + 1];
- // use a big global array as scratch pad for passing the child process env vars
- #define MAX_ENV_LEN 10000
- __declspec(thread) char EnvFlags[MAX_ENV_LEN];
- //
- // Global variables read and written by the worker threads: these need to
- // either be protected by synchronization or use thread-local storage.
- //
- // currently, none
- LOCAL void __cdecl
- RunCleanUp()
- {
- }
- void
- RunInit()
- {
- char *opts; // value of EXEC_TESTS_FLAGS environment variable
- int numOptions;
- int numPogoOptions;
- int i;
- atexit(RunCleanUp);
- // Break EXEC_TESTS up into different sets of flags. The sets should
- // be separated by semi-colons. Options don't apply to Pogo testing
- // unless prefixed with POGO_TEST_PREFIX. Those options _only_ apply
- // to Pogo tests.
- opts = EXEC_TESTS_FLAGS;
- ASSERTNR(opts);
- numOptions = numPogoOptions = 0;
- while (opts) {
- while (isspace(*opts))
- opts++;
- if (*opts == '\0')
- break;
- if (!_strnicmp(opts, POGO_TEST_PREFIX, strlen(POGO_TEST_PREFIX))) {
- opts += strlen(POGO_TEST_PREFIX);
- PogoOptFlags[numPogoOptions] = opts;
- ++numPogoOptions;
- if (numPogoOptions == MAXOPTIONS)
- Fatal("Too many options in EXEC_TESTS_FLAGS");
- }
- else {
- OptFlags[numOptions] = opts;
- ++numOptions;
- if (numOptions == MAXOPTIONS)
- Fatal("Too many options in EXEC_TESTS_FLAGS");
- }
- opts = strchr(opts, ';');
- if (opts)
- *opts++ = '\0';
- }
- for (i = 0; i < numPogoOptions; i++) {
- if (strstr(PogoOptFlags[i], "GL") == NULL) {
- Fatal("Pogo without LTCG is not supported");
- }
- }
- OptFlags[numOptions] = NULL;
- PogoOptFlags[numPogoOptions] = NULL;
- if (FVerbose) {
- printf("(Normal) Exec flags:");
- for (i = 0; i < numOptions; i++) {
- printf(" '%s'", OptFlags[i]);
- }
- printf("\nPogo Exec flags:");
- for (i = 0; i < numPogoOptions; i++) {
- printf(" '%s'", PogoOptFlags[i]);
- }
- printf("\n");
- }
- }
- BOOL
- RunStartDir(
- char * /*dir -- unused*/
- )
- {
- return TRUE;
- }
- void
- DumpFileToLog(
- char* path
- )
- {
- FILE* fp;
- char buf[BUFFER_SIZE];
- char* p;
- fp = fopen_unsafe(path, "r");
- if (fp == NULL) {
- LogError("ERROR: DumpFileToLog couldn't open file '%s' with error '%s'", path, strerror_unsafe(errno));
- }
- else {
- int fd = _fileno(fp);
- struct _stat64 fileStats;
- if(fd != -1 && _fstat64(fd, &fileStats) != -1)
- {
- char creationTime[256];
- char accessTime[256];
- char currTime[256];
- __time64_t now = _time64(NULL);
- _ctime64_s(currTime, &now);
- _ctime64_s(creationTime, &fileStats.st_ctime);
- _ctime64_s(accessTime, &fileStats.st_atime);
- auto stripNewline = [](char *buf) {
- if(char *ptr = strchr(buf, '\n'))
- *ptr = '\0';
- };
- stripNewline(creationTime);
- stripNewline(accessTime);
- stripNewline(currTime);
- LogOut("ERROR: name of output file: %s; size: %I64d; creation: %s, last access: %s, now: %s", path, fileStats.st_size, creationTime, accessTime, currTime);
- }
- LogOut("ERROR: bad output file follows ============");
- while (fgets(buf, BUFFER_SIZE, fp) != NULL) {
- // Strip the newline, since LogOut adds one
- p = strchr(buf, '\n');
- if (p != NULL) {
- *p = '\0';
- }
- LogOut("%s", buf);
- }
- fclose(fp);
- LogOut("ERROR: end of bad output file ============");
- }
- }
- // Use a state machine to recognize the word "pass"
- BOOL
- LookForPass(
- char *p
- )
- {
- int state = 0;
- for(; *p != '\0'; p++) {
- switch(tolower(*p)) {
- case 'p':
- state = 1;
- break;
- case 'a':
- if (state == 1)
- state = 2;
- else
- state = 0;
- break;
- case 's':
- if (state == 2)
- state = 3;
- else if (state == 3)
- return TRUE;
- else
- state = 0;
- break;
- default:
- state = 0;
- }
- }
- return FALSE;
- }
- // Return TRUE if the specified test is Pogo-specific.
- BOOL
- IsPogoTest(
- Test * pTest
- )
- {
- return HasInfo(pTest->defaultTestInfo.data[TIK_TAGS], XML_DELIM, "Pogo");
- }
- // Return TRUE if the specified test should NOT use nogpfnt.obj.
- BOOL
- SuppressNoGPF(
- Test * pTest
- )
- {
- return HasInfo(pTest->defaultTestInfo.data[TIK_RL_DIRECTIVES], XML_DELIM,
- "NoGPF");
- }
- template <size_t bufSize>
- void
- FillNoGPFFlags(
- char (&nogpfFlags)[bufSize],
- BOOL fSuppressNoGPF
- )
- {
- nogpfFlags[0] = '\0';
- if (FNogpfnt && TargetInfo[TargetMachine].fUseNoGPF) {
- if (!fSuppressNoGPF) {
- sprintf_s(nogpfFlags,
- " %s\\bin\\%s\\nogpfnt.obj /entry:nogpfntStartup",
- REGRESS, TargetInfo[TargetMachine].name);
- }
- }
- }
- BOOL
- CheckForPass(char * filename, char * optReportBuf, char * cmdbuf, BOOL fDumpOutputFile = TRUE)
- {
- FILE * fp;
- char buf[BUFFER_SIZE];
- // Check to see if the exe ran at all.
- fp = fopen_unsafe(filename, "r");
- if (fp == NULL) {
- LogOut("ERROR: Test failed to run. Unable to open file '%s', error '%s' (%s):", filename, strerror_unsafe(errno), optReportBuf);
- LogOut(" %s", cmdbuf);
- return FALSE;
- }
- // Parse the output file and verify that all lines must be pass/passed, or empty lines
- BOOL pass = FALSE;
- while(fgets(buf, BUFFER_SIZE, fp) != NULL)
- {
- if(!_strcmpi(buf, "pass\n") || !_strcmpi(buf, "passed\n"))
- {
- // Passing strings were found - pass
- pass = TRUE;
- }
- else if(_strcmpi(buf, "\n") != 0)
- {
- // Something else other than a newline was found - this is a failure.
- pass = FALSE;
- break;
- }
- }
- fclose(fp);
- if (!pass)
- {
- LogOut("ERROR: Test failed to run correctly: pass not found in output file (%s):", optReportBuf);
- LogOut(" %s", cmdbuf);
- if (fDumpOutputFile)
- {
- DumpFileToLog(filename);
- }
- }
- return pass;
- }
- void CopyRebaseFile(PCSTR testout, PCSTR baseline)
- {
- if (FRebase)
- {
- char rebase[_MAX_PATH];
- sprintf_s(rebase, "%s.rebase", baseline);
- CopyFile(testout, rebase, FALSE);
- }
- }
- // Handle external test scripts. We support three kinds, makefiles (rl.mak),
- // command shell (dotest.cmd), and JScript (*.js).
- //
- // Standardized makefiles have the following targets:
- // clean: delete all generated files
- // build: build the test (OPT=compile options)
- // run: run the test
- // copy: copy the generated files to a subdirectory (COPYDIR=subdir)
- //
- int
- DoOneExternalTest(
- CDirectory* pDir,
- TestVariant *pTestVariant,
- const char *optFlags,
- const char *inCCFlags,
- const char *inLinkFlags,
- const char *testCmd,
- ExternalTestKind kind,
- BOOL fSyncVariationWhenFinished,
- BOOL fCleanBefore,
- BOOL fCleanAfter,
- BOOL fSuppressNoGPF,
- void *envFlags,
- DWORD millisecTimeout
- )
- {
- #define NMAKE "nmake -nologo -R -f "
- char full[MAX_PATH];
- char cmdbuf[BUFFER_SIZE];
- char buf[BUFFER_SIZE];
- char ccFlags[BUFFER_SIZE];
- char linkFlags[BUFFER_SIZE];
- char nogpfFlags[BUFFER_SIZE];
- char optReportBuf[BUFFER_SIZE];
- char nonZeroReturnBuf[BUFFER_SIZE];
- const char *reason = NULL;
- time_t start_variation;
- UINT elapsed_variation;
- time_t start_build_variation;
- UINT elapsed_build_variation;
- LARGE_INTEGER start_run, end_run, frequency;
- UINT elapsed_run;
- BOOL fFailed = FALSE;
- BOOL fDumpOutputFile = FVerbose;
- BOOL fFileToDelete = FALSE;
- int cmdResult;
- static unsigned int testCount = 0;
- unsigned int localTestCount = InterlockedIncrement(&testCount);
- // Avoid conditionals by copying/creating ccFlags appropriately.
- if (inCCFlags)
- {
- if (pDir->HasTestInfoData(TIK_SOURCE_PATH))
- {
- sprintf_s(ccFlags, " %s -baselinePath:%s", inCCFlags, pDir->GetDirectoryPath());
- }
- else
- {
- sprintf_s(ccFlags, " %s", inCCFlags);
- }
- }
- else
- {
- ccFlags[0] = '\0';
- }
- switch (TargetMachine) {
- case TM_WVM:
- case TM_WVMX86:
- case TM_WVM64:
- strcat_s(ccFlags, " /BC ");
- break;
- }
- if (inLinkFlags)
- strcpy_s(linkFlags, inLinkFlags);
- else
- linkFlags[0] = '\0';
- sprintf_s(optReportBuf, "%s%s%s", optFlags, *linkFlags ? ";" : "", linkFlags);
- // Update the status.
- sprintf_s(buf, " (%s)", optReportBuf);
- ThreadInfo[ThreadId].SetCurrentTest(pDir->GetDirectoryName(),
- buf, pDir->IsBaseline());
- UpdateTitleStatus();
- // Make sure the file that will say pass or fail is not present.
- sprintf_s(full, "%s\\testout%d", pDir->GetDirectoryPath(), localTestCount);
- DeleteFileIfFound(full);
- start_variation = time(NULL);
- if (kind == TK_MAKEFILE) {
- Message(""); // newline
- Message("Processing %s with '%s' flags",
- testCmd, optReportBuf);
- Message(""); // newline
- if (FTest)
- {
- return 0;
- }
- if (fCleanBefore) {
- // Clean the directory.
- sprintf_s(cmdbuf, NMAKE"%s clean", testCmd);
- Message(cmdbuf);
- ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf);
- }
- FillNoGPFFlags(nogpfFlags, fSuppressNoGPF);
- // Build the test.
- start_build_variation = time(NULL);
- sprintf_s(cmdbuf, NMAKE"%s build OPT=\"%s %s%s\" LINKFLAGS=\"%s %s %s\"",
- testCmd,
- optFlags, EXTRA_CC_FLAGS, ccFlags,
- LINKFLAGS, linkFlags, nogpfFlags);
- if (strlen(cmdbuf) > BUFFER_SIZE - 1)
- Fatal("Buffer overrun");
- Message(cmdbuf);
- fFailed = ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf);
- elapsed_build_variation = (int)(time(NULL) - start_build_variation);
- if (Timing & TIME_VARIATION) {
- Message("RL: Variation elapsed time (build) (%s, %s, %s): %02d:%02d",
- pDir->GetDirectoryName(),
- "rl.mak",
- optReportBuf,
- elapsed_build_variation / 60, elapsed_build_variation % 60);
- }
- if (fFailed) {
- reason = "build failure";
- goto logFailure;
- }
- // Run the test.
- QueryPerformanceCounter(&start_run);
- sprintf_s(cmdbuf, NMAKE"%s run", testCmd);
- Message(cmdbuf);
- cmdResult = ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf, millisecTimeout);
- QueryPerformanceCounter(&end_run);
- QueryPerformanceFrequency(&frequency);
- elapsed_run = (int) (((end_run.QuadPart - start_run.QuadPart) * 1000UI64) / frequency.QuadPart);
- if (Timing & TIME_VARIATION) {
- Message("RL: Variation elapsed time (run) (%s, %s, %s): %02d:%02d.%03d",
- pDir->GetDirectoryName(),
- "rl.mak",
- optReportBuf,
- elapsed_run / 60000, (elapsed_run % 60000)/1000, elapsed_run % 1000);
- }
- }
- else if (kind == TK_CMDSCRIPT)
- {
- // Build up the test command string
- sprintf_s(cmdbuf, "%s %s %s%s >testout%d", testCmd, optFlags, EXTRA_CC_FLAGS, ccFlags, localTestCount);
- Message("Running '%s'", cmdbuf);
- if (FTest)
- {
- return 0;
- }
- cmdResult = ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf, millisecTimeout, envFlags);
- }
- else if (kind == TK_JSCRIPT || kind==TK_HTML || kind == TK_COMMAND)
- {
- char tempExtraCCFlags[MAX_PATH*2] = {0};
- // Only append when EXTRA_CC_FLAGS isn't empty.
- if (EXTRA_CC_FLAGS[0])
- {
- // Append test case unique identifier to the end of EXTRA_CC_FLAGS.
- if (FAppendTestNameToExtraCCFlags)
- {
- sprintf_s(tempExtraCCFlags, "%s.%s", EXTRA_CC_FLAGS, pTestVariant->testInfo.data[TIK_FILES]);
- }
- else
- {
- strcpy_s(tempExtraCCFlags, EXTRA_CC_FLAGS);
- }
- }
- const char* cmd = JCBinary;
- if (kind != TK_JSCRIPT && kind != TK_HTML)
- {
- cmd = pTestVariant->testInfo.data[TIK_COMMAND];
- }
- sprintf_s(cmdbuf, "%s %s %s %s %s >%s 2>&1", cmd, optFlags, tempExtraCCFlags, ccFlags, testCmd, full);
- Message("Running '%s'", cmdbuf);
- if(FTest)
- {
- DeleteFileIfFound(full);
- return 0;
- }
- cmdResult = ExecuteCommand(pDir->GetFullPathFromSourceOrDirectory(), cmdbuf, millisecTimeout, envFlags);
- if (cmdResult && cmdResult != WAIT_TIMEOUT && !pTestVariant->testInfo.data[TIK_BASELINE]) // failure code, not baseline diffing
- {
- fFailed = TRUE;
- sprintf_s(nonZeroReturnBuf, "non-zero (%08X) return value from test command", cmdResult);
- reason = nonZeroReturnBuf;
- goto logFailure;
- }
- }
- else
- {
- ASSERTNR(UNREACHED);
- cmdResult = NOERROR; // calm compiler warning about uninitialized variable usage
- }
- // Check for timeout.
- if (cmdResult == WAIT_TIMEOUT) {
- ASSERT(millisecTimeout != INFINITE);
- sprintf_s(nonZeroReturnBuf, "timed out after %u second%s", millisecTimeout / 1000, millisecTimeout == 1000 ? "" : "s");
- reason = nonZeroReturnBuf;
- fFailed = TRUE;
- goto logFailure;
- }
- // If we have a baseline test, we need to check the baseline file.
- if (pTestVariant->testInfo.data[TIK_BASELINE]) {
- char baseline_file[_MAX_PATH];
- sprintf_s(baseline_file, "%s\\%s", pDir->GetFullPathFromSourceOrDirectory(),
- pTestVariant->testInfo.data[TIK_BASELINE]);
- if (DoCompare(baseline_file, full)) {
- reason = "diffs from baseline";
- sprintf_s(optReportBuf, "%s", baseline_file);
- fFailed = TRUE;
- CopyRebaseFile(full, baseline_file);
- }
- }
- else if ((kind == TK_JSCRIPT || kind == TK_HTML || kind == TK_COMMAND) && !pTestVariant->testInfo.hasData[TIK_BASELINE]) {
- if (!CheckForPass(full, optReportBuf, cmdbuf, fDumpOutputFile)) {
- fFailed = TRUE;
- goto SkipLogFailure;
- }
- }
- logFailure:
- if (fFailed) {
- LogOut("ERROR: Test failed to run correctly: %s (%s):",
- reason, optReportBuf);
- LogOut(" %s", cmdbuf);
- if (fDumpOutputFile) {
- DumpFileToLog(full);
- }
- }
- SkipLogFailure:
- if (fFileToDelete && !FNoDelete) {
- DeleteFileRetryMsg(full);
- }
- elapsed_variation = (int)(time(NULL) - start_variation);
- if (Timing & TIME_VARIATION) {
- Message("RL: Variation elapsed time (%s, %s, %s): %02d:%02d",
- pDir->GetDirectoryName(),
- kind == TK_MAKEFILE ? "rl.mak" : "dotest.cmd",
- optReportBuf,
- elapsed_variation / 60, elapsed_variation % 60);
- }
- if (kind == TK_MAKEFILE) {
- // If the test failed and we are asked to copy the failures, do so.
- if (fFailed && FCopyOnFail) {
- sprintf_s(cmdbuf, NMAKE"%s copy COPYDIR=\"fail.%s.%s\"",
- testCmd, optFlags, linkFlags);
- Message(cmdbuf);
- ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf);
- }
- // Clean up after ourselves.
- if (!FNoDelete && (fFailed || fCleanAfter)) {
- sprintf_s(cmdbuf, NMAKE"%s clean", testCmd);
- Message(cmdbuf);
- ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf);
- }
- }
- if (FSyncVariation) {
- if (FRLFE && fFailed) {
- RLFEAddLog(pDir, RLFES_FAILED, testCmd,
- optReportBuf, ThreadOut->GetText());
- }
- if (fSyncVariationWhenFinished)
- FlushOutput();
- }
- DeleteFileIfFound(full);
- return fFailed ? -1 : 0;
- }
- // null terminated string of null terminated strings
- // name=data from testinfo and all of parent process env, arg for CreateProcess()
- void * GetEnvFlags
- (
- TestVariant * pTestVariant
- )
- {
- char temp[BUFFER_SIZE];
- size_t len = 0, totalEnvLen = 0;
- char * envFlags = NULL;
- Xml::Node *env = (Xml::Node *)pTestVariant->testInfo.data[TIK_ENV];
- if (env != NULL) {
- // use a fixed global array for memory
- memset(EnvFlags, '\0', MAX_ENV_LEN);
- *temp = '\0';
- for ( Xml::Node * child = env->ChildList; child != NULL; child = child->Next) {
- sprintf_s(temp, "%s=%s", child->Name, child->Data);
- if (envFlags == NULL) {
- sprintf_s(EnvFlags, "%s", temp);
- envFlags = EnvFlags;
- } else {
- strcat_s(envFlags, REMAININGARRAYLEN(EnvFlags, envFlags), temp);
- }
- len = strlen(envFlags);
- envFlags += len+1;
- }
- LPTSTR lpszParentEnv = GetEnvironmentStrings();
- totalEnvLen = len;
- ASSERT(totalEnvLen < BUFFER_SIZE);
- len = 0;
- while(!((lpszParentEnv[len] == '\0') && (lpszParentEnv[len+1] == '\0'))) {
- len++;
- }
- ASSERT(totalEnvLen+len+2 < MAX_ENV_LEN);
- memcpy(envFlags, lpszParentEnv, len+2);
- FreeEnvironmentStrings(lpszParentEnv);
- envFlags = EnvFlags;
- }
- return (void*)envFlags;
- }
- int
- DoExternalTest(
- CDirectory* pDir,
- TestVariant * pTestVariant,
- char *testCmd,
- ExternalTestKind kind,
- BOOL fSuppressNoGPF,
- DWORD millisecTimeout
- )
- {
- const char *ccFlags = pTestVariant->testInfo.data[TIK_COMPILE_FLAGS];
- void *envFlags = GetEnvFlags(pTestVariant);
- return DoOneExternalTest(pDir, pTestVariant, pTestVariant->optFlags, ccFlags, NULL,
- testCmd, kind, TRUE, TRUE, TRUE, fSuppressNoGPF, envFlags, millisecTimeout);
- }
- int
- DoPogoExternalTest(
- CDirectory* pDir,
- TestVariant * pTestVariant,
- char *testCmd,
- ExternalTestKind kind,
- BOOL fSuppressNoGPF,
- DWORD millisecTimeout
- )
- {
- static const char *pgc = "*.pgc";
- static const char *pgd = POGO_PGD;
- char pgdFull[MAX_PATH];
- char ccFlags[BUFFER_SIZE];
- char linkFlags[BUFFER_SIZE];
- char cmdbuf[BUFFER_SIZE];
- BOOL fFailed;
- void *envFlags = GetEnvFlags(pTestVariant);
- sprintf_s(pgdFull, "%s\\%s", pDir->GetDirectoryPath(), pgd);
- const char * inCCFlags = pTestVariant->testInfo.data[TIK_COMPILE_FLAGS];
- const char * optFlags = pTestVariant->optFlags;
- DeleteFileIfFound(pgdFull);
- DeleteMultipleFiles(pDir, pgc);
- fFailed = FALSE;
- // Pogo requires LTCG
- ASSERT(strstr(optFlags, "GL") != NULL);
- if (!kind == TK_MAKEFILE) {
- Warning("'%s\\%s' is not a makefile test; Pogo almost certainly won't work", pDir->GetDirectoryPath(), testCmd);
- }
- sprintf_s(ccFlags, "%s %s", PogoForceErrors, optFlags);
- sprintf_s(linkFlags, "-ltcg:pgi -pgd:%s", pgd);
- if (DoOneExternalTest(pDir, pTestVariant, ccFlags, inCCFlags, linkFlags,
- testCmd, kind, FALSE, TRUE, FALSE, fSuppressNoGPF, envFlags, millisecTimeout)) {
- fFailed = TRUE;
- goto logFailure;
- }
- sprintf_s(ccFlags, "%s %s", PogoForceErrors, optFlags);
- sprintf_s(linkFlags, "-ltcg:pgo -pgd:%s", pgd);
- // Manually erase EXE and DLL files to get makefile to relink.
- // Also erase ASM files because some makefiles try to rebuild from
- // them.
- DeleteMultipleFiles(pDir, "*.exe");
- DeleteMultipleFiles(pDir, "*.dll");
- DeleteMultipleFiles(pDir, "*.asm");
- if (DoOneExternalTest(pDir, pTestVariant, ccFlags, inCCFlags, linkFlags,
- testCmd, kind, FALSE, FALSE, TRUE, fSuppressNoGPF, envFlags, millisecTimeout)) {
- fFailed = TRUE;
- }
- logFailure:
- if (FSyncVariation) {
- if (FRLFE && fFailed) {
- sprintf_s(cmdbuf, "%s%s%s", ccFlags,
- *linkFlags ? ";" : "", linkFlags);
- RLFEAddLog(pDir, RLFES_FAILED, testCmd,
- cmdbuf, ThreadOut->GetText());
- }
- FlushOutput();
- }
- if (FRLFE)
- RLFETestStatus(pDir);
- if (!FNoDelete) {
- DeleteFileRetryMsg(pgdFull);
- DeleteMultipleFiles(pDir, pgc);
- }
- return fFailed ? -1 : 0;
- }
- BOOL
- DoOneSimpleTest(
- CDirectory *pDir,
- Test * pTest,
- TestVariant * pTestVariant,
- const char *optFlags,
- const char *inCCFlags,
- const char *inLinkFlags,
- BOOL fSyncVariationWhenFinished,
- BOOL fCleanAfter,
- BOOL fLinkOnly, // relink only
- BOOL fSuppressNoGPF,
- DWORD millisecTimeout
- )
- {
- int rc;
- char *p = NULL;
- char cmdbuf[BUFFER_SIZE*2];
- char ccFlags[BUFFER_SIZE];
- char linkFlags[BUFFER_SIZE];
- char nogpfFlags[BUFFER_SIZE];
- char optReportBuf[BUFFER_SIZE];
- char full[MAX_PATH];
- char exebuf[BUFFER_SIZE];
- char fullexebuf[BUFFER_SIZE];
- char buf[BUFFER_SIZE];
- char failDir[BUFFER_SIZE];
- char copyName[BUFFER_SIZE];
- char tmp_file1[MAX_PATH];
- char tmp_file2[MAX_PATH];
- time_t start_variation;
- UINT elapsed_variation;
- BOOL fFailed;
- void *envFlags = GetEnvFlags(pTestVariant);
- // Avoid conditionals by copying/creating ccFlags appropriately.
- if (inCCFlags)
- sprintf_s(ccFlags, " %s", inCCFlags);
- else
- ccFlags[0] = '\0';
- switch (TargetMachine) {
- case TM_WVM:
- case TM_WVMX86:
- case TM_WVM64:
- strcat_s(ccFlags, " /BC ");
- break;
- }
- if (inLinkFlags)
- strcpy_s(linkFlags, inLinkFlags);
- else
- linkFlags[0] = '\0';
- sprintf_s(optReportBuf, "%s%s%s", optFlags, *linkFlags ? ";" : "", linkFlags);
- // Figure out the exe name and path
- strcpy_s(exebuf, pTest->name);
- p = strrchr(exebuf, '.');
- if (p != NULL)
- {
- strcpy_s(p + 1, REMAININGARRAYLEN(exebuf, p + 1), "exe");
- }
- else
- {
- strcat_s(exebuf, ".exe");
- }
- sprintf_s(fullexebuf, "%s\\%s", pDir->GetDirectoryPath(), exebuf);
- start_variation = time(NULL);
- // Build up the compile command string.
- sprintf_s(cmdbuf, "%s %s%s %s", REGR_CL,
- optFlags, ccFlags, EXTRA_CC_FLAGS);
- for (StringList * pFile = pTest->files; pFile != NULL; pFile = pFile->next)
- {
- strcat_s(cmdbuf, " ");
- strcat_s(cmdbuf, pFile->string);
- // If we're only relinking, hammer the extension to .obj
- if (fLinkOnly)
- {
- p = strrchr(cmdbuf, '.');
- sprintf_s(p, REMAININGARRAYLEN(cmdbuf, p), ".obj");
- }
- }
- // Build the link option string.
- if (LINKFLAGS && LINKFLAGS[0] != '\0') {
- strcat_s(linkFlags, " ");
- strcat_s(linkFlags, LINKFLAGS);
- }
- FillNoGPFFlags(nogpfFlags, fSuppressNoGPF);
- strcat_s(linkFlags, nogpfFlags);
- switch (TargetMachine) {
- case TM_X86:
- case TM_IA64:
- case TM_AMD64:
- case TM_AMD64SYS:
- case TM_AM33:
- case TM_ARM:
- case TM_ARM64:
- case TM_THUMB:
- case TM_M32R:
- case TM_MIPS:
- case TM_SH3:
- case TM_SH4:
- case TM_SH5M:
- case TM_SH5C:
- case TM_WVMX86:
- if (*linkFlags) {
- strcat_s(cmdbuf, " /link ");
- strcat_s(cmdbuf, linkFlags);
- }
- break;
- case TM_WVM:
- strcat_s(cmdbuf, " /c ");
- break;
- case TM_PPCWCE:
- if (*linkFlags) {
- strcat_s(cmdbuf, " ");
- strcat_s(cmdbuf, linkFlags);
- }
- break;
- }
- sprintf_s(buf, "%s (%s)", pTest->name, optReportBuf);
- ThreadInfo[ThreadId].SetCurrentTest(pDir->GetDirectoryName(), buf, pDir->IsBaseline());
- UpdateTitleStatus();
- // Remove exe if it's already there. We have to keep trying to delete it
- // until it's gone, or else the link will fail (if it is somehow still in
- // use).
- DeleteFileRetryMsg(fullexebuf);
- if (FTest)
- {
- Message("%s", cmdbuf);
- if (pTestVariant->testInfo.data[TIK_BASELINE]) {
- Message(" (baseline %s)", pTestVariant->testInfo.data[TIK_BASELINE]);
- }
- return 0;
- }
- // Do the compile.
- Message("Compiling:");
- Message(" %s", cmdbuf);
- rc = ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf);
- // Some machines require separate linking of the
- // compiler and/or assembler output.
- if (rc == 0)
- {
- switch (TargetMachine)
- {
- case TM_WVM:
- // Build up the linker command string.
- strcpy_s(cmdbuf, LINKER);
- for (StringList * pFile = pTest->files;
- pFile != NULL;
- pFile = pFile->next)
- {
- strcat_s(cmdbuf, " ");
- strcat_s(cmdbuf, pFile->string);
- p = strrchr(cmdbuf, '.');
- strcpy_s(p + 1, REMAININGARRAYLEN(cmdbuf, p + 1), "obj");
- }
- if (linkFlags) {
- strcat_s(cmdbuf, " ");
- strcat_s(cmdbuf, linkFlags);
- }
- // Do the link.
- Message("Linking:");
- Message(" %s", cmdbuf);
- ExecuteCommand(pDir->GetDirectoryPath(), cmdbuf);
- break;
- default:
- break;
- }
- }
- // See if the compile succeeded by checking for the existence
- // of the executable.
- if ((rc != 0) || GetFileAttributes(fullexebuf) == INVALID_FILE_ATTRIBUTES) {
- LogOut("ERROR: Test failed to compile or link (%s):", optReportBuf);
- LogOut(" %s", cmdbuf);
- fFailed = TRUE;
- goto logFailure;
- }
- // Run the resulting exe.
- if (TargetVM) {
- strcpy_s(buf, TargetVM);
- strcat_s(buf, " ");
- strcat_s(buf, exebuf);
- // Copy the VM command to cmdbuf, so we get a useful error message
- // in the log file if test fails.
- strcpy_s(cmdbuf, buf);
- }
- else {
- strcpy_s(buf, exebuf);
- }
- // We need some temporary files.
- // Note: these are full pathnames, not relative pathnames. Also, note that
- // mytmpnam creates the file to be used. To avoid losing that file, and
- // risking another program using it, don't delete the file before use.
- // We currently delete the file before running the test, so we can see if
- // the test ever creates it. We should probably create the temp files in
- // the same directory as the test, since we guarantee that no other copies
- // of RL are running in the same directory.
- if (mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file1) == NULL ||
- mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file2) == NULL) {
- Fatal("Unable to create temporary files");
- }
- ThreadInfo[ThreadId].AddToTmpFileList(tmp_file1);
- ThreadInfo[ThreadId].AddToTmpFileList(tmp_file2);
- if (FVerbose)
- Message("INFO: tmp file 1 = %s, tmp file 2 = %s", tmp_file1, tmp_file2);
- Message("Running the test (%s)", buf);
- strcat_s(buf, " > ");
- strcat_s(buf, tmp_file1);
- // Make sure the output file isn't there.
- DeleteFileIfFound(tmp_file1);
- fFailed = FALSE;
- // Check for timeout.
- {
- int retval = ExecuteCommand(pDir->GetDirectoryPath(), buf, millisecTimeout, envFlags);
- if (retval == WAIT_TIMEOUT) {
- ASSERT(millisecTimeout != INFINITE);
- LogOut("ERROR: Test timed out after %ul seconds", millisecTimeout / 1000);
- fFailed = TRUE;
- goto logFailure;
- }
- }
- // Check the output.
- if (pTestVariant->testInfo.data[TIK_BASELINE]) {
- int spiff_ret;
- // Check to see if the exe ran at all.
- if (GetFileAttributes(tmp_file1) == INVALID_FILE_ATTRIBUTES) {
- LogOut("ERROR: Test failed to run. Couldn't find file '%s' (%s):", tmp_file1, optReportBuf);
- LogOut(" %s", cmdbuf);
- fFailed = TRUE;
- }
- else {
- sprintf_s(full, "%s\\%s", pDir->GetFullPathFromSourceOrDirectory(),
- pTestVariant->testInfo.data[TIK_BASELINE]);
- if (DoCompare(tmp_file1, full)) {
- // Output differs, run spiff to see if it's just minor
- // floating point anomalies.
- DeleteFileIfFound(tmp_file2);
- sprintf_s(buf, "spiff -m -n -s \"command spiff\" %s %s > %s",
- tmp_file1, full, tmp_file2);
- spiff_ret = ExecuteCommand(pDir->GetDirectoryPath(), buf);
- if (GetFileAttributes(tmp_file2) == INVALID_FILE_ATTRIBUTES) {
- LogError("ERROR: spiff failed to run");
- fFailed = TRUE;
- }
- else if (spiff_ret) {
- LogOut("ERROR: Test failed to run correctly. spiff returned %d (%s):", spiff_ret, optReportBuf);
- LogOut(" %s", cmdbuf);
- fFailed = TRUE;
- }
- }
- }
- }
- else {
- if (!CheckForPass(tmp_file1, optReportBuf, cmdbuf)) {
- fFailed = TRUE;
- }
- }
- logFailure:
- if (fFailed) {
- if (FCopyOnFail) {
- if (FVerbose)
- Message("INFO: Copying '%s' failure", optReportBuf);
- sprintf_s(failDir, "%s\\fail.%s",
- pDir->GetDirectoryPath(), optReportBuf);
- if ((GetFileAttributes(failDir) == INVALID_FILE_ATTRIBUTES) &&
- !CreateDirectory(failDir, NULL)) {
- Message("ERROR: Couldn't create directory '%s'", failDir);
- }
- else
- {
- for (StringList * pFile = pTest->files;
- pFile != NULL;
- pFile = pFile->next)
- {
- sprintf_s(copyName, "%s\\%s", failDir, pFile->string);
- p = strrchr(copyName, '.') + 1;
- strcpy_s(p, REMAININGARRAYLEN(copyName, p + 1), "obj");
- sprintf_s(buf, "%s\\%s", pDir->GetDirectoryPath(),
- pFile->string);
- p = strrchr(buf, '.') + 1;
- strcpy_s(p, REMAININGARRAYLEN(buf, p + 1), "obj");
- if (!CopyFile(buf, copyName, FALSE)) {
- Message("ERROR: Couldn't copy '%s' to '%s'",
- buf, copyName);
- }
- }
- sprintf_s(copyName, "%s\\%s", failDir, exebuf);
- if (!CopyFile(fullexebuf, copyName, FALSE)) {
- Message("ERROR: Couldn't copy '%s' to '%s'",
- fullexebuf, copyName);
- }
- }
- }
- }
- if (FRLFE) {
- RLFETestStatus(pDir);
- }
- if (FVerbose)
- Message("INFO: cleaning up test run");
- // Remove the exe.
- if (!FNoDelete) {
- DeleteFileRetryMsg(fullexebuf);
- }
- // Don't trash fullexebuf!
- strcpy_s(buf, fullexebuf);
- p = strrchr(buf, '.') + 1;
- // Remove the pdb(s) (if it exists).
- strcpy_s(p, REMAININGARRAYLEN(buf, p), "pdb");
- DeleteFileIfFound(buf);
- DeleteMultipleFiles(pDir, "*.pdb");
- // Remove the ilk (if it exists).
- strcpy_s(p, REMAININGARRAYLEN(buf, p), "ilk");
- DeleteFileIfFound(buf);
- // Remove the objs.
- if (!FNoDelete)
- {
- for (StringList * pFile = pTest->files;
- pFile != NULL;
- pFile = pFile->next)
- {
- sprintf_s(buf, "%s\\%s", pDir->GetDirectoryPath(), pFile->string);
- p = strrchr(buf, '.') + 1;
- if (fCleanAfter)
- {
- strcpy_s(p, REMAININGARRAYLEN(buf, p), "obj");
- DeleteFileRetryMsg(buf);
- }
- if (REGR_ASM) {
- strcpy_s(p, REMAININGARRAYLEN(buf, p), "asm");
- DeleteFileRetryMsg(buf);
- }
- }
- }
- elapsed_variation = (int)(time(NULL) - start_variation);
- if (Timing & TIME_VARIATION) {
- Message("RL: Variation elapsed time (%s, %s, %s): %02d:%02d",
- pDir->GetDirectoryName(), pTest->name, optReportBuf,
- elapsed_variation / 60, elapsed_variation % 60);
- }
- if (FSyncVariation) {
- if (FRLFE && fFailed)
- RLFEAddLog(pDir, RLFES_FAILED, pTest->name, optReportBuf, ThreadOut->GetText());
- if (fSyncVariationWhenFinished)
- FlushOutput();
- }
- ThreadInfo[ThreadId].DeleteTmpFileList();
- return fFailed ? -1 : 0;
- }
- int
- DoSimpleTest(
- CDirectory *pDir,
- Test * pTest,
- TestVariant * pTestVariant,
- BOOL fSuppressNoGPF,
- DWORD millisecTimeout
- )
- {
- return DoOneSimpleTest(pDir, pTest, pTestVariant, pTestVariant->optFlags,
- pTestVariant->testInfo.data[TIK_COMPILE_FLAGS],
- pTestVariant->testInfo.data[TIK_LINK_FLAGS],
- TRUE, TRUE, FALSE, fSuppressNoGPF, millisecTimeout);
- }
- int
- DoPogoSimpleTest(
- CDirectory *pDir,
- Test * pTest,
- TestVariant * pTestVariant,
- BOOL fSuppressNoGPF,
- DWORD millisecTimeout
- )
- {
- static const char *pgc = "*.pgc";
- static const char *pgd = POGO_PGD;
- char pgdFull[MAX_PATH];
- char ccFlags[BUFFER_SIZE];
- char linkFlags[BUFFER_SIZE];
- BOOL fFailed;
- sprintf_s(pgdFull, "%s\\%s", pDir->GetDirectoryPath(), pgd);
- const char * inCCFlags = pTestVariant->testInfo.data[TIK_COMPILE_FLAGS];
- const char * optFlags = pTestVariant->optFlags;
- DeleteFileIfFound(pgdFull);
- DeleteMultipleFiles(pDir, pgc);
- fFailed = FALSE;
- // Pogo requires LTCG
- ASSERT(strstr(optFlags, "GL") != NULL);
- sprintf_s(ccFlags, "%s %s", PogoForceErrors, optFlags);
- sprintf_s(linkFlags, "-ltcg:pgi -pgd:%s", pgd);
- if (DoOneSimpleTest(pDir, pTest, pTestVariant, ccFlags, inCCFlags,
- linkFlags, FALSE, FALSE, FALSE, fSuppressNoGPF, millisecTimeout)) {
- fFailed = TRUE;
- goto logFailure;
- }
- if (FTest)
- {
- return 0;
- }
- sprintf_s(ccFlags, "%s %s", PogoForceErrors, optFlags);
- sprintf_s(linkFlags, "-ltcg:pgo -pgd:%s", pgd);
- if (DoOneSimpleTest(pDir, pTest, pTestVariant, ccFlags, inCCFlags,
- linkFlags, FALSE, TRUE, TRUE, fSuppressNoGPF, millisecTimeout)) {
- fFailed = TRUE;
- }
- logFailure:
- if (FSyncVariation) {
- #if 0
- if (FRLFE && fFailed) {
- sprintf_s(cmdbuf, "%s%s%s", ccFlags,
- *linkFlags ? ";" : "", linkFlags);
- RLFEAddLog(pDir, RLFES_FAILED, testCmd,
- cmdbuf, ThreadOut->GetText());
- }
- #endif
- FlushOutput();
- }
- if (!FNoDelete) {
- DeleteFileRetryMsg(pgdFull);
- DeleteMultipleFiles(pDir, pgc);
- }
- return fFailed ? -1 : 0;
- }
- int
- ExecTest
- (
- CDirectory* pDir,
- Test * pTest,
- TestVariant * pTestVariant
- )
- {
- char *p = NULL;
- char full[MAX_PATH];
- DWORD millisecTimeout = DEFAULT_TEST_TIMEOUT;
- const char *strTimeout = pTestVariant->testInfo.data[TIK_TIMEOUT];
- if (strTimeout) {
- char *end;
- _set_errno(0);
- uint32 secTimeout = strtoul(strTimeout, &end, 10);
- millisecTimeout = 1000 * secTimeout;
- // Validation has already occurred so this string should
- // parse fine and the value shouldn't overflow the DWORD.
- ASSERT(errno == 0 && *end == 0);
- ASSERT(millisecTimeout > secTimeout);
- }
- // Check to see if all of the files exist.
- for (StringList * pFile = pTest->files; pFile != NULL; pFile = pFile->next)
- {
- // Get a pointer to the filename sans path, if present.
- p = GetFilenamePtr(pFile->string);
- // If we have no pathname, use the current directory.
- if (p == pFile->string) {
- sprintf_s(full, "%s\\", pDir->GetFullPathFromSourceOrDirectory());
- }
- else {
- // Look for %REGRESS% specifier.
- if (!_strnicmp(pFile->string, "%REGRESS%",
- strlen("%REGRESS%"))) {
- // Temporarily truncate the filename.
- ASSERT(p[-1] == '\\');
- p[-1] = '\0';
- sprintf_s(full, "%s%s\\",
- REGRESS, pFile->string + strlen("%REGRESS%"));
- p[-1] = '\\';
- }
- else {
- *p = '\0';
- }
- }
- strcat_s(full, p);
- if (GetFileAttributes(full) == INVALID_FILE_ATTRIBUTES) {
- LogError("ERROR: '%s' does not exist", full);
- return -1;
- }
- }
- const char* ext = GetFilenameExt(p);
- // Special case dotest.cmd
- if (!_stricmp(p, "dotest.cmd")) {
- // We don't handle these yet.
- ASSERTNR(pTestVariant->testInfo.data[TIK_LINK_FLAGS] == NULL);
- if (IsPogoTest(pTest))
- return DoPogoExternalTest(pDir, pTestVariant, full, TK_CMDSCRIPT, TRUE, millisecTimeout);
- else
- return DoExternalTest(pDir, pTestVariant, full, TK_CMDSCRIPT, TRUE, millisecTimeout);
- }
- // Special case for standardized RL makefiles.
- else if (!_stricmp(p, "rl.mak")) {
- // We don't handle these yet.
- ASSERTNR(pTestVariant->testInfo.data[TIK_LINK_FLAGS] == NULL);
- if (IsPogoTest(pTest))
- return DoPogoExternalTest(pDir, pTestVariant, full, TK_MAKEFILE, FALSE, millisecTimeout);
- else
- return DoExternalTest(pDir, pTestVariant, full, TK_MAKEFILE, SuppressNoGPF(pTest), millisecTimeout);
- }
- // Special case for files ending with ".js", ".html", ".htm" (<command> dealt with separately)
- else if (pTestVariant->testInfo.data[TIK_COMMAND] == NULL
- && !_stricmp(ext, ".js"))
- {
- return DoExternalTest(pDir, pTestVariant, full, TK_JSCRIPT, FALSE, millisecTimeout);
- }
- else if (pTestVariant->testInfo.data[TIK_COMMAND] == NULL
- && (!_stricmp(ext, ".html") || !_stricmp(ext, ".htm")))
- {
- return DoExternalTest(pDir, pTestVariant, full, TK_HTML, FALSE, millisecTimeout);
- }
- // Special case for tests with a <command> argument
- else if (pTestVariant->testInfo.data[TIK_COMMAND] != NULL)
- {
- return DoExternalTest(pDir, pTestVariant, full, TK_COMMAND, FALSE, millisecTimeout);
- }
- // Non-scripted test.
- else {
- if (IsPogoTest(pTest)) {
- // We don't handle these yet.
- ASSERTNR(pTestVariant->testInfo.data[TIK_LINK_FLAGS] == NULL);
- return DoPogoSimpleTest(pDir, pTest, pTestVariant, FALSE, millisecTimeout);
- }
- else
- {
- return DoSimpleTest(pDir, pTest, pTestVariant, SuppressNoGPF(pTest), millisecTimeout);
- }
- }
- }
|