finish refactoring options.

Deprecate: -t and -T
use -DCONNECTION_TIMEOUT=... -DDISPLAY_TIMEOUT=... instead
New feature: -DSTUCK_TIMEOUT=
kill tasks when they don't show any progress for that long.
This commit is contained in:
espie 2011-04-25 11:58:46 +00:00
parent f65c5de38a
commit afdb81839a
7 changed files with 249 additions and 185 deletions

View File

@ -1,7 +1,7 @@
#! /usr/bin/perl
# ex:ts=8 sw=4:
# $OpenBSD: dpb,v 1.17 2011/04/24 09:14:45 espie Exp $
# $OpenBSD: dpb,v 1.18 2011/04/25 11:58:46 espie Exp $
#
# Copyright (c) 2010 Marc Espie <espie@openbsd.org>
#
@ -31,15 +31,17 @@ package DPB::State;
our @ISA = qw(OpenBSD::State);
use OpenBSD::State;
use DPB::Heuristics;
use OpenBSD::Paths;
use DPB::Heuristics;
use DPB::PkgPath;
use DPB::Logger;
sub init
{
my $self = shift;
$self->SUPER::init;
$self->{export_level}++;
$self->{no_exports} = 1;
$self->{heuristics} = DPB::Heuristics->new;
$self->{make} = $ENV{MAKE} || OpenBSD::Paths->make;
($self->{ports}, $self->{repo}, $self->{localarch}, $self->{distdir}) =
@ -53,26 +55,89 @@ sub init
sub handle_options
{
my $state = shift;
$state->{opt}{A} = sub {
$state->{arch} = shift;
};
$state->{opt}{L} = sub {
$state->{logdir} = shift;
};
$state->{opt}{r} = sub {
$state->heuristics->random;
};
$state->{opt}{m} = sub {
$state->heuristics->set_threshold(shift);
$state->{opt} = {
a => sub {
$state->{all} = 1;
},
A => sub {
$state->{arch} = shift;
},
L => sub {
$state->{logdir} = shift;
},
r => sub {
$state->heuristics->random;
},
m => sub {
$state->heuristics->set_threshold(shift)
},
P => sub {
my $file = shift;
open my $fh, '<', $file or die "Can't open $file\n";
my $_;
while (<$fh>) {
chomp;
next if m/^\#/;
unshift @main::ARGV, $_;
}
},
b => sub {
push(@{$state->{build_files}}, shift);
},
S => sub {
$state->parse_size_file(shift, $state->heuristics);
},
};
$state->SUPER::handle_options('aceqrRsuUh:xA:f:j:m:P:b:d:L:S:t:T:',
$state->SUPER::handle_options('aceqrRsuUh:xA:f:j:m:P:b:L:S:t:T:',
"[-acerRsuUx] [-A arch] [-j N] [-P plist] [-h hosts] [-L logdir]",
"[-b log] [-t ctimeout] [-T dtimeout] [-m threshold] [path ...]");
"[-b log] [-t ctimeout] [-m threshold] [path ...]");
$state->{fullrepo} = join("/", $state->{repo}, $state->arch, "all");
$state->{logdir} //= $ENV{LOGDIR} //
join("/", $state->ports, "logs", $state->arch);
if (defined $state->opt('j')) {
if ($state->localarch ne $state->arch) {
$state->usage(
"Can't use -j if -A arch is not local architecture");
}
if ($state->opt('j') !~ m/^\d+$/) {
$state->usage("-j takes a numerical argument");
}
}
$state->{logger} = DPB::Logger->new($state->logdir, $state->opt('c'));
$state->heuristics->set_logger($state->logger);
$state->{display_timeout} =
$state->{subst}->value('DISPLAY_TIMEOUT') // $state->opt('T') // 10;
$state->{connection_timeout} =
$state->{subst}->value('CONNECTION_TIMEOUT') // $state->opt('t');
$state->{stuck_timeout} = $state->{subst}->value('STUCK_TIMEOUT');
}
sub start_cores
{
my $state = shift;
if ($state->opt('h')) {
DPB::Core->parse_hosts_file($state->opt('h'), $state);
}
my $prop = {};
if ($state->opt('j')) {
$prop->{jobs} = $state->opt('j');
}
if ($state->{stuck_timeout}) {
$prop->{stuck} = $state->{stuck_timeout};
}
if ($state->opt('j') || !$state->opt('h')) {
DPB::Core::Factory->new('localhost', $prop);
}
DPB::Core::Factory->init_cores($state);
}
sub logger
{
return shift->{logger};
}
sub heuristics
@ -115,31 +180,52 @@ sub logdir
return shift->{logdir};
}
package main;
sub parse_build_line
{
return split(/\s+/, shift);
}
sub parse_build_file
{
my ($state, $fname, @consumers) = @_;
if (!-f $fname) {
my $arch = $state->arch;
if (-f "$fname/$arch/build.log") {
$fname = "$fname/$arch/build.log";
} elsif (-f "$fname/build.log") {
$fname = "$fname/build.log";
}
}
open my $fh, '<', $fname or
$state->fatal("Couldn't open build file #1: #2", $fname, $!);
my $_;
while (<$fh>) {
chomp;
my ($pkgpath, $host, $time, $sz, @rest) = parse_build_line($_);
next if (!defined $sz) || $sz =~ m/!$/;
my $o = DPB::PkgPath->new_hidden($pkgpath);
for my $c (@consumers) {
$c->add_build_info($o, $host, $time, $sz);
}
}
}
use DPB::PkgPath;
use DPB::Core;
use DPB::Vars;
use DPB::PortInfo;
use DPB::Engine;
use DPB::PortBuilder;
use DPB::Reporter;
use OpenBSD::Error;
use DPB::Locks;
use DPB::Logger;
use DPB::Job;
use DPB::Grabber;
our ($opt_t, $opt_d, $opt_e, $opt_T, $opt_c, $opt_h, $opt_j, $opt_a,
$opt_q, $opt_R, $opt_s, $opt_u, $opt_U,
$opt_f, $opt_x);
my @subdirlist;
sub handle_build_files
{
my $state = shift;
return unless defined $state->{build_files};
for my $file (@{$state->{build_files}}) {
$state->parse_build_file($file, $state->heuristics,
"DPB::Job::Port");
}
$state->heuristics->finished_parsing;
}
sub parse_size_file
{
my ($fname, @consumers) = @_;
open my $fh, '<', $fname or die "Couldn't open build file $fname\n";
my ($state, $fname, @consumers) = @_;
open my $fh, '<', $fname or
$state->fatal("Couldn't open build file #1: #2", $fname, $!);
my $_;
while (<$fh>) {
chomp;
@ -154,60 +240,31 @@ sub parse_size_file
}
}
sub parse_build_line
{
return split(/\s+/, shift);
}
package main;
sub parse_build_file
{
my ($fname, $arch, @consumers) = @_;
if (!-f $fname) {
if (-f "$fname/$arch/build.log") {
$fname = "$fname/$arch/build.log";
} elsif (-f "$fname/build.log") {
$fname = "$fname/build.log";
}
}
open my $fh, '<', $fname or die "Couldn't open build file $fname\n";
my $_;
while (<$fh>) {
chomp;
my ($pkgpath, $host, $time, $sz, @rest) = parse_build_line($_);
next if (!defined $sz) || $sz =~ m/!$/;
my $o = DPB::PkgPath->new_hidden($pkgpath);
for my $c (@consumers) {
$c->add_build_info($o, $host, $time, $sz);
}
}
}
my @build_files = ();
use DPB::PkgPath;
use DPB::Core;
use DPB::Vars;
use DPB::PortInfo;
use DPB::Engine;
use DPB::PortBuilder;
use DPB::Reporter;
use OpenBSD::Error;
use DPB::Locks;
use DPB::Job;
use DPB::Grabber;
my @subdirlist;
my $state = DPB::State->new('dpb');
$state->{opt} = {
P => sub {
my $file = shift;
open my $fh, '<', $file or die "Can't open $file\n";
my $_;
while (<$fh>) {
chomp;
next if m/^\#/;
unshift @ARGV, $_;
}
},
b => sub {
push(@build_files, shift);
},
S => sub {
parse_size_file(shift, $state->heuristics);
}
};
$state->handle_options;
$state->start_cores;
my $dpb = $opt_f ? "fetch" : "normal";
$state->handle_build_files;
if (@ARGV == 0) {
$opt_a = 1;
$state->{all} = 1;
}
for my $arg (@ARGV) {
$arg =~ s/\/+$//;
@ -223,56 +280,22 @@ for my $arg (@ARGV) {
$pkgpath->add_to_subdirlist(\@subdirlist);
}
my $logger = DPB::Logger->new($state->logdir, $opt_c);
$state->heuristics->set_logger($logger);
if (defined $opt_j && $state->localarch ne $state->arch) {
$state->usage("Can't use -j if -A arch is not local architecture");
}
if (defined $opt_j && $opt_j !~ m/^\d+$/) {
$state->usage("-j takes a numerical argument");
}
if ($opt_h) {
DPB::Core->parse_hosts_file($opt_h, $state->arch, $opt_t, $logger, $state->heuristics);
}
my $prop = {};
if ($opt_j) {
$prop->{jobs} = $opt_j;
}
if ($opt_j || !$opt_h) {
DPB::Core::Factory->new('localhost', $prop);
}
if (@build_files > 0) {
for my $file (@build_files) {
parse_build_file($file, $state->arch, $state->heuristics, "DPB::Job::Port");
}
$state->heuristics->finished_parsing;
}
DPB::Core::Factory->init_cores($logger);
my $builder = DPB::PortBuilder->new(
$opt_c, $opt_s, $opt_u, $opt_U, $opt_R, $state->fullrepo, $logger,
$state->ports, $state->make, $state->heuristics);
my $builder = DPB::PortBuilder->new($state);
my $locker = DPB::Locks->new(join("/", $state->logdir, "locks"));
my $engine = DPB::Engine->new($builder, $state->heuristics, $logger, $locker);
my $reporter = DPB::Reporter->new($opt_x, $state->heuristics, "DPB::Core", $engine);
my $engine = DPB::Engine->new($builder, $state->heuristics, $state->logger,
$locker);
my $reporter = DPB::Reporter->new($state->opt('x'),
$state->heuristics, "DPB::Core", $engine);
while (!DPB::Core->avail) {
DPB::Core->reap;
sleep 1;
}
my $core = DPB::Core->get;
#my $dump = DPB::Util->make_hot($logger->open('dump'));
#my $dump = DPB::Util->make_hot($state->logger->open('dump'));
my $keep_going = 1;
$opt_T //= 10;
my $last_time = time() - $opt_T;
my $last_time = time() - $state->{display_timeout};
sub handle_non_waiting_jobs
{
@ -290,7 +313,8 @@ sub handle_non_waiting_jobs
}
if ($need_clock) {
my $current = time();
if ($current >= $last_time + $opt_T || $reaped) {
if ($current >= $last_time + $state->{display_timeout} ||
$reaped) {
$reporter->report;
$last_time = $current;
}
@ -300,13 +324,13 @@ sub handle_non_waiting_jobs
return $keep_going;
}
my $grabber = DPB::Grabber->new($state->ports, $state->make, $logger, $engine,
$dpb, sub { handle_non_waiting_jobs(1) });
my $grabber = DPB::Grabber->new($state, $engine,
sub { handle_non_waiting_jobs(1) });
if ($opt_a) {
if ($state->{all}) {
# when restarting interrupted dpb,
# find the most important paths first
my $list = $engine->find_best($logger->logfile("dependencies"), 10);
my $list = $engine->find_best($state->logger->logfile("dependencies"), 10);
# if we have them, list them before the full ports tree walk.
if (@$list > 0) {
$grabber->grab_subdirs($core, $list);
@ -319,7 +343,7 @@ if (@subdirlist > 0) {
$grabber->complete_subdirs($core);
if ($opt_a) {
if ($state->{all}) {
$grabber->grab_subdirs($core);
}
@ -327,19 +351,19 @@ if ($opt_a) {
$grabber->complete_subdirs($core);
# give back "our" core to the pool.
if (!$opt_e) {
if (!$state->opt('e')) {
$core->mark_ready;
}
# and let's wait for all jobs now.
if ($opt_a) {
if ($state->{all}) {
$engine->dump_dependencies;
}
#$engine->dump($dump);
$engine->check_buildable;
#$engine->dump($dump);
DPB::Core->start_clock($opt_T);
DPB::Core->start_clock($state->{display_timeout});
while (1) {
while (1) {
handle_non_waiting_jobs(0);
@ -354,7 +378,7 @@ while (1) {
DPB::Core->reap_wait;
}
}
if (!$opt_q || !$engine->recheck_errors) {
if (!$state->opt('q') || !$engine->recheck_errors) {
last;
}
}
@ -362,5 +386,5 @@ while (1) {
$reporter->reset;
DPB::Core->cleanup;
print $engine->report;
$engine->dump_category('tobuild', $logger->open('dump'));
$engine->dump_category('tobuild', $state->logger->open('dump'));

View File

@ -1,5 +1,5 @@
# ex:ts=8 sw=4:
# $OpenBSD: Core.pm,v 1.6 2011/03/22 19:49:56 espie Exp $
# $OpenBSD: Core.pm,v 1.7 2011/04/25 11:58:46 espie Exp $
#
# Copyright (c) 2010 Marc Espie <espie@openbsd.org>
#
@ -127,6 +127,12 @@ sub sf
return $self->prop->{sf};
}
sub stuck_timeout
{
my $self = shift;
return $self->prop->{stuck};
}
sub memory
{
my $self = shift;
@ -397,13 +403,13 @@ sub new
}
}
my $inited = 0;
sub init_cores
{
my ($self, $logger, $startup) = @_;
return if $inited;
my ($self, $state) = @_;
my $logger = $state->logger;
my $startup = $state->{startup_script};
DPB::Core->set_logdir($logger->{logdir});
for my $core (values %$init) {
my $job = DPB::Job::Init->new($logger);
@ -426,7 +432,6 @@ sub init_cores
}
$core->start_job($job);
}
$inited = 1;
}
package DPB::Core;
@ -466,7 +471,7 @@ sub one_core
my $hostname = $core->hostname;
return $core->job->name." [$core->{pid}]".
(DPB::Host->name_is_localhost($hostname) ? "" : " on ".$hostname).
$core->job->watched($time);
$core->job->watched($time, $core);
}
sub report
@ -583,18 +588,18 @@ sub has_sf
sub parse_hosts_file
{
my ($class, $filename, $arch, $timeout, $logger, $heuristics) = @_;
open my $fh, '<', $filename or die "Can't read host files $filename\n";
my ($class, $filename, $state) = @_;
open my $fh, '<', $filename or
$state->fatal("Can't read host files #1: #2", $filename, $!);
my $_;
my $sf;
my $cores = {};
my $startup_script;
while (<$fh>) {
chomp;
s/\s*\#.*$//;
next if m/^$/;
if (m/^STARTUP=\s*(.*)\s*$/) {
$startup_script = $1;
$state->{startup_script} = $1;
next;
}
my $prop = {};
@ -604,7 +609,7 @@ sub parse_hosts_file
$prop->{$1} = $2;
}
}
if (defined $prop->{arch} && $prop->{arch} ne $arch) {
if (defined $prop->{arch} && $prop->{arch} ne $state->arch) {
next;
}
if (defined $prop->{mem}) {
@ -614,12 +619,15 @@ sub parse_hosts_file
if (defined $prop->{sf} && $prop->{sf} != $sf) {
$has_sf = 1;
}
if (defined $timeout) {
$prop->{timeout} //= $timeout;
if (defined $state->{connection_timeout}) {
$prop->{timeout} //= $state->{connection_timeout};
}
$heuristics->calibrate(DPB::Core::Factory->new($host, $prop));
if (defined $state->{stuck_timeout}) {
$prop->{stuck} //= $state->{stuck_timeout};
}
$state->heuristics->calibrate(DPB::Core::Factory->new($host,
$prop));
}
DPB::Core::Factory->init_cores($logger, $startup_script);
}
sub start_pipe

View File

@ -1,5 +1,5 @@
# ex:ts=8 sw=4:
# $OpenBSD: Engine.pm,v 1.15 2010/12/07 10:56:26 espie Exp $
# $OpenBSD: Engine.pm,v 1.16 2011/04/25 11:58:46 espie Exp $
#
# Copyright (c) 2010 Marc Espie <espie@openbsd.org>
#

View File

@ -1,5 +1,5 @@
# ex:ts=8 sw=4:
# $OpenBSD: Grabber.pm,v 1.10 2010/12/05 10:06:12 espie Exp $
# $OpenBSD: Grabber.pm,v 1.11 2011/04/25 11:58:46 espie Exp $
#
# Copyright (c) 2010 Marc Espie <espie@openbsd.org>
#
@ -24,12 +24,13 @@ use DPB::Util;
package DPB::Grabber;
sub new
{
my ($class, $ports, $make, $logger, $engine, $dpb, $endcode) = @_;
my $o = bless { ports => $ports, make => $make,
loglist => DPB::Util->make_hot($logger->open("vars")),
logger => $logger,
my ($class, $state, $engine, $endcode) = @_;
my $o = bless { ports => $state->ports, make => $state->make,
loglist => DPB::Util->make_hot($state->logger->open("vars")),
logger => $state->logger,
engine => $engine,
dpb => $dpb,
dpb => $state->opt('f') ? "fetch" : "normal",
keep_going => 1,
endcode => $endcode
}, $class;

View File

@ -1,5 +1,5 @@
# ex:ts=8 sw=4:
# $OpenBSD: Port.pm,v 1.6 2010/11/01 10:55:26 espie Exp $
# $OpenBSD: Port.pm,v 1.7 2011/04/25 11:58:46 espie Exp $
#
# Copyright (c) 2010 Marc Espie <espie@openbsd.org>
#
@ -388,6 +388,10 @@ sub finished_task
sub finalize
{
my $self = shift;
if ($self->{stuck}) {
open my $fh, ">>", $self->{log};
print $fh $self->{stuck}, "\n";
}
$self->SUPER::finalize(@_);
}
@ -443,7 +447,7 @@ sub watch
sub watched
{
my ($self, $current) = @_;
my ($self, $current, $core) = @_;
return "" unless defined $self->{watched};
$self->watch;
my $progress = '';
@ -458,15 +462,26 @@ sub watched
}
my $diff = $current - $self->{time};
my $unchanged = " unchanged for ";
if ($diff > 7200) {
return "$progress unchanged for ".int($diff/3600)." hours";
$unchanged .= int($diff/3600)." hours";
} elsif ($diff > 300) {
return "$progress unchanged for ".int($diff/60)." minutes";
$unchanged .= int($diff/60)." minutes";
} elsif ($diff > 10) {
return "$progress unchanged for ".int($diff)." seconds";
$unchanged .= int($diff)." seconds";
} else {
return $progress;
$unchanged = "";
}
my $stuck = $core->stuck_timeout;
if (defined $stuck) {
if ($diff / $core->sf > $stuck) {
$self->{stuck} =
"KILLED: $self->{current} stuck at $progress,$unchanged";
kill 9, $core->{pid};
return $self->{stuck};
}
}
return $progress.$unchanged;
}
sub really_watch

View File

@ -1,5 +1,5 @@
# ex:ts=8 sw=4:
# $OpenBSD: PortBuilder.pm,v 1.6 2010/11/02 20:32:59 espie Exp $
# $OpenBSD: PortBuilder.pm,v 1.7 2011/04/25 11:58:46 espie Exp $
#
# Copyright (c) 2010 Marc Espie <espie@openbsd.org>
#
@ -27,18 +27,20 @@ use DPB::Job::Port;
sub new
{
my $class = shift;
my ($opt_c, $opt_s, $opt_u, $opt_U, $opt_R, $fullrepo, $logger, $ports, $make,
$h) = @_;
my $self = bless {clean => $opt_c, size => $opt_s,
rebuild => $opt_R,
fullrepo => $fullrepo,
logger => $logger, ports => $ports, make => $make,
heuristics => $h}, $class;
if ($opt_u || $opt_U) {
my ($class, $state) = @_;
my $self = bless {
clean => $state->opt('c'),
size => $state->opt('s'),
rebuild => $state->opt('R'),
fullrepo => $state->fullrepo,
logger => $state->logger,
ports => $state->ports,
make => $state->make,
heuristics => $state->heuristics}, $class;
if ($state->opt('u') || $state->opt('U')) {
$self->{update} = 1;
}
if ($opt_U) {
if ($state->opt('U')) {
$self->{forceupdate} = 1;
}
$self->init;

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: dpb.1,v 1.10 2010/11/02 11:34:29 espie Exp $
.\" $OpenBSD: dpb.1,v 1.11 2011/04/25 11:58:46 espie Exp $
.\"
.\" Copyright (c) 2010 Marc Espie <espie@openbsd.org>
.\"
@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: November 2 2010 $
.Dd $Mdocdate: April 25 2011 $
.Dt DPB 1
.Os
.Sh NAME
@ -26,14 +26,13 @@
.Op Fl aceqrRsuUx
.Op Fl A Ar arch
.Op Fl b Ar logfile
.Op Fl D Ar PARAM Ns = Ns Ar value
.Op Fl h Ar hosts
.Op Fl j Ar n
.Op Fl L Ar logdir
.Op Fl m Ar threshold
.Op Fl P Ar subdirlist
.Op Fl S Ar sizefile
.Op Fl t Ar ctimeout
.Op Fl T Ar dtimeout
.Op Ar pkgpath ...
.Ek
.Sh DESCRIPTION
@ -60,6 +59,25 @@ Prime the heuristics module with a previous build log, so that packages that
take a long time to build will happen earlier.
.It Fl c
Clean port working directory and log before each build.
.It Fl D Ar PARAM Ns = Ns Ar value
Set defined parameter to value.
Known parameters are as follows:
.Bl -tag -width DISPLAY
.It Ar CONNECTION_TIMEOUT
Connection timeout for ssh.
Defaults to 60 seconds.
.It Ar DISPLAY_TIMEOUT
Display timeout (in seconds) while waiting for jobs to finish, so that the
display is updated even if jobs didn't finish.
Defaults to 10 seconds.
.It Ar STUCK_TIMEOUT
Timeout (in seconds * speed factor) after which tasks that don't show
any progress will be killed.
This can be set on a per-core basis as the
.Sq stuck
property.
Note that this will always be divided by the core's speed factor.
.El
.It Fl e
The listing job is extra and won't be given back to the pool when it's
finished.
@ -93,6 +111,9 @@ The machine (or machines) with the highest speed factor will
get access to all jobs, whereas other machines will be clamped
to stuff which does not take too long.
Requires previous build information to be effective.
.It stuck=s
Stuck timeout (in seconds * sf) after which tasks which show no progress
will get killed.
.It timeout=s
Defines a specific connection timeout for ssh to that host.
.El
@ -135,13 +156,6 @@ Compute workdir sizes before cleaning up, and stash them in log file
.Pa ${LOGDIR}/size.log .
.It Fl S Ar sizefile
Read a size log file and use it for choosing to put WRKDIR in memory.
.It Fl t Ar ctimeout
Connection timeout for ssh.
Defaults to 60 seconds.
.It Fl T Ar dtimeout
Display timeout (in seconds) while waiting for jobs to finish, so that the
display is updated even if jobs didn't finish.
Defaults to 10 seconds.
.It Fl u
Update existing packages during dependency solving.
Can be used to run a bulk-build on a machine with installed packages,