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