Commit | Line | Data |
---|---|---|
8e4296c2 FR |
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 | ||
14 | use strict 'refs'; | |
15 | use strict subs; | |
16 | ||
17 | use Getopt::Long; | |
18 | use 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 | #______________________________________________________________________________ | |
29 | sub 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 | #______________________________________________________________________________ | |
88 | sub usage { | |
89 | ||
90 | # ***** when editing, be careful to not put tabs in the string printed: | |
91 | ||
92 | print STDERR | |
93 | " | |
94 | usage: | |
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 | ||
164 | if (!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 | ||
184 | if ($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 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
194 | if ($help){ | |
195 | ||
196 | &usage; | |
197 | ||
198 | exit $EX_OK; | |
199 | } | |
200 | ||
201 | ||
202 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
203 | ||
204 | if ($version) { | |
205 | print STDERR "\n$script_name $VUFX\n\n"; | |
206 | print STDERR "\n"; | |
207 | ||
208 | exit $EX_OK; | |
209 | } | |
210 | ||
211 | ||
212 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
213 | if ($#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 | ||
248 | LINE: | |
249 | while ($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 | ||
390 | if (! $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 | ||
402 | if (@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 | } |