//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #undef UNICODE #undef _UNICODE #include #include #include "rl.h" //#define TRACE char *RLFEOpts = NULL; BOOL FRLFE = FALSE; static HANDLE HPipe = INVALID_HANDLE_VALUE; static CRITICAL_SECTION CS; static BOOL FConnected = FALSE; static void SendCommand(RLFE_COMMAND command, DWORD dataLen); static int SpawnRLFE( const char *options ) { char cmd[1024]; const char *str = "Unexpected error"; enum SD_STATUS { SD_NONE, SD_IN, SD_ERR, SD_OUT, SD_CREATE } status; STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; HANDLE NULStderr = 0, NULStdout = 0, NULStdin = 0; sprintf_s(cmd, "rlfe %s", options); if (strlen(cmd) > 1023) { printf("cmd buffer too small\n"); return -2; } sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; status = SD_IN; NULStdin = CreateFile("NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, INVALID_HANDLE_VALUE); if (NULStdin == INVALID_HANDLE_VALUE) goto cleanup; status = SD_ERR; NULStderr = CreateFile("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, INVALID_HANDLE_VALUE); if (NULStderr == INVALID_HANDLE_VALUE) goto cleanup; status = SD_OUT; NULStdout = CreateFile("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, INVALID_HANDLE_VALUE); if (NULStdout == INVALID_HANDLE_VALUE) goto cleanup; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = NULStdout; si.hStdError = NULStderr; si.hStdInput = NULStdin; #ifdef TRACE printf("Spawning RLFE (%s)\n", cmd); #endif status = SD_CREATE; if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) goto cleanup; status = SD_NONE; // Close handles. CloseHandle(NULStdin); CloseHandle(NULStderr); CloseHandle(NULStdout); #ifdef TRACE printf("Successful spawn\n"); #endif return 0; cleanup: switch (status) { case SD_CREATE: CloseHandle(NULStdout); case SD_OUT: CloseHandle(NULStderr); case SD_ERR: CloseHandle(NULStdin); case SD_IN: break; } switch (status) { case SD_CREATE: str = "Unable to spawn RL"; break; case SD_ERR: case SD_IN: case SD_OUT: str = "Unable to obtain file handle"; break; } printf("%s\n", str); return 1; } // This function takes care of creating the pipe between RL and RLFE and // starts RLFE. BOOL RLFEConnect( const char *prefix ) { char pipeName[32]; char options[512], *pOpt, *s; char pipeID[9], *pID; DWORD id = 0; pOpt = options; pID = NULL; // Parse RLFE options. while (RLFEOpts && *RLFEOpts) { s = strchr(RLFEOpts, ','); if (s) *s++ = '\0'; if (!_stricmp(RLFEOpts, "min")) pOpt += sprintf_s(pOpt, REMAININGARRAYLEN(options, pOpt), " -min"); else if (!_strnicmp(RLFEOpts, "pipe", 4)) pID = RLFEOpts + 4; RLFEOpts = s; } if (pID == NULL) { id = GetCurrentProcessId(); sprintf_s(pipeID, "%08x", id); pID = pipeID; } sprintf_s(pipeName, "%s%s", PIPE_NAME, pID); if (strlen(pipeName) > 31) { printf("RLFE pipename buffer too small\n"); return FALSE; } pOpt += sprintf_s(pOpt, REMAININGARRAYLEN(options, pOpt), " -pipe %s", pID); if (prefix) pOpt += sprintf_s(pOpt, REMAININGARRAYLEN(options, pOpt), " -prefix \"%s\"", prefix); HPipe = CreateNamedPipe( pipeName, PIPE_ACCESS_OUTBOUND | FILE_FLAG_WRITE_THROUGH, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 2048, 2048, 5 * 1000, NULL); if (HPipe == INVALID_HANDLE_VALUE) { fprintf(stderr, "RLFE pipe creation failed\n"); return FALSE; } if (id) { if (strlen(options) > 511) { printf("RLFE options buffer too small\n"); return FALSE; } if (SpawnRLFE(options)) { CloseHandle(HPipe); return FALSE; } } else { printf("Please launch RLFE manually with -pipe %s\n", pID); } if (!ConnectNamedPipe(HPipe, NULL)) { CloseHandle(HPipe); fprintf(stderr, "RLFE failed to connected\n"); return FALSE; } FConnected = TRUE; return TRUE; } void RLFEDisconnect( BOOL fKilled ) { if (FConnected) { if (!fKilled) { SendCommand(RLFE_DONE, 0); FlushFileBuffers(HPipe); } DeleteCriticalSection(&CS); CloseHandle(HPipe); HPipe = INVALID_HANDLE_VALUE; FConnected = FALSE; } FRLFE = FALSE; } BOOL RLFEEnterCritSec( void ) { EnterCriticalSection(&CS); return FRLFE; } void RLFELeaveCritSec( void ) { LeaveCriticalSection(&CS); } static void SendData( BYTE *buf, DWORD len ) { DWORD wb; if (!WriteFile(HPipe, buf, len, &wb, NULL) || (wb != len)) { fprintf(stderr, "Write to RLFE pipe failed; RLFE output disabled\n"); RLFEDisconnect(TRUE); } } static void SendCommand( RLFE_COMMAND command, DWORD dataLen ) { BYTE cmdBuf[3]; cmdBuf[0] = (char)command; cmdBuf[1] = BYTE(dataLen / 256); cmdBuf[2] = BYTE(dataLen % 256); if (RLFEEnterCritSec()) { SendData(cmdBuf, 3); RLFELeaveCritSec(); } } static void SendDataWithLen( BYTE *data, DWORD len ) { BYTE lenBuf[2]; lenBuf[0] = BYTE(len / 256); lenBuf[1] = BYTE(len % 256); if (RLFEEnterCritSec()) { SendData(lenBuf, 2); if (len) SendData(data, len); RLFELeaveCritSec(); } } void RLFEInit( BYTE numThreads, int numDirs ) { BYTE buf[4]; ASSERTNR((SHORT)numDirs == numDirs); InitializeCriticalSection(&CS); #ifdef TRACE printf("RLFEAddInit: %d, %d\n", numDirs, numThreads); #endif *(SHORT *)buf = (SHORT)numDirs; buf[2] = numThreads; buf[3] = (BYTE)RLFE_VERSION; SendCommand(RLFE_INIT, 4); SendData(buf, 4); } static const char * StatString( RL_STATS stat ) { switch (stat) { case RLS_TOTAL: return ""; case RLS_EXE: return "Exec"; case RLS_BASELINES: return "Baselines"; case RLS_DIFFS: return "Diffs"; } return "RL_ERROR"; } void RLFEAddRoot( RL_STATS stat, DWORD total ) { int len; const char *path; path = StatString(stat); len = (int)strlen(path) + 5; #ifdef TRACE printf("RLFEAddRoot: %d, %d\n", stat, total); #endif if (RLFEEnterCritSec()) { SendCommand(RLFE_ADD_ROOT, len); SendData((BYTE *)path, len - 5); SendData((BYTE *)&total, 4); SendData((BYTE *)&stat, 1); RLFELeaveCritSec(); } } void RLFEAddTest( RL_STATS stat, CDirectory *pDir ) { const char *path; int len, num; path = pDir->GetDirectoryName(); num = pDir->GetDirectoryNumber(); ASSERTNR((SHORT)num == num); #ifdef TRACE printf("RLFEAddTest: %d, %s, %d, %d\n", stat, path, num, pDir->NumVariations); #endif len = (int)strlen(path) + 7; if (RLFEEnterCritSec()) { SendCommand(RLFE_ADD_TEST, len); SendData((BYTE *)path, len - 7); SendData((BYTE *)&pDir->NumVariations, 4); SendData((BYTE *)&stat, 1); SendData((BYTE *)&num, 2); RLFELeaveCritSec(); } } void RLFEAddLog( CDirectory *pDir, RLFE_STATUS status, const char *testName, const char *subTestName, const char *logText ) { int pathLen, logLen, num; BYTE buf[4]; char pathBuf[BUFFER_SIZE], *path; ASSERTNR(testName || subTestName); ASSERTNR(logText); path = pathBuf; if (testName) path += sprintf_s(path, REMAININGARRAYLEN(pathBuf, path), "%c%s", PIPE_SEP_CHAR, testName); if (subTestName) path += sprintf_s(path, REMAININGARRAYLEN(pathBuf, path), "%c%s", PIPE_SEP_CHAR, subTestName); // String has a leading PIPE_SEP_CHAR, so chop that off. path = pathBuf + 1; pathLen = (int)strlen(path); ASSERTNR(pathLen < BUFFER_SIZE - 1); logLen = (int)strlen(logText); num = pDir->GetDirectoryNumber(); ASSERTNR((SHORT)num == num); *(SHORT *)buf = (SHORT)num; buf[2] = (BYTE)pDir->stat; buf[3] = (BYTE)status; #ifdef TRACE printf("RLFEAddLog: %d, %d, %s, %s\n", pDir->stat, num, path, logText); #endif if (RLFEEnterCritSec()) { SendCommand(RLFE_ADD_LOG, 2 + pathLen + 4 + logLen); SendDataWithLen((BYTE *)path, pathLen); SendData(buf, 4); SendData((BYTE *)logText, logLen); RLFELeaveCritSec(); } } void RLFETestStatus( CDirectory *pDir ) { DWORD *d; BYTE buf[3]; BYTE statBuf[3 * 4]; DWORD dataLen; int num; num = pDir->GetDirectoryNumber(); ASSERTNR((SHORT)num == num); if (pDir->stat == RLS_DIFFS) dataLen = 3 * 4; else dataLen = 2 * 4; d = (DWORD *)statBuf; *d++ = (int) pDir->NumVariationsRun; *d++ = (int) pDir->NumFailures; if (pDir->stat == RLS_DIFFS) *d++ = (int) pDir->NumDiffs; *(SHORT *)buf = (SHORT)num; buf[2] = (BYTE)pDir->stat; #ifdef TRACE printf("RLFETestStatus: %d, %d, %d, %d, %d\n", pDir->stat, num, pDir->NumVariationsRun, pDir->NumFailures, pDir->NumDiffs); #endif if (RLFEEnterCritSec()) { SendCommand(RLFE_TEST_STATUS, 2 + 1 + dataLen); SendData((BYTE *)&buf, 3); SendData(statBuf, dataLen); RLFELeaveCritSec(); } } void RLFEThreadDir( CDirectory *pDir, BYTE threadNum ) { BYTE buf[4]; int num; if (pDir) { num = pDir->GetDirectoryNumber(); ASSERTNR((SHORT)num == num); } else { num = 0; } --threadNum; // RL is 1-based, RLFE is 0-based *(SHORT *)buf = (SHORT)num; buf[2] = threadNum; if (pDir) buf[3] = (BYTE)pDir->stat; #ifdef TRACE printf("RLFEThreadDir: %d, %d, %d\n", num, threadNum, buf[3]); #endif if (RLFEEnterCritSec()) { SendCommand(RLFE_THREAD_DIR, 4); SendData(buf, 4); RLFELeaveCritSec(); } } void RLFEThreadStatus( BYTE num, const char *text ) { DWORD len; --num; // RL is 1-based, RLFE is 0-based len = (DWORD)strlen(text); #ifdef TRACE printf("RLFEThreadStatus: %d, %s\n", num, text); #endif if (RLFEEnterCritSec()) { SendCommand(RLFE_THREAD_STATUS, len + 1); SendData(&num, 1); SendData((BYTE *)text, len); RLFELeaveCritSec(); } }