409 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			409 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/perl
 | |
| # SPDX-License-Identifier: GPL-2.0
 | |
| #
 | |
| # Copyright 2020, 2022 Sony Corporation
 | |
| #
 | |
| # Author: Frank Rowand
 | |
| 
 | |
| # This program is meant to be an aid to reading the verbose output of
 | |
| # on the console log that results from executing the Linux kernel
 | |
| # devicetree unittest (drivers/of/unitest.c).
 | |
| 
 | |
| $VUFX = "220201a";
 | |
| 
 | |
| use strict 'refs';
 | |
| use strict subs;
 | |
| 
 | |
| use Getopt::Long;
 | |
| use Text::Wrap;
 | |
| 
 | |
| # strip off everything before final "/"
 | |
| (undef, $script_name) = split(/^.*\//, $0);
 | |
| 
 | |
| # following /usr/include/sysexits.h
 | |
| $EX_OK=0;
 | |
| $EX_USAGE=64;
 | |
| 
 | |
| 
 | |
| #______________________________________________________________________________
 | |
| sub compare {
 | |
| 	my ($expect, $got) = @_;
 | |
| 	my $expect_next;
 | |
| 	my $expect_next_lit;
 | |
| 	my $got_next;
 | |
| 	my $type;
 | |
| 
 | |
| 	while ($expect) {
 | |
| 
 | |
| 		($expect_next, $type) = split(/<</, $expect);
 | |
| 		($type) = split(/>>/, $type);
 | |
| 		$expect =~ s/^.*?>>//;	# '?' is non-greedy, minimal match
 | |
| 
 | |
| 		# literal, ignore all metacharacters when used in a regex
 | |
| 		$expect_next_lit = quotemeta($expect_next);
 | |
| 
 | |
| 		$got_next = $got;
 | |
| 		$got_next =~ s/^($expect_next_lit).*/\1/;
 | |
| 		$got       =~ s/^$expect_next_lit//;
 | |
| 
 | |
| 		if ($expect_next ne $got_next) {
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		if ($type eq "int") {
 | |
| 			if ($got =~ /^[+-]*[0-9]+/) {
 | |
| 				$got =~ s/^[+-]*[0-9]+//;
 | |
| 			} else {
 | |
| 				return 0;
 | |
| 			}
 | |
| 		} elsif ($type eq "hex") {
 | |
| 			if ($got =~ /^(0x)*[0-9a-f]+/) {
 | |
| 				$got =~ s/^(0x)*[0-9a-f]+//;
 | |
| 			} else {
 | |
| 				return 0;
 | |
| 			}
 | |
| 		} elsif ($type eq "") {
 | |
| 			if ($expect_next ne $got_next) {
 | |
| 				return 0;
 | |
| 			} else {
 | |
| 				return 1;
 | |
| 			}
 | |
| 		} else {
 | |
| 			$internal_err++;
 | |
| 			print "** ERROR: special pattern not recognized: <<$type>>, CONSOLE_LOG line: $.\n";
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	# should not get here
 | |
| 	$internal_err++;
 | |
| 	print "** ERROR: $script_name internal error, at end of compare(), CONSOLE_LOG line: $.\n";
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| #______________________________________________________________________________
 | |
| sub usage {
 | |
| 
 | |
| # ***** when editing, be careful to not put tabs in the string printed:
 | |
| 
 | |
| 	print STDERR
 | |
| "
 | |
| usage:
 | |
| 
 | |
|   $script_name CONSOLE_LOG
 | |
| 
 | |
|      -h                print program usage
 | |
|     --help             print program usage
 | |
|     --hide-expect      suppress output of EXPECTed lines
 | |
|     --line-num         report line number of CONSOLE_LOG
 | |
|     --no-expect-stats  do not report EXPECT statistics
 | |
|     --no-strip-ts      do not strip leading console timestamps
 | |
|     --verbose          do not suppress EXPECT begin and end lines
 | |
|     --version          print program version and exit
 | |
| 
 | |
| 
 | |
|   Process a console log for EXPECTed test related messages to either
 | |
|   highlight expected devicetree unittest related messages or suppress
 | |
|   the messages.  Leading console timestamps will be stripped.
 | |
| 
 | |
|   Various unittests may trigger kernel messages from outside the
 | |
|   unittest code.  The unittest annotates that it expects the message
 | |
|   to occur with an 'EXPECT \\ : text' (begin) before triggering the
 | |
|   message, and an 'EXPECT / : text' (end) after triggering the message.
 | |
| 
 | |
|   If an expected message does not occur, that will be reported.
 | |
| 
 | |
|   For each expected message, the 'EXPECT \\ : text' (begin) and
 | |
|   'EXPECT / : text' (end), 'text' will contain the message text.
 | |
| 
 | |
|   If 'EXPECT \\' (begin) and 'EXPECT /' (end) lines do not contain
 | |
|   matching 'text', that will be reported.
 | |
| 
 | |
|   If EXPECT lines are nested, 'EXPECT /' (end) lines must be in the
 | |
|   reverse order of the corresponding 'EXPECT \\' (begin) lines.
 | |
| 
 | |
|   'EXPECT \\ : text' (begin) and 'EXPECT / : text' (end) lines can
 | |
|   contain special patterns in 'text':
 | |
| 
 | |
|      <<int>> matches: [+-]*[0-9]+
 | |
|      <<hex>> matches: (0x)*[0-9a-f]+
 | |
| 
 | |
|   'EXPECT \\' (begin) and 'EXPECT /' (end) lines are suppressed.
 | |
| 
 | |
|   A prefix is added to every line of output:
 | |
| 
 | |
|     'ok ' Line matches an enclosing EXPECT begin/end pair
 | |
| 
 | |
|     '** ' Line reports $script_name warning or error
 | |
| 
 | |
|     '-> ' Line reports start or end of the unittests
 | |
| 
 | |
|     '>> ' Line reports a unittest test FAIL
 | |
| 
 | |
|     '   ' Lines that are not otherwise prefixed
 | |
| 
 | |
|   Issues detected in CONSOLE_LOG are reported to STDOUT, not to STDERR.
 | |
| 
 | |
|   Known Issues:
 | |
| 
 | |
|     --line-num causes the CONSOLE_LOG line number to be printed in 4 columns.
 | |
|        If CONSOLE_LOG contains more than 9999 lines then more columns will be
 | |
|        used to report the line number for lines greater than 9999 (eg for
 | |
|        lines 10000 - 99999, 5 columns will be used).
 | |
| ";
 | |
| 
 | |
| 	return {};
 | |
| }
 | |
| 
 | |
| #______________________________________________________________________________
 | |
| #______________________________________________________________________________
 | |
| 
 | |
| if (!GetOptions(
 | |
| 	"h"               => \$help,
 | |
| 	"help"            => \$help,
 | |
| 	"hide-expect"     => \$hide_expect,
 | |
| 	"line-num"        => \$print_line_num,
 | |
| 	"no-expect-stats" => \$no_expect_stats,
 | |
| 	"no-strip-ts"     => \$no_strip_ts,
 | |
| 	"verbose"         => \$verbose,
 | |
| 	"version"         => \$version,
 | |
| 	)) {
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "ERROR processing command line options\n";
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "For help, type '$script_name --help'\n";
 | |
| 	print STDERR "\n";
 | |
| 
 | |
| 	exit $EX_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| if ($no_strip_ts) {
 | |
| 	$strip_ts = 1;
 | |
| 	$no_strip_ts = 0;
 | |
| } else {
 | |
| 	$strip_ts = 0;
 | |
| 	$no_strip_ts = 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| if ($help){
 | |
| 
 | |
| 	&usage;
 | |
| 
 | |
| 	exit $EX_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| 
 | |
| if ($version) {
 | |
| 	print STDERR "\n$script_name  $VUFX\n\n";
 | |
| 	print STDERR "\n";
 | |
| 
 | |
| 	exit $EX_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| if ($#ARGV != 0) {
 | |
| 
 | |
| 	# Limit input files to exactly one.
 | |
| 	#
 | |
| 	# 'while ($line = <ARGV>) {' in the code below supports multiple file
 | |
| 	# names on the command line, but the EXPECT statistics are reported
 | |
| 	# once for all input - it is not an expected use case to generate one
 | |
| 	# set of statistics for multiple input files.
 | |
| 
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "Required arguments: CONSOLE_LOG\n";
 | |
| 	print STDERR "\n";
 | |
| 
 | |
| 	exit $EX_USAGE;
 | |
| }
 | |
| 
 | |
| 
 | |
| #______________________________________________________________________________
 | |
| 
 | |
| # Patterns to match 'EXPECT \ : ' (begin) and 'EXPECT / : ' (end)
 | |
| #
 | |
| # $exp_* are used as regex match patterns,
 | |
| # so '\\\\' in $exp_begin matches a single '\'
 | |
| # quotemeta() does not do the right thing in this case
 | |
| #
 | |
| # $pr_fmt is the prefix that unittest prints for every message
 | |
| 
 | |
| $pr_fmt = "### dt-test ### ";
 | |
| $exp_begin = "${pr_fmt}EXPECT \\\\ : ";
 | |
| $exp_end   = "${pr_fmt}EXPECT / : ";
 | |
| 
 | |
| 
 | |
| $line_num = "";
 | |
| $timestamp = "";
 | |
| 
 | |
| LINE:
 | |
| while ($line = <ARGV>) {
 | |
| 
 | |
| 	chomp $line;
 | |
| 
 | |
| 	$prefix = "  ";  ## 2 characters
 | |
| 
 | |
| 
 | |
| 	if ($strip_ts) {
 | |
| 
 | |
| 		$timestamp = $line;
 | |
| 
 | |
| 		if ($timestamp =~ /^\[\s*[0-9]+\.[0-9]*\] /) {
 | |
| 			($timestamp, $null) = split(/]/, $line);
 | |
| 			$timestamp = $timestamp . "] ";
 | |
| 
 | |
| 		} else {
 | |
| 			$timestamp = "";
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	$line =~ s/^\[\s*[0-9]+\.[0-9]*\] //;
 | |
| 
 | |
| 
 | |
| 	# -----  find EXPECT begin
 | |
| 
 | |
| 	if ($line =~ /^\s*$exp_begin/) {
 | |
| 		$data = $line;
 | |
| 		$data =~ s/^\s*$exp_begin//;
 | |
| 		push @begin, $data;
 | |
| 
 | |
| 		if ($verbose) {
 | |
| 			if ($print_line_num) {
 | |
| 				$line_num = sprintf("%4s ", $.);
 | |
| 			}
 | |
| 			printf "%s %s%s%s\n", $prefix, $line_num,  $timestamp, $line;
 | |
| 		}
 | |
| 
 | |
| 		next LINE;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	# -----  find EXPECT end
 | |
| 
 | |
| 	if ($line =~ /^\s*$exp_end/) {
 | |
| 		$data = $line;
 | |
| 		$data =~ s/^\s*$exp_end//;
 | |
| 
 | |
| 		if ($verbose) {
 | |
| 			if ($print_line_num) {
 | |
| 				$line_num = sprintf("%4s ", $.);
 | |
| 			}
 | |
| 			printf "%s %s%s%s\n", $prefix, $line_num,  $timestamp, $line;
 | |
| 		}
 | |
| 
 | |
| 		$found = 0;
 | |
| 		$no_begin = 0;
 | |
| 		if (@found_or_begin > 0) {
 | |
| 			$begin = pop @found_or_begin;
 | |
| 			if (compare($data, $begin)) {
 | |
| 				$found = 1;
 | |
| 			}
 | |
| 		} elsif (@begin > 0) {
 | |
| 			$begin = pop @begin;
 | |
| 		} else {
 | |
| 			$no_begin = 1;
 | |
| 		}
 | |
| 
 | |
| 		if ($no_begin) {
 | |
| 
 | |
| 			$expect_missing_begin++;
 | |
| 			print "** ERROR: EXPECT end without any EXPECT begin:\n";
 | |
| 			print "       end ---> $line\n";
 | |
| 
 | |
| 		} elsif (! $found) {
 | |
| 
 | |
| 			if ($print_line_num) {
 | |
| 				$line_num = sprintf("%4s ", $.);
 | |
| 			}
 | |
| 
 | |
| 			$expect_not_found++;
 | |
| 			printf "** %s%s$script_name WARNING - not found ---> %s\n",
 | |
| 					$line_num,  $timestamp, $data;
 | |
| 
 | |
| 		} elsif (! compare($data, $begin)) {
 | |
| 
 | |
| 			$expect_missing_end++;
 | |
| 			print "** ERROR: EXPECT end does not match EXPECT begin:\n";
 | |
| 			print "       begin -> $begin\n";
 | |
| 			print "       end ---> $line\n";
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 			$expect_found++;
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		next LINE;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	# -----  not an EXPECT line
 | |
| 
 | |
| 	if (($line =~ /^${pr_fmt}start of unittest - you will see error messages$/) ||
 | |
| 	    ($line =~ /^${pr_fmt}end of unittest - [0-9]+ passed, [0-9]+ failed$/ )   ) {
 | |
| 		$prefix = "->"; # 2 characters
 | |
| 	} elsif ($line =~ /^${pr_fmt}FAIL /) {
 | |
| 		$unittest_fail++;
 | |
| 		$prefix = ">>"; # 2 characters
 | |
| 	}
 | |
| 
 | |
| 	$found = 0;
 | |
| 	foreach $begin (@begin) {
 | |
| 		if (compare($begin, $line)) {
 | |
| 			$found = 1;
 | |
| 			last;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if ($found) {
 | |
| 		$begin = shift @begin;
 | |
| 		while (! compare($begin, $line)) {
 | |
| 			push @found_or_begin, $begin;
 | |
| 			$begin = shift @begin;
 | |
| 		}
 | |
| 		push @found_or_begin, $line;
 | |
| 
 | |
| 		if ($hide_expect) {
 | |
| 			$suppress_line = 1;
 | |
| 			next LINE;
 | |
| 		}
 | |
| 		$prefix = "ok"; # 2 characters
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	if ($print_line_num) {
 | |
| 		$line_num = sprintf("%4s ", $.);
 | |
| 	}
 | |
| 
 | |
| 	printf "%s %s%s%s\n", $prefix, $line_num,  $timestamp, $line;
 | |
| }
 | |
| 
 | |
| if (! $no_expect_stats) {
 | |
| 	print  "\n";
 | |
| 	print  "** EXPECT statistics:\n";
 | |
| 	print  "**\n";
 | |
| 	printf "**   EXPECT found          : %4i\n", $expect_found;
 | |
| 	printf "**   EXPECT not found      : %4i\n", $expect_not_found;
 | |
| 	printf "**   missing EXPECT begin  : %4i\n", $expect_missing_begin;
 | |
| 	printf "**   missing EXPECT end    : %4i\n", $expect_missing_end;
 | |
| 	printf "**   unittest FAIL         : %4i\n", $unittest_fail;
 | |
| 	printf "**   internal error        : %4i\n", $internal_err;
 | |
| }
 | |
| 
 | |
| if (@begin) {
 | |
| 	print "** ERROR: EXPECT begin without any EXPECT end:\n";
 | |
| 	print "          This list may be misleading.\n";
 | |
| 	foreach $begin (@begin) {
 | |
| 		print "       begin ---> $begin\n";
 | |
| 	}
 | |
| }
 |