[Intel-gfx] [PATCH i-g-t 1/5] lib: print recent log output if a test fails

Chris Wilson chris at chris-wilson.co.uk
Wed Dec 17 09:19:21 PST 2014


On Wed, Dec 17, 2014 at 04:45:43PM +0000, Thomas Wood wrote:
> Print recent log entries if a test or subtest fails and the current log
> level is set to info.
> 
> v2: Write log to stderr after test or subtest failure and make log
>     buffering thread safe.
> 
> Signed-off-by: Thomas Wood <thomas.wood at intel.com>
> ---
>  lib/igt_core.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 96 insertions(+), 7 deletions(-)
> 
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 08e0c47..457b3b1 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -50,6 +50,8 @@
>  #include <termios.h>
>  #include <errno.h>
>  #include <time.h>
> +#include <limits.h>
> +#include <pthread.h>
>  
>  #include "drmtest.h"
>  #include "intel_chipset.h"
> @@ -228,8 +230,18 @@ enum {
>   OPT_HELP = 'h'
>  };
>  
> +static const char *command_str;
> +static int igt_exitcode = IGT_EXIT_SUCCESS;
> +
>  static char* igt_log_domain_filter;
>  
> +static struct {
> +	char *entries[256];
> +	uint8_t start, end;
> +} log_buffer;
> +static pthread_mutex_t log_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
> +
> +
>  __attribute__((format(printf, 1, 2)))
>  static void kmsg(const char *format, ...)
>  #define KERN_EMER	"<0>"
> @@ -353,6 +365,52 @@ static void low_mem_killer_disable(bool disable)
>  	chmod(adj_fname, buf.st_mode);
>  }
>  
> +#define write_log_msg(x) write(STDERR_FILENO, x, strlen(x))
> +
> +/* this function must be safe to be called inside a signal handler */
> +static void write_log(void)
> +{
> +	uint8_t i;
> +	char *last_line;
> +
> +	pthread_mutex_lock(&log_buffer_mutex);
> +
> +	/* don't write an empty log */
> +	if (log_buffer.start == log_buffer.end) {
> +		pthread_mutex_unlock(&log_buffer_mutex);
> +		return;
> +	}
> +
> +	if (in_subtest) {
> +		write_log_msg("--- Subtest ");
> +		write_log_msg(in_subtest);
> +	} else {
> +		write_log_msg("--- Test ");
> +		write_log_msg(command_str);
> +	}
> +	write_log_msg(" failed.\n");
> +	write_log_msg("--- Log Start\n");
> +
> +	i = log_buffer.start;
> +	do {
> +		last_line = log_buffer.entries[i];
> +		write_log_msg(last_line);

What happens if nothing is in the log:
log_buffer.start == log_buffer.end == 0

last_line[pos] is a *NULL, as may strlen(NULL).

When something is in the log, start != end, so this can just be a simple

if (log_buffer.start == log_buffer.end)
	return;

i = log_buffer.start;
do {
> +
> +		/* ensure each line ends in a newline */
> +		for (int pos = 0; last_line[pos] != '\0'; pos++) {
> +			if (last_line[pos] != '\n'
> +			    && last_line[pos + 1] == '\0')
> +				write_log_msg("\n");
> +		}

pos = strlen(last_line);
if (pos > 0 && last_line[pos-1] != '\n')
	write_log_msg("\n");

if (i == log_buffer.end)
	break;

i++;
} while (1);

It's not the neatest of loops, but it should be effective.

> +		i++;
> +	} while (i != log_buffer.start && i != log_buffer.end);
> +
> +	write_log_msg("--- Log End\n");

After it has dumped the log, do you want to reset the log_buffer?
i.e. log_buffer.start = log_buffer.end;

> +	pthread_mutex_unlock(&log_buffer_mutex);

I was worried by taking locks inside a signal handler, but this looks
like it could be made safe, and with a bit more work, you can make the
other half robust as well:

char *new, *old;

asprintf(&new,
	 "(%s:%d) %s%s%s: %s",
	 program_name, getpid(),
	 (domain) ? domain : "",
	 (domain) ? "-" : "",
	 igt_log_level_str[level],
	 line);

pthread_mutex_lock(&log_buffer_mutex);
old = log_buffer.entries[log_buffer.end];
log_bufffer.entries[log_buffer.end] = new;
if (++log_buffer.end == log_buffer.start)
	log_buffer.start++;
pthread_mutex_unlock(&log_buffer_mutex);

free(old);

i.e. only do the pointer head/tail assignment inside the lock to
minimise exposure.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre


More information about the Intel-gfx mailing list