Skip to content

Commit

Permalink
Fixed some bugs in allowOne. Added command line options. Tweaked algo…
Browse files Browse the repository at this point in the history
…rithm to start throttling earlier due to what I'm seeing during a lengthy Chromium build.
  • Loading branch information
K9spud LLC committed Feb 8, 2021
1 parent b181546 commit 8e2efc1
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 17 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,23 @@ How to Use
==========
Run this program as root, or at least as the "portage" user, so that it will have permission to send stop and continue "kill" signals to the cc1plus processes spawned by your emerge build. You can freely start up, terminate, or re-start this program at any time and it will pick right up suspending and resuming the on-going portage build processes for you.

If your builds aren't running as the "portage" user, you may need to use
the "-u" command line option to specify which processes to watch/manage.

Sometimes it can be handy to forcibly limit the number of concurrent processes,
or to even suspend the build entirely if you're trying to use your computer
for a bit and the builds are just taking up too much RAM.

"-1" will limit running builds to one process at a time.
"-2" will limit builds to two or three processes at a time.
"-s" will limit existing builds to one process at a time, until they've all
finished, then DrainingTheSwamp will exit, with the build suspended. Probably
only works on ninja builds right now.

Limitations
===========
Unfortunately, this program does not work against "rustc" builds, as rust seems to do some funny business with CPU usage reporting and the way it manages to carry out builds. But it should work great for programs compiled with gcc or clang.

Sometimes the build may still get stuffed up with processes lodged in swap
after a long run, after some truly huge build processes, etc. Still
tweaking the algorithms and trying to come up with ways to solve this.
92 changes: 87 additions & 5 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "womper.h"

#include <stdio.h>
#include <string.h>

#include <unistd.h>
#include <signal.h>
#include <QProcess>
Expand All @@ -26,29 +28,109 @@
QProcess exec;
QStringList args;

