dglib.pm 13 KB

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