#!/usr/bin/perl $swaplimit=shift; if($swaplimit==0) { $swaplimit=200000; } $midfactor=64; $hifactor=$midfactor*2; $lowfactor=$hifactor*2; $MINRUN=15; $maxrun=$MAXRUN=20; $mintime=2; $minnice=0; #$MINRUN=8; #$maxrun=$MAXRUN=12; $fast=5; $fast=0; $slow=15; $extraslow=30; $pausecont=$extraslow; if($fast) { $pause=$fast; } else { $pause=$slow; } $notstop_regex="|^xz"; $iomin=0.02; $iomax=0.20; $MINIDLE=0.005; $MAXIDLE=0.1; $continue=$pause; $lastwait=-1; $lastsum=-1; $iowait=0; sub sigquit { my ($sig)=@_; print STDERR "Caught a SIG$sig. Save stopped pids and quit.\n"; open(D,">pids.stop"); foreach $k (keys %stop) { if($stop{$k}>0) { print D "$k=$stop{$k}\n"; } } close(D); exit(0); } $SIG{TERM}=\&sigquit; $SIG{QUIT}=\&sigquit; $SIG{INT}=\&sigquit; $SIG{HUP}=\&sigquit; $SIG{TSTP}=\&sigquit; sub loadpids { if(open(D,"pids.stop")) { while($l=) { ($pid,$time)=split("=",$l); $time+=0; $stop{$pid}=$time; } close(D); } } sub getcc1plus { # cpu open(CPU,"/proc/stat"); (undef,$cpu_user,$cpu_nice,$cpu_sytem,$cpu_idle,$cpu_iowait)=split(' ',); close(CPU); $sum=$cpu_user+$cpu_nice+$cpu_sytem+$cpu_idle+$cpu_iowait; if($lastsum<0) { $lastsum=$sum; $lastwait=$cpu_iowait; $lastidle=$cpu_idle; } elsif(($sum-$lastsum)>15000) { $iowait=($cpu_iowait-$lastwait)/($sum-$lastsum); $idle=($cpu_idle-$lastidle)/($sum-$lastsum); $lastsum=$sum; $lastwait=$cpu_iowait; $lastidle=$cpu_idle; } # USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND # root 12722 0.8 3.8 56452 9644 pts/1 T+ 14:07 0:04 /usr/libexec/gcc/armv7hl-slackware-linux-gnueabi/5.3.0/cc1plus -quiet -I # root@opzero:~# ps -eo pid,ppid,stat,pcpu,vsz,rss,size,time,comm | head -2 # PID PPID STAT %CPU VSZ RSS SIZE TIME COMMAND # 1 0 Ss 0.2 1848 1296 296 00:00:05 init #my @ps=grep(genautomata|genattrtab|/cc1|smokegen|\/as|\/ld/,split("\n",`ps aux`)); my @ps=grep(/ xz| cc1| gnat1| go1| ecj| ecj1| jc1| java| gij| rust| clang| f951| lto1| makeinfo| genautomata| genattrtab| smokegen| ld/,split("\n",`ps -eo pid,ppid,stat,pcpu,vsz,rss,size,times,nice,comm`)); my @ret=(); $running=0; $sleeping=0; $stopped=0; $genautomata=0; $maxres=-1; $maxall=-1; $resdiff=-1; $candidaterun=-1; $candidateall=-1; $swapstop=0; $totres=0; foreach($i=0;$i<=$#ps;$i++) { #($pid,$ppid,$stat,$cpu,$vsz,$res,$size,$time,$nice,@cmd)=split(' ',$ps[$i]); #($hour,$min,$sec)=split(":",$time); #$time=$hour*3600+$min*60+$sec; #if($ps[$i]=~m/\.SlackBuild|ldconfig/ || ($time<$mintime && !($stat=~m/^T/))) if($ps[$i]=~m/\.SlackBuild|ldconfig/) { # Remove any SlackBuild script or defunct process splice(@ps,$i,1); $i--; } } %stopped=(); foreach(@ps) { ($pid,$ppid,$stat,$cpu,$vsz,$res,$size,$time,$nice,@cmd)=split; $cmd=join(" ",@cmd); #($hour,$min,$sec)=split(":",$time); #$time=$hour*3600+$min*60+$sec; #if($cmd=~m/genautomata/) { $genautomata=$pid; } # Avoid detecting genautomata on powerful host if($stat=~m/^Z/) { # Zombie/defunct, do not count next; } elsif($stat=~m/^T/) { $stopped{$pid}=1; if($stop{$pid}==0) { $stop{$pid}=time(); } $stopped++; $curres=$vsz-$res; if($maxres<0 || $res>$maxres) { if($resdiff>0) { $swapstop+=$resdiff; } $maxres=$res; $resdiff=$curres; $candidaterun=$pid; $candidatevsz=$vsz; } else { $swapstop+=$curres; } } else { if($time>0) { $running++; } else { $sleeping++; } $totres+=$res; } if($maxall<0 || $res>$maxall) { $maxall=$res; $candidateall=$pid; } push(@ret,"$res * $pid $stat ($vsz $time $nice) $cmd"); } $lastrun=$lastrun*0.9+$running*0.1; foreach(keys %stop) { if(!$stopped{$_}) { delete($stop{$_}); } } return(sort { $b <=> $a } @ret); } loadpids(); @ps=getcc1plus(); for($i=0;$i<=$#ps;$i++) { print $ps[$i],"\n"; } $firstprint=1; while(1) { open(F,"/proc/meminfo"); $total=0; $avail=0; #MemTotal: 248572 kB #MemAvailable: 104560 kB while() { if(m/MemAvailable/) { (undef,$avail)=split; } elsif(m/MemTotal/) { (undef,$total)=split; } } close(F); $maxswap=$total/2; if($maxswap>$swaplimit) { $maxswap=$swaplimit; } $swapuse=0; #Filename Type Size Used Priority #/dev/mmcblk0p3 partition 1678788 205568 -1 do("./conf.dat"); open(F,"/proc/swaps"); ; while() { (undef,undef,undef,$used)=split; $swapuse+=$used; } close(F); if($continue>0) { @ps=getcc1plus(); $continue--; } elsif($recheck<=0 || 1) { @ps=getcc1plus(); $recheck=30; } $recheck--; $swapnostop=$swapuse-$swapstop; if($firstprint) { print scalar(localtime)," - FTOT=$total, AVAIL=$avail, SWAP=$swapuse ($swapnostop), IO=".sprintf("%.1f",$iowait*100).", ID=".sprintf("%.1f",$idle*100)." (run=$running, stop=$stopped, slp=$sleeping) MAXRES=$maxres ($resdiff), CANDIDATE=$candidaterun\n"; $firstprint=0; } elsif($avail>($total/$hifactor)) { $juststopped--; } if($running==1 && $stopped>0 && $avail<($total/$lowfactor)) { $continue=$extraslow; $norun2=$extraslow; } if($running>0 && (($totres)/$running)>($avail-($total/$hifactor))) { $continue=$extraslow; } if($norun2>0) { $norun2--; } $dbg++; if($dbg>=10 && ($continue>0 || $norun2>0 || $juststopped>0 || $iowait>$iomax || ($iowait>=$iomin && $stopped>0) || ($lastrun>=$MAXRUN) || ($running<$MINRUN && $stopped>0) || 1 )) { print scalar(localtime)," - AVAIL=$avail, TOTRES=$totres, IO=".sprintf("%.1f",$iowait*100).", ID=".sprintf("%.1f",$idle*100)." (run=$running, stop=$stopped, slp=$sleeping, cont=$continue, nor2=$norun2, js=$juststopped, ls=".sprintf("%.2f",$lastrun).")\n"; $dbg=0; } if( $running>1 && ( ($norun2>0 && 0) || ( $stopped<($running-1) && ((($swapuse/2)>($avail)) || ($avail<($total/$lowfactor))) ) || ($genautomata && 0) || ($avail<($total/$hifactor)) || ($avail<($total/$midfactor) && $juststopped>0 ) || ($iowait>$iomax && $avail<($total/2)) ) ) { print scalar(localtime)," - STOT=$total, AVAIL=$avail, SWAP=$swapuse, IO=".sprintf("%.1f",$iowait*100).", ID=".sprintf("%.1f",$idle*100)." (run=$running, stop=$stopped, slp=$sleeping, js=$juststopped)\n"; @ps=getcc1plus(); if($run<$stopped) { #if($avail<($total/$hifactor)) { $juststopped=$extraslow; } #else { $juststopped+=5; } } else { $juststopped=0; } $firstprint=1; $first=0; if($norun2<=10) { $continue+=10; } elsif($continue<$pause) { $continue=$pause; } for($i=$#ps;$i>=0;$i--) { ($res,undef,$pid,$stat,$vsz,$time,$nice,@cmd)=split(" ",$ps[$i]); $cmd=$cmd[0]; if(!($stat=~m/^T/) && ($time>$mintime) && ($nice>$minnice)) { if($cmd=~m/cc1plus|clang|rust/) { system("kill -STOP $pid"); $stop{$pid}=time(); $first++; print "A# Stopping $ps[$i]...\n"; sleep(10); last; } else { #print "## Leave continuing $ps[$i]...\n"; } } else { #print "## Already stopped $ps[$i].\n"; } } if(!$first) { for($i=$#ps;$i>=0;$i--) { ($res,undef,$pid,$stat,$vsz,$time,$nice,@cmd)=split(" ",$ps[$i]); $cmd=$cmd[0]; if(!($stat=~m/^T/) && ($time>0)) { if(($genautomata && $genautomata!=$pid) || (!$genautomata && $first>=0)) { system("kill -STOP $pid"); $stop{$pid}=time(); $first++; print "B# Stopping $ps[$i]...\n"; sleep(10); last; } else { print "## Leave continuing $ps[$i]...\n"; } } else { #print "## Already stopped $ps[$i].\n"; } } } if($first>0) { #sleep(20); } @ps=getcc1plus(); } elsif(((($running<$MINRUN) && ($lastrun<$MINRUN)) || ($idle>=$MAXIDLE && $iowait<$iomax)) && ($continue==0 && $norun2<=0 && $stopped && ($iowait<$iomin) && (!$genautomata || $running==0) && ( ($swapuse<($avail) && ($avail>($total/$midfactor) || ($avail>($total/$hifactor) && $juststopped<=0) ) ) || ($avail>($total/2)) || ($running==0) )) ) { # Check if the new process will fit in memory after full run #if($running==0 || (($total-$avail)/$running)<($avail-$total/$hifactor)) if(($running==0 || (($totres)/$running)<($avail-$total/$hifactor))) { print scalar(localtime)," - CTOT=$total, AVAIL=$avail, SWAP=$swapuse, IO=".sprintf("%.1f",$iowait*100).", ID=".sprintf("%.1f",$idle*100)." (run=$running, stop=$stopped, slp=$sleeping, lastrun=$lastrun)\n"; @ps=getcc1plus(); $firstprint=1; if((($running<$MINRUN) && ($lastrun<$MINRUN))|| ($idle>=$MAXIDLE && $iowait<$iomax)) { # Changing priority based on stopped time $ci=-1; $now=time(); $maxval=2000000000; $co=-1; $copid=undef; $oldest=0; for($i=0;$i<=$#ps;$i++) { ($res,undef,$pid,$stat,$vsz,$time,$nice)=split(" ",$ps[$i]); if($stat=~m/^T/ && (!$genautomata || $genautomata==$pid)) { if($stop{$pid}==0) { $stop{$pid}=time(); } $val=$i+$nice*2-($now-$stop{$pid})/1000; if($val<$maxval) { $ci=$i; $ni=$nice; $maxval=$val; } if(($now-$stop{$pid})>$oldest) { $co=$i; $copid=$pid; $no=$nice; $oldest=($now-$stop{$pid}); if($oldest>$maxold && $oldest<1000000000) { $maxold=$oldest; } } } } if($ci>=0) { $i=$ci; ($res,undef,$pid,$stat,$vsz,$time,$nice)=split(" ",$ps[$i]); system("kill -CONT $pid"); $val=$now-$stop{$pid}; print "## Continuing $ps[$i] ($pid=>$maxval [$i, $val/$ni], oldest=$copid [$co, $oldest/$no] max=$maxold)...\n"; delete($stop{$pid}); $continue=$pausecont; @ps=getcc1plus(); } } else { for($i=0;$i<=$#ps;$i++) { ($res,undef,$pid,$stat,$vsz,$time,$nice)=split(" ",$ps[$i]); if($stat=~m/^T/ && (!$genautomata || $genautomata==$pid)) { system("kill -CONT $pid"); print "## Continuing $ps[$i]...\n"; delete($stop{$pid}); $continue=$pausecont; @ps=getcc1plus(); last; } } } } } elsif(((($running<$MINRUN) && ($lastrun<$MINRUN))|| ($idle>=$MAXIDLE && $iowait<$iomax)) && ($continue==0 && $norun2<=0 && $stopped>0 && ($iowait<$iomin) && (!$genautomata || $running==0) && $candidaterun>0 && ($resdiff*1.5)<($avail-$swapuse/2) && $candidatevsz<$avail && $avail>($total/$hifactor) && $swapnostop<$avail && ($avail>($total/$midfactor) || $juststopped<=0)) ) { if(0) { # First of all, stop running processes for($i=0;$i<=$#ps;$i++) { ($res,undef,$pid,$stat,$vsz,$time,$nice)=split(" ",$ps[$i]); if(!($stat=~m/^T/)) { system("kill -STOP $pid"); $stop{$pid}=time(); print "C# Stopping $ps[$i] to allow more res running...\n"; } } } # The run the candidate for($i=0;$i<=$#ps;$i++) { ($res,undef,$pid,$stat,$vsz,$time,$nice)=split(" ",$ps[$i]); if($pid==$candidaterun) { system("kill -CONT $pid"); print "## Fit in memory, continuing $ps[$i]...\n"; break; } } $continue=$pausecont; sleep(1); @ps=getcc1plus(); } elsif($stopped>0 && (($running<$MINRUN && $lastrun<$MINRUN)|| ($idle>=$MAXIDLE && $iowait<$iomax))) { if($running==0) { if($pauserun<=0) { $running=1; } else { $pauserun--; } } if($running>0 && ($running<$MINRUN || ($idle>=$MAXIDLE && $iowait<$iomax))) { # First of all, stop running processes if(0) { for($i=0;$i<=$#ps;$i++) { ($res,undef,$pid,$stat,$vsz,$time,$nice)=split(" ",$ps[$i]); if(!($stat=~m/^T/) && $pid!=$candidateall) { system("kill -STOP $pid"); $stop{$pid}=time(); print "D# ".scalar(localtime)." - Stopping $ps[$i] to allow best candidate to run...\n"; } } } # The run the candidate for($i=0;$i<=$#ps;$i++) { ($res,undef,$pid,$stat,$vsz,$time,$nice)=split(" ",$ps[$i]); if(($stat=~m/^T/) && $pid==$candidateall) { system("kill -CONT $pid"); print "## ".scalar(localtime)." - Continuing the best candidate $ps[$i] ($continue)...\n"; delete($stop{$pid}); if($norun2<=0 && $continue<=10) { $continue+=10; } $norun2+=10; break; } } #$continue=$pause; $pauserun=5; } } elsif($lastrun>$maxrun && $running>$MINRUN && $idle<$MINIDLE) { print scalar(localtime)," - STOT=$total, AVAIL=$avail, SWAP=$swapuse, IO=".sprintf("%.1f",$iowait*100).", ID=".sprintf("%.1f",$idle*100)." (run=$running, stop=$stopped, slp=$sleeping, lastrun=$lastrun)\n"; $maxrun=$MINRUN; for($i=$#ps;$i>=0;$i--) { ($res,undef,$pid,$stat,$vsz,$time,$nice,@cmd)=split(" ",$ps[$i]); $cmd=join(" ",@cmd); if(!($stat=~m/^T/) && ($time>$mintime) && ($nice>$minnice)) { #if(1 || $cmd=~m/cc1plus|clang|rust/) if(! ($cmd=~m/$notstop_regex/)) { system("kill -STOP $pid"); $stop{$pid}=time(); $first++; print "R# Stopping $ps[$i]...\n"; sleep(10); last; } else { #print "## Leave continuing $ps[$i]...\n"; } } else { #print "## Already stopped $ps[$i].\n"; } } } else { $maxrun=$MAXRUN; } sleep(1); }