blob: 09894112e621886998f56635cbcbb729ae536cfe [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// 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 <windows.h>
#include <stdio.h>
#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();
}
}