From 8e2efc18c9d39ef5167a2b7b240b066baa703721 Mon Sep 17 00:00:00 2001 From: K9spud LLC Date: Mon, 8 Feb 2021 11:39:24 -0700 Subject: [PATCH] Fixed some bugs in allowOne. Added command line options. Tweaked algorithm to start throttling earlier due to what I'm seeing during a lengthy Chromium build. --- README.md | 19 +++++++++++ main.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++--- womper.cpp | 81 +++++++++++++++++++++++++++++++++++++++++------ womper.h | 6 ++-- 4 files changed, 181 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ba5ed62..4c70375 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/main.cpp b/main.cpp index 942163c..f847580 100644 --- a/main.cpp +++ b/main.cpp @@ -17,6 +17,8 @@ #include "womper.h" #include +#include + #include #include #include @@ -26,16 +28,96 @@ 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) { @@ -43,12 +125,12 @@ int main(void) 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; diff --git a/womper.cpp b/womper.cpp index 7a27044..ecac56e 100644 --- a/womper.cpp +++ b/womper.cpp @@ -20,11 +20,11 @@ #include #include -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) { @@ -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); @@ -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) { @@ -112,7 +112,7 @@ void Womper::allowOne() } } - foreach(pid_t pid, running) + foreach(pid, swamped) { if(foundFirst || pid == ninja) { @@ -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() @@ -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; +} diff --git a/womper.h b/womper.h index 2aa334d..fb76999 100644 --- a/womper.h +++ b/womper.h @@ -11,24 +11,26 @@ class QProcess; class Womper { public: - Womper(); + Womper(QString watchUser); void scan(); void allowOne(); void allowTwo(); void allowAll(); + bool suspendToOne(); QVector running; QVector swamped; QVector stopped; QHash memory; + pid_t ninja; + private: QProcess* process; QStringList args; QStringList watches; - pid_t ninja; }; #endif // WOMPER_H