diff --git a/cmake/modules/TestFile.cmake b/cmake/modules/TestFile.cmake --- a/cmake/modules/TestFile.cmake +++ b/cmake/modules/TestFile.cmake @@ -1,3 +1,13 @@ +##===-- TestFile.cmake ------------------------------------------*- C++ -*-===## +#* *# +#* Part of the LLVM Project, under the Apache License v2.0 with LLVM *# +#* Exceptions. *# +#* See https://llvm.org/LICENSE.txt for license information. *# +#* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *# +#* *# +#*===----------------------------------------------------------------------===*# + + ##===- TestSuite.cmake ----------------------------------------------------===## # # Defines helper functions to create .test files that describe how to run a @@ -37,7 +47,8 @@ set(TESTSCRIPT "" PARENT_SCOPE) endif() if(DEFINED ARGS_WORKDIR) - set(ARGS_EXECUTABLE "cd ${ARGS_WORKDIR} ; ${ARGS_EXECUTABLE}") + #set(ARGS_EXECUTABLE "cd ${ARGS_WORKDIR} ; ${ARGS_EXECUTABLE}") + set(ARGS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/${ARGS_EXECUTABLE}") endif() llvm_test_check_paths("${ARGS_EXECUTABLE}") # ARGS_UNPARSED_ARGUMENTS is a semicolon-separated list. Change it into a @@ -45,7 +56,8 @@ string(REPLACE ";" " " JOINED_ARGUMENTS "${ARGS_UNPARSED_ARGUMENTS}") llvm_test_check_paths("${JOINED_ARGUMENTS}") if(NOT DEFINED ARGS_RUN_TYPE OR "${ARGS_RUN_TYPE}" STREQUAL "${TEST_SUITE_RUN_TYPE}") - set(TESTSCRIPT "${TESTSCRIPT}RUN: ${ARGS_EXECUTABLE} ${JOINED_ARGUMENTS}\n") + #set(TESTSCRIPT "${TESTSCRIPT}RUN: ${ARGS_EXECUTABLE} ${JOINED_ARGUMENTS}\n") + set(TESTSCRIPT "${TESTSCRIPT}RUN: ${ARGS_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/${JOINED_ARGUMENTS}\n") endif() endmacro() @@ -114,6 +126,7 @@ endfunction() function(llvm_add_test_for_target target) - llvm_add_test($.test %S/$) + #llvm_add_test($.test %S/$) + llvm_add_test($.test $) set(TESTSCRIPT "" PARENT_SCOPE) endfunction() diff --git a/litsupport/modules/timeit.py b/litsupport/modules/timeit.py --- a/litsupport/modules/timeit.py +++ b/litsupport/modules/timeit.py @@ -1,3 +1,12 @@ +#*===-- timeit.py -----------------------------------------------*- C++ -*-===*# +#* *# +#* Part of the LLVM Project, under the Apache License v2.0 with LLVM *# +#* Exceptions. *# +#* See https://llvm.org/LICENSE.txt for license information. *# +#* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *# +#* *# +#*===----------------------------------------------------------------------===*# + from litsupport import shellcommand from litsupport import testplan import os @@ -17,11 +26,12 @@ else: timeit_name = "timeit-target" timeit = "%s/tools/%s" % (config.test_source_root, timeit_name) - args = ["--limit-core", "0"] - args += ["--limit-cpu", "7200"] - args += ["--timeout", "7200"] - args += ["--limit-file-size", "104857600"] - args += ["--limit-rss-size", "838860800"] + ##temporarily disasbled as this arguments are supporting on Windows/WoA platform + #args = ["--limit-core", "0"] + #args += ["--limit-cpu", "7200"] + args = ["--timeout", "7200"] + #args += ["--limit-file-size", "104857600"] + #args += ["--limit-rss-size", "838860800"] workdir = cmd.workdir if not config.traditional_output: stdout = cmd.stdout @@ -50,7 +60,8 @@ args += ["--redirect-input", stdin] cmd.stdin = None else: - args += ["--redirect-input", "/dev/null"] + pass #Removing the below as /de/null is not compatable on windows/WoA platform + #args += ["--redirect-input", "/dev/null"] if workdir is not None: args += ["--chdir", workdir] cmd.workdir = None diff --git a/tools/timeit.c b/tools/timeit.c --- a/tools/timeit.c +++ b/tools/timeit.c @@ -7,17 +7,27 @@ |* *| \*===----------------------------------------------------------------------===*/ + #include #include #include +#include #include #include + +#ifdef _WIN32 +#include +#include +#else +//-----------------------------------POSIX-------------------------------------- #include #include #include #include +//-----------------------------------POSIX-------------------------------------- +#endif /* Enumeration for our exit status codes. */ enum ExitCode { @@ -51,8 +61,37 @@ */ static int g_timeout_in_seconds = 0; +#ifdef _WIN32 +typedef HANDLE process_id_t; +typedef DWORD exit_status_t; + +/* \brief Job object used to kill the monitored process with all its children*/ +static HANDLE g_job_object = INVALID_HANDLE_VALUE; + +/* \brief Handle of the timer used to close the monitored app by timeout*/ +static HANDLE timer_handle = INVALID_HANDLE_VALUE; + +static HANDLE g_child_stdin_rd = INVALID_HANDLE_VALUE; +static HANDLE g_child_stdin_wr = INVALID_HANDLE_VALUE; +static HANDLE g_child_stdout_rd = INVALID_HANDLE_VALUE; +static HANDLE g_child_stdout_wr = INVALID_HANDLE_VALUE; + +const int FILE_BUF_SIZE = 4096; + +#else +typedef pid_t process_id_t; +typedef int exit_status_t; +#endif /*#ifdef _WIN32*/ + +/* \brief The PID (*nix) or HANDLE (Windows) of the process being monitored. */ +static process_id_t g_monitored_pid; + +//-----------------------------------POSIX-------------------------------------- /* \brief If non-zero, the PID of the process being monitored. */ -static pid_t g_monitored_pid = 0; +//static pid_t g_monitored_pid = 0; +// pid_t can be a DWORD. A handle to the process may also be required +// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid +//-----------------------------------POSIX-------------------------------------- /* \brief If non-zero, the path to attempt to chdir() to before executing the * target. */ @@ -65,6 +104,8 @@ /* \brief If non-zero, the path to redirect the target standard input to. */ static const char *g_target_redirect_input = 0; +static const char *g_target_redirect_output = 0; + /* \brief If non-zero, the path to redirect the target stdout to. */ static const char *g_target_redirect_stdout = 0; @@ -74,6 +115,8 @@ /* \brief If non-zero, append exit status at end of output file. */ static int g_append_exitstats = 0; +#ifndef _WIN32 +//-----------------------------------POSIX-------------------------------------- /* @name Resource Limit Variables */ /* @{ */ @@ -102,10 +145,379 @@ static rlim_t g_target_subprocess_count_limit = ~(rlim_t) 0; /* @} */ +//-----------------------------------POSIX-------------------------------------- +#else +/* When an unhandled fatal signal terminates a process, the exit code is 3. */ +#define WIFSIGNALED(x) ((x) == 3) +#define WIFEXITED(x) ((x) != 3) + +/* The signal that terminated a process is not known posthum. */ +#define WTERMSIG(x) SIGTERM +#define WEXITSTATUS(x) (x) + +/* \brief Formats a readable Win error message. */ +static void win_error(PTSTR function_name) { + void *msg_buf, *display_buf; + DWORD error = GetLastError(); + const char *extra_text = "failed with error"; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msg_buf, 0, NULL); + + display_buf = (void *)LocalAlloc(LMEM_ZEROINIT, + strlen((const char *)msg_buf) + + strlen((const char *)function_name) + + strlen(extra_text) + 10); + + sprintf(display_buf, "%s %s %d: %s", function_name, extra_text, error, + (char *)msg_buf); + fprintf(stderr, display_buf); + + LocalFree(msg_buf); + LocalFree(display_buf); +} + +static void close_handles() { + if (timer_handle != INVALID_HANDLE_VALUE) + CloseHandle(timer_handle); + + if (g_monitored_pid != INVALID_HANDLE_VALUE) + CloseHandle(g_monitored_pid); + + if (g_job_object != INVALID_HANDLE_VALUE) + CloseHandle(g_job_object); + + if (g_child_stdin_rd != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdin_rd); + + if (g_child_stdin_wr != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdin_wr); + + if (g_child_stdout_rd != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdout_rd); + + if (g_child_stdout_wr != INVALID_HANDLE_VALUE) + CloseHandle(g_child_stdout_wr); + + timer_handle = g_monitored_pid = g_job_object = g_child_stdin_rd = + g_child_stdin_wr = g_child_stdout_rd = g_child_stdout_wr = + INVALID_HANDLE_VALUE; +} + +static int create_pipe(HANDLE *read_handle, HANDLE *write_handle) { + SECURITY_ATTRIBUTES security_attrs; + + security_attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attrs.bInheritHandle = TRUE; + security_attrs.lpSecurityDescriptor = NULL; + + if (CreatePipe(read_handle, write_handle, &security_attrs, 0) == 0) { + win_error("CreatePipe"); + return 0; + } + + return 1; +} + +static int make_handle_not_inherited(HANDLE handle) { + if (SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0) == 0) { + win_error("SetHandleInformation"); + return 0; + } + + return 1; +} + +static int set_current_dir(const char *dir); + +static int execute_target_process(char *const argv[]) { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_object_info = {0}; + STARTUPINFO proc_start_info; + PROCESS_INFORMATION process_info; + + /*Create unnamed pipes to redirect stdout and stdin for the child process if + * necessary.*/ + /*Read handle to the pipe for stdout and write handle to the pipe for stdin + must not be inherited by the monitored child process*/ + if (g_target_redirect_input) { + if (create_pipe(&g_child_stdin_rd, &g_child_stdin_wr) == 0 || + make_handle_not_inherited(g_child_stdin_wr) == 0) { + close_handles(); + return 0; + } + } + + if (g_target_redirect_output) { + if (create_pipe(&g_child_stdout_rd, &g_child_stdout_wr) == 0 || + make_handle_not_inherited(g_child_stdout_rd) == 0) { + close_handles(); + return 0; + } + } + + /*create a job object*/ + g_job_object = CreateJobObject(NULL, NULL); + + if (g_job_object == INVALID_HANDLE_VALUE) { + win_error("CreateJobObject"); + close_handles(); + return 0; + } + + /*make all associated child processes terminate when the job closes*/ + job_object_info.BasicLimitInformation.LimitFlags = + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + if (SetInformationJobObject(g_job_object, JobObjectExtendedLimitInformation, + &job_object_info, sizeof(job_object_info)) == 0) { + win_error("SetInformationJobObject"); + close_handles(); + return 0; + } + + /* Honor the desired target execute directory. */ + if (g_target_exec_directory && + set_current_dir(g_target_exec_directory) == 0) { + close_handles(); + return 0; + } + + ZeroMemory(&proc_start_info, sizeof(STARTUPINFO)); + proc_start_info.cb = sizeof(STARTUPINFO); + proc_start_info.hStdError = g_child_stdout_wr; + proc_start_info.hStdOutput = g_child_stdout_wr; + proc_start_info.hStdInput = g_child_stdin_rd; + proc_start_info.dwFlags |= STARTF_USESTDHANDLES; + + //printf("\n Retryings----------------"); + //printf("\nargv[0] : %s",argv[0]); + + if (CreateProcess(NULL, strstr(GetCommandLine(), argv[0]), NULL, NULL, TRUE, + 0, NULL, NULL, &proc_start_info, &process_info) == 0) { + /*if (CreateProcess(NULL, "C:\\LNT\\wos_toolchain_release\\install\\bin\\clang-cl.exe /nologo -DNDEBUG -fuse-ld=lld-link --target=aarch64-pc-windows /MDd /Zi /Ob0 /Od /RTC1 -w -Werror=date-time -std=c99 /showIncludes /FoSingleSource\\UnitTests\\Vector\\AArch64\\CMakeFiles\\aarch64_neon_intrinsics.dir\\aarch64_neon_intrinsics.c.obj /FdSingleSource\\UnitTests\\Vector\\AArch64\\CMakeFiles\\aarch64_neon_intrinsics.dir\\ -c -- C:\\LNT\\llvm-test-suite\\SingleSource\\UnitTests\\Vector\\AArch64\\aarch64_neon_intrinsics.c", NULL, NULL, TRUE, + 0, NULL, NULL, &proc_start_info, &process_info) == 0) { */ + win_error("CreateProcess"); + close_handles(); + return 0; + } + + g_monitored_pid = process_info.hProcess; + + /*we do not need thread handle of the monitored process*/ + /*Child-side stdout/stdin handles are also no longer needed*/ + CloseHandle(process_info.hThread); + + if (g_child_stdout_wr != INVALID_HANDLE_VALUE) { + CloseHandle(g_child_stdout_wr); + } + + if (g_child_stdin_rd != INVALID_HANDLE_VALUE) { + CloseHandle(g_child_stdin_rd); + } + + g_child_stdout_wr = g_child_stdin_rd = INVALID_HANDLE_VALUE; + + /*assign the monitored process to the job*/ + if (AssignProcessToJobObject(g_job_object, g_monitored_pid) == 0) { + win_error("AssignProcessToJobObject"); + close_handles(); + return 0; + } + return 1; +} + +static int redirect_target_input_from_file(const char *file_name) { + HANDLE file_handle = CreateFile(file_name, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + + if (file_handle == INVALID_HANDLE_VALUE) { + win_error("CreateFile"); + close_handles(); + return 0; + } + + DWORD bytes_read, bytes_written; + char buf[4096]; + + for (;;) { + if (ReadFile(file_handle, buf, FILE_BUF_SIZE, &bytes_read, NULL) == 0 || + bytes_read == 0) { + break; + } + + if (WriteFile(g_child_stdin_wr, buf, bytes_read, &bytes_written, NULL) == + 0) { + break; + } + } + + CloseHandle(file_handle); + + /*close the pipe handle to make the child stop reading*/ + CloseHandle(g_child_stdin_wr); + + return 1; +} + +static int redirect_target_output_to_file(const char *file_name) { + DWORD bytes_read, bytes_written; + char buf[4096]; + + HANDLE file_handle = CreateFile(file_name, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file_handle == INVALID_HANDLE_VALUE) { + win_error("CreateFile"); + close_handles(); + return 0; + } + + for (;;) { + if (ReadFile(g_child_stdout_rd, buf, FILE_BUF_SIZE, &bytes_read, NULL) == + 0 || + bytes_read == 0) { + break; + } + + if (WriteFile(file_handle, buf, bytes_read, &bytes_written, NULL) == 0) { + break; + } + } + + CloseHandle(file_handle); + + return 1; +} + +#endif /*#ifndef _WIN32*/ + +static void kill_monitored_process() { +#ifdef _WIN32 + if (g_job_object != INVALID_HANDLE_VALUE) { + CloseHandle(g_job_object); + g_job_object = INVALID_HANDLE_VALUE; + } +#else + kill(-g_monitored_pid, SIGKILL); +#endif +} + +static int wait_for_monitored_process(exit_status_t *exit_status) { +#ifdef _WIN32 + if (WaitForSingleObject(g_monitored_pid, INFINITE) < 0) { + win_error("WaitForSingleObject"); + close_handles(); + return 0; + } + + GetExitCodeProcess(g_monitored_pid, exit_status); +#else + int res; + + do { + res = waitpid(g_monitored_pid, exit_status, 0); + } while (res < 0 && errno == EINTR); + if (res < 0) { + perror("waitpid"); + return 0; + } +#endif /*#ifdef _WIN32*/ + + return 1; +} + +struct cpu_usage_t { + double user_time; + double system_time; +}; + +static int get_cpu_usage(struct cpu_usage_t *cpu_usage) { +#ifdef _WIN32 + FILETIME creation_ftime, exit_ftime, kernel_ftime, user_ftime; + SYSTEMTIME kernel_time, user_time; + + if (GetProcessTimes(g_monitored_pid, &creation_ftime, &exit_ftime, + &kernel_ftime, &user_ftime) == 0) { + win_error("GetProcessTimes"); + close_handles(); + return 0; + } + + if (FileTimeToSystemTime(&kernel_ftime, &kernel_time) == 0 || + FileTimeToSystemTime(&user_ftime, &user_time) == 0) { + win_error("FileTimeToSystemTime"); + close_handles(); + return 0; + } + + cpu_usage->user_time = (double)user_time.wHour * 3600 + + user_time.wMinute * 60 + user_time.wSecond + + ((double)user_time.wMilliseconds) / 1000; + cpu_usage->system_time = (double)kernel_time.wHour * 3600 + + kernel_time.wMinute * 60 + kernel_time.wSecond + + ((double)kernel_time.wMilliseconds) / 1000; +#else + struct rusage usage; + + if (getrusage(RUSAGE_CHILDREN, &usage) < 0) { + perror("getrusage"); + return 0; + } + + cpu_usage->user_time = + (double)usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1000000.0; + cpu_usage->system_time = + (double)usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1000000.0; +#endif /*#ifdef _WIN32*/ + + return 1; +} + +static int set_current_dir(const char *dir) { +#ifdef _WIN32 + if (SetCurrentDirectory(dir) == 0) { + win_error("SetCurrentDirectory"); + return 0; + } +#else + if (chdir(dir) < 0) { + perror("chdir"); + return 0; + } +#endif /*#ifdef _WIN32*/ + + return 1; +} + +int gettimeofday(struct timeval * tp, struct timezone * tzp) { + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC) + // until 00:00:00 January 1, 1970 + static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime( &system_time ); + SystemTimeToFileTime( &system_time, &file_time ); + time = ((uint64_t)file_time.dwLowDateTime ) ; + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long) ((time - EPOCH) / 10000000L); + tp->tv_usec = (long) (system_time.wMilliseconds * 1000); + return 0; +} static double sample_wall_time(void) { struct timeval t; +//-----------------------------------POSIX-------------------------------------- gettimeofday(&t, NULL); + // Query Performance Counter can be used + // https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps#guidance-for-acquiring-time-stamps +//-----------------------------------POSIX-------------------------------------- return (double) t.tv_sec + t.tv_usec * 1.e-6; } @@ -113,56 +525,92 @@ /* If we are monitoring a process, kill its process group and assume we will * complete normally. */ +#ifdef _WIN32 + if (g_monitored_pid != INVALID_HANDLE_VALUE) { +#else if (g_monitored_pid) { +#endif /*#ifdef _WIN32*/ fprintf(stderr, ("%s: error: received signal %d. " "killing monitored process(es): %s\n"), g_program_name, signal, g_target_program); - /* Kill the process group of monitored_pid. */ - kill(-g_monitored_pid, SIGKILL); + /* Kill the monitored process group. */ + kill_monitored_process(); return; } - fprintf(stderr, "%s: error: received signal %d. exiting.\n", - g_program_name, signal); - /* Otherwise, we received a signal we should treat as for ourselves, and exit + fprintf(stderr, "%s: error: received signal %d. exiting.\n", g_program_name, + signal); + +/* Otherwise, we received a signal we should treat as for ourselves, and exit * quickly. */ +#ifdef __WIN32 + close_handles(); +#endif + _exit(EXITCODE_SIGNALLED); } +#ifdef _WIN32 +static VOID CALLBACK timeout_handler(PVOID param, BOOLEAN timer_or_wait_fired) { +#else static void timeout_handler(int signal) { - (void)signal; +#endif fprintf(stderr, "%s: TIMING OUT PROCESS: %s\n", g_program_name, g_target_program); /* We should always be monitoring a process when we receive an alarm. Kill its * process group and assume we will terminate normally. */ - kill(-g_monitored_pid, SIGKILL); + kill_monitored_process(); } -static int monitor_child_process(pid_t pid, double start_time) { - double real_time, user_time, sys_time; - struct rusage usage; - int res, status; +/* Sets up a signal handler to terminate the process on timeout. */ +static int set_alarm(int timeout_in_seconds) { +#ifdef _WIN32 + if (CreateTimerQueueTimer(&timer_handle, 0, + (WAITORTIMERCALLBACK)timeout_handler, 0, + timeout_in_seconds * 1000, 0, 0) == 0) { + win_error("CreateTimerQueueTimer"); + close_handles(); + return 0; + } +#else + signal(SIGALRM, timeout_handler); + alarm(timeout_in_seconds); +#endif /*#ifdef _WIN32*/ - /* Record the PID we are monitoring, for use in the signal handlers. */ - g_monitored_pid = pid; + return 1; +} + +int monitor_child_process(double start_time) { + double real_time, user_time, sys_time; /* If we are running with a timeout, set up an alarm now. */ if (g_timeout_in_seconds) { - sigset_t masked; - sigemptyset(&masked); - sigaddset(&masked, SIGALRM); + if (set_alarm(g_timeout_in_seconds) == 0) { + return EXITCODE_MONITORING_FAILURE; + } + } - alarm(g_timeout_in_seconds); +#ifdef _WIN32 + /*if stdin for the monitored process is redirected to be read from a file, + read the file and write its content to the child's stdin*/ + if (g_target_redirect_input) { + if (redirect_target_input_from_file(g_target_redirect_input) == 0) { + return EXITCODE_MONITORING_FAILURE; + } } + if (g_target_redirect_output) { + if (redirect_target_output_to_file(g_target_redirect_output) == 0) { + return EXITCODE_MONITORING_FAILURE; + } + } +#endif /*#ifdef _WIN32*/ + /* Wait for the process to terminate. */ - do { - res = waitpid(pid, &status, 0); - } while (res < 0 && errno == EINTR); - if (res < 0) { - perror("waitpid"); + exit_status_t exit_status; + if (wait_for_monitored_process(&exit_status) == 0) { return EXITCODE_MONITORING_FAILURE; } @@ -170,26 +618,27 @@ real_time = sample_wall_time() - start_time; /* Just in case, kill the child process group. */ - kill(-pid, SIGKILL); + kill_monitored_process(); /* Collect the other resource data on the children. */ - if (getrusage(RUSAGE_CHILDREN, &usage) < 0) { - perror("getrusage"); + struct cpu_usage_t cpu_usage; + if (get_cpu_usage(&cpu_usage) == 0) { return EXITCODE_MONITORING_FAILURE; } - user_time = (double) usage.ru_utime.tv_sec + usage.ru_utime.tv_usec/1000000.0; - sys_time = (double) usage.ru_stime.tv_sec + usage.ru_stime.tv_usec/1000000.0; + +#ifdef __WIN32 + close_handles(); +#endif /* If the process was signalled, report a more interesting status. */ - int exit_status; - if (WIFSIGNALED(status)) { + if (WIFSIGNALED(exit_status)) { fprintf(stderr, "%s: error: child terminated by signal %d\n", - g_program_name, WTERMSIG(status)); + g_program_name, WTERMSIG(exit_status)); /* Propagate the signalled status to the caller. */ - exit_status = 128 + WTERMSIG(status); - } else if (WIFEXITED(status)) { - exit_status = WEXITSTATUS(status); + exit_status = 128 + WTERMSIG(exit_status); + } else if (WIFEXITED(exit_status)) { + exit_status = WEXITSTATUS(exit_status); } else { /* This should never happen, but if it does assume some kind of failure. */ exit_status = EXITCODE_MONITORING_FAILURE; @@ -199,11 +648,11 @@ // would. if (!g_summary_file) { if (g_posix_mode) { - fprintf(stderr, "real %12.4f\nuser %12.4f\nsys %12.4f\n", - real_time, user_time, sys_time); + fprintf(stderr, "real %12.4f\nuser %12.4f\nsys %12.4f\n", real_time, + cpu_usage.user_time, cpu_usage.system_time); } else { - fprintf(stderr, "%12.4f real %12.4f user %12.4f sys\n", - real_time, user_time, sys_time); + fprintf(stderr, "%12.4f real %12.4f user %12.4f sys\n", real_time, + cpu_usage.user_time, cpu_usage.system_time); } } else { /* Otherwise, write the summary data in a simple parsable format. */ @@ -215,8 +664,8 @@ fprintf(fp, "exit %d\n", exit_status); fprintf(fp, "%-10s %.4f\n", "real", real_time); - fprintf(fp, "%-10s %.4f\n", "user", user_time); - fprintf(fp, "%-10s %.4f\n", "sys", sys_time); + fprintf(fp, "%-10s %.4f\n", "user", cpu_usage.user_time); + fprintf(fp, "%-10s %.4f\n", "sys", cpu_usage.system_time); fclose(fp); } @@ -234,7 +683,10 @@ return exit_status; } +//-----------------------------------POSIX-------------------------------------- +#ifndef _WIN32 +//-----------------------------------POSIX-------------------------------------- #define set_resource_limit(resource, value) \ set_resource_limit_actual(#resource, resource, value) static void set_resource_limit_actual(const char *resource_name, int resource, @@ -253,11 +705,13 @@ (unsigned long) requested.rlim_max); } } +//-----------------------------------POSIX-------------------------------------- static int streq(const char *a, const char *b) { return strcmp(a, b) == 0; } +//-----------------------------------POSIX-------------------------------------- static int execute_target_process(char * const argv[]) { /* Create a new process group for pid, and the process tree it may spawn. We * do this, because later on we might want to kill pid _and_ all processes @@ -333,7 +787,7 @@ if (g_target_data_size_limit != ~(rlim_t) 0) { set_resource_limit(RLIMIT_DATA, g_target_data_size_limit); } -#if defined(RLIMIT_RSS) && !defined(__APPLE__) +#if !defined(__APPLE__) // On Apple platforms, RLIMIT_RSS is mapped to RLIMIT_AS and setting RLIMIT_AS // to a value smaller than the current virtual memory size will fail, This is // incompatible with the current usage in timeit and can cause issues on @@ -352,15 +806,13 @@ if (g_target_file_count_limit != ~(rlim_t) 0) { set_resource_limit(RLIMIT_NOFILE, g_target_file_count_limit); } -#ifdef RLIMIT_NPROC if (g_target_subprocess_count_limit != ~(rlim_t) 0) { set_resource_limit(RLIMIT_NPROC, g_target_subprocess_count_limit); } -#endif /* Honor the desired target execute directory. */ - if (g_target_exec_directory) { - if (chdir(g_target_exec_directory) < 0) { + if (g_target_exec_directory && + set_current_dir(g_target_exec_directory) == 0) { perror("chdir"); return EXITCODE_MONITORING_FAILURE; } @@ -377,39 +829,50 @@ return EXITCODE_EXEC_FAILURE; } +#endif +//-----------------------------------POSIX-------------------------------------- +//-----------------------------------POSIX-------------------------------------- static int execute(char * const argv[]) { double start_time; - pid_t pid; + process_id_t pid; /* Set up signal handlers so we can terminate the monitored process(es) on * SIGINT or SIGTERM. */ signal(SIGINT, terminate_handler); signal(SIGTERM, terminate_handler); - /* Set up a signal handler to terminate the process on timeout. */ - signal(SIGALRM, timeout_handler); - start_time = sample_wall_time(); + start_time = sample_wall_time(); +#ifdef _WIN32 + if (execute_target_process(argv) == 0) { + return EXITCODE_MONITORING_FAILURE; + } +#else /*#ifdef _WIN32*/ /* Fork the child process. */ - pid = fork(); - if (pid < 0) { + g_monitored_pid = fork(); + if (g_monitored_pid < 0) { perror("fork"); return EXITCODE_MONITORING_FAILURE; } /* If we are in the context of the child process, spawn it. */ - if (pid == 0) { + if (g_monitored_pid == 0) { /* Setup and execute the target process. This never returns except on * failure. */ return execute_target_process(argv); } - /* Otherwise, we are in the context of the monitoring process. */ - return monitor_child_process(pid, start_time); +/* Otherwise, we are in the context of the monitoring process. */ +#endif /*#ifdef _WIN32*/ + + return monitor_child_process(start_time); } +static int streq(const char *a, const char *b) { return strcmp(a, b) == 0; } +//-----------------------------------POSIX-------------------------------------- + static void usage(int is_error) { #define WRAPPED "\n " fprintf(stderr, "usage: %s [options] command ... arguments ...\n", @@ -544,13 +1007,19 @@ } if (strncmp(arg, "--limit-", 8) == 0) { + #ifdef _WIN32 + ++i; + #else +//-----------------------------------POSIX-------------------------------------- rlim_t value; +//-----------------------------------POSIX-------------------------------------- if (i + 1 == argc) { fprintf(stderr, "error: %s argument requires an option\n", arg); usage(/*is_error=*/1); } + value = atoi(argv[++i]); if (streq(arg, "--limit-cpu")) { g_target_cpu_limit = value; @@ -573,8 +1042,8 @@ usage(/*is_error=*/1); } continue; + #endif } - fprintf(stderr, "error: invalid argument '%s'\n", arg); usage(/*is_error=*/1); }