singrdk/base/Windows/RunAll/runall.c

321 lines
9.0 KiB
C
Raw Normal View History

2008-03-05 09:52:00 -05:00
// ----------------------------------------------------------------------------
//
// Program: RunAll
//
// Purpose: Run all processes simultaneously with optional fatal error
// on any process failure or timeout. This is intended to be
// used by the automated build process to wrapper running
// bootd and the kernel debugger side-by-side.
//
2008-11-17 18:29:00 -05:00
// Copyright (c) Microsoft Corporation. All Rights Reserved.
2008-03-05 09:52:00 -05:00
//
// ----------------------------------------------------------------------------
#define _WIN32_WINNT 0x0500
#include <winlean.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static BOOL g_bVerbose = FALSE;
#define tprintf if (g_bVerbose) printf
static BOOL WINAPI CtrlHandler(DWORD dwCtrlType)
{
printf("Caught control event %I32u", dwCtrlType);
switch (dwCtrlType) {
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
GenerateConsoleCtrlEvent(dwCtrlType, 0);
default:
break;
}
ExitProcess((UINT)-2);
}
static void
ErrorReport(char *function)
{
VOID *message;
DWORD error = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &message,
0, NULL );
printf("runall: %s failed with error %d: %s",
function, error, message);
LocalFree(message);
}
void usage()
{
printf("Usage: runall [options] cmd [cmd1] ...\n\n");
printf("Options:\n");
2008-11-17 18:29:00 -05:00
printf(" /L:logdir - Write output from processes to logdir (must exist).\n");
printf(" /T:seconds - Set timeout in seconds.\n");
printf("\nRuns specified commands in parallel and wait for all to terminate successfully.\n\nThe program returns -1 if any process fails to launch, or exits with a non zero error-code, or the option timeout expires.");
2008-03-05 09:52:00 -05:00
}
static BOOL
ParseInt64(LPCSTR lpszValue, PINT64 pValue)
{
PCHAR pszStop;
errno = 0;
*pValue = _strtoi64(lpszValue, &pszStop, 10);
// Return no overflow and all characters parsed.
return errno == 0 && *pszStop == '\0';
}
2008-11-17 18:29:00 -05:00
static HANDLE
OpenLogFile(LPCSTR lpszLogDir, int fileNumber)
{
HANDLE hLogFile;
CHAR lpszLogPath[_MAX_PATH];
_snprintf_s(lpszLogPath, sizeof(lpszLogPath), _TRUNCATE,
"%s\\%d.log", lpszLogDir, fileNumber);
lpszLogPath[_MAX_PATH - 1] = '0';
hLogFile = CreateFile(lpszLogPath, GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hLogFile) {
ErrorReport("CreateFile (for log)");
}
return hLogFile;
}
2008-03-05 09:52:00 -05:00
static int
RunAndWait(int nProcesses,
LPPROCESS_INFORMATION lpProcessInfo,
LPCSTR lpszCommandLine[],
time_t endTime)
{
HANDLE hProcesses[MAXIMUM_WAIT_OBJECTS];
int nProcessOrdinal[MAXIMUM_WAIT_OBJECTS];
int i;
for (i = 0; i < nProcesses; i++) {
if (ResumeThread(lpProcessInfo[i].hThread) == -1) {
ErrorReport("ResumeThread");
return -1;
}
hProcesses[i] = lpProcessInfo[i].hProcess;
nProcessOrdinal[i] = i;
}
while (nProcesses > 0) {
DWORD dwMilliseconds;
DWORD dwResult;
time_t t;
time(&t);
2008-11-17 18:29:00 -05:00
if (t >= endTime) {
2008-03-05 09:52:00 -05:00
printf("runall: timeout\n");
return -1;
}
2008-11-17 18:29:00 -05:00
t = (endTime - t) * 1000;
// Note INFINITE - 1 so we don't sleep forever.
2008-03-05 09:52:00 -05:00
dwMilliseconds = (DWORD)((t < INFINITE) ? t : INFINITE - 1);
dwResult = WaitForMultipleObjects(nProcesses,
hProcesses,
FALSE,
dwMilliseconds);
2008-11-17 18:29:00 -05:00
tprintf("Next timeout = %d ms\n", dwMilliseconds);
2008-03-05 09:52:00 -05:00
if (dwResult == WAIT_TIMEOUT) {
2008-11-17 18:29:00 -05:00
tprintf("runall: WAIT_TIMEOUT\n");
2008-03-05 09:52:00 -05:00
}
else if (dwResult >= WAIT_ABANDONED_0 &&
dwResult <= WAIT_ABANDONED_0 + nProcesses) {
printf("runall: wait abandoned (process %d)\n",
dwResult - WAIT_ABANDONED_0);
return -1;
}
else {
DWORD dwProcess = dwResult - WAIT_OBJECT_0;
DWORD dwExitCode;
hProcesses[dwProcess] = hProcesses[--nProcesses];
nProcessOrdinal[dwProcess] = nProcessOrdinal[nProcesses];
if (GetExitCodeProcess(hProcesses[dwProcess], &dwExitCode) == 0) {
ErrorReport("GetExitCodeProcess");
return -1;
}
printf("runall: Command \"%s\" => exited %d\n",
lpszCommandLine[nProcessOrdinal[dwProcess]],
dwExitCode);
if (dwExitCode != 0) {
return -1;
}
}
}
return 0;
}
static int
2008-11-17 18:29:00 -05:00
RunAll(int nProcesses,
LPCSTR lpszCommandLine[],
LPCSTR logDir,
time_t endTime)
2008-03-05 09:52:00 -05:00
{
PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS];
STARTUPINFO si;
int exitCode = -1;
int i;
for (i = 0; i < nProcesses; i++) {
DWORD dwCreationFlags = CREATE_SUSPENDED | CREATE_NO_WINDOW;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
2008-11-17 18:29:00 -05:00
if (NULL != logDir) {
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = OpenLogFile(logDir, i);
if (INVALID_HANDLE_VALUE == si.hStdOutput) {
break;
}
SetHandleInformation(si.hStdOutput,
HANDLE_FLAG_INHERIT,
HANDLE_FLAG_INHERIT);
si.hStdError = si.hStdOutput;
}
if (!CreateProcess(NULL, (LPSTR)lpszCommandLine[i], NULL, NULL, TRUE,
2008-03-05 09:52:00 -05:00
dwCreationFlags, NULL, NULL, &si, &pi[i])) {
ErrorReport("CreateProcess");
break;
}
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
tprintf("runall: Created PID %u: %s\n",
pi[i].dwProcessId, lpszCommandLine[i]);
}
if (i == nProcesses) {
exitCode = RunAndWait(nProcesses, pi, lpszCommandLine, endTime);
}
while (--i >= 0) {
if (TerminateProcess(pi[i].hProcess, -1)) {
tprintf("runall: Terminated PID %u: %s\n",
pi[i].dwProcessId, lpszCommandLine[i]);
}
}
return exitCode;
}
2008-11-17 18:29:00 -05:00
static double
FileTimeToSeconds(FILETIME ftValue)
{
PINT64 pV = (PINT64)&ftValue;
double d = (*pV) * 1e-7;
return d;
}
static void
DisplayProcessTimes()
{
FILETIME ftCreation;
FILETIME ftExitTime;
FILETIME ftKernelTime;
FILETIME ftUserTime;
GetProcessTimes((HANDLE)-1,
&ftCreation,
&ftExitTime,
&ftKernelTime,
&ftUserTime);
printf("runall: Process Time Information\n");
printf("runall: Kernel time: %.2f\n", FileTimeToSeconds(ftKernelTime));
printf("runall: User time: %.2f\n", FileTimeToSeconds(ftUserTime));
}
2008-03-05 09:52:00 -05:00
int
main(int argc, LPCSTR argv[])
{
2008-11-17 18:29:00 -05:00
BOOL bAccounting = FALSE;
2008-03-05 09:52:00 -05:00
BOOL bLaunchFailure = FALSE;
time_t endTime = _I64_MAX;
INT64 timeout = 0;
2008-11-17 18:29:00 -05:00
LPCSTR lpszLogDir = NULL;
INT32 exitCode;
2008-03-05 09:52:00 -05:00
argc--;
argv++;
while (argc != 0 && (argv[0][0] == '/' || argv[0][0] == '-')) {
switch (tolower(argv[0][1])) {
2008-11-17 18:29:00 -05:00
case 'a':
bAccounting = TRUE;
break;
2008-03-05 09:52:00 -05:00
case 't':
if (argv[0][2] == ':' && ParseInt64(&argv[0][3], &timeout)) {
time(&endTime);
endTime += (time_t)timeout;
}
else {
printf("Bad timeout argument.");
return -1;
}
break;
2008-11-17 18:29:00 -05:00
case 'l':
if (argv[0][2] == ':') {
lpszLogDir = argv[0] + 3;
}
else {
printf("Bad log dir argument.");
return -1;
}
break;
2008-03-05 09:52:00 -05:00
case 'v':
g_bVerbose = TRUE;
2008-11-17 18:29:00 -05:00
break;
2008-03-05 09:52:00 -05:00
default:
usage();
return -1;
}
argc --;
argv ++;
}
if (argc == 0) {
usage();
return -1;
} else if (argc > MAXIMUM_WAIT_OBJECTS) {
printf("Supported process limit exceeded.");
return -1;
}
SetConsoleCtrlHandler(CtrlHandler, TRUE);
2008-11-17 18:29:00 -05:00
exitCode = RunAll(argc, argv, lpszLogDir, endTime);
if (bAccounting) {
DisplayProcessTimes();
}
return exitCode;
2008-03-05 09:52:00 -05:00
}