1 1.1 mrg # Library of functions for dealing with DejaGNU, or which are otherwise 2 1.1 mrg # generally useful for the DejaGNU tool stack. 3 1.1 mrg # 4 1.1 mrg # Author: Matthew Sachs <msachs (at] apple.com> 5 1.1 mrg # 6 1.1 mrg # Functions: 7 1.1 mrg # parseLogFile: See "sub parseLogFile" below for details. This function 8 1.1 mrg # returns a detailed parse of a DejaGNU log or sum file. 9 1.1 mrg # ispass: Takes a DejaGNU result (e.g. "PASS", "XPASS") and returns 10 1.1 mrg # true if and only if it is a passing result (PASS, XFAIL, or 11 1.1 mrg # KFAIL.) 12 1.1 mrg # 13 1.1 mrg # Copyright (c) 2006 Free Software Foundation. 14 1.1 mrg # 15 1.1 mrg # This file is part of GCC. 16 1.1 mrg # 17 1.1 mrg # GCC is free software; you can redistribute it and/or modify 18 1.1 mrg # it under the terms of the GNU General Public License as published by 19 1.1 mrg # the Free Software Foundation; either version 3, or (at your option) 20 1.1 mrg # any later version. 21 1.1 mrg # 22 1.1 mrg # GCC is distributed in the hope that it will be useful, 23 1.1 mrg # but WITHOUT ANY WARRANTY; without even the implied warranty of 24 1.1 mrg # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 1.1 mrg # GNU General Public License for more details. 26 1.1 mrg # 27 1.1 mrg # You should have received a copy of the GNU General Public License 28 1.1 mrg # along with GCC; see the file COPYING. If not, write to 29 1.1 mrg # the Free Software Foundation, 51 Franklin Street, Fifth Floor, 30 1.1 mrg # Boston, MA 02110-1301, USA. 31 1.1 mrg 32 1.1 mrg package dglib; 33 1.1 mrg 34 1.1 mrg use strict; 35 1.1 mrg use warnings; 36 1.1 mrg use Exporter; 37 1.1 mrg 38 1.1 mrg our @ISA = qw(Exporter); 39 1.1 mrg our @EXPORT = qw(ispass parseLogFile); 40 1.1 mrg 41 1.1 mrg use File::Basename; 42 1.1 mrg use POSIX qw(mktime); 43 1.1 mrg 44 1.1 mrg 45 1.1 mrg # Create a group hierarchy, returning the leaf node 46 1.1 mrg sub mkGroupPath { 47 1.1 mrg my($root, $groups, @newgroups) = @_; 48 1.1 mrg 49 1.1 mrg my $parent = $root; 50 1.1 mrg my $fullname = ""; 51 1.1 mrg foreach my $group(@newgroups) { 52 1.1 mrg $fullname .= "/" if $fullname; 53 1.1 mrg $fullname .= $group; 54 1.1 mrg if(exists($groups->{$fullname})) { 55 1.1 mrg $parent = $groups->{$fullname}; 56 1.1 mrg } else { 57 1.1 mrg my $newgroup = {name => $group, parent => $parent}; 58 1.1 mrg $groups->{$fullname} = $newgroup; 59 1.1 mrg $parent->{testgroup} ||= []; 60 1.1 mrg push @{$parent->{testgroup}}, $newgroup; 61 1.1 mrg $parent = $newgroup; 62 1.1 mrg } 63 1.1 mrg } 64 1.1 mrg 65 1.1 mrg return $parent; 66 1.1 mrg } 67 1.1 mrg 68 1.1 mrg # Extract information from DejaGNU log or sum files. 69 1.1 mrg # Options, if provided, should be a hashref with zero or more of the following keys: 70 1.1 mrg # gccdir: 71 1.1 mrg # Passing in the full path to the root of the gcc/testsuite directory 72 1.1 mrg # will help in the parsing, but if it isn't provided, it will be guessed. 73 1.1 mrg # diagnostics: 74 1.1 mrg # If set to 0, diagnostics will not be returned. This can save a lot 75 1.1 mrg # of memory if you are not using this information. 76 1.1 mrg # fullname: 77 1.1 mrg # If set to 0, the fullname key will not be included in tests. 78 1.1 mrg # Returns a hash with the following keys: 79 1.1 mrg # incomplete: 1 if the summary file appears truncated, otherwise 0 80 1.1 mrg # diagnostics: List of (type, value) for any errors detected. Type can be ERROR, WARNING, or NOTE. 81 1.1 mrg # test: Array of root-level tests, with keys: 82 1.1 mrg # name: Name of the test, relative to the enclosing test group. 83 1.1 mrg # fullname: Fully-qualified name of the test. 84 1.1 mrg # result: DejaGNU result (PASS, FAIL, XPASS, &c) 85 1.1 mrg # detail: For multi-phase (e.g. compile/link/execute), this will be 86 1.1 mrg # the furthest phase which the test was able to attempt, 87 1.1 mrg # so if the result is FAIL and this is "link phase", the test 88 1.1 mrg # compiled but failed to link. This key may contain other 89 1.1 mrg # auxiliary data. 90 1.1 mrg # pseudotest: If 1, this test may not really exist; see "pseudotest" below. 91 1.1 mrg # testgroup: Array of root-level testgroups, with keys: 92 1.1 mrg # name: Name of the group. 93 1.1 mrg # parent: Parent test group. 94 1.1 mrg # test: As per above. 95 1.1 mrg # testgroup: Child test groups. 96 1.1 mrg # compiler: Version string from compiler used to run the tests (if detected) 97 1.1 mrg sub parseLogFile($;$) { 98 1.1 mrg my($logfile, $options) = @_; 99 1.1 mrg $options ||= {}; 100 1.1 mrg my $gccdir = $options->{gccdir} || ""; 101 1.1 mrg my $return_diags = exists($options->{diagnostics}) ? $options->{diagnostics} : 1; 102 1.1 mrg my $emit_fullname = exists($options->{fullname}) ? $options->{fullname} : 1; 103 1.1 mrg my $is_gdb = 0; 104 1.1 mrg my $gdbhack = ""; 105 1.1 mrg 106 1.1 mrg my %ret = (incomplete => 1, diagnostics => [], testgroup => []); 107 1.1 mrg my(%testindex, %groupindex); 108 1.1 mrg 109 1.1 mrg open(LOGFILE, $logfile) or die "Couldn't open log file $logfile: $!\n"; 110 1.1 mrg 111 1.1 mrg my($currgroup, $currtest, $lastrun); 112 1.1 mrg $currgroup = \%ret; 113 1.1 mrg 114 1.1 mrg my %monmap = (Jan => 0, Feb => 1, Mar => 2, Apr => 3, May => 4, Jun => 5, Jul => 6, Aug => 7, Sep => 8, Oct => 9, Nov => 10, Dec => 11); 115 1.1 mrg 116 1.1 mrg # We don't want gccdir matching on an empty string. 117 1.1 mrg $gccdir ||= "this will never match, or my name isn't Reginald St. Croix"; 118 1.1 mrg 119 1.1 mrg my $line = 1; 120 1.1 mrg while(<LOGFILE>) { 121 1.1 mrg chomp; 122 1.1 mrg s/\x{d}$//; #^M 123 1.1 mrg next if $_ eq ""; 124 1.1 mrg 125 1.1 mrg if(/^gcc version/) { 126 1.1 mrg $ret{compiler} = $_; 127 1.1 mrg } elsif(/^got a .* signal, interrupted by user /) { 128 1.1 mrg $ret{incomplete} = 2; 129 1.1 mrg } elsif(/^\s*=== gdb/) { 130 1.1 mrg $is_gdb = 1; 131 1.1 mrg # The log file from the GDB test suite is prone to have random crap 132 1.1 mrg # in front of test result lines, so we need to be looser about how 133 1.1 mrg # we parse those for GDB. 134 1.1 mrg $gdbhack = ".*"; 135 1.1 mrg } elsif(/^(Test Run By \S+ on|runtest completed at) ... (.{3}) (\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2}) (\d{4})/) { 136 1.1 mrg my $time = mktime($6, $5, $4, $3, $monmap{$2}, $7 - 1900); 137 1.1 mrg if($1 eq "runtest completed at") { 138 1.1 mrg $ret{end_time} = $time; 139 1.1 mrg } else { 140 1.1 mrg $ret{start_time} = $time; 141 1.1 mrg } 142 1.1 mrg } elsif(m<^Running (?!target )\Q$gccdir\E/?(\S+)> or m<^Running (?!target )\S*?((?:gcc|gdb|libstdc\+\+-v3)/testsuite/\S+)>) { 143 1.1 mrg # We keep track of the last "Running foo/bar/baz.exp" line because 144 1.1 mrg # some tests don't bother printing out the full paths of their files, 145 1.1 mrg # and this gives us the directory information. 146 1.1 mrg 147 1.1 mrg $lastrun = $1; 148 1.1 mrg $lastrun =~ s!/[^/]*/\.\.!!; # foo/bar/../baz -> foo/baz 149 1.1 mrg $currgroup = mkGroupPath(\%ret, \%groupindex, split(m!/!, $lastrun)); 150 1.1 mrg #$currgroup->{testfile} = $lastrun; 151 1.1 mrg } elsif(/^Executing on (.*?):(.*)/) { 152 1.1 mrg # Okay, if it's "Executing on host", it's a new 153 1.1 mrg # file. If it's "Executing on unix", it's probably 154 1.1 mrg # a test within the file -- an execution test, specifically -- 155 1.1 mrg # (makes sense, no?) But not always, sometimes we 156 1.1 mrg # see "Executing on unix" outside the context of a 157 1.1 mrg # file. 158 1.1 mrg 159 1.1 mrg # Try to pick out the gccdir-relative filename. 160 1.1 mrg # If we can't find it, it isn't really a new testfile, 161 1.1 mrg # but a derived file. 162 1.1 mrg my($exectype, $execwhat) = ($1, $2); 163 1.1 mrg next if $execwhat =~ /^dsymutil/; 164 1.1 mrg $execwhat =~ 165 1.1 mrg s!.*?\s\Q$gccdir\E/?(\S+).*!$1! or 166 1.1 mrg s!.*?/((?:gcc|gdb|libstdc\+\+-v3)/testsuite/\S+).*!$1! or 167 1.1 mrg $exectype = "unix"; 168 1.1 mrg 169 1.1 mrg if($exectype eq "host" or !$currgroup) { 170 1.1 mrg # New file 171 1.1 mrg 172 1.1 mrg my $nogroup = 0; 173 1.1 mrg if($execwhat =~ / /) { 174 1.1 mrg # We probably haven't parsed the file correctly. 175 1.1 mrg # Try getting it from $lastrun. 176 1.1 mrg 177 1.1 mrg $execwhat = dirname($lastrun) . "/" . basename($execwhat) if $lastrun and $execwhat; 178 1.1 mrg $execwhat =~ s/\s.*//; 179 1.1 mrg 180 1.1 mrg # At the end of each tool, it invokes "gcc -v" or "c++ -v" 181 1.1 mrg # as a test. We don't really want to treat this as a test. 182 1.1 mrg if($execwhat =~ m!/(gcc|c\+\+)$!) { 183 1.1 mrg undef $currtest; 184 1.1 mrg undef $currgroup; 185 1.1 mrg $nogroup = 1; 186 1.1 mrg } 187 1.1 mrg } 188 1.1 mrg 189 1.1 mrg if(!$nogroup) { 190 1.1 mrg undef $currtest; 191 1.1 mrg $execwhat =~ s!/[^/]*/\.\.!!; # foo/bar/../baz -> foo/baz 192 1.1 mrg 193 1.1 mrg if($lastrun) { 194 1.1 mrg my $lastbase = dirname($lastrun); 195 1.1 mrg my $basegroup = $execwhat; 196 1.1 mrg $basegroup =~ s!^\Q$lastbase\E/!!; 197 1.1 mrg $execwhat = "$lastrun/$basegroup"; 198 1.1 mrg } 199 1.1 mrg 200 1.1 mrg $currgroup = mkGroupPath(\%ret, \%groupindex, split(m!/!, $execwhat)); 201 1.1 mrg #$currgroup->{testfile} = $execwhat; 202 1.1 mrg } 203 1.1 mrg } else { 204 1.1 mrg # New test within current file 205 1.1 mrg 206 1.1 mrg $currtest = {}; 207 1.1 mrg } 208 1.1 mrg } elsif(/^# of/) { 209 1.1 mrg # This line appears should appear near the end of summary files. 210 1.1 mrg # If it doesn't, something went wrong. 211 1.1 mrg 212 1.1 mrg if($ret{incomplete} == 2) { 213 1.1 mrg #Ah, but we previously saw indication that we were killed via a signal. 214 1.1 mrg $ret{incomplete} = 1; 215 1.1 mrg } else { 216 1.1 mrg $ret{incomplete} = 0; 217 1.1 mrg } 218 1.1 mrg } elsif(/^testcase .* completed/) { 219 1.1 mrg # End of a .exp file 220 1.1 mrg undef $currtest; 221 1.1 mrg undef $currgroup; 222 1.1 mrg } elsif(/^$gdbhack(FAIL|PASS|UNRESOLVED|UNSUPPORTED|UNTESTED|XFAIL|XPASS|KFAIL|KPASS): (.*)/) { 223 1.1 mrg # If the currtest already has a name, that means we've already seen 224 1.1 mrg # its results, so what we have now is a new test. However, if we 225 1.1 mrg # haven't seen results for currtest yet, that means currtest just 226 1.1 mrg # has some diagnostics associated with it but no actual results, 227 1.1 mrg # so just use that one. 228 1.1 mrg undef $currtest if $currtest->{name}; 229 1.1 mrg 230 1.1 mrg my $phase = ""; # compile/link/execute 231 1.1 mrg my($test, $result) = ($2, $1); 232 1.1 mrg 233 1.1 mrg # Compile/(link/)execute combining 234 1.1 mrg if($test =~ /^(.*) compile\s*$/) { 235 1.1 mrg $test = "$1 compile,link,execute"; 236 1.1 mrg $phase = "compile"; 237 1.1 mrg } elsif($test =~ /^(.*)-(.*) (link|execute)\s*$/) { 238 1.1 mrg $test = "$1 compile,link,execute"; 239 1.1 mrg if($3 eq "link") { 240 1.1 mrg $phase = "link"; 241 1.1 mrg } else { 242 1.1 mrg $phase = "execute"; 243 1.1 mrg } 244 1.1 mrg } elsif($test =~ /(compile|compilation|execute|execution)/) { 245 1.1 mrg my $phasematch = $1; 246 1.1 mrg if($test =~ /^com/) { 247 1.1 mrg $phase = "compile"; 248 1.1 mrg } else { 249 1.1 mrg $phase = "execute"; 250 1.1 mrg } 251 1.1 mrg $test =~ s!\Q$phasematch\E!compile,execute!; 252 1.1 mrg } 253 1.1 mrg 254 1.1 mrg # gcov tests behave in non-standard fashion. 255 1.1 mrg my $failwhy = ""; 256 1.1 mrg $test =~ s/ gcov failed: (.*)// and $failwhy = $1; 257 1.1 mrg 258 1.1 mrg # And some other tests have random information after a colon :( 259 1.1 mrg # But for scan-assembler, this really is part of the name. 260 1.1 mrg if(!$is_gdb and $test !~ /scan-assembler/ and $test =~ s/:\s*(.+)//) { 261 1.1 mrg $failwhy = $1; 262 1.1 mrg } 263 1.1 mrg 264 1.1 mrg $test =~ s/\s*$//; 265 1.1 mrg $test =~ s/^\s*$//; 266 1.1 mrg 267 1.1 mrg # Sometimes there's a test which shows up as: 268 1.1 mrg # foo (test for excess errors) 269 1.1 mrg # foo (something else) 270 1.1 mrg # foo: error executing dg-final 271 1.1 mrg # if it runs, but just: 272 1.1 mrg # foo 273 1.1 mrg # if it doesn't. When we see the top form, we create a 274 1.1 mrg # "pseudotest" in the bottom form, so that comparisons 275 1.1 mrg # can be made. 276 1.1 mrg my $basetest = $test; 277 1.1 mrg $basetest =~ s/:? .*//; 278 1.1 mrg 279 1.1 mrg if(exists($testindex{$test}) and !$testindex{$test}->{pseudotest}) { 280 1.1 mrg $currtest = $testindex{$test}; 281 1.1 mrg if(ispass($currtest->{result})) { 282 1.1 mrg $currtest->{result} = $result; 283 1.1 mrg $currtest->{detail} = "$phase phase"; 284 1.1 mrg $currtest->{detail} .= "; $failwhy" if $failwhy; 285 1.1 mrg } 286 1.1 mrg } else { 287 1.1 mrg # This might have been created earlier as a pseudotest. 288 1.1 mrg # If so, overwrite it. 289 1.1 mrg $currtest ||= $testindex{$test} || {}; 290 1.1 mrg 291 1.1 mrg $currtest->{name} = basename($test); 292 1.1 mrg if($emit_fullname) { 293 1.1 mrg $currtest->{fullname} = ($currgroup->{name} || dirname($test)) . "/$currtest->{name}"; 294 1.1 mrg } 295 1.1 mrg my $grpname = $currgroup->{name} || ""; 296 1.1 mrg $currtest->{name} =~ s/^\s*\Q$grpname\E\s*//; 297 1.1 mrg $currtest->{name} =~ s/^: // if $is_gdb; 298 1.1 mrg # Sometimes there's a test at the root of the group. 299 1.1 mrg # For instance, you'll have: 300 1.1 mrg # FAIL: foo/bar.c (test for excess errors) 301 1.1 mrg # UNRESOLVED: foo/bar.c: couldn't open "bar.s": no such file or directory 302 1.1 mrg # In this case, groupname *is* the entire name, so the regex above will make the test name empty. 303 1.1 mrg # In this case, we actually want to use the parent group and make this a test within that group. 304 1.1 mrg my $orig_currgroup = $currgroup; 305 1.1 mrg if(!$currtest->{name}) { 306 1.1 mrg $currtest->{name} = $grpname; 307 1.1 mrg $currgroup = $currgroup->{parent}; 308 1.1 mrg $grpname = $currgroup->{name} || ""; 309 1.1 mrg } 310 1.1 mrg 311 1.1 mrg $currtest->{result} = $result; 312 1.1 mrg if($phase and $failwhy) { 313 1.1 mrg $currtest->{detail} = "$phase phase; $failwhy" if $phase; 314 1.1 mrg } elsif($phase) { 315 1.1 mrg $currtest->{detail} = "$phase phase"; 316 1.1 mrg } elsif($failwhy) { 317 1.1 mrg $currtest->{detail} = $failwhy; 318 1.1 mrg } 319 1.1 mrg 320 1.1 mrg $currgroup->{test} ||= []; 321 1.1 mrg push @{$currgroup->{test}}, $currtest; 322 1.1 mrg $testindex{$test} = $currtest; 323 1.1 mrg $currgroup = $orig_currgroup; 324 1.1 mrg 325 1.1 mrg if($basetest ne $test) { 326 1.1 mrg if(!exists($testindex{$basetest}) ) { 327 1.1 mrg my $btbase = basename($basetest); 328 1.1 mrg $testindex{$basetest} = { 329 1.1 mrg name => $btbase, 330 1.1 mrg result => $result, 331 1.1 mrg pseudotest => 1, 332 1.1 mrg fullname => $btbase 333 1.1 mrg }; 334 1.1 mrg if($emit_fullname) { 335 1.1 mrg $testindex{basetest}->{fullname} = ($currgroup->{name} || dirname($basetest)) . "/$btbase"; 336 1.1 mrg } 337 1.1 mrg push @{$currgroup->{parent}->{test}}, $testindex{$basetest}; 338 1.1 mrg } else { 339 1.1 mrg # Only let the base test pass if all the sub-tests pass 340 1.1 mrg $testindex{$basetest}->{result} = $result if !ispass($result); 341 1.1 mrg } 342 1.1 mrg } 343 1.1 mrg 344 1.1 mrg } 345 1.1 mrg } elsif(/^\s+=== .* Summary ===\s*$/) { 346 1.1 mrg undef $currgroup; 347 1.1 mrg undef $currtest; 348 1.1 mrg } 349 1.1 mrg 350 1.1 mrg my $severity; 351 1.1 mrg if(/^(ERROR|WARNING|NOTE): (.*)/) { 352 1.1 mrg $severity = $1; 353 1.1 mrg my $message = $2; 354 1.1 mrg 355 1.1 mrg if($message eq "program timed out.") { 356 1.1 mrg $currtest->{result} = "TIMEDOUT"; 357 1.1 mrg } elsif( 358 1.1 mrg $message =~ /can't read "(HOSTCC|libiconv)": no such variable/ or 359 1.1 mrg $message =~ /no files matched glob pattern/ or 360 1.1 mrg $message =~ /error executing dg-final: .*: no such file/ 361 1.1 mrg ) { 362 1.1 mrg $severity = "NOTE"; 363 1.1 mrg } 364 1.1 mrg } else { 365 1.1 mrg $severity = "logline"; 366 1.1 mrg } 367 1.1 mrg 368 1.1 mrg if($return_diags) { 369 1.1 mrg my $dobj; 370 1.1 mrg if($currtest) { 371 1.1 mrg $currtest->{diagnostics} ||= []; 372 1.1 mrg $dobj = $currtest->{diagnostics}; 373 1.1 mrg } elsif($currgroup) { 374 1.1 mrg $currgroup->{diagnostics} ||= []; 375 1.1 mrg $dobj = $currgroup->{diagnostics}; 376 1.1 mrg } else { 377 1.1 mrg $dobj = $ret{diagnostics}; 378 1.1 mrg } 379 1.1 mrg 380 1.1 mrg push @$dobj, {message => $_, severity => $severity, line => $line}; 381 1.1 mrg } 382 1.1 mrg } continue { 383 1.1 mrg $line++; 384 1.1 mrg } 385 1.1 mrg close LOGFILE; 386 1.1 mrg 387 1.1 mrg return %ret; 388 1.1 mrg } 389 1.1 mrg 390 1.1 mrg # Split a test into testdivs 391 1.1 mrg sub splitTest($$) { 392 1.1 mrg my($root, $test) = @_; 393 1.1 mrg 394 1.1 mrg $test->{fullname} =~ /^(\S+)\s*(.*)/; 395 1.1 mrg my($path, $descriptor) = ($1, $2); 396 1.1 mrg my @nodes = split(m!/!, $path); 397 1.1 mrg push @nodes, $descriptor if $descriptor; 398 1.1 mrg my $lastnode = pop @nodes; 399 1.1 mrg 400 1.1 mrg my $hash = $root; 401 1.1 mrg foreach (@nodes) { 402 1.1 mrg $hash->{testdiv} ||= {}; 403 1.1 mrg $hash = $hash->{testdiv}->{$_} ||= {}; 404 1.1 mrg } 405 1.1 mrg 406 1.1 mrg 407 1.1 mrg $hash->{test} ||= {}; 408 1.1 mrg $hash->{test}->{$lastnode} = $test; 409 1.1 mrg } 410 1.1 mrg 411 1.1 mrg 412 1.1 mrg # ==== Comparison ==== 413 1.1 mrg 414 1.1 mrg sub ispass($) { 415 1.1 mrg my $result = shift; 416 1.1 mrg 417 1.1 mrg if($result eq "PASS" or $result eq "XFAIL" or $result eq "KFAIL") { 418 1.1 mrg return 1; 419 1.1 mrg } else { 420 1.1 mrg return 0; 421 1.1 mrg } 422 1.1 mrg } 423 1.1 mrg 424 1.1 mrg 1; 425