diff --git a/modules/ksb/DebugOrderHints.pm b/modules/ksb/DebugOrderHints.pm new file mode 100644 index 0000000..66bfb5d --- /dev/null +++ b/modules/ksb/DebugOrderHints.pm @@ -0,0 +1,127 @@ +package ksb::DebugOrderHints; + +# This module provides some utilities to estimate which module build failures +# are the most 'interesting' + +use strict; +use warnings; +use 5.014; + + +sub _getPhaseScore +{ + my $phase = shift; + + # + # Assumption: build & install phases are interesting. + # Install is particularly interesting because that should 'rarely' fail, + # and so if it does there are probably underlying system issues at work. + # + # Assumption: 'test' is opt in and therefore the user has indicated a + # special interest in that particular module? + # + # Assumption: source updates are likely not that interesting due to + # e.g. transient network failure. But it might also indicate something + # more serious such as an unclean git repository, causing scm commands + # to bail. + # + + return 4 if ($phase eq 'install'); + return 3 if ($phase eq 'test'); + return 2 if ($phase eq 'build'); + return 1 if ($phase eq 'update'); + return 0; +} + +sub _compareDebugOrder +{ + my ($moduleGraph, $extraDebugInfo, $a, $b) = @_; + + # + # Enforce a strict dependency ordering. + # The case where both are true should never happen, since that would + # amount to a cycle, and cycle detection is supposed to have been + # performed beforehand. + # + # Assumption: if A depends on B, and B is broken then a failure to build + # A is probably due to lacking a working B. + # + my $bDependsOnA = $moduleGraph->{$a}->{votes}->{$b} // 0; + my $aDependsOnB = $moduleGraph->{$b}->{votes}->{$a} // 0; + my $order = $bDependsOnA ? -1 : ($aDependsOnB ? 1 : 0); + + return $order if $order; + + # + # TODO we could tag explicitly selected modules from command line? + # If we do so, then the user is probably more interested in debugging + # those first, rather than 'unrelated' noise from modules pulled in due + # to possibly overly broad dependency declarations. In that case we + # should sort explicitly tagged modules next highest, after dependency + # ordering. + # + + # + # Assuming no dependency relation, next sort by 'popularity': + # the item with the most votes (back edges) is depended on the most. + # + # Assumption: it is probably a good idea to debug that one earlier. + # This would point the user to fixing the most heavily used dependencies + # first before investing time in more 'exotic' modules + # + my $voteA = scalar keys %{$moduleGraph->{$a}->{votes}}; + my $voteB = scalar keys %{$moduleGraph->{$b}->{votes}}; + my $votes = $voteB <=> $voteA; + + return $votes if $votes; + + # + # Try and see if there is something 'interesting' that might e.g. indicate + # issues with the system itself, preventing a successful build. + # + my $phaseA = _getPhaseScore($extraDebugInfo->{phases}->{$a} // ''); + my $phaseB = _getPhaseScore($extraDebugInfo->{phases}->{$b} // ''); + my $phase = $phaseB <=> $phaseA; + + return $phase if $phase; + + # + # Assumption: persistently failing modules do not prompt the user + # to act and therefore these are likely not that interesting. + # Conversely *new* failures are. + # + # If we get this wrong the user will likely be on the case anyway: + # someone does not need prodding if they have been working on it + # for the past X builds or so already. + # + my $failCountA = $extraDebugInfo->{failCount}->{$a} // 0; + my $failCountB = $extraDebugInfo->{failCount}->{$b} // 0; + my $failCount = $failCountA <=> $failCountB; + + return $failCount if $failCount; + + # + # If there is no good reason to perfer one module over another, + # simply sort by name to get a reproducible order. + # That simplifies autotesting and/or reproducible builds. + # (The items to sort are supplied as a hash so the order of keys is by + # definition not guaranteed.) + # + my $name = ($a cmp $b); + + return $name; +} + +sub sortFailuresInDebugOrder +{ + my ($moduleGraph, $extraDebugInfo, $failuresRef) = @_; + my @failures = @{$failuresRef}; + + my @prioritised = sort { + _compareDebugOrder($moduleGraph, $extraDebugInfo, $a, $b); + } (@failures); + + return @prioritised; +} + +1;