1#!/usr/bin/perl 2# SPDX-License-Identifier: GPL-2.0 3# 4# Copyright 2020, 2022 Sony Corporation 5# 6# Author: Frank Rowand 7 8# This program is meant to be an aid to reading the verbose output of 9# on the console log that results from executing the Linux kernel 10# devicetree unittest (drivers/of/unitest.c). 11 12$VUFX = "220201a"; 13 14use strict 'refs'; 15use strict subs; 16 17use Getopt::Long; 18use Text::Wrap; 19 20# strip off everything before final "/" 21(undef, $script_name) = split(/^.*\//, $0); 22 23# following /usr/include/sysexits.h 24$EX_OK=0; 25$EX_USAGE=64; 26 27 28#______________________________________________________________________________ 29sub compare { 30 my ($expect, $got) = @_; 31 my $expect_next; 32 my $expect_next_lit; 33 my $got_next; 34 my $type; 35 36 while ($expect) { 37 38 ($expect_next, $type) = split(/<</, $expect); 39 ($type) = split(/>>/, $type); 40 $expect =~ s/^.*?>>//; # '?' is non-greedy, minimal match 41 42 # literal, ignore all metacharacters when used in a regex 43 $expect_next_lit = quotemeta($expect_next); 44 45 $got_next = $got; 46 $got_next =~ s/^($expect_next_lit).*/\1/; 47 $got =~ s/^$expect_next_lit//; 48 49 if ($expect_next ne $got_next) { 50 return 0; 51 } 52 53 if ($type eq "int") { 54 if ($got =~ /^[+-]*[0-9]+/) { 55 $got =~ s/^[+-]*[0-9]+//; 56 } else { 57 return 0; 58 } 59 } elsif ($type eq "hex") { 60 if ($got =~ /^(0x)*[0-9a-f]+/) { 61 $got =~ s/^(0x)*[0-9a-f]+//; 62 } else { 63 return 0; 64 } 65 } elsif ($type eq "") { 66 if ($expect_next ne $got_next) { 67 return 0; 68 } else { 69 return 1; 70 } 71 } else { 72 $internal_err++; 73 print "** ERROR: special pattern not recognized: <<$type>>, CONSOLE_LOG line: $.\n"; 74 return 0; 75 } 76 77 } 78 79 # should not get here 80 $internal_err++; 81 print "** ERROR: $script_name internal error, at end of compare(), CONSOLE_LOG line: $.\n"; 82 83 return 0; 84} 85 86 87#______________________________________________________________________________ 88sub usage { 89 90# ***** when editing, be careful to not put tabs in the string printed: 91 92 print STDERR 93" 94usage: 95 96 $script_name CONSOLE_LOG 97 98 -h print program usage 99 --help print program usage 100 --hide-expect suppress output of EXPECTed lines 101 --line-num report line number of CONSOLE_LOG 102 --no-expect-stats do not report EXPECT statistics 103 --no-strip-ts do not strip leading console timestamps 104 --verbose do not suppress EXPECT begin and end lines 105 --version print program version and exit 106 107 108 Process a console log for EXPECTed test related messages to either 109 highlight expected devicetree unittest related messages or suppress 110 the messages. Leading console timestamps will be stripped. 111 112 Various unittests may trigger kernel messages from outside the 113 unittest code. The unittest annotates that it expects the message 114 to occur with an 'EXPECT \\ : text' (begin) before triggering the 115 message, and an 'EXPECT / : text' (end) after triggering the message. 116 117 If an expected message does not occur, that will be reported. 118 119 For each expected message, the 'EXPECT \\ : text' (begin) and 120 'EXPECT / : text' (end), 'text' will contain the message text. 121 122 If 'EXPECT \\' (begin) and 'EXPECT /' (end) lines do not contain 123 matching 'text', that will be reported. 124 125 If EXPECT lines are nested, 'EXPECT /' (end) lines must be in the 126 reverse order of the corresponding 'EXPECT \\' (begin) lines. 127 128 'EXPECT \\ : text' (begin) and 'EXPECT / : text' (end) lines can 129 contain special patterns in 'text': 130 131 <<int>> matches: [+-]*[0-9]+ 132 <<hex>> matches: (0x)*[0-9a-f]+ 133 134 'EXPECT \\' (begin) and 'EXPECT /' (end) lines are suppressed. 135 136 A prefix is added to every line of output: 137 138 'ok ' Line matches an enclosing EXPECT begin/end pair 139 140 '** ' Line reports $script_name warning or error 141 142 '-> ' Line reports start or end of the unittests 143 144 '>> ' Line reports a unittest test FAIL 145 146 ' ' Lines that are not otherwise prefixed 147 148 Issues detected in CONSOLE_LOG are reported to STDOUT, not to STDERR. 149 150 Known Issues: 151 152 --line-num causes the CONSOLE_LOG line number to be printed in 4 columns. 153 If CONSOLE_LOG contains more than 9999 lines then more columns will be 154 used to report the line number for lines greater than 9999 (eg for 155 lines 10000 - 99999, 5 columns will be used). 156"; 157 158 return {}; 159} 160 161#______________________________________________________________________________ 162#______________________________________________________________________________ 163 164if (!GetOptions( 165 "h" => \$help, 166 "help" => \$help, 167 "hide-expect" => \$hide_expect, 168 "line-num" => \$print_line_num, 169 "no-expect-stats" => \$no_expect_stats, 170 "no-strip-ts" => \$no_strip_ts, 171 "verbose" => \$verbose, 172 "version" => \$version, 173 )) { 174 print STDERR "\n"; 175 print STDERR "ERROR processing command line options\n"; 176 print STDERR "\n"; 177 print STDERR "For help, type '$script_name --help'\n"; 178 print STDERR "\n"; 179 180 exit $EX_OK; 181} 182 183 184if ($no_strip_ts) { 185 $strip_ts = 1; 186 $no_strip_ts = 0; 187} else { 188 $strip_ts = 0; 189 $no_strip_ts = 1; 190} 191 192 193# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 194if ($help){ 195 196 &usage; 197 198 exit $EX_OK; 199} 200 201 202# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 203 204if ($version) { 205 print STDERR "\n$script_name $VUFX\n\n"; 206 print STDERR "\n"; 207 208 exit $EX_OK; 209} 210 211 212# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 213if ($#ARGV != 0) { 214 215 # Limit input files to exactly one. 216 # 217 # 'while ($line = <ARGV>) {' in the code below supports multiple file 218 # names on the command line, but the EXPECT statistics are reported 219 # once for all input - it is not an expected use case to generate one 220 # set of statistics for multiple input files. 221 222 print STDERR "\n"; 223 print STDERR "Required arguments: CONSOLE_LOG\n"; 224 print STDERR "\n"; 225 226 exit $EX_USAGE; 227} 228 229 230#______________________________________________________________________________ 231 232# Patterns to match 'EXPECT \ : ' (begin) and 'EXPECT / : ' (end) 233# 234# $exp_* are used as regex match patterns, 235# so '\\\\' in $exp_begin matches a single '\' 236# quotemeta() does not do the right thing in this case 237# 238# $pr_fmt is the prefix that unittest prints for every message 239 240$pr_fmt = "### dt-test ### "; 241$exp_begin = "${pr_fmt}EXPECT \\\\ : "; 242$exp_end = "${pr_fmt}EXPECT / : "; 243 244 245$line_num = ""; 246$timestamp = ""; 247 248LINE: 249while ($line = <ARGV>) { 250 251 chomp $line; 252 253 $prefix = " "; ## 2 characters 254 255 256 if ($strip_ts) { 257 258 $timestamp = $line; 259 260 if ($timestamp =~ /^\[\s*[0-9]+\.[0-9]*\] /) { 261 ($timestamp, $null) = split(/]/, $line); 262 $timestamp = $timestamp . "] "; 263 264 } else { 265 $timestamp = ""; 266 } 267 } 268 269 $line =~ s/^\[\s*[0-9]+\.[0-9]*\] //; 270 271 272 # ----- find EXPECT begin 273 274 if ($line =~ /^\s*$exp_begin/) { 275 $data = $line; 276 $data =~ s/^\s*$exp_begin//; 277 push @begin, $data; 278 279 if ($verbose) { 280 if ($print_line_num) { 281 $line_num = sprintf("%4s ", $.); 282 } 283 printf "%s %s%s%s\n", $prefix, $line_num, $timestamp, $line; 284 } 285 286 next LINE; 287 } 288 289 290 # ----- find EXPECT end 291 292 if ($line =~ /^\s*$exp_end/) { 293 $data = $line; 294 $data =~ s/^\s*$exp_end//; 295 296 if ($verbose) { 297 if ($print_line_num) { 298 $line_num = sprintf("%4s ", $.); 299 } 300 printf "%s %s%s%s\n", $prefix, $line_num, $timestamp, $line; 301 } 302 303 $found = 0; 304 $no_begin = 0; 305 if (@found_or_begin > 0) { 306 $begin = pop @found_or_begin; 307 if (compare($data, $begin)) { 308 $found = 1; 309 } 310 } elsif (@begin > 0) { 311 $begin = pop @begin; 312 } else { 313 $no_begin = 1; 314 } 315 316 if ($no_begin) { 317 318 $expect_missing_begin++; 319 print "** ERROR: EXPECT end without any EXPECT begin:\n"; 320 print " end ---> $line\n"; 321 322 } elsif (! $found) { 323 324 if ($print_line_num) { 325 $line_num = sprintf("%4s ", $.); 326 } 327 328 $expect_not_found++; 329 printf "** %s%s$script_name WARNING - not found ---> %s\n", 330 $line_num, $timestamp, $data; 331 332 } elsif (! compare($data, $begin)) { 333 334 $expect_missing_end++; 335 print "** ERROR: EXPECT end does not match EXPECT begin:\n"; 336 print " begin -> $begin\n"; 337 print " end ---> $line\n"; 338 339 } else { 340 341 $expect_found++; 342 343 } 344 345 next LINE; 346 } 347 348 349 # ----- not an EXPECT line 350 351 if (($line =~ /^${pr_fmt}start of unittest - you will see error messages$/) || 352 ($line =~ /^${pr_fmt}end of unittest - [0-9]+ passed, [0-9]+ failed$/ ) ) { 353 $prefix = "->"; # 2 characters 354 } elsif ($line =~ /^${pr_fmt}FAIL /) { 355 $unittest_fail++; 356 $prefix = ">>"; # 2 characters 357 } 358 359 $found = 0; 360 foreach $begin (@begin) { 361 if (compare($begin, $line)) { 362 $found = 1; 363 last; 364 } 365 } 366 367 if ($found) { 368 $begin = shift @begin; 369 while (! compare($begin, $line)) { 370 push @found_or_begin, $begin; 371 $begin = shift @begin; 372 } 373 push @found_or_begin, $line; 374 375 if ($hide_expect) { 376 $suppress_line = 1; 377 next LINE; 378 } 379 $prefix = "ok"; # 2 characters 380 } 381 382 383 if ($print_line_num) { 384 $line_num = sprintf("%4s ", $.); 385 } 386 387 printf "%s %s%s%s\n", $prefix, $line_num, $timestamp, $line; 388} 389 390if (! $no_expect_stats) { 391 print "\n"; 392 print "** EXPECT statistics:\n"; 393 print "**\n"; 394 printf "** EXPECT found : %4i\n", $expect_found; 395 printf "** EXPECT not found : %4i\n", $expect_not_found; 396 printf "** missing EXPECT begin : %4i\n", $expect_missing_begin; 397 printf "** missing EXPECT end : %4i\n", $expect_missing_end; 398 printf "** unittest FAIL : %4i\n", $unittest_fail; 399 printf "** internal error : %4i\n", $internal_err; 400} 401 402if (@begin) { 403 print "** ERROR: EXPECT begin without any EXPECT end:\n"; 404 print " This list may be misleading.\n"; 405 foreach $begin (@begin) { 406 print " begin ---> $begin\n"; 407 } 408} 409