int main(void)
int main(int argc, char* argv[])
{
double totalRam, availableRam, percentFree;
useconds_t sleepTime;
char bounce[] = ".oO0Oo. ";
QString watchUser = "portage";
int i = 0;

printf("Draining the Swamp v1.0.0\n");
printf("Draining the Swamp v1.0.1\n");

bool stayAtOne = false;
bool stayAtTwo = false;
bool suspendBuild = false;

for(int i = 1; i < argc; i++)
{
if(strcmp(argv[i], "-1") == 0)
{
stayAtOne = true;
printf("staying at one process allowed to run.\n");
}
else if(strcmp(argv[i], "-2") == 0)
{
stayAtTwo = true;
printf("staying at two processes allowed to run.\n");
}
else if(strcmp(argv[i], "-s") == 0)
{
suspendBuild = true;
printf("Letting only existing compilations complete, one at a time (suspend the build).\n");
}
else if(strcmp(argv[i], "-u") == 0)
{
i++;
if(i >= argc)
{
printf("must provide user name for '-u userid'\n");
return 1;
}
watchUser = QString::fromLatin1(argv[i]);
}
else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
{
printf(R"EOF(
Command line options:
-1 Stay at one running process max.
-2 Stay at two/three running processes max.
-u "user id"
Select which user's processes should be monitored/managed.
The default is "portage" when this option is not used.
-s Let existing builds finish, one at a time (suspend the build).
)EOF");
return 0;
}
}

if(suspendBuild)
{
Womper womper(watchUser);
womper.scan();
while(1)
{
meminfo();
totalRam = kb_main_total;
availableRam = kb_main_available;
percentFree = (availableRam / totalRam) * 100.0f;
if(womper.suspendToOne())
{
printf("\nAll compilation processes have been cleared and the build is suspended.\n");
return 0;
}
sleepTime = 500000;
printf("%c Free memory: %.1f%% (%d stopped, %d running, %d swamped) \r", bounce[i], percentFree, womper.stopped.count(), womper.running.count(), womper.swamped.count());
i++;
if(i > 7)
{
i = 0;
}

fflush(stdout);
usleep(sleepTime);

womper.scan();
}
}

Womper womper;
Womper womper(watchUser);
womper.scan();
while(1)
{
meminfo();
totalRam = kb_main_total;
availableRam = kb_main_available;
percentFree = (availableRam / totalRam) * 100.0f;
if(percentFree < 10.0f)
if(percentFree < 13.0f || stayAtOne)
{
womper.allowOne();
sleepTime = 400000;
}
else if(percentFree < 20.0f)
else if(percentFree < 25.0f || stayAtTwo)
{
womper.allowTwo();
sleepTime = 500000;
Expand Down
81 changes: 71 additions & 10 deletions womper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
#include <QProcess>
#include <QString>

Womper::Womper()
Womper::Womper(QString watchUser)
{
process = new QProcess();
ninja = -1;
args << "-u" << "portage" << "-o" << "pid,state,rss,%cpu,comm" << "--sort" << "-rss" << "--no-headers" << "-w" << "-w";
args << "-u" << watchUser << "-o" << "pid,state,rss,%cpu,comm" << "--sort" << "-rss" << "--no-headers" << "-w" << "-w";
printf("ps");
foreach(QString s, args)
{
Expand Down Expand Up @@ -65,15 +65,15 @@ void Womper::scan()
continue;
}

pid = columns.at(0).toInt();
status = columns.at(1);
rss = columns.at(2);
memory[pid] = rss.toLong();
if(cmd == "ninja")
{
ninja = pid;
}

pid = columns.at(0).toInt();
status = columns.at(1);
rss = columns.at(2);
memory[pid] = rss.toLong();
if(status.startsWith("R") || status.startsWith("S"))
{
running.append(pid);
Expand All @@ -93,14 +93,14 @@ void Womper::allowOne()
{
bool foundFirst = false;
pid_t pid;

/*
if(swamped.count() + running.count() == 1)
{
// already allowing only one process to run
return;
}

foreach(pid, swamped)
*/
foreach(pid_t pid, running)
{
if(foundFirst || pid == ninja)
{
Expand All @@ -112,7 +112,7 @@ void Womper::allowOne()
}
}

foreach(pid_t pid, running)
foreach(pid, swamped)
{
if(foundFirst || pid == ninja)
{
Expand All @@ -123,6 +123,21 @@ void Womper::allowOne()
foundFirst = true;
}
}

if(foundFirst == false)
{
if(stopped.contains(ninja))
{
kill(ninja, SIGCONT);
}
else
{
if(stopped.count())
{
kill(stopped.first(), SIGCONT);
}
}
}
}

void Womper::allowTwo()
Expand Down Expand Up @@ -293,3 +308,49 @@ void Womper::allowAll()
kill(pid, SIGCONT);
}
}


bool Womper::suspendToOne()
{
bool foundFirst = false;
pid_t pid;

foreach(pid_t pid, running)
{
if(foundFirst || pid == ninja)
{
kill(pid, SIGSTOP);
}
else
{
foundFirst = true;
}
}

foreach(pid, swamped)
{
if(foundFirst || pid == ninja)
{
kill(pid, SIGSTOP);
}
else
{
foundFirst = true;
}
}

if(foundFirst == false)
{
foreach(pid_t pid, stopped)
{
if(pid != ninja)
{
kill(pid, SIGCONT);
foundFirst = true;
break;
}
}
}

return !foundFirst;
}
6 changes: 4 additions & 2 deletions womper.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,26 @@ class QProcess;
class Womper
{
public:
Womper();
Womper(QString watchUser);

void scan();
void allowOne();
void allowTwo();
void allowAll();
bool suspendToOne();

QVector<pid_t> running;
QVector<pid_t> swamped;
QVector<pid_t> stopped;
QHash<pid_t, long> memory;

pid_t ninja;

private:
QProcess* process;
QStringList args;
QStringList watches;

pid_t ninja;
};

#endif // WOMPER_H

0 comments on commit 8e2efc1

Please sign in to comment.