Return-Path: Delivered-To: apmail-incubator-stdcxx-dev-archive@www.apache.org Received: (qmail 4060 invoked from network); 27 Sep 2006 20:57:02 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 27 Sep 2006 20:57:02 -0000 Received: (qmail 17585 invoked by uid 500); 27 Sep 2006 20:57:02 -0000 Delivered-To: apmail-incubator-stdcxx-dev-archive@incubator.apache.org Received: (qmail 17559 invoked by uid 500); 27 Sep 2006 20:57:02 -0000 Mailing-List: contact stdcxx-dev-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: stdcxx-dev@incubator.apache.org Delivered-To: mailing list stdcxx-dev@incubator.apache.org Received: (qmail 17548 invoked by uid 99); 27 Sep 2006 20:57:02 -0000 Received: from idunn.apache.osuosl.org (HELO idunn.apache.osuosl.org) (140.211.166.84) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 27 Sep 2006 13:57:02 -0700 Authentication-Results: idunn.apache.osuosl.org smtp.mail=ablack@roguewave.com; spf=permerror X-ASF-Spam-Status: No, hits=0.0 required=5.0 tests= Received-SPF: error (idunn.apache.osuosl.org: domain roguewave.com from 208.30.140.160 cause and error) Received: from [208.30.140.160] ([208.30.140.160:28131] helo=moroha.quovadx.com) by idunn.apache.osuosl.org (ecelerity 2.1.1.8 r(12930)) with ESMTP id AF/A1-01778-A95EA154 for ; Wed, 27 Sep 2006 13:56:59 -0700 Received: from [10.70.3.48] ([10.70.3.48]) by moroha.quovadx.com (8.13.6/8.13.6) with ESMTP id k8RKuWEc005542 for ; Wed, 27 Sep 2006 20:56:33 GMT Message-ID: <451AE595.2020005@roguewave.com> Date: Wed, 27 Sep 2006 14:56:53 -0600 From: Andrew Black User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060910 SeaMonkey/1.0.5 MIME-Version: 1.0 To: stdcxx-dev@incubator.apache.org Subject: [PATCH] global cleanup for exec utility Content-Type: multipart/mixed; boundary="------------020605080307060007080008" X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N --------------020605080307060007080008 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Greetings all. Attached is a patch that aims to clean up the use of globals within the exec utility. In the process of doing so, I moved the definition of the target_status structure to a new header (target.h), and added a companion target_opts structure to encapsulate the environment used to run a target. --Andrew Black Log: * target.h: Add new header. (target_opts): Define. (rw_rlimit, ProcessStatus, target_status): Move from display.h. (rw_timeval, RLIM_INFINITY, RLIM_SAVED_CUR, RLIM_SAVED_MAX): Move from cmdopt.h. * cmdopt.h (rw_timeval, RLIM_INFINITY, RLIM_SAVED_CUR, RLIM_SAVED_MAX): Move to target.h. (timeout, compat, verbose, exe_opts, in_root, child_limits): Remove global declarations. (target_name): Move from global scope to runall.cpp local scope. (eval_options): Alter declaration to take storage structure, handle. (get_target): Declare, document accessor function, defined in runall.cpp. * cmdopt.cpp (timeout, compat, verbose, exe_opts, in_root, target_name, child_limits): Remove global definitions. (usage_text): Remove -q and -v switch descriptions (switches were unused). (parse_limit_opts): Alter definition to take storage structure, use storage structure. (eval_options): Alter definition to take storage structure, handle, update documentation, store parsed values in provided structure/handle, remove -q, -v switches. * display.h (rw_rlimit, ProcessStatus, target_status): Move to target.h (short_st_name): Make immutable. (print_target): Alter declaration to take target_opts rather than target_status. * display.cpp (print_target_plain): Alter definition to match print_target. (print_status_plain): Alter usage to match changes to target_status structure. (short_st_name): Make immutable. (print_target): Alter definition to match declaration. * exec.h (exec_attrs): Remove. (target.h): Include. (exec_file): Alter declaration to accept target_opts in addition to target_status. * exec.cpp (target.h): Include instead of display.h (open_input): Alter definition to take data_dir, update documentation. [!_WIN32 && !_WIN64] (alarm_timeout): Clean documentation slightly [!_WIN32 && !_WIN64] (wait_for_child): Alter definition to take timeout, target_status, store results directly in struct. [!_WIN32 && !_WIN64] (LIMIT, limit_process): Alter definition to take target_opts, update documentation, check for valid pointer to limit structure rather than invalid values. [!_WIN32 && !_WIN64 && _XOPEN_UNIX] (calculate_usage): Store calculated times in static local vartiables, and store references to these variables in target_status struct. [!_WIN32 && !_WIN64] (exec_file): Alter to match prototype, altered function usage to match definitions. [_WIN32 || _WIN64] (kill_child_process): New helper function to simplify child process kill code. [_WIN32 || _WIN64] (exec_file): Alter to match prototype, altered functions to match definitions, use new helper function, some cleaning, process execution results. * output.h (parse_output): Alter declaration to take target_opts in addition to target_status. * output.cpp (check_test): Retrieve target_name via get_target. (check_example): Alter definition to take data_dir, update documentation. (parse_output): Alter definition to match prototype, use target_opts in place of removed globals. * runall.cpp (target.h): Included. (check_target_ok): Alter definition to take target (char*) in addition to target_status, retrieve target_name via get_target. (process_results): Remove, functionality rolled into exec_file. (target_name): moved to file local scope from global scope. (get_target): Declare accessor function for target_name, defined in cmdopt.h. (run_target): Alter definition to take target_opts structure rather than argv array. (main): Create local target_opts structure to fill in eval_opts and use in target_template, free any rlimit structures allocated in parse_limit_opts. * util.h (reference_name): Alter declaration to take data_dir, update documentation. * util.cpp (warn, terminate): Retrieve target_name via get_target (reference_name): Retrieve target_name via get_target, use data_dir rather than (removed) in_root global. --------------020605080307060007080008 Content-Type: text/x-patch; name="global_elim2.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="global_elim2.diff" Index: output.h =================================================================== --- output.h (revision 450090) +++ output.h (working copy) @@ -28,6 +28,6 @@ #define OUTPUT_H void -parse_output (struct target_status* status); +parse_output (const struct target_opts* options, struct target_status* status); #endif // OUTPUT_H Index: display.h =================================================================== --- display.h (revision 450090) +++ display.h (working copy) @@ -27,39 +27,9 @@ #ifndef RW_DISPLAY_H #define RW_DISPLAY_H -#if !defined (_WIN32) && !defined (_WIN64) -# include /* for _XOPEN_UNIX */ -#endif +#include "target.h" /* For target_opts */ -#ifdef _XOPEN_UNIX -# include /* for struct timeval */ /** - Abstraction typedef for struct timeval using real struct -*/ -typedef struct timeval rw_timeval; -#else -/** - Placeholder time_t for use in rw_timeval -*/ -typedef long rw_time_t; -/** - Placeholder suseconds_t for use in rw_timeval -*/ -typedef long rw_suseconds_t; -/** - Placeholder struct timeval to use if _XOPEN_UNIX isn't defined -*/ -struct rw_timeval { - rw_time_t tv_sec; - rw_suseconds_t tv_usec; -}; -/** - Abstraction typedef for struct timeval using placeholder struct -*/ -typedef struct rw_timeval rw_timeval; -#endif - -/** Output format mode enumeration. Used to determine the mode used in producing output. @@ -73,49 +43,11 @@ }; /** - Status codes to denote result of analysis. -*/ -enum ProcessStatus { - ST_OK = 0, /**< Default (OK) status */ - ST_COMPILE, /**< Target failed to compile */ - ST_LINK, /**< Target failed to link */ - ST_EXIST, /**< Target doesn't exist? */ - ST_EXECUTE_FLAG, /**< Target lacks X flag */ - ST_EXECUTE, /**< Target failed to execute? */ - ST_NO_OUTPUT, /**< Target produced no output */ - ST_NO_REF, /**< No reference file found */ - ST_BAD_REF, /**< Invalid reference file found */ - ST_BAD_OUTPUT, /**< Incorrect output found */ - ST_FORMAT, /**< Incorrectly formatted (test) output found */ - ST_OVERFLOW, /**< Assertion counter overflow */ - ST_SYSTEM_ERROR, /**< System error during file system operation */ - ST_KILLED, /**< Target killed by exec utility */ - ST_NOT_KILLED, /** Target not killed by exec utility */ - ST_LAST /**< Array terminator */ -}; - -/** ProcessStatus enum lookup table for 'short' (6 character) strings. */ -extern const char* short_st_name [ST_LAST]; +extern const char* const short_st_name [ST_LAST]; /** - Structure encapsulating the results extracted from a run. -*/ -struct target_status { - char** argv; /**< Target argv array. */ - int exit; /**< exit code for process. */ - int signal; /**< Termination signal for process. */ - int run; /**< Flag indicating if the process executed. */ - enum ProcessStatus status; /**< Textual process status. */ - rw_timeval user; /**< Elapsed user time spent in execution. */ - rw_timeval sys; /**< Elapsed system time spent in execution. */ - unsigned warn; /**< Number of (test) warnings. */ - unsigned assert; /**< Number of (test) assertions. */ - unsigned failed; /**< Number of failed (test) assertions. */ -}; - -/** Sets the output functions referenced. */ void set_output_format (enum OutputFmt format); @@ -140,7 +72,7 @@ @see target_name */ -extern void (*print_target) (const struct target_status* status); +extern void (*print_target) (const struct target_opts* options); /** Updates the display of a (non-final) status indicator Index: util.h =================================================================== --- util.h (revision 450090) +++ util.h (working copy) @@ -53,15 +53,17 @@ const char* const file, const unsigned line); /** - Generates the name of a reference (input/output) file, based on dir and mode. + Generates the name of a reference (input/output) file. This function allocates memory which is to be freed by the caller. - @param dir example subdirectory to reference + @param data_dir location of example data directory + @param subdir example subdirectory to reference @param mode type of file to generate name for (should be 'in' or 'out') - @return translation of 'in_root/dir/mode/target_name.mode' + @return translation of 'data_dir/subdir/mode/target_name.mode' */ -char* reference_name (const char* dir, const char* mode); +char* reference_name (const char* data_dir, const char* subdir, + const char* mode); /** Generates the name of the output file for the executable target. Index: exec.cpp =================================================================== --- exec.cpp (revision 450090) +++ exec.cpp (working copy) @@ -51,7 +51,7 @@ #include #include "cmdopt.h" -#include "display.h" /* for struct target_status */ +#include "target.h" /* For struct target_opts */ #include "util.h" #include "exec.h" @@ -63,7 +63,6 @@ Value is 1 when alarm has been triggered and hasn't been handled @see handle_alrm - @see open_input */ static int alarm_timeout; @@ -404,8 +403,8 @@ @see handle_alrm @see alarm_timeout */ -static struct exec_attrs -wait_for_child (pid_t child_pid) +static void +wait_for_child (pid_t child_pid, int timeout, struct target_status* result) { static const int signals [] = { SIGHUP, SIGINT, SIGTERM, SIGKILL, SIGKILL @@ -415,8 +414,6 @@ struct sigaction act; - struct exec_attrs state = {-1, 0}; - unsigned siginx = 0; int stopped = 0; @@ -426,6 +423,9 @@ #else int waitopts = WUNTRACED; #endif + + int status; + assert (1 < child_pid); /* Clear timeout */ @@ -448,14 +448,27 @@ alarm (timeout); while (1) { - const pid_t wait_pid = waitpid (child_pid, &state.status, waitopts); + const pid_t wait_pid = waitpid (child_pid, &status, waitopts); if (child_pid == wait_pid) { - if (WIFEXITED (state.status) || WIFSIGNALED (state.status)) + if (WIFEXITED (status)) { + result->exit = WEXITSTATUS (status); + switch (result->exit) { + case 126: + result->status = ST_EXIST; + break; + case 127: + result->status = ST_EXECUTE; + break; + } break; /*we've got an exit state, so let's bail*/ - else if (WIFSTOPPED (state.status)) - stopped = state.status; + } else if (WIFSIGNALED (status)) { + result->exit = WTERMSIG (status); + result->signaled = 1; + break; /*we've got an exit state, so let's bail*/ + } else if (WIFSTOPPED (status)) + stopped = status; #ifdef WIFCONTINUED /*Perhaps we should guard WIFSTOPPED with this guard also */ - else if (WIFCONTINUED (state.status)) + else if (WIFCONTINUED (status)) stopped = 0; #endif else @@ -469,7 +482,8 @@ recieving signals. Therefore, we'll record this and break out of the loop. */ - state.status = stopped; + result->exit = WTERMSIG (stopped); + result->signaled = 1; break; } @@ -502,7 +516,7 @@ } /* Record the signal used*/ - state.killed = signals [siginx]; + /* result->killed = signals [siginx];*/ ++siginx; @@ -512,8 +526,7 @@ Therefore, we'll set error flags and break out of the loop. */ - state.status = -1; - state.killed = -1; + result->status = ST_NOT_KILLED; break; } @@ -560,40 +573,38 @@ ++siginx; sleep(1); } - - return state; } /** Opens an input file, based on exec_name - Takes an executable name, and tries to open an input file based on this - name and the value of the in_root global variable. If a file is found in - neither of two locattions derived from these variables, this method tries - to fall back on /dev/null. + Takes a data directory and an executable name, and tries to open an input + file based on these variables. If a file is found in neither of two + locattions derived from these variables, this method tries to fall back on + /dev/null. Source file locations: - - [in_root]/manual/in/[exec_name].in - - [in_root]/tutorial/in/[exec_name].in + - [data_dir]/manual/in/[exec_name].in + - [data_dir]/tutorial/in/[exec_name].in - /dev/null + @param data_dir the path of the reference data directory @param exec_name the name of executable being run @returns the file descriptor of the opened file - @see in_root */ static int -open_input (const char* exec_name) +open_input (const char* data_dir, const char* exec_name) { int intermit = -1; assert (0 != exec_name); - assert (0 != in_root); + assert (0 != data_dir); - if (strlen (in_root)) { + if (strlen (data_dir)) { char* tmp_name; - /* Try in_root/manual/in/exec_name.in */ - tmp_name = reference_name ("manual", "in"); + /* Try data_dir/manual/in/exec_name.in */ + tmp_name = reference_name (data_dir, "manual", "in"); intermit = open (tmp_name, O_RDONLY); /* If we opened the file, return the descriptor */ @@ -607,9 +618,9 @@ terminate (1, "open (%s) failed: %s\n", tmp_name, strerror (errno)); - /* Try in_root/tutorial/in/exec_name.in */ + /* Try data_dir/tutorial/in/exec_name.in */ free (tmp_name); - tmp_name = reference_name ("tutorial", "in"); + tmp_name = reference_name (data_dir, "tutorial", "in"); intermit = open (tmp_name, O_RDONLY); /* If we opened the file, return the descriptor */ @@ -673,21 +684,23 @@ Utility macro to generate an rlimit tuple. @parm val 'short' resource name (no leading RLIMIT_). - @param idx limit structure name in child_limits global + @param idx rw_rlimit pointer in the target_opts structure @see limit_process - @see child_limits + @see target_opts */ #undef LIMIT -#define LIMIT(val, idx) { RLIMIT_ ## val, &child_limits.idx, #val } +#define LIMIT(val, idx) { RLIMIT_ ## val, options->idx, #val } /** Set process resource limits, based on the child_limits global. - This method uses the LIMIT macro to build an internal array of limits - to try setting. If setrlimit fails + This method uses the LIMIT macro to build an internal array of limits to + try setting. If setrlimit fails, we print a warning message (into the + output file) and move on. + @param options structure containing limits to set. */ static void -limit_process () +limit_process (const struct target_opts* options) { static const struct { int resource; @@ -719,13 +732,12 @@ }; for (size_t i = 0; limits [i].limit; ++i) { - rw_rlimit local; + struct rlimit local; - if ( RLIM_SAVED_CUR == limits [i].limit->rlim_cur - && RLIM_SAVED_MAX == limits [i].limit->rlim_max ) + if (!limits [i].limit) continue; - memcpy (&local, limits [i].limit, sizeof (struct rlimit)); + memcpy (&local, limits [i].limit, sizeof local); if (setrlimit (limits [i].resource, &local)) { warn ("error setting process limits for %s (soft: %lu, hard: " @@ -754,12 +766,15 @@ #ifdef _XOPEN_UNIX static bool init = 0; static struct rusage history; + static rw_timeval user, sys; struct rusage now; assert (0 != result); if (!init) { memset (&history, 0, sizeof history); + memset (&user, 0, sizeof user); + memset (&sys, 0, sizeof sys); init = 1; } @@ -769,23 +784,24 @@ } /* time calculations */ - result->user.tv_sec = now.ru_utime.tv_sec - history.ru_utime.tv_sec; - result->user.tv_usec = now.ru_utime.tv_usec - history.ru_utime.tv_usec; - result->sys.tv_sec = now.ru_stime.tv_sec - history.ru_stime.tv_sec; - result->sys.tv_usec = now.ru_stime.tv_usec - history.ru_stime.tv_usec; + user.tv_sec = now.ru_utime.tv_sec - history.ru_utime.tv_sec; + user.tv_usec = now.ru_utime.tv_usec - history.ru_utime.tv_usec; + sys.tv_sec = now.ru_stime.tv_sec - history.ru_stime.tv_sec; + sys.tv_usec = now.ru_stime.tv_usec - history.ru_stime.tv_usec; /* Adjust seconds/microseconds */ if (now.ru_utime.tv_usec < history.ru_utime.tv_usec) { - --result->user.tv_sec; - result->user.tv_usec += 1000000; + --user.tv_sec; + user.tv_usec += 1000000; } if (now.ru_stime.tv_usec < history.ru_stime.tv_usec) { - --result->sys.tv_sec; - result->sys.tv_usec += 1000000; + --sys.tv_sec; + sys.tv_usec += 1000000; } /* Tag result as having run */ - result->run = 1; + result->user = &user; + result->sys = &sys; /* Cache the values retrieved */ memcpy (&history, &now, sizeof (struct rusage)); @@ -807,16 +823,16 @@ @return structure describing how the child procees exited @see wait_for_child () */ -struct exec_attrs -exec_file (struct target_status* result) +void exec_file (const struct target_opts* options, struct target_status* result) { const pid_t child_pid = fork (); - assert (0 != result); - assert (0 != result->argv); - assert (0 != result->argv [0]); + assert (0 != options); + assert (0 != options->argv); + assert (0 != options->argv [0]); if (0 == child_pid) { /* child */ + const char* const target_name = get_target (); FILE* error_file; assert (0 != target_name); @@ -844,13 +860,13 @@ /* Redirect stdin */ { - const int intermit = open_input (target_name); + const int intermit = open_input (options->data_dir, target_name); replace_file (intermit, 0, "stdin"); } /* Redirect stdout */ { - char* const tmp_name = output_name (result->argv [0]); + char* const tmp_name = output_name (options->argv [0]); int intermit; intermit = open (tmp_name, O_WRONLY | O_CREAT | O_TRUNC, @@ -871,28 +887,25 @@ strerror (errno)); #ifdef _XOPEN_UNIX - limit_process (); + limit_process (options); #endif /* _XOPEN_UNIX */ - execv (result->argv [0], result->argv); + execv (options->argv [0], options->argv); fprintf (error_file, "%s (%s): execv (\"%s\", ...) error: %s\n", - exe_name, target_name, result->argv [0], strerror (errno)); + exe_name, target_name, options->argv [0], strerror (errno)); exit (1); } if (-1 == child_pid) { - /* Fake a failue to execute status return structure */ - struct exec_attrs state = {127, -1}; - warn ("Unable to create child process for %s: %s\n", result->argv [0], + result->status = ST_EXECUTE; + warn ("Unable to create child process for %s: %s\n", options->argv [0], strerror (errno)); - return state; } else { /* parent */ - struct exec_attrs state = wait_for_child (child_pid); + wait_for_child (child_pid, options->timeout, result); calculate_usage (result); - return state; } } #else /* _WIN{32,64} */ @@ -900,39 +913,39 @@ Opens an input file, based on exec_name, using the child_sa security setting. - Takes an executable name and security setting, and tries to open an input - file based on these variables and the value of the in_root global variable. + Takes a data directory, an executable name and security setting, and tries + to open an input file based on these variables. If a file is found in neither of two locations derived from these - variables, or if in_root is a null string, it returns null. + variables, or if data_dir is a null string, it returns null. If a file system error occurs when opening a file, INVALID_HANDLE_VALUE is returned (and should be checked for). Source file locations: - - [in_root]/manual/in/[exec_name].in - - [in_root]/tutorial/in/[exec_name].in + - [data_dir]/manual/in/[exec_name].in + - [data_dir]/tutorial/in/[exec_name].in + @param data_dir the path of the reference data directory @param exec_name the name of executable being run @param child_sa pointer to a SECURITY_ATTRIBUTES structure @returns the file descriptor of the opened file - @see in_root */ static HANDLE -open_input (const char* exec_name, SECURITY_ATTRIBUTES* child_sa) +open_input (const char* data_dir, const char* exec_name, + SECURITY_ATTRIBUTES* child_sa) { - const size_t root_len = strlen (in_root); HANDLE intermit; DWORD error; char* tmp_name; assert (0 != exec_name); - assert (0 != in_root); + assert (0 != data_dir); assert (0 != child_sa); - if (!root_len) + if (!strlen (data_dir)) return 0; - /* Try in_root\manual\in\exec_name.in */ - tmp_name = reference_name ("manual", "in"); + /* Try data_dir\manual\in\exec_name.in */ + tmp_name = reference_name (data_dir, "manual", "in"); intermit = CreateFile (tmp_name, GENERIC_READ, FILE_SHARE_READ, child_sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); @@ -944,9 +957,9 @@ return intermit; } - /* Try in_root\tutorial\in\exec_name.in */ + /* Try data_dir\tutorial\in\exec_name.in */ free (tmp_name); - tmp_name = reference_name ("tutorial", "in"); + tmp_name = reference_name (data_dir, "tutorial", "in"); intermit = CreateFile (tmp_name, GENERIC_READ, FILE_SHARE_READ, child_sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); @@ -1033,6 +1046,65 @@ } /** + Try killing the child process with what (limited) facilities we have at + our disposal. Does not collect exit code or close child process handle. + + @param process Process handle of process to kill. + @param result status structure to record system errors in. +*/ +static void +kill_child_process (PROCESS_INFORMATION child, struct target_status* result) +{ + OSVERSIONINFO OSVer; + + OSVer.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + if (0 == GetVersionEx (&OSVer)) { + result->status = ST_SYSTEM_ERROR; + warn_last_error ("Retrieving host version"); + return; + } + /* Try to soft kill child process group if it didn't terminate, but only + on NT */ + + if (VER_PLATFORM_WIN32_NT == OSVer.dwPlatformId) { + + struct sig_event + { + DWORD signal_; + const char* msg_; + } + sig_events [] = { + { CTRL_C_EVENT, "Sending child process Control-C" }, + { CTRL_BREAK_EVENT, "Sending child process Control-Break" } + }; + + for (unsigned long i = 0; + i < sizeof (sig_events) / sizeof (*sig_events); ++i) { + DWORD wait_code; + + if (0 == GenerateConsoleCtrlEvent (sig_events [i].signal_, + child.dwProcessId)) + warn_last_error (sig_events [i].msg_); + + wait_code = WaitForSingleObject (child.hProcess, 1000); + if (WAIT_TIMEOUT == wait_code) + continue; + + if (WAIT_OBJECT_0 != wait_code) { + result->status = ST_SYSTEM_ERROR; + warn_last_error ("Waiting for child process"); + } + return; + } + } + /* Then hard kill the child process */ + if (0 == TerminateProcess (child.hProcess, 3)) + warn_last_error ("Terminating child process"); + else if (WAIT_FAILED == WaitForSingleObject (child.hProcess, 1000)) + warn_last_error ("Waiting for child process"); +} + +/** Entry point to the child process (watchdog) subsystem. This method creates a process using the windows CreateProcess API call. @@ -1049,31 +1121,21 @@ @return structure describing how the child procees exited @see wait_for_child () */ -struct exec_attrs -exec_file (struct target_status* result) +void exec_file (const struct target_opts* options, struct target_status* result) { char* merged; PROCESS_INFORMATION child; - OSVERSIONINFO OSVer; STARTUPINFO context; SECURITY_ATTRIBUTES child_sa = /* SA for inheritable handle. */ {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; - struct exec_attrs status; - const DWORD real_timeout = (timeout > 0) ? timeout * 1000 : INFINITE; - DWORD wait_code; + DWORD real_timeout, wait_code; - assert (0 != result); - assert (0 != result->argv); - assert (0 != result->argv [0]); + assert (0 != options); + assert (0 != options->argv); + assert (0 != options->argv [0]); - memset (&status, 0, sizeof status); + real_timeout = (options->timeout > 0) ? options->timeout * 1000 : INFINITE; - OSVer.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); - if (0 == GetVersionEx (&OSVer)) { - status.status = -1; - status.error = warn_last_error ("Retrieving host version"); - return status; - } /* Borrow our startup info */ GetStartupInfo (&context); context.dwFlags = STARTF_USESTDHANDLES; @@ -1081,31 +1143,32 @@ /* Create I/O handles */ { /* Output redirection */ - char* const tmp_name = output_name (result->argv [0]); + char* const tmp_name = output_name (options->argv [0]); context.hStdOutput = CreateFile (tmp_name, GENERIC_WRITE, FILE_SHARE_WRITE, &child_sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == context.hStdOutput) { - status.status = -1; - status.error = warn_last_error ("Opening child output stream"); - return status; + result->status = ST_SYSTEM_ERROR; + warn_last_error ("Opening child output stream"); + return; } context.hStdError = context.hStdOutput; free (tmp_name); /* Input redirection */ - context.hStdInput = open_input (target_name, &child_sa); + context.hStdInput = open_input (options->data_dir, get_target (), + &child_sa); if (INVALID_HANDLE_VALUE == context.hStdInput) { CloseHandle (context.hStdOutput); - status.status = -1; - status.error = warn_last_error ("Opening child input stream"); - return status; + result->status = ST_SYSTEM_ERROR; + warn_last_error ("Opening child input stream"); + return; } } - merged = merge_argv (result->argv); + merged = merge_argv (options->argv); /* set appropriate error mode (the child process inherits this error mode) to disable displaying the critical-error-handler @@ -1113,11 +1176,11 @@ UINT old_mode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); /* Create the child process */ - if (0 == CreateProcess (result->argv [0], merged, 0, 0, 1, + if (0 == CreateProcess (options->argv [0], merged, 0, 0, 1, CREATE_NEW_PROCESS_GROUP, 0, 0, &context, &child)) { /* record the status if we failed to create the process */ - status.status = -1; - status.error = warn_last_error ("Creating child process");; + result->status = ST_SYSTEM_ERROR; + warn_last_error ("Creating child process");; } /* restore the previous error mode */ @@ -1134,87 +1197,41 @@ free (merged); /* Return if we failed to create the child process */ - if (-1 == status.status) - return status; + if (ST_SYSTEM_ERROR == result->status) + return; if (0 == CloseHandle (child.hThread)) warn_last_error ("Closing child main thread handle"); /* Wait for the child process to terminate */ wait_code = WaitForSingleObject (child.hProcess, real_timeout); - if (WAIT_TIMEOUT != wait_code) { - if (WAIT_OBJECT_0 == wait_code) { - if (0 == GetExitCodeProcess (child.hProcess, &status.status)) { - warn_last_error ("Retrieving child process exit code"); - status.status = -1; - } - status.error = 0; - } - else { - status.status = -1; - status.error = warn_last_error ("Waiting for child process"); - } - if (0 == CloseHandle (child.hProcess)) - warn_last_error ("Closing child process handle"); - return status; - } + switch (wait_code) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + /* Child process didn't shut down, so note that we killed it. */ + result->status = ST_KILLED; - /* Try to soft kill child process group if it didn't terminate, but only - on NT */ - if (VER_PLATFORM_WIN32_NT == OSVer.dwPlatformId) { - - struct sig_event - { - DWORD signal_; - const char* msg_; - } - sig_events [] = { - { CTRL_C_EVENT, "Sending child process Control-C" }, - { CTRL_BREAK_EVENT, "Sending child process Control-Break" } - }; - - for (unsigned long i = 0; - i < sizeof (sig_events) / sizeof (*sig_events); ++i) { - - if (0 == GenerateConsoleCtrlEvent (sig_events [i].signal_, - child.dwProcessId)) - warn_last_error (sig_events [i].msg_); - - wait_code = WaitForSingleObject (child.hProcess, 1000); - if (WAIT_TIMEOUT == wait_code) - continue; - - if (WAIT_OBJECT_0 == wait_code) { - if (0 == GetExitCodeProcess (child.hProcess, &status.status)) { - warn_last_error ("Retrieving child process exit code"); - status.status = -1; - } - status.error = i + 1; - } - else { - status.status = -1; - status.error = warn_last_error ("Waiting for child process"); - } - - if (0 == CloseHandle (child.hProcess)) - warn_last_error ("Closing child process handle"); - return status; - } + kill_child_process (child, result); + break; + default: + result->status = ST_SYSTEM_ERROR; + warn_last_error ("Waiting for child process"); } - /* Then hard kill the child process */ - if (0 == TerminateProcess (child.hProcess, 3)) - warn_last_error ("Terminating child process"); - else if (WAIT_FAILED == WaitForSingleObject (child.hProcess, 1000)) - warn_last_error ("Waiting for child process"); - if (0 == GetExitCodeProcess (child.hProcess, &status.status)) { + if (0 == GetExitCodeProcess (child.hProcess, (LPDWORD)&result->exit)) { warn_last_error ("Retrieving child process exit code"); - status.status = -1; + result->status = ST_SYSTEM_ERROR; } - status.error = 3; + if (0 == CloseHandle (child.hProcess)) warn_last_error ("Closing child process handle"); - return status; + + if (STATUS_ACCESS_VIOLATION == result->exit) { + result->exit = SIGSEGV; + result->signaled = 1; + } } + #endif /* _WIN{32,64} */ Index: cmdopt.cpp =================================================================== --- cmdopt.cpp (revision 450090) +++ cmdopt.cpp (working copy) @@ -47,18 +47,12 @@ #endif /* _WIN{32,64} */ #include "exec.h" +#include "target.h" #include "util.h" #include "cmdopt.h" -int timeout = 10; /**< Child process timeout. Default 10. */ -int compat = 0; /**< Test compatability mode switch. Defaults to 0 (off). */ -unsigned verbose = 0; -const char* exe_opts = ""; /**< Global command line switches for child - processes. */ -const char* in_root = ""; /**< Root directory for input/reference files. */ const char* exe_name; /**< Alias for process argv [0]. */ -const char* target_name; #if !defined (_WIN32) && !defined (_WIN64) const char escape_code = '\\'; const char default_path_sep = '/'; @@ -71,17 +65,6 @@ const size_t exe_suffix_len = 4; /* strlen(".exe") == 4 */ #endif -struct limit_set child_limits = { - { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, - { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, - { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, - { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, - { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, - { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, - { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, - { RLIM_SAVED_CUR, RLIM_SAVED_MAX } -}; - static const char usage_text[] = { "Usage: %s [OPTIONS] [targets]\n" @@ -95,8 +78,6 @@ " -h, -? Display usage information and exit.\n" " -t seconds Set timeout before killing target (default is 10\n" " seconds).\n" - " -q Set verbosity level to 0 (default).\n" - " -v Increase verbosity of output.\n" " -x opts Specify command line options to pass to targets.\n" " -- Terminate option processing and treat all arguments\n" " that follow as targets.\n" @@ -259,10 +240,10 @@ @see child_limits */ static bool -parse_limit_opts (const char* opts) +parse_limit_opts (const char* opts, struct target_opts* defaults) { static const struct { - rw_rlimit* limit; + rw_rlimit** limit; const char* name; const char* caps; const char* mixd; @@ -270,49 +251,49 @@ } limits[] = { { #ifdef RLIMIT_CORE - &child_limits.core, + &defaults->core, #else 0, #endif // RLIMIT_CORE "core", "CORE", "Core", 4 }, { #ifdef RLIMIT_CPU - &child_limits.cpu, + &defaults->cpu, #else 0, #endif // RLIMIT_CPU "cpu", "CPU", "Cpu", 3 }, { #ifdef RLIMIT_DATA - &child_limits.data, + &defaults->data, #else 0, #endif // RLIMIT_DATA "data", "DATA", "Data", 4 }, { #ifdef RLIMIT_FSIZE - &child_limits.fsize, + &defaults->fsize, #else 0, #endif // RLIMIT_FSIZE "fsize", "FSIZE", "Fsize", 5 }, { #ifdef RLIMIT_NOFILE - &child_limits.nofile, + &defaults->nofile, #else 0, #endif // RLIMIT_NOFILE "nofile", "NOFILE", "Nofile", 6 }, { #ifdef RLIMIT_STACK - &child_limits.stack, + &defaults->stack, #else 0, #endif // RLIMIT_STACK "stack", "STACK", "Stack", 5 }, { #ifdef RLIMIT_AS - &child_limits.as, + &defaults->as, #else 0, #endif // RLIMIT_AS @@ -355,11 +336,17 @@ break; if (limits [i].limit) { + if (!*limits [i].limit) { + (*limits [i].limit) = + (rw_rlimit*)RW_MALLOC (sizeof (rw_rlimit)); + (*limits [i].limit)->rlim_cur = RLIM_SAVED_CUR; + (*limits [i].limit)->rlim_max = RLIM_SAVED_MAX; + } if (soft) - limits [i].limit->rlim_cur = lim; + (*limits [i].limit)->rlim_cur = lim; if (hard) - limits [i].limit->rlim_max = lim; + (*limits [i].limit)->rlim_max = lim; } else warn ("Unable to process %s limit: Not supported\n", limits [i].name); @@ -423,16 +410,15 @@ /** Parses command line arguments for switches and options. - @param argc number of command line arguments - @param argv command line arguments + @param argc number of command line arguments. + @param argv command line arguments. + @param defaults target_status structure containing default values. + @param exe_opts handle to default child process arguments string @return number of command line arguments parsed. - @see timeout - @see compat - @see in_root - @see exe_opts */ int -eval_options (int argc, char **argv) +eval_options (int argc, char **argv, struct target_opts* defaults, + char** exe_opts) { const char opt_timeout[] = "-t"; const char opt_data_dir[] = "-d"; @@ -476,7 +462,7 @@ if (optarg) { if (!isdigit (*optarg)) bad_value (optname, optarg); - timeout = strtol (optarg, &end, 10); + defaults->timeout = strtol (optarg, &end, 10); if (*end || errno) bad_value (optname, optarg); } @@ -487,23 +473,16 @@ case 'd': optname = opt_data_dir; - in_root = get_short_val (argv, &i); - if (!in_root) + defaults->data_dir = get_short_val (argv, &i); + if (!defaults->data_dir) missing_value (optname); break; case 'x': optname = opt_t_flags; - exe_opts = get_short_val (argv, &i); - if (!exe_opts) + *exe_opts = get_short_val (argv, &i); + if (!*exe_opts) missing_value (optname); break; - case 'v': - ++verbose; - break; - case 'q': - verbose = 0; - break; - case '-': { const size_t arglen = strlen (argv [i]); @@ -514,12 +493,12 @@ if ( sizeof opt_compat - 1 == arglen && !memcmp (opt_compat, argv [i], sizeof opt_compat)) { - compat = 1; + defaults->compat = 1; break; } else if ( sizeof opt_nocompat - 1 == arglen && !memcmp (opt_nocompat, argv [i], sizeof opt_nocompat)) { - compat = 0; + defaults->compat = 0; break; } else if ( sizeof opt_exit - 1 <= arglen @@ -587,7 +566,7 @@ optname = opt_ulimit; optarg = get_long_val (argv, &i, sizeof opt_ulimit - 1); if (optarg && *optarg) { - if (!parse_limit_opts (optarg)) { + if (!parse_limit_opts (optarg, defaults)) { break; } } Index: output.cpp =================================================================== --- output.cpp (revision 450090) +++ output.cpp (working copy) @@ -63,6 +63,8 @@ unsigned fsm = 0; char tok; + const char* const target_name = get_target (); + assert (0 != target_name); assert (0 != data); assert (0 != status); @@ -185,8 +187,8 @@ reference file name is generated by the reference_name function. Reference file locations: - - [in_root]/manual/out/[target_name].out - - [in_root]/tutorial/out/[target_name].out + - [data_dir]/manual/out/[target_name].out + - [data_dir]/tutorial/out/[target_name].out @todo add logic to handle differing line endings (CR vs LF vs CRLF) @@ -196,7 +198,8 @@ @see reference_name */ static void -check_example (char* const out_name, FILE* fout, struct target_status* status) +check_example (const char* const data_dir, char* const out_name, FILE* fout, + struct target_status* status) { struct stat file_info; char* ref_name; @@ -207,8 +210,8 @@ assert (0 != fout); assert (0 != status); - /* Try in_root/manual/out/target_name.out */ - ref_name = reference_name ("manual", "out"); + /* Try data_dir/manual/out/target_name.out */ + ref_name = reference_name (data_dir, "manual", "out"); if (0 > stat (ref_name, &file_info)) { if (ENOENT != errno) { @@ -220,9 +223,9 @@ } /* If that doesn't exist, try - in_root/tutorial/out/target_name.out */ + data_dir/tutorial/out/target_name.out */ free (ref_name); - ref_name = reference_name ("tutorial", "out"); + ref_name = reference_name (data_dir, "tutorial", "out"); if (0 > stat (ref_name, &file_info)) { if (ENOENT != errno) { @@ -306,14 +309,14 @@ @param status status object to record results in. */ void -parse_output (struct target_status* status) +parse_output (const struct target_opts* options, struct target_status* status) { char* out_name; FILE* data; assert (0 != status); - assert (0 != status->argv [0]); - out_name = output_name (status->argv [0]); + assert (0 != options->argv [0]); + out_name = output_name (options->argv [0]); data = fopen (out_name, "r"); @@ -323,17 +326,17 @@ status->status = ST_NO_OUTPUT; } else { - if (!strlen (in_root)) { + if (!strlen (options->data_dir)) { /* If there is not an input directory, look at the assertion tags */ - if (!compat) + if (!options->compat) check_test (data, status); else check_compat_test (data, status); } else { /* Otherwise, diff against the output file */ - check_example (out_name, data, status); + check_example (options->data_dir, out_name, data, status); } fclose (data); } Index: display.cpp =================================================================== --- display.cpp (revision 450090) +++ display.cpp (working copy) @@ -41,8 +41,9 @@ "SYS"); } -void print_target_plain (const struct target_status*) +void print_target_plain (const struct target_opts*) { + const char* const target_name = get_target (); printf ("%-25.25s ", target_name); fflush (stdout); } @@ -53,14 +54,15 @@ assert (0 != status); assert (ST_OK <= status->status && ST_LAST > status->status); - valid_timing = status->run && ST_NOT_KILLED != status->status; + valid_timing = status->user && status->sys && + ST_NOT_KILLED != status->status; if (status->status) /* if status is set, print it */ printf ("%6s", short_st_name [status->status]); + else if (status->signaled) /* if exit signal is non-zero, print it */ + printf ("%6s", get_signame (status->exit)); else if (status->exit) /* if exit code is non-zero, print it */ printf ("%6d", status->exit); - else if (status->signal) /* if exit signal is non-zero, print it */ - printf ("%6s", get_signame (status->signal)); else printf (" 0"); @@ -74,9 +76,9 @@ /* Print timings, if available */ if (valid_timing) - printf (" %3ld.%03ld %3ld.%03ld\n", status->user.tv_sec, - status->user.tv_usec/1000, status->sys.tv_sec, - status->sys.tv_usec/1000); + printf (" %3ld.%03ld %3ld.%03ld\n", status->user->tv_sec, + status->user->tv_usec/1000, status->sys->tv_sec, + status->sys->tv_usec/1000); else { puts (""); } @@ -85,7 +87,7 @@ void print_footer_plain () {} -const char* short_st_name [ST_LAST] = { +const char* const short_st_name [ST_LAST] = { "OK", /*ST_OK*/ "COMP", /*ST_COMPILE*/ "LINK", /*ST_LINK*/ @@ -105,6 +107,6 @@ void (*print_header) () = print_header_plain; -void (*print_target) (const struct target_status*) = print_target_plain; +void (*print_target) (const struct target_opts*) = print_target_plain; void (*print_status) (const struct target_status* status) = print_status_plain; void (*print_footer) () = print_footer_plain; Index: util.cpp =================================================================== --- util.cpp (revision 450090) +++ util.cpp (working copy) @@ -35,6 +35,7 @@ void warn (const char* const format, ...) { + const char* const target_name = get_target (); va_list args; assert (0 != format); @@ -58,6 +59,7 @@ void terminate (const int state, const char* const format, ...) { + const char* const target_name = get_target (); va_list args; assert (0 != format); @@ -128,30 +130,31 @@ } char* -reference_name (const char* dir, const char* mode) +reference_name (const char* data_dir, const char* subdir, const char* mode) { size_t root_len, cmp_len, dir_len, mode_len, net_len; char* ref_name; char* tail; + const char* const target_name = get_target (); - assert (0 != in_root); + assert (0 != data_dir); assert (0 != target_name); - assert (0 != dir); + assert (0 != subdir); assert (0 != mode); - root_len = strlen (in_root); + root_len = strlen (data_dir); cmp_len = strlen (target_name) - exe_suffix_len; - dir_len = strlen (dir); + dir_len = strlen (subdir); mode_len = strlen (mode); net_len = root_len + cmp_len + dir_len + mode_len * 2 + 5; /* 5 comes from 3 path seperator characters, the suffix seperator character, and the trailing null */ tail = ref_name = (char*)RW_MALLOC (net_len); - memcpy (tail, in_root, root_len); + memcpy (tail, data_dir, root_len); tail += root_len; *tail++ = default_path_sep; - memcpy (tail , dir, dir_len); + memcpy (tail , subdir, dir_len); tail += dir_len; *tail++ = default_path_sep; memcpy (tail , mode, mode_len); Index: exec.h =================================================================== --- exec.h (revision 450090) +++ exec.h (working copy) @@ -27,21 +27,12 @@ #ifndef RW_EXEC_H #define RW_EXEC_H -struct exec_attrs { -#if !defined (_WIN32) && !defined (_WIN64) - int status; - int killed; -#else - /* AKA DWORD */ - unsigned long status; - unsigned long error; -#endif /* _WIN{32,64} */ -}; +#include "target.h" /* For struct target_opts */ int get_signo (const char* signame); const char* get_signame (int signo); -struct exec_attrs exec_file (struct target_status* result); +void exec_file (const struct target_opts* options, struct target_status* result); #endif // RW_EXEC_H Index: cmdopt.h =================================================================== --- cmdopt.h (revision 450090) +++ cmdopt.h (working copy) @@ -27,76 +27,33 @@ #ifndef RW_PARSE_OPTS_H #define RW_PARSE_OPTS_H -#if !defined (_WIN32) && !defined (_WIN64) -# include /* For _XOPEN_UNIX */ -#endif +#include "target.h" /* For struct target_opts */ -extern int timeout; -extern int compat; -extern unsigned verbose; /**< Verbose output mode switch. Defaults to 0 */ -extern const char* exe_opts; -extern const char* in_root; extern const char* exe_name; -extern const char* target_name; /**< Alias for current target name. */ extern const char escape_code; /**< Escape character used in paths. */ extern const char default_path_sep; /**< Primary path seperator */ extern const char suffix_sep; /**< File suffix seperator. */ extern const size_t exe_suffix_len; /**< Length of executable suffix. */ -#ifdef _XOPEN_UNIX -# include /* for struct rlimit */ -/** - Abstraction typedef for struct rlimit using real struct -*/ -typedef struct rlimit rw_rlimit; -#else -/** - Placeholder rlim_t for use in rw_rlimit -*/ -typedef unsigned long rw_rlim_t; -/** - Placeholder struct rlimit to use if _XOPEN_UNIX isn't defined -*/ -struct rw_rlimit { - rw_rlim_t rlim_cur; - rw_rlim_t rlim_max; -}; -/** - Abstraction typedef for struct rlimit using placeholder struct -*/ -typedef struct rw_rlimit rw_rlimit; -#endif - -#ifndef RLIM_INFINITY -# define RLIM_INFINITY -1 -#endif /* RLIM_INFINITY */ - -#ifndef RLIM_SAVED_CUR -# define RLIM_SAVED_CUR RLIM_INFINITY -#endif /* RLIM_SAVED_CUR */ - -#ifndef RLIM_SAVED_MAX -# define RLIM_SAVED_MAX RLIM_INFINITY -#endif /* RLIM_SAVED_MAX */ - -extern struct limit_set { - rw_rlimit core; - rw_rlimit cpu; - rw_rlimit data; - rw_rlimit fsize; - rw_rlimit nofile; - rw_rlimit stack; - rw_rlimit mem; - rw_rlimit as; -} child_limits; /**< Container holding child process limits. */ - void show_usage (int status); int -eval_options (int argc, char** argv); +eval_options (int argc, char** argv, struct target_opts* defaults, + char** exe_opts); char** split_opt_string (const char* opts); +/** + Accessor method for current execution target. + + This function is defined in runall.cpp (not cmdopt.cpp). + + This value is a pointer into argv[0] of the current target. + + @returns (Base) name of current execution target. +*/ +const char* const get_target (); + #endif // RW_PARSE_OPTS_H Index: runall.cpp =================================================================== --- runall.cpp (revision 450090) +++ runall.cpp (working copy) @@ -45,6 +45,8 @@ #include "output.h" #include "util.h" +#include "target.h" + #ifndef ENOENT # define ENOENT 2 #endif // ENOENT @@ -206,18 +208,17 @@ @return 1 if valid target to run, 0 otherwise. */ static int -check_target_ok (struct target_status* status) +check_target_ok (const char* target, struct target_status* status) { struct stat file_info; int exists = 1; + assert (0 != target); assert (0 != status); - assert (0 != status->argv); - assert (0 != status->argv[0]); - if (0 > stat (status->argv [0], &file_info)) { + if (0 > stat (target, &file_info)) { if (ENOENT != errno) { - warn ("Error stating %s: %s\n", status->argv [0], strerror (errno)); + warn ("Error stating %s: %s\n", target, strerror (errno)); status->status = ST_SYSTEM_ERROR; return 0; } @@ -228,12 +229,12 @@ /* This is roughly equivlent to the -x bash operator. It checks if the file can be run, /not/ if we can run it */ - const size_t path_len = strlen (status->argv [0]); + const size_t path_len = strlen (target); char* tmp_name; #if 0 /* Disable .o target check as unused */ /* If target is a .o file, check if it exists */ - if ('.' == status->argv [0] [path_len-1] && 'o' == status->argv [0] [path_len]) { + if ('.' == target [path_len-1] && 'o' == target [path_len]) { if (!exists) status->status = ST_COMPILE; return 0; @@ -249,18 +250,19 @@ #if !defined (_WIN32) && !defined (_WIN64) /* Otherwise, check for the .o file on non-windows systems */ tmp_name = (char*)RW_MALLOC (path_len + 3); - memcpy (tmp_name, status->argv [0], path_len + 1); + memcpy (tmp_name, target, path_len + 1); strcat (tmp_name,".o"); #else /* Or the target\target.obj file on windows systems*/ { + const char* const target_name = get_target (); size_t target_len = strlen (target_name); size_t tmp_len = path_len + target_len - 2; /* - 2 comes from removing 4 characters (extra .exe) and adding 2 characters (\ directory seperator and trailing null) */ tmp_name = (char*)RW_MALLOC (tmp_len); - memcpy (tmp_name, status->argv [0], path_len - 4); + memcpy (tmp_name, target, path_len - 4); tmp_name [path_len - 4] = default_path_sep; memcpy (tmp_name + path_len - 3, target_name, target_len); tmp_name [tmp_len - 4] = 'o'; @@ -289,69 +291,6 @@ } /** - Post target execution result analysis. - - This method examines the content of result, dispatching the processing - to pares_output () if target had a return code of 0. - Otherwise, this method determines why target terminated, based on the - return code and fills the result structure with these results. - - @param target the path to the executable that was run - @param result the return code data from running the target - @param status status object to record results in. - @see parse_output () -*/ -static void -process_results (const struct exec_attrs* result, - struct target_status* status) -{ - assert (0 != result); - assert (0 != status); - - if (0 == result->status) { - parse_output (status); - } -#if !defined (_WIN32) && !defined (_WIN64) - else if (WIFEXITED (result->status)) { - const int retcode = WEXITSTATUS (result->status); - switch (retcode) { - case 126: - status->status = ST_EXIST; - break; - case 127: - status->status = ST_EXECUTE; - break; - default: - status->exit = retcode; - } - } - else if (WIFSIGNALED (result->status)) { - status->signal = WTERMSIG (result->status); - } - else if (WIFSTOPPED (result->status)) { - status->signal = WSTOPSIG (result->status); - } - else if (-1 == result->status && -1 == result->killed) { - status->status = ST_NOT_KILLED; - } - else { - warn ("Unknown result status: %d, %d\n", result->status, - result->killed); - status->status = ST_SYSTEM_ERROR; - } -#else - else if (-1 == result->status) - status->status = ST_SYSTEM_ERROR; - else if (result->error) - status->status = ST_KILLED; - else if (STATUS_ACCESS_VIOLATION == result->status) - status->signal = SIGSEGV; - else - status->exit = result->status; -#endif /* _WIN{32,64} */ -} - -/** (Re)implementation of the POSIX basename function. This is a simplistic (re)implementation of the basename function @@ -382,6 +321,13 @@ return mark; } +static const char* target_name; + +const char* const get_target () +{ + return target_name; +} + /** High level method to run target, using childargv as arguments. @@ -396,33 +342,37 @@ @see process_results */ static void -run_target (char* target, char** argv) +run_target (char* target, const struct target_opts *target_template) { + struct target_opts options; struct target_status results; assert (0 != target); - assert (0 != argv); + assert (0 != target_template); + assert (0 != target_template->argv); + memcpy (&options, target_template, sizeof options); memset (&results, 0, sizeof results); - results.argv = merge_argv (target, argv); + options.argv = merge_argv (target, options.argv); - assert (0 != results.argv); - assert (0 != results.argv [0]); + assert (0 != options.argv); + assert (0 != options.argv [0]); - target_name = rw_basename (results.argv [0]); + target_name = rw_basename (options.argv [0]); - print_target (&results); + print_target (&options); - if (check_target_ok (&results)) { - struct exec_attrs status = exec_file (&results); - process_results (&status, &results); + if (check_target_ok (options.argv [0], &results)) { + exec_file (&options, &results); + if (0 == results.exit && 0 == results.signaled) + parse_output (&options, &results); } print_status (&results); - free (results.argv [0]); - free (results.argv); + free (options.argv [0]); + free (options.argv); } /** @@ -439,10 +389,17 @@ int main (int argc, char *argv []) { + struct target_opts target_template; + char* exe_opts = ""; + exe_name = argv [0]; + memset (&target_template, 0, sizeof target_template); + target_template.timeout = 10; + target_template.data_dir = ""; + if (1 < argc && '-' == argv [1][0]) { - const int nopts = eval_options (argc, argv); + const int nopts = eval_options (argc, argv, &target_template, &exe_opts); if (0 > nopts) return 1; @@ -457,22 +414,30 @@ if (0 < argc) { int i; - char** childargv = split_opt_string (exe_opts); + target_template.argv = split_opt_string (exe_opts); - assert (0 != childargv); + assert (0 != target_template.argv); print_header (); for (i = 0; i < argc; ++i) { - run_target (argv [i], childargv); + run_target (argv [i], &target_template); } print_footer (); - if (childargv [0]) - free (childargv [0]); - free (childargv); + if (target_template.argv [0]) + free (target_template.argv [0]); + free (target_template.argv); } + if (target_template.core) free (target_template.core); + if (target_template.cpu) free (target_template.cpu); + if (target_template.data) free (target_template.data); + if (target_template.fsize) free (target_template.fsize); + if (target_template.nofile) free (target_template.nofile); + if (target_template.stack) free (target_template.stack); + if (target_template.as) free (target_template.as); + return 0; } --------------020605080307060007080008 Content-Type: text/x-c-header; name="target.h" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="target.h" /************************************************************************ * * cmdopt.h - Interface declaration for the option parsing subsystem * * $Id: cmdopt.h 443494 2006-09-14 21:55:29Z sebor $ * ************************************************************************ * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * **************************************************************************/ #ifndef RW_TARGET_H #define RW_TARGET_H #if !defined (_WIN32) && !defined (_WIN64) # include /* For _XOPEN_UNIX */ #endif #ifdef _XOPEN_UNIX # include /* for struct rlimit */ # include /* for struct timeval */ /** Abstraction typedef for struct rlimit using real struct */ typedef struct rlimit rw_rlimit; /** Abstraction typedef for struct timeval using real struct */ typedef struct timeval rw_timeval; #else /** Placeholder rlim_t for use in rw_rlimit */ typedef unsigned long rw_rlim_t; /** Placeholder struct rlimit to use if _XOPEN_UNIX isn't defined */ struct rw_rlimit { rw_rlim_t rlim_cur; rw_rlim_t rlim_max; }; /** Abstraction typedef for struct rlimit using placeholder struct */ typedef struct rw_rlimit rw_rlimit; /** Placeholder time_t for use in rw_timeval */ typedef long rw_time_t; /** Placeholder suseconds_t for use in rw_timeval */ typedef long rw_suseconds_t; /** Placeholder struct timeval to use if _XOPEN_UNIX isn't defined */ struct rw_timeval { rw_time_t tv_sec; rw_suseconds_t tv_usec; }; /** Abstraction typedef for struct timeval using placeholder struct */ typedef struct rw_timeval rw_timeval; #endif #ifndef RLIM_INFINITY # define RLIM_INFINITY -1 #endif /* RLIM_INFINITY */ #ifndef RLIM_SAVED_CUR # define RLIM_SAVED_CUR RLIM_INFINITY #endif /* RLIM_SAVED_CUR */ #ifndef RLIM_SAVED_MAX # define RLIM_SAVED_MAX RLIM_INFINITY #endif /* RLIM_SAVED_MAX */ /** Structure encapsulating the options involved in executing a run. */ struct target_opts { char** argv; /**< Target argv array. */ unsigned timeout; /**< Allowed time for process execution. */ char* data_dir; /**< Root directory for reference input/output files. */ int compat; /**< Test suite compatability mode switch. */ rw_rlimit* core; rw_rlimit* cpu; rw_rlimit* data; rw_rlimit* fsize; rw_rlimit* nofile; rw_rlimit* stack; rw_rlimit* as; }; /** Status codes to denote result of analysis. */ enum ProcessStatus { ST_OK = 0, /**< Default (OK) status */ ST_COMPILE, /**< Target failed to compile */ ST_LINK, /**< Target failed to link */ ST_EXIST, /**< Target doesn't exist? */ ST_EXECUTE_FLAG, /**< Target lacks X flag */ ST_EXECUTE, /**< Target failed to execute? */ ST_NO_OUTPUT, /**< Target produced no output */ ST_NO_REF, /**< No reference file found */ ST_BAD_REF, /**< Invalid reference file found */ ST_BAD_OUTPUT, /**< Incorrect output found */ ST_FORMAT, /**< Incorrectly formatted (test) output found */ ST_OVERFLOW, /**< Assertion counter overflow */ ST_SYSTEM_ERROR, /**< System error during file system operation */ ST_KILLED, /**< Target killed by exec utility */ ST_NOT_KILLED, /** Target not killed by exec utility */ ST_LAST /**< Array terminator */ }; /** Structure encapsulating the results extracted from a run. */ struct target_status { int exit; /**< exit/signal code for process. */ int signaled; /**< 1 if exit is a signal number, 0 denoting exit code otherwise */ enum ProcessStatus status; /**< Textual process status. */ const rw_timeval* user; /**< Elapsed user time spent in execution. */ const rw_timeval* sys; /**< Elapsed system time spent in execution. */ unsigned warn; /**< Number of (test) warnings. */ unsigned assert; /**< Number of (test) assertions. */ unsigned failed; /**< Number of failed (test) assertions. */ }; #endif /* RW_TARGET_H */ --------------020605080307060007080008--