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