mirror of
https://github.com/boostorg/build.git
synced 2026-02-13 00:12:11 +00:00
Handle message truncation, zombies, sigchld handling.
Changes to get verbose compiler output down to a manageable size. Added option, -mN to limit output to N kb. Default is to capture all compiler generated output. Added this option to ignore massive compiler diagnostic outout associated with some compilers. With this change, I can increase the nightly timeout to longer than 300 seconds to give some slower compilers a change to finish compiling. [SVN r76862]
This commit is contained in:
@@ -66,7 +66,7 @@
|
||||
*/
|
||||
|
||||
static clock_t tps = 0;
|
||||
static struct timeval tv;
|
||||
static struct timespec tv;
|
||||
static int select_timeout = 0;
|
||||
static int intr = 0;
|
||||
static int cmdsrunning = 0;
|
||||
@@ -91,6 +91,7 @@ static struct
|
||||
void (*func)( void *closure, int status, timing_info*, const char *, const char * );
|
||||
void *closure;
|
||||
time_t start_dt; /* start of command timestamp */
|
||||
long msgsize[2];
|
||||
} cmdtab[ MAXJOBS ] = {{0}};
|
||||
|
||||
/*
|
||||
@@ -342,28 +343,36 @@ void exec_cmd
|
||||
|
||||
int read_descriptor( int i, int s )
|
||||
{
|
||||
int ret;
|
||||
int len;
|
||||
int ret = 1, len, err;
|
||||
char buffer[BUFSIZ];
|
||||
|
||||
while ( 0 < ( ret = fread( buffer, sizeof(char), BUFSIZ-1, cmdtab[ i ].stream[ s ] ) ) )
|
||||
{
|
||||
buffer[ret] = 0;
|
||||
if ( !cmdtab[ i ].buffer[ s ] )
|
||||
{
|
||||
/* Never been allocated. */
|
||||
cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( ret + 1 );
|
||||
memcpy( cmdtab[ i ].buffer[ s ], buffer, ret + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Previously allocated. */
|
||||
char * tmp = cmdtab[ i ].buffer[ s ];
|
||||
len = strlen( tmp );
|
||||
cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( len + ret + 1 );
|
||||
memcpy( cmdtab[ i ].buffer[ s ], tmp, len );
|
||||
memcpy( cmdtab[ i ].buffer[ s ] + len, buffer, ret + 1 );
|
||||
BJAM_FREE( tmp );
|
||||
while ( 0 < ( ret = fread( buffer, sizeof(char), BUFSIZ-1, cmdtab[ i ].stream[ s ] ) ) ) {
|
||||
|
||||
/* only copy action data until hit buffer limit, then ignore rest of data */
|
||||
if (cmdtab[i].msgsize[s] < globs.maxbuf) {
|
||||
cmdtab[i].msgsize[s] += ret;
|
||||
buffer[ret] = 0;
|
||||
if ( !cmdtab[ i ].buffer[ s ] )
|
||||
{
|
||||
/* Never been allocated. */
|
||||
cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( ret + 1 );
|
||||
memcpy( cmdtab[ i ].buffer[ s ], buffer, ret + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Previously allocated. */
|
||||
char * tmp = cmdtab[ i ].buffer[ s ];
|
||||
len = strlen( tmp );
|
||||
cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( len + ret + 1 );
|
||||
memcpy( cmdtab[ i ].buffer[ s ], tmp, len );
|
||||
memcpy( cmdtab[ i ].buffer[ s ] + len, buffer, ret + 1 );
|
||||
BJAM_FREE( tmp );
|
||||
}
|
||||
|
||||
/* buffer was truncated, append newline to ensure pjl can find line end */
|
||||
if (globs.maxbuf <= cmdtab[i].msgsize[s]) {
|
||||
cmdtab[i].buffer[s][cmdtab[i].msgsize[s]-1] = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,6 +388,8 @@ void close_streams( int i, int s )
|
||||
|
||||
close(cmdtab[ i ].fd[ s ]);
|
||||
cmdtab[ i ].fd[ s ] = 0;
|
||||
|
||||
cmdtab[i].msgsize[s] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -423,6 +434,61 @@ void populate_file_descriptors( int * fmax, fd_set * fds)
|
||||
*fmax = fd_max;
|
||||
}
|
||||
|
||||
void cleanup_child(int i, int status)
|
||||
{
|
||||
int rstat;
|
||||
struct tms new_time;
|
||||
timing_info time_info;
|
||||
|
||||
cmdtab[ i ].pid = 0;
|
||||
|
||||
/* Set reason for exit if not timed out. */
|
||||
if ( WIFEXITED( status ) ) {
|
||||
cmdtab[ i ].exit_reason = 0 == WEXITSTATUS( status )
|
||||
? EXIT_OK : EXIT_FAIL;
|
||||
}
|
||||
|
||||
/* Print out the rule and target name. */
|
||||
out_action( cmdtab[ i ].action, cmdtab[ i ].target,
|
||||
cmdtab[ i ].command, cmdtab[ i ].buffer[ OUT ],
|
||||
cmdtab[ i ].buffer[ ERR ], cmdtab[ i ].exit_reason
|
||||
);
|
||||
|
||||
times( &new_time );
|
||||
|
||||
time_info.system = (double)( new_time.tms_cstime - old_time.tms_cstime ) / CLOCKS_PER_SEC;
|
||||
time_info.user = (double)( new_time.tms_cutime - old_time.tms_cutime ) / CLOCKS_PER_SEC;
|
||||
time_info.start = cmdtab[ i ].start_dt;
|
||||
time_info.end = time( 0 );
|
||||
|
||||
old_time = new_time;
|
||||
|
||||
if ( intr )
|
||||
rstat = EXEC_CMD_INTR;
|
||||
else if ( status != 0 )
|
||||
rstat = EXEC_CMD_FAIL;
|
||||
else
|
||||
rstat = EXEC_CMD_OK;
|
||||
|
||||
/* Assume -p0 in effect so only pass buffer[ 0 ]
|
||||
* containing merged output.
|
||||
*/
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time_info, cmdtab[ i ].command, cmdtab[ i ].buffer[ 0 ] );
|
||||
|
||||
BJAM_FREE( cmdtab[ i ].buffer[ OUT ] );
|
||||
cmdtab[ i ].buffer[ OUT ] = 0;
|
||||
|
||||
BJAM_FREE( cmdtab[ i ].buffer[ ERR ] );
|
||||
cmdtab[ i ].buffer[ ERR ] = 0;
|
||||
|
||||
BJAM_FREE( cmdtab[ i ].command );
|
||||
cmdtab[ i ].command = 0;
|
||||
|
||||
cmdtab[ i ].func = 0;
|
||||
cmdtab[ i ].closure = 0;
|
||||
cmdtab[ i ].start_time = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* exec_wait() - wait and drive at most one execution completion.
|
||||
@@ -430,16 +496,13 @@ void populate_file_descriptors( int * fmax, fd_set * fds)
|
||||
|
||||
int exec_wait()
|
||||
{
|
||||
int i;
|
||||
int i, j;
|
||||
int ret;
|
||||
int fd_max;
|
||||
int pid;
|
||||
int status;
|
||||
int finished;
|
||||
int rstat;
|
||||
timing_info time_info;
|
||||
fd_set fds;
|
||||
struct tms new_time;
|
||||
|
||||
/* Handle naive make1() which does not know if commands are running. */
|
||||
if ( !cmdsrunning )
|
||||
@@ -457,17 +520,47 @@ int exec_wait()
|
||||
/* Force select() to timeout so we can terminate expired processes.
|
||||
*/
|
||||
tv.tv_sec = select_timeout;
|
||||
tv.tv_usec = 0;
|
||||
tv.tv_nsec = 0;
|
||||
|
||||
/* select() will wait until: i/o on a descriptor, a signal, or we
|
||||
* time out.
|
||||
*/
|
||||
ret = select( fd_max + 1, &fds, 0, 0, &tv );
|
||||
ret = pselect( fd_max + 1, &fds, 0, 0, &tv, &empty_sigmask );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* select() will wait until i/o on a descriptor or a signal. */
|
||||
ret = select( fd_max + 1, &fds, 0, 0, 0 );
|
||||
/* pselect() will wait until i/o on a descriptor or a signal. */
|
||||
ret = pselect( fd_max + 1, &fds, 0, 0, 0, &empty_sigmask );
|
||||
}
|
||||
|
||||
if (-1 == ret && errno != EINTR) {
|
||||
perror("pselect()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (0 < child_events) {
|
||||
/* child terminated via SIGCHLD */
|
||||
for (i=0; i<MAXJOBS; ++i) {
|
||||
if (0 < terminated_children[i].pid) {
|
||||
pid_t pid = terminated_children[i].pid;
|
||||
/* get index of terminated pid */
|
||||
for (j=0; j<globs.jobs; ++j) {
|
||||
if (pid == cmdtab[j].pid) {
|
||||
/* cleanup loose ends for terminated process */
|
||||
close_streams(j, OUT);
|
||||
if ( globs.pipe_action != 0 ) close_streams(j, ERR);
|
||||
cleanup_child(j, terminated_children[i].status);
|
||||
--cmdsrunning;
|
||||
finished = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* clear entry from list */
|
||||
terminated_children[i].status = 0;
|
||||
terminated_children[i].pid = 0;
|
||||
--child_events;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 < ret )
|
||||
@@ -496,62 +589,10 @@ int exec_wait()
|
||||
|
||||
if ( pid == cmdtab[ i ].pid )
|
||||
{
|
||||
/* move into function so signal handler can also use */
|
||||
finished = 1;
|
||||
pid = 0;
|
||||
cmdtab[ i ].pid = 0;
|
||||
|
||||
/* Set reason for exit if not timed out. */
|
||||
if ( WIFEXITED( status ) )
|
||||
{
|
||||
cmdtab[ i ].exit_reason = 0 == WEXITSTATUS( status )
|
||||
? EXIT_OK
|
||||
: EXIT_FAIL;
|
||||
}
|
||||
|
||||
/* Print out the rule and target name. */
|
||||
out_action( cmdtab[ i ].action, cmdtab[ i ].target,
|
||||
cmdtab[ i ].command, cmdtab[ i ].buffer[ OUT ],
|
||||
cmdtab[ i ].buffer[ ERR ], cmdtab[ i ].exit_reason
|
||||
);
|
||||
|
||||
times( &new_time );
|
||||
|
||||
time_info.system = (double)( new_time.tms_cstime - old_time.tms_cstime ) / CLOCKS_PER_SEC;
|
||||
time_info.user = (double)( new_time.tms_cutime - old_time.tms_cutime ) / CLOCKS_PER_SEC;
|
||||
time_info.start = cmdtab[ i ].start_dt;
|
||||
time_info.end = time( 0 );
|
||||
|
||||
old_time = new_time;
|
||||
|
||||
/* Drive the completion. */
|
||||
cleanup_child(i, status);
|
||||
--cmdsrunning;
|
||||
|
||||
if ( intr )
|
||||
rstat = EXEC_CMD_INTR;
|
||||
else if ( status != 0 )
|
||||
rstat = EXEC_CMD_FAIL;
|
||||
else
|
||||
rstat = EXEC_CMD_OK;
|
||||
|
||||
/* Assume -p0 in effect so only pass buffer[ 0 ]
|
||||
* containing merged output.
|
||||
*/
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat,
|
||||
&time_info, cmdtab[ i ].command,
|
||||
cmdtab[ i ].buffer[ 0 ] );
|
||||
|
||||
BJAM_FREE( cmdtab[ i ].buffer[ OUT ] );
|
||||
cmdtab[ i ].buffer[ OUT ] = 0;
|
||||
|
||||
BJAM_FREE( cmdtab[ i ].buffer[ ERR ] );
|
||||
cmdtab[ i ].buffer[ ERR ] = 0;
|
||||
|
||||
BJAM_FREE( cmdtab[ i ].command );
|
||||
cmdtab[ i ].command = 0;
|
||||
|
||||
cmdtab[ i ].func = 0;
|
||||
cmdtab[ i ].closure = 0;
|
||||
cmdtab[ i ].start_time = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -562,7 +603,6 @@ int exec_wait()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "limits.h"
|
||||
|
||||
#include "jam.h"
|
||||
#include "option.h"
|
||||
#include "patchlevel.h"
|
||||
@@ -134,9 +136,36 @@
|
||||
#endif
|
||||
|
||||
/* And UNIX for this. */
|
||||
#ifdef unix
|
||||
#if defined(unix) || defined(__unix)
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <signal.h>
|
||||
|
||||
sigset_t empty_sigmask;
|
||||
volatile sig_atomic_t child_events = 0;
|
||||
struct terminated_child terminated_children[MAXJOBS] = {{ 0 }};
|
||||
|
||||
void child_sig_handler(int x) {
|
||||
pid_t pid;
|
||||
int i, status;
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
if (0 < pid) {
|
||||
/* save terminated child pid and status */
|
||||
for (i=0; i<MAXJOBS; ++i) {
|
||||
/* find first available slot */
|
||||
if (terminated_children[i].pid == 0) {
|
||||
terminated_children[i].pid = pid;
|
||||
terminated_children[i].status = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
++child_events;
|
||||
signal(SIGCHLD, child_sig_handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct globs globs =
|
||||
@@ -152,7 +181,9 @@ struct globs globs =
|
||||
{ 0, 1 }, /* debug ... */
|
||||
#endif
|
||||
0, /* output commands, not run them */
|
||||
0 /* action timeout */
|
||||
0, /* action timeout */
|
||||
0,
|
||||
INT_MAX /* default is to accept all action output */
|
||||
};
|
||||
|
||||
/* Symbols to be defined as true for use in Jambase. */
|
||||
@@ -230,6 +261,19 @@ int main( int argc, char * * argv, char * * arg_environ )
|
||||
char const * progname = argv[0];
|
||||
module_t * environ_module;
|
||||
|
||||
#if defined(unix) || defined(__unix)
|
||||
sigset_t sigmask;
|
||||
struct sigaction sa;
|
||||
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &sigmask, NULL);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = child_sig_handler;
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
sigemptyset(&empty_sigmask);
|
||||
#endif
|
||||
|
||||
saved_argv0 = argv[0];
|
||||
|
||||
BJAM_MEM_INIT();
|
||||
@@ -241,7 +285,7 @@ int main( int argc, char * * argv, char * * arg_environ )
|
||||
--argc;
|
||||
++argv;
|
||||
|
||||
if ( getoptions( argc, argv, "-:l:d:j:p:f:gs:t:ano:qv", optv ) < 0 )
|
||||
if ( getoptions( argc, argv, "-:l:m:d:j:p:f:gs:t:ano:qv", optv ) < 0 )
|
||||
{
|
||||
printf( "\nusage: %s [ options ] targets...\n\n", progname );
|
||||
|
||||
@@ -251,6 +295,7 @@ int main( int argc, char * * argv, char * * arg_environ )
|
||||
/* printf( "-g Build from newest sources first.\n" ); */
|
||||
printf( "-jx Run up to x shell commands concurrently.\n" );
|
||||
printf( "-lx Limit actions to x number of seconds after which they are stopped.\n" );
|
||||
printf( "-mx Limit action output buffer to x kb's of data, after which action output is read and ignored.\n" );
|
||||
printf( "-n Don't actually execute the updating actions.\n" );
|
||||
printf( "-ox Write the updating actions to file x.\n" );
|
||||
printf( "-px x=0, pipes action stdout and stderr merged into action output.\n" );
|
||||
@@ -318,6 +363,9 @@ int main( int argc, char * * argv, char * * arg_environ )
|
||||
if ( ( s = getoptval( optv, 'l', 0 ) ) )
|
||||
globs.timeout = atoi( s );
|
||||
|
||||
if ( ( s = getoptval( optv, 'm', 0 ) ) )
|
||||
globs.maxbuf = atoi( s ) * 1024;
|
||||
|
||||
/* Turn on/off debugging */
|
||||
for ( n = 0; ( s = getoptval( optv, 'd', n ) ); ++n )
|
||||
{
|
||||
@@ -396,7 +444,7 @@ int main( int argc, char * * argv, char * * arg_environ )
|
||||
VAR_SET );
|
||||
|
||||
/* Set JAMUNAME. */
|
||||
#ifdef unix
|
||||
#if defined(unix) || defined(__unix)
|
||||
{
|
||||
struct utsname u;
|
||||
|
||||
|
||||
@@ -456,10 +456,20 @@ struct globs
|
||||
* default 0 for no limit.
|
||||
*/
|
||||
int dart; /* output build and test results formatted for Dart */
|
||||
int maxbuf; /* limit action output buffer to maxbuf kb's of data */
|
||||
};
|
||||
|
||||
extern struct globs globs;
|
||||
|
||||
struct terminated_child
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
};
|
||||
|
||||
extern struct terminated_child terminated_children[MAXJOBS];
|
||||
|
||||
|
||||
#define DEBUG_MAKE ( globs.debug[ 1 ] ) /* show actions when executed */
|
||||
#define DEBUG_MAKEQ ( globs.debug[ 2 ] ) /* show even quiet actions */
|
||||
#define DEBUG_EXEC ( globs.debug[ 2 ] ) /* show text of actons */
|
||||
@@ -493,4 +503,12 @@ extern struct globs globs;
|
||||
/* They also get the profile functions. */
|
||||
#include "debug.h"
|
||||
|
||||
/* Handle child process termination */
|
||||
#if defined(unix) || defined(__unix)
|
||||
#include <signal.h>
|
||||
extern sigset_t empty_sigmask;
|
||||
extern volatile sig_atomic_t child_events;
|
||||
void child_sig_handler(int x);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user