Be tighter on matching "SIGBUS"
[buildd-scripts.git] / bin / analyze_results
1 #! /usr/bin/perl
2 #
3 # analyze_results
4 #
5 # Script to analyze failed build logs. Look for specific regexps to
6 # classify things
7 #
8 #
9 # (c) 2018 Steve McIntyre <steve@einval.com> GPL v2+
10
11 use strict;
12 use warnings;
13 use POSIX qw(strftime);
14 use Data::Dumper;
15 use Getopt::Std;
16 use HTTP::Tiny;
17 use URI::Encode qw(uri_encode uri_decode);
18
19 my $name = "analyze_results";
20 my $repo = "https://git.einval.com/cgi-bin/gitweb.cgi?p=buildd-scripts.git";
21 my $hostname;
22 my $date;
23 my $time_start = time();
24 my $time_end;
25 my $time_taken;
26 my $num_fail = 0;
27 my $awaiting_analysis = 0;
28 my $lines_read = 0;
29 my $existing_bugs = 0;
30 my $buildd_base_url="https://buildd.debian.org/status/package.php?p=";
31
32 my $new_bugs_filed = 0;
33 my $new_bugs_arm64_alignment = 0;
34 my $new_bugs_arm64_misdetect = 0;
35 my $new_bugs_arm64_test_fail = 0;
36 my $new_bugs_arm64_other = 0;
37 my $new_bugs_arch_any = 0;
38
39 use constant {
40     ERR_ARCH_MISMATCH      => 1,
41     ERR_NO_SOURCE          => 2,
42     ERR_INFRA              => 3,
43     ERR_BD_PROBLEM         => 4,
44     ERR_DETECT_WRONG_ARCH  => 5,
45     ERR_CRASH              => 6,
46     ERR_BUILD_PROBLEM      => 7,
47     ERR_TEST_PROBLEM       => 8,
48     ERR_BUILD_TIMEOUT      => 9,
49 };
50
51 my @err_descriptions;
52 $err_descriptions [ERR_ARCH_MISMATCH]     = "Architecture mismatches";
53 $err_descriptions [ERR_NO_SOURCE]         = "No source found";
54 $err_descriptions [ERR_INFRA]             = "Infrastructure errors";
55 $err_descriptions [ERR_BD_PROBLEM]        = "Problems with build-deps";
56 $err_descriptions [ERR_DETECT_WRONG_ARCH] = "Builds detected wrong architecture";
57 $err_descriptions [ERR_CRASH]             = "Crashes detected";
58 $err_descriptions [ERR_BUILD_PROBLEM]     = "Problems detected during build phase";
59 $err_descriptions [ERR_TEST_PROBLEM]      = "Problems detected during test phase";
60 $err_descriptions [ERR_BUILD_TIMEOUT]     = "Package builds timed out";
61
62 # Known failure modes to look for
63 my @logcheck = (
64     {
65         # "rchitecture mismatch" -> should never build on this arch
66         # Stop working on this log at this point
67         string   => 'rchitecture mismatch',
68         message  => 'Architecture mismatch',
69         check    => 1,
70         stop     => 1,
71         analyze  => 0,
72         type     => ERR_ARCH_MISMATCH,
73     },
74     {
75         string   => 'not in arch list or does not match any',
76         message  => 'Architecture mismatch',
77         check    => 1,
78         stop     => 1,
79         analyze  => 0,
80         type     => ERR_ARCH_MISMATCH,
81     },
82     {
83         # "binary build with no binary artifacts found" -> no packages
84         # built. Why not picked up already above?.
85         # Stop working on this log at this point
86         string   => 'binary build with no binary artifacts found',
87         message  => 'No binaries built',
88         check    => 1,
89         stop     => 1,
90         analyze  => 1,
91         type     => ERR_ARCH_MISMATCH,
92     },
93     {
94         string   => 'E: Can not find version \S+ of package',
95         message  => 'Could not find specified source package',
96         check    => 1,
97         stop     => 1,
98         analyze  => 0,
99         type     => ERR_NO_SOURCE,
100     },
101     {
102         string   => 'schroot.*File is not owned by user root',
103         message  => 'Schroot setup failure',
104         check    => 1,
105         stop     => 1,
106         analyze  => 0,
107         type     => ERR_INFRA,
108     },
109     {
110         string   => 'No space left on device',
111         message  => 'Build ran out of disk space',
112         check    => 1,
113         stop     => 1,
114         analyze  => 0,
115         type     => ERR_INFRA,
116     },
117     {
118         string   => 'The system has no more ptys',
119         message  => 'Build ran out of ptys',
120         check    => 1,
121         stop     => 1,
122         analyze  => 0,
123         type     => ERR_INFRA,
124     },
125     {
126         string   => 'aarch64-unknown-linux-gnu',
127         message  => 'Wrong arch detected',
128         check    => 1,
129         stop     => 1,
130         analyze  => 1,
131         type     => ERR_DETECT_WRONG_ARCH,
132     },
133     {
134         string   => 'binutils-aarch64',
135         message  => 'Wrong arch detected',
136         check    => 0,
137         stop     => 1,
138         analyze  => 1,
139         type     => ERR_DETECT_WRONG_ARCH,
140     },
141     {
142         string   => 'lib.linux-aarch64',
143         message  => 'Wrong arch detected',
144         check    => 1,
145         stop     => 1,
146         analyze  => 1,
147         type     => ERR_DETECT_WRONG_ARCH,
148     },
149     {
150         # "Bus error" -> alignment bug
151         string   => 'Bus error',
152         message  => 'Alignment problem',
153         check    => 1,
154         stop     => 1,
155         analyze  => 1,
156         type     => ERR_CRASH,
157     },
158     {
159         # "SIGBUS" -> alignment bug
160         string   => 'signal SIGBUS',
161         message  => 'Alignment problem',
162         check    => 1,
163         stop     => 1,
164         analyze  => 1,
165         type     => ERR_CRASH,
166     },
167     {
168         # "Segmentation fault" -> code problem
169         string   => 'Segmentation fault',
170         pstring  => 'Setting up (\S+)',
171         message  => 'Segmentation fault when installing RESULT',
172         check    => 1,
173         stop     => 1,
174         analyze  => 1,
175         type     => ERR_CRASH,
176     },
177     {
178         # "Segmentation fault" -> code problem
179         string   => 'Segmentation fault',
180         message  => 'Segmentation fault',
181         check    => 1,
182         stop     => 1,
183         analyze  => 1,
184         type     => ERR_CRASH,
185     },
186     {
187         # "Illegal instruction" -> bad build target?
188         string   => 'Illegal instruction',
189         pstring  => 'Setting up (\S+)',
190         message  => 'Illegal instruction when installing RESULT',
191         check    => 1,
192         stop     => 1,
193         analyze  => 1,
194         type     => ERR_CRASH,
195     },
196     {
197         # "Illegal instruction" -> bad build target?
198         string   => 'Illegal instruction',
199         message  => 'Illegal instruction',
200         check    => 1,
201         stop     => 1,
202         analyze  => 1,
203         type     => ERR_CRASH,
204     },
205     {
206         # Installing build-deps failed
207         string   => 'dpkg: error processing package (\S+)',
208         message  => 'Build-dep failed to install (RESULT)',
209         check    => 1,
210         stop     => 0,
211         analyze  => 0,
212         type     => ERR_BD_PROBLEM,
213     },
214     {
215         # Installing build-deps failed
216         string   => 'E: pbuilder-satisfydepends failed.',
217         message  => 'Pbuilder build-deps failed',
218         check    => 1,
219         stop     => 1,
220         analyze  => 0,
221         type     => ERR_BD_PROBLEM,
222     },
223     {
224         # Installing build-deps failed
225         string   => 'E: Unmet dependencies',
226         message  => 'Build-deps failed',
227         check    => 1,
228         stop     => 1,
229         analyze  => 0,
230         type     => ERR_BD_PROBLEM,
231     },
232     {
233         # Installing build-deps failed
234         string   => 'unsat-dependency: (\S+)',
235         message  => 'Missing build-dep (RESULT)',
236         check    => 1,
237         stop     => 1,
238         analyze  => 0,
239         type     => ERR_BD_PROBLEM,
240     },
241     {
242         # Installing build-deps failed
243         string   => 'unsat-conflict: (\S+)',
244         message  => 'Unsatisfiable build-dep conflict (RESULT)',
245         check    => 1,
246         stop     => 1,
247         analyze  => 0,
248         type     => ERR_BD_PROBLEM,
249     },
250     {
251         # Build failed - missing build-dep?
252         string   => 'build dependencies/conflicts unsatisfied',
253         message  => 'Build-deps not satisfiable',
254         check    => 1,
255         stop     => 1,
256         analyze  => 0,
257         type     => ERR_BD_PROBLEM,
258     },
259     {
260         # Build failed
261         string   => 'dpkg-source: error: unrepresentable changes to source',
262         message  => 'dpkg-source failure',
263         check    => 1,
264         stop     => 1,
265         analyze  => 0,
266         type     => ERR_BUILD_PROBLEM,
267     },
268     {
269         # Build failed - missing build-dep?
270         string   => 'ld: cannot find',
271         message  => 'Build failure: missing library - missing build-dep?',
272         check    => 1,
273         stop     => 1,
274         analyze  => 0,
275         type     => ERR_BUILD_PROBLEM,
276     },
277     {
278         # Build failed - missing build-dep?
279         string   => 'fatal error:.*No such file or directory',
280         message  => 'Build failure: missing header - missing build-dep?',
281         check    => 1,
282         stop     => 1,
283         analyze  => 0,
284         type     => ERR_BUILD_PROBLEM,
285     },
286     {
287         # Build failed - missing build-dep?
288         string   => 'SEVERE: Cannot resolve dependencies',
289         message  => 'Build failure - missing build-dep?',
290         check    => 1,
291         stop     => 1,
292         analyze  => 0,
293         type     => ERR_BUILD_PROBLEM,
294     },
295     {
296         # Build failed - can't exec something...
297         string   => 'error trying to exec.*execvp: No',
298         message  => 'Build failure (missing binary)',
299         check    => 1,
300         stop     => 1,
301         analyze  => 1,
302         type     => ERR_BUILD_PROBLEM,
303     },
304     {
305         # Build failed
306         string   => 'BUILD FAIL',
307         message  => 'Build failure (java/javadoc)',
308         check    => 1,
309         stop     => 1,
310         analyze  => 0,
311         type     => ERR_BUILD_PROBLEM,
312     },
313     {
314         # Build failed
315         string   => '^make.*\*\*\*.* \[debian/rules.*Error \d+$',
316         message  => 'Build failure (other)',
317         check    => 1,
318         stop     => 1,
319         analyze  => 1,
320         type     => ERR_BUILD_PROBLEM,
321     },
322     {
323         # Build failed
324         string   => 'make.*returned exit code',
325         message  => 'Build failure (other)',
326         check    => 1,
327         stop     => 0,
328         analyze  => 1,
329         type     => ERR_BUILD_PROBLEM,
330     },
331     {
332         # Build failure
333         string   => 'dh_auto_build:.*returned exit code \d+',
334         message  => 'Build failure (other)',
335         check    => 1,
336         stop     => 1,
337         analyze  => 1,
338         type     => ERR_BUILD_PROBLEM,
339     },
340     {
341         # Build failure
342         string   => 'dh_auto_clean:.*returned exit code \d+',
343         message  => 'Build failure (clean failed)',
344         check    => 1,
345         stop     => 1,
346         analyze  => 0,
347         type     => ERR_BUILD_PROBLEM,
348     },
349     {
350         # Build failure
351         string   => 'dh_auto_install:.*returned exit code \d+',
352         message  => 'Build failure (install failed)',
353         check    => 1,
354         stop     => 1,
355         analyze  => 1,
356         type     => ERR_BUILD_PROBLEM,
357     },
358     {
359         # Build failure
360         string   => 'dh_auto_configure:.*returned exit code \d+',
361         message  => 'Build failure (configure failed)',
362         check    => 1,
363         stop     => 1,
364         analyze  => 1,
365         type     => ERR_BUILD_PROBLEM,
366     },
367     {
368         # Build failed
369         string   => '^debian/.*recipe for target (\S+) failed',
370         message  => 'Build failure (RESULT)',
371         check    => 1,
372         stop     => 1,
373         analyze  => 1,
374         type     => ERR_BUILD_PROBLEM,
375     },
376     {
377         # Build failed
378         string   => '^debian/.*\*\*\* (.*).  Stop.',
379         message  => 'Build error (RESULT)',
380         check    => 1,
381         stop     => 1,
382         analyze  => 1,
383         type     => ERR_BUILD_PROBLEM,
384     },
385     {
386         # Build failed
387         string   => 'dpkg-buildpackage: error:.*(debian/rules \S+) subprocess returned exit status 2',
388         message  => 'Build error (RESULT)',
389         check    => 1,
390         stop     => 1,
391         analyze  => 1,
392         type     => ERR_BUILD_PROBLEM,
393     },
394     {
395         # Build failed
396         string   => 'fakeroot debian/rules binary',
397         message  => 'Build failure (other)',
398         check    => 0,
399         stop     => 1,
400         analyze  => 1,
401         type     => ERR_BUILD_PROBLEM,
402     },
403     {
404         # Build failure
405         string   => 'constant \d+ overflows.*int',
406         message  => 'Go 32-bit integer overflow',
407         check    => 1,
408         stop     => 0,
409         analyze  => 1,
410         type     => ERR_BUILD_PROBLEM,
411     },
412     {
413         # Test failure
414         string   => 'OSError: \[Errno 13\] Permission denied',
415         message  => 'Python EPERM test failure',
416         check    => 1,
417         stop     => 1,
418         analyze  => 1,
419         type     => ERR_TEST_PROBLEM,
420     },
421     {
422         # Test failure
423         string   => 'dh_auto_test:.*returned exit code \d+',
424         message  => 'Test failure',
425         check    => 1,
426         stop     => 1,
427         analyze  => 1,
428         type     => ERR_TEST_PROBLEM,
429     },
430     {
431         # Timeout. pbuilder is too dumb to do this properly :-(
432         string   => 'I: Terminating build process due to timeout',
433         message  => 'Pbuilder build timeout',
434         check    => 1,
435         stop     => 1,
436         analyze  => 0,
437         type     => ERR_BUILD_TIMEOUT,
438     },
439     {
440         # Timeout from sbuild
441         string   => 'Build killed with signal TERM after \d+ minutes of inactivity',
442         message  => 'Sbuild build timeout',
443         check    => 1,
444         stop     => 1,
445         analyze  => 0,
446         type     => ERR_BUILD_TIMEOUT,
447     },
448 );
449
450 my %log_results;
451 my %udd;
452
453 #foreach my $checktmp (@logcheck) {
454 #    my %check = %$checktmp;
455 #    print "looking for \"$check{string}\"\n";
456 #    print "  with log message \"$check{message}\"\n";
457 #    print "  check this regexp: $check{check}\n";
458 #    print "  stop if found: $check{stop}\n";
459 #}
460
461 print "<html>\n";
462 print "<head>\n";
463 print "<title>Build log analysis</title>\n";
464 print "</head>\n";
465 print "<body>\n";
466 print "<h1>Build log analysis</h1>\n";
467 print "<p><a href=\"#summary\">Summary</a></p>\n";
468 print "<h2>Packages</h2>\n";
469 print "<ol>\n";
470
471 my $line;
472
473 our($opt_u);
474 getopts('u:') or die "getopts failure\n";
475
476 if (defined $opt_u) {
477     open (UDD, "< $opt_u") or die "Can't open UDD list file $opt_u for reading\n";
478     while (defined ($line = <UDD>)) {
479         chomp $line;
480         my ($pkg, $bugno, $bugsubj) = split (/ /, $line, 3);
481         my @buglist;
482         my $tmp = $udd{"$pkg"};
483         if ($tmp) {
484             @buglist = @$tmp;
485         }
486         push (@buglist, "$bugno: $bugsubj");
487         $udd{"$pkg"} = \@buglist;
488     }
489     close UDD;
490
491 #    foreach my $key (keys %udd) {
492 #       my $tmp = $udd{$key};
493 #       my $num = scalar (@$tmp);
494 #       print "$key has $num bugs:\n";
495 #       foreach my $entry (@$tmp) {
496 #           print "  $entry\n";
497 #       }
498 #    }
499 }
500
501 sub check_buildd_status {
502     my $pkg = shift;
503     my $version = shift;
504     my $arch = shift;
505
506     my $url = "$buildd_base_url$pkg";
507
508 #    print "Looking at URL $url\n";
509
510     my $built_on_arches = "";
511     my $built_my_arch = 0;
512     my $ftbfs_my_arch = 0;
513     my $not_my_arch = 0;
514     my $current_version = "";
515
516 #    print "Checking buildd data for package $pkg, arch $arch\n";
517
518     my $response = HTTP::Tiny->new->get($url);
519
520     if ($response->{success}) {
521 #       print "$response->{status} $response->{reason}\n";
522         my $content = $response->{content};
523 #       print "$content\n";
524
525         # Split the content up into lines
526         my @lines = split("\n", $content);
527
528         # Now look for two things:
529         # 1. how many arches have built this package?
530         # 2. has *our* arch built it?
531 #       print "we tried $version on arch $arch\n";
532         foreach my $line (@lines) {
533             chomp $line;
534             $line = uri_decode($line);
535             if ($line =~ /https:\/\/buildd.debian.org\/stats\/graph/) {
536                 my @info = split (/<\/td>/, $line);
537                 # Grab the info from our fields
538                 my $this_arch = "";
539                 if ($info[0] =~ /architecture.php\?a=([^&]*)&amp/) {
540                     $this_arch = $1;
541 #                   print "Found entry for arch $this_arch\n";
542                 } else {
543                     print "  <li>FAILED TO PARSE $url\n";
544                 }
545                 $current_version = $info[1];
546                 $current_version =~ s/^<td>//;
547 #               print "current_version for $this_arch is $current_version, we tried $version\n";
548                 my $status = "";
549                 if ($info[2] =~ /class="status status-([a-z]*)/) {
550                     $status = $1;
551 #                   print "Found status $status\n";
552                 } else {
553                     print "  <li>FAILED TO PARSE $url\n";
554                 }
555 #               foreach my $field(@info) {
556 #                   print "$field\n";
557 #               }
558 #               print "  On $this_arch we have version $current_version, status $status\n";
559                 if ($arch =~ /^\Q$this_arch\E$/) {
560                     if ($status =~ /installed/) {
561                         $built_my_arch++;
562                     } else {
563                         $ftbfs_my_arch++;
564                     }
565                 }
566                 if ($status =~ /installed/) {
567                     $built_on_arches .= "$this_arch ";
568                 }
569             }
570
571             if ($line =~ /$arch is not present in the architecture list set by the maintainer/) {
572                 $not_my_arch++;
573             }
574             if ($line =~ /No entry in $arch database/) {
575                 $not_my_arch++;
576             }
577         }
578         print "  <li>Built on this arch: $built_my_arch\n";
579         print "  <li>Built on arches: $built_on_arches\n";
580         if ($not_my_arch) {
581             print "  <li>Not set to build on $arch\n";
582         }
583         if ($ftbfs_my_arch) {
584             print "  <li>Already seen to FTBFS on $arch\n";
585         }
586         if (length($current_version) < 2) {
587             print "  <li>No current version found - has this been removed?\n";
588         } elsif (! ($current_version =~ /\Q$version\E/)) {
589             print "  <li><strong>New version $current_version available - retry?</strong>\n";
590         }
591     } else {
592         print "  <LI>FAILED TO RETRIEVE buildd URL $url\n";
593     }
594 }
595     
596
597 foreach my $input (@ARGV) {
598     open (IN, "< $input") or die "Can't read $input: $!\n";
599     $num_fail++;
600     my $stop = 0;
601     my $lineno = 0;
602     my $errors = 0;
603     my $errors_need_analysis = 0;
604     my $oldline = "";
605     my %file_results;
606     my $pav;
607     my $pkg;
608     my $arch;
609     my $version;
610
611     $pav = $input;
612     $pav =~ s,^.*/,,g;
613     $pav =~ s,\.log,,g;
614     ($pkg, $version, $arch) = split (/_/, $pav);
615
616     print "<li>Looking at <a href=\"$input\">$pkg version $version on arch $arch</a>:\n";
617     print "<ul>\n";
618     while (defined ($line = <IN>) and !$stop) {
619         $lineno++;
620         $lines_read++;
621         foreach my $checktmp (@logcheck) {
622             my %check = %$checktmp;
623             if ($check{check}) {
624                 if (!$stop
625                     and ($line =~ m/$check{string}/)
626                     and (!$check{pstring} or $oldline =~ m/$check{pstring}/)) {
627 #                   print "  Line $lineno: found \"$check{string}\"\n";
628                     my $match = $1;
629                     my $message = $check{message};
630                     $message =~ s,RESULT,$match,g;
631                     $file_results{$message} = $check{type};
632                     if (($check{type} == ERR_BUILD_TIMEOUT) and ($errors == 0)) {
633                         print "  <li>Line $lineno: $message\n";
634                         print "  <li>Build killed by timeout before any errors at line $lineno\n";
635                     } else {
636                         print "  <li>Line $lineno: $message\n";
637                         $errors++;
638                     }
639                     if ($check{analyze}) {
640                         $errors_need_analysis++;
641                     }
642                     if ($check{stop}) {
643 #                       print "  stopping processing\n";
644                         $stop = 1;
645                     }
646                 }
647             }
648         }
649         $oldline = $line;
650     }
651     close IN;
652     # End of checking this package
653     print "  <li><strong>Found errors: $errors</strong>\n";
654     if (!$errors) {
655         print " (maybe just timed out during build?)\n";
656     }
657
658     # Look for a note for manually-added logfile analysis
659     my $note = $input;
660     $note =~ s,\.log$,.note,;
661     if (-f $note) {
662         open (IN, "< $note") or die "Can't open $note for reading: $!\n";
663         while (defined (my $line = <IN>)) {
664             chomp $line;
665             if ($line =~ m,#(\d+),) {
666                 my $bugno = $1;
667                 $existing_bugs++;
668                 $line =~ s,#(\d+),<a href="https://bugs.debian.org/$1">#$1</a>,g;
669             }
670             print "  <li>$line\n";
671             if ($line =~ m,^Filed,) {
672                 $new_bugs_filed++;
673                 if ($line =~ m,-on-arm64,) {
674                     if ($line =~ m,misdetected architecture,) {
675                         $new_bugs_arm64_misdetect++;
676                     } elsif ($line =~ m,alignment,) {
677                         $new_bugs_arm64_alignment++;
678                     } elsif ($line =~ m,test,) {
679                         $new_bugs_arm64_test_fail++;
680                     } else {
681                         $new_bugs_arm64_other++;
682                     }
683                 } else {
684                     $new_bugs_arch_any++;
685                 }
686             }
687         }
688         close IN;
689     } else {
690         if ($errors_need_analysis) {
691             print "  <li><strong>Needs analysis</strong>\n";
692             if ($udd{$pkg}) {
693                 my $tmp = $udd{$pkg};
694                 my $num = scalar (@$tmp);
695                 print "  <li>$num reported FTBFS bugs:\n";
696                 print "  <ul>\n";
697                 foreach my $entry (@$tmp) {
698                     print "    <li>#$entry\n";
699                 }
700                 print "  </ul>\n";
701             }
702             check_buildd_status($pkg, $version, $arch);
703             $awaiting_analysis++;
704         }
705     }
706
707     print "</ul>\n";
708     foreach my $key (keys %file_results) {
709         $log_results{$file_results{$key}}{$key} += 1;
710 #       print "now have $log_results{$key} for \"$key\"\n";
711     }
712 }
713 print "</ol>\n";
714
715 print "<a name=\"summary\"</a>\n";
716 print "<h2>Summary of results from $num_fail failed builds:</h2>\n";
717 print "<p>$awaiting_analysis logs still need analysis</p>\n";
718 print "<ul>\n";
719 print "  <li>Linked to $existing_bugs bugs in the Debian BTS</li>\n";
720 foreach my $type (sort keys %log_results) {
721     print "  <h3>$err_descriptions[$type]</h3>\n";
722     my $tmp = $log_results{$type};
723     my %result = %$tmp;
724     foreach my $key (sort { $result{$b} <=> $result{$a} }keys %result) {
725         print "  <li>Found $result{$key} log(s) showing $key\n";
726     }
727 }
728 print "</ul>\n";
729
730 print "<h2>Filed $new_bugs_filed new bugs</h2>\n";
731 print "<ul>\n";
732 print "  <li>$new_bugs_arm64_misdetect bugs for arch misdetection\n";
733 print "  <li>$new_bugs_arm64_alignment bugs for alignment problems\n";
734 print "  <li>$new_bugs_arm64_test_fail bugs for arch-specific test failures\n";
735 print "  <li>$new_bugs_arm64_other bugs for arch-specific misc failures\n";
736 print "  <li>$new_bugs_arch_any bugs for general arch-independent FTBFS\n";
737 print "</ul>\n";
738
739 $time_end = time();
740 $time_taken = $time_end - $time_start;
741 $date = strftime "%a %b %e %H:%M:%S %Z %Y", localtime;
742 $hostname = `hostname`;
743 chomp $hostname;
744
745 print "<hr>\n";
746 print "<p>Log analysis generated on $hostname, $date.\n";
747 print "<br>Output from $name - see <a href=\"$repo\">$repo</a> for source.\n";
748 print "<br>Read $lines_read lines of logs and took $time_taken seconds to complete.\n";
749
750 print "</body>\n";
751 print "</html>\n";