Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package oomstaller for openSUSE:Factory 
checked in at 2024-12-13 22:39:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/oomstaller (Old)
 and      /work/SRC/openSUSE:Factory/.oomstaller.new.29675 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "oomstaller"

Fri Dec 13 22:39:31 2024 rev:2 rq:1230794 version:0.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/oomstaller/oomstaller.changes    2024-11-19 
22:24:45.956075441 +0100
+++ /work/SRC/openSUSE:Factory/.oomstaller.new.29675/oomstaller.changes 
2024-12-13 22:41:47.426701895 +0100
@@ -1,0 +2,9 @@
+Fri Dec 13 12:44:22 UTC 2024 - Andreas Stieger <andreas.stie...@gmx.de>
+
+- update to 0.3.0:
+  * Add --max-parallel, --max-parallel-thrash options
+  * Add --show-stat
+  * Add a manpage
+  * Improve handling of multithreaded processes
+
+-------------------------------------------------------------------

Old:
----
  oomstaller-0.2.0.tar.gz

New:
----
  oomstaller-0.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ oomstaller.spec ++++++
--- /var/tmp/diff_new_pack.JAlnwD/_old  2024-12-13 22:41:47.842719248 +0100
+++ /var/tmp/diff_new_pack.JAlnwD/_new  2024-12-13 22:41:47.842719248 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           oomstaller
-Version:        0.2.0
+Version:        0.3.0
 Release:        0
 Summary:        A tool for suppressing swap thrashing at build time
 License:        BSL-1.0
@@ -40,9 +40,11 @@
 
 %install
 install -D -m0755 %{name} %{buildroot}%{_bindir}/%{name}
+install -D -m0644 %{name}.1 %{buildroot}%{_mandir}/man1/%{name}.1
 
 %files
 %license LICENSE.txt
 %doc README.md
 %{_bindir}/*
+%{_mandir}/man1/%{name}.1%{?ext_man}
 

++++++ oomstaller-0.2.0.tar.gz -> oomstaller-0.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oomstaller-0.2.0/CODE_OF_CONDUCT.md 
new/oomstaller-0.3.0/CODE_OF_CONDUCT.md
--- old/oomstaller-0.2.0/CODE_OF_CONDUCT.md     2024-11-03 10:06:09.000000000 
+0100
+++ new/oomstaller-0.3.0/CODE_OF_CONDUCT.md     2024-12-09 14:15:44.000000000 
+0100
@@ -1,7 +1,7 @@
 
 # Community Guidelines for Sustainability of Open-Source Ecosystem
 
-Version 1.0, 2024-??-??
+Version 1.0, 2024-12-01
 
 Copyright Naoki Shibata 2024. https://github.com/shibatch/nofreelunch
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oomstaller-0.2.0/Makefile 
new/oomstaller-0.3.0/Makefile
--- old/oomstaller-0.2.0/Makefile       2024-11-03 10:06:09.000000000 +0100
+++ new/oomstaller-0.3.0/Makefile       2024-12-09 14:15:44.000000000 +0100
@@ -1,5 +1,5 @@
 oomstaller : oomstaller.cpp
-       c++ -Os -Wall oomstaller.cpp -o oomstaller
+       $(CXX) -Os -Wall oomstaller.cpp -o oomstaller
 
 clean :
        rm -f oomstaller *~ *.s *.o *.out
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oomstaller-0.2.0/README.md 
new/oomstaller-0.3.0/README.md
--- old/oomstaller-0.2.0/README.md      2024-11-03 10:06:09.000000000 +0100
+++ new/oomstaller-0.3.0/README.md      2024-12-09 14:15:44.000000000 +0100
@@ -17,9 +17,9 @@
 good way to know the best number of CPU cores to use beforehand.
 
 This tool monitors the memory usage of each process when performing a
-build, and suspends processes as necessary to prevent swapping from
-occurring. This allows you to build using all CPU cores without
-worrying about swapping occurring.
+build, and suspends processes as necessary to prevent swap thrashing
+from occurring. This allows you to build using all CPU cores without
+worrying about swap thrashing.
 
 
 #### How it works
@@ -51,6 +51,24 @@
 can reduce the total execution time compared to when thrashing is
 occurring.
 
+When selecting which processes to suspend and which to execute,
+priority is given first to the execution of the process occupying the
+largest amount of memory. The process occupying the largest amount of
+memory will not be suspended under any circumstances. The remaining
+running processes are selected based on the time when the process
+started running. The earlier a process is started, the more priority
+it will be given to execution. This avoids the same process being
+suspended and resumed repeatedly, disrupting the build order, or
+suspending processes that are crucial to the build.
+
+The target processes to be suspended are selected by referencing the
+parent-child relationship of the processes. Only processes that are
+descendants of the command invoked as an argument of oomstaller will
+be suspended, thus processes that are not related to the build will
+never be suspended. It does not require information about which
+processes are involved in the build, and can reliably control only the
+processes involved in the build.
+
 
 ### How to build
 
@@ -82,35 +100,68 @@
 
 ### Options
 
-`--thres <percentage>`                     default:  75.0
+`--thres <percentage>`                         default:  75.0
 
 This tool suspends processes so that memory usage by running build
 processes does not exceed the specified percentage of available
 memory.
 
-`--period <seconds>`                       default:   1.0
+`--max-parallel <number of processes>`         default:   0
+
+Suspends processes so that the number of running build processes does
+not exceed the specified number. 0 means no limit. A process is
+counted as one process even if it has multiple threads.
+
+`--max-parallel-thrash <number of processes>`  default:   1
+
+Specifies the maximum number of processes to run when thrashing is
+detected. 0 means no limit.
+
+`--period <seconds>`                           default:   1.0
 
 Specifies the interval at which memory usage of each process is checked
 and processes are controlled.
 
-`--thrash <minimum available memory (MB)>`  default: 256.0
+`--thrash <minimum available memory (MB)>`     default: 256.0
 
 If the amount of available memory falls below the specified value, it is
 assumed that swap thrashing is occurring.
 
+`--show-stat`
+
+Displays statistics when finished.
+
 
 ### Tips
 
 Lowering the value of --thres results in increased time where some
 cores are not used in order to reduce memory usage. Increasing this
-value too much would increase the time where only one process can run
-due to swap thrashing.
+value too much would increase the time where only a few processes can
+run due to swap thrashing.
 
-We recommend specifying "-j `nproc`" option to ninja. ninja usually runs
-jobs with more threads than CPU cores. This is effective to reduce build
-time if there is sufficient memory. However, this will only consume extra
-memory in situations where there is not enough memory in which you might
-want to use this tool.
+When the available memory is less than the value set by --thrash
+option, oomstaller assumes that swap thrashing is occurring and
+suspends all processes except the process occupying the largest amount
+of memory. This has the effect of minimizing swapping.
+
+Although oomstaller is designed primarily to suppress swapping during
+builds by suspending the build process, it can also be configured to
+allow swapping while attempting to reduce the build time. The number
+of processes executed when thrashing is detected can be specified with
+--max-parallel-thrash option. Even when this number of processes is
+increased, the build processes are suspended as necessary depending on
+memory usage. By increasing this number, it may be possible to reduce
+build time by actively swapping processes. However, the actual effect
+depends greatly on the speed of swap space, and in most cases the
+overhead of swapping outweighs the benefit of processing with more
+threads. Furthermore, increasing this number of processes may require
+a large swap space.
+
+We recommend specifying "-j \`nproc\`" option to ninja. ninja usually
+runs jobs with more threads than CPU cores. This is effective to
+reduce build time if there is sufficient memory. However, this will
+only consume extra memory in situations where there is not enough
+memory in which you might want to use this tool.
 
 If you kill this tool with SIGKILL, a large number of build processes
 will remain suspended with SIGSTOP. To prevent this from happening,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oomstaller-0.2.0/oomstaller.1 
new/oomstaller-0.3.0/oomstaller.1
--- old/oomstaller-0.2.0/oomstaller.1   1970-01-01 01:00:00.000000000 +0100
+++ new/oomstaller-0.3.0/oomstaller.1   2024-12-09 14:15:44.000000000 +0100
@@ -0,0 +1,54 @@
+.\" Manpage for oomstaller
+.\" Contact shiba...@users.sourceforge.net to correct errors.
+.TH oomstaller 1 "01 Dec 2024" "0.3.0" "oomstaller man page"
+.SH NAME
+oomstaller \- suppress swap thrashing at build time
+.SH SYNOPSIS
+oomstaller [<options>] command [arg] ...
+.SH DESCRIPTION
+This tool monitors the memory usage of each process when performing a build, 
and suspends processes as necessary to prevent swap thrashing from occuring.
+.sp
+.TP
+To perform a build using this tool, specify make or ninja as the argument of 
this tool and execute as follows.
+.INDENT 4
+.sp
+.EX
+$ oomstaller make -j `nproc`
+.EE
+.UNINDENT
+.SH OPTIONS
+.TP
+.BR \-\-thres " " <percentage, " " default=75>
+This tool suspends processes so that memory usage by running build processes 
does not exceed the specified percentage of available memory.
+.TP
+.BR \-\-max\-parallel " " <number " " of " " processes, " " default=0>
+Suspends processes so that the number of running build processes does not 
exceed the specified number. 0 means no limit. A process is counted as one 
process even if it has multiple threads.
+.TP
+.BR \-\-max\-parallel-thrash " " <number " " of " " processes, " " default=1>
+Specifies the maximum number of processes to run when thrashing is detected. 0 
means no limit.
+.TP
+.BR \-\-period " " <seconds, " " default=1>
+Specifies the interval at which memory usage of each process is checked and 
processes are controlled.
+.TP
+.BR \-\-thrash " " <minimum " "available " "memory " "(MB), " " default=256>
+If the amount of available memory falls below the specified value, it is 
assumed that swap thrashing is occurring.
+.TP
+.BR \-\-show\-stat
+Displays statistics when finished.
+.SH TIPS
+If you kill this tool with SIGKILL, a large number of build processes will 
remain suspended with SIGSTOP. To prevent this from happening, use SIGTERM or 
SIGINT to kill this tool.
+.sp
+.TP
+You can send SIGCONT to all processes run by you with the following command.
+.INDENT 4
+.sp
+.EX
+$ killall -v -s CONT -u $USER -r '.*'
+.EE
+.UNINDENT
+.SH SEE ALSO
+.TP
+.B Home Page
+\fI\%https://github.com/shibatch/oomstaller\fP
+.SH AUTHOR
+Naoki Shibata (shiba...@users.sourceforge.net)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oomstaller-0.2.0/oomstaller.cpp 
new/oomstaller-0.3.0/oomstaller.cpp
--- old/oomstaller-0.2.0/oomstaller.cpp 2024-11-03 10:06:09.000000000 +0100
+++ new/oomstaller-0.3.0/oomstaller.cpp 2024-12-09 14:15:44.000000000 +0100
@@ -1,4 +1,4 @@
-// Written by Naoki Shibata  https://github.com/shibatch
+// Written by Naoki Shibata  https://shibatch.github.io
 
 #include <iostream>
 #include <string>
@@ -9,6 +9,7 @@
 #include <cstdio>
 #include <cstdint>
 #include <cstdlib>
+#include <cstring>
 #include <thread>
 #include <mutex>
 #include <condition_variable>
@@ -23,32 +24,31 @@
 using namespace std;
 
 double memThres = 0.75, period = 1.0, minFreeMem = 256 * 1024;
+int maxParallel = 0, maxParallelThrash = 1;
 
 const long pageSize = sysconf(_SC_PAGESIZE);
 uid_t uid = getuid();
 const pid_t pid = getpid();
 
-unordered_map<string, uint64_t> readMeminfo() {
+bool showStat = false;
+unordered_map<int, long> statInfo;
+
+uint64_t readMemInfo(const string &s) {
   FILE *fp = fopen("/proc/meminfo", "r");
   if (!fp) throw(runtime_error("readMeminfo() : could not open 
/proc/meminfo"));
-  vector<char> line(1024), key(1024);
-  unordered_map<string, uint64_t> map;
+  vector<char> line(1024);
 
   while(!feof(fp)) {
     if (fgets(line.data(), line.size(), fp) == NULL) break;
-    unsigned long long ull;
-    if (sscanf(line.data(), "%1000s %llu", key.data(), &ull) != 2) continue;
-    map[key.data()] = ull;
+    if (strncmp(line.data(), s.c_str(), s.size()) == 0) {
+      unsigned long long ull;
+      if (sscanf(line.data() + s.size(), " %llu", &ull) != 1)
+       throw(runtime_error("readMeminfo() : /proc/meminfo format error"));
+      fclose(fp);
+      return ull;
+    }
   }
-  fclose(fp);
-
-  return map;
-}
-
-uint64_t readMemInfo(const string &s) {
-  auto m = readMeminfo();
-  if (m.count(s) == 0) throw(runtime_error(("readMemInfo() : /proc/meminfo 
does not have entry for " + s).c_str()));
-  return m[s];
+  throw(runtime_error(("readMemInfo() : /proc/meminfo does not have entry for 
" + s).c_str()));
 }
 
 struct ProcInfo {
@@ -56,24 +56,62 @@
   int pid, ppid, pgrp, session;
   long long unsigned starttime;
   long unsigned vsize;
-  long int rss;
+  long int rss, num_threads;
   char state;
 
   ProcInfo() {}
 
-  ProcInfo(const char *s) {
-    vector<char> t(1024);
-    //                 1  2      3  4  5  6  7   8   9   10  11  12  13  14  
15  16  17  18  19  20  21  22   23  24
-    int n = sscanf(s, "%d %1020s %c %d %d %d %*d %*d %*u %*u %*u %*u %*u %*u 
%*u %*d %*d %*d %*d %*d %*d %llu %lu %ld",
-                  &pid, t.data(), &state, &ppid, &pgrp, &session, &starttime, 
&vsize, &rss);
-    if (n != 9) throw(runtime_error("/proc/<pid>/stat format error"));
-    comm = t.data();
-    comm = comm.substr(1, comm.size()-2);
+  ProcInfo(FILE *fpstat, const char *dn) {
+    vector<char> line(1024);
+    if (fgets(line.data(), line.size(), fpstat) == NULL)
+      throw(runtime_error("Could not read /proc/<pid>/stat"));
+
+    string ss = line.data();
+    size_t ps = ss.find_first_of('('), pe = ss.find_last_of(')');
+    if (ps == string::npos || pe == string::npos)
+      throw(runtime_error("Could not read process name in /proc/<pid>/stat"));
+
+    int n = sscanf(line.data(), "%d", &pid);
+    if (n != 1) throw(runtime_error("Could not read pid in /proc/<pid>/stat"));
+
+    //                                 3  4  5  6  7   8   9   10  11  12  13  
14  15  16  17  18  19  20  21  22   23  24
+    n = sscanf(line.data() + pe + 1, " %c %d %d %d %*d %*d %*u %*u %*u %*u %*u 
%*u %*u %*d %*d %*d %*d %ld %*d %llu %lu %ld",
+              &state, &ppid, &pgrp, &session, &num_threads, &starttime, 
&vsize, &rss);
+    if (n != 8) throw(runtime_error("/proc/<pid>/stat format error"));
+
+    comm = ss.substr(ps + 1, pe - ps - 1);
+
+    //
+
+    if (num_threads > 1) {
+      DIR *taskdir = opendir((string("/proc/") + dn + "/task").c_str());
+      if (!taskdir) return;
+
+      struct dirent *entry;
+      while((entry = readdir(taskdir))) {
+       char *p;
+       strtoul(entry->d_name, &p, 10);
+       if (*p != '\0') continue;
+
+       FILE *fp = fopen((string("/proc/") + dn + "/task/" + entry->d_name + 
"/stat").c_str(), "r");
+       if (!fp) continue;
+
+       if (fgets(line.data(), line.size(), fp) != NULL) {
+         pe = string(line.data()).find_last_of(')');
+         char c;
+         if (pe != string::npos && sscanf(line.data() + pe + 1, " %c", &c) == 
1 &&
+             (c == 'R' || c == 'T' || c == 'D')) state = c;
+       }
+
+       fclose(fp);
+      }
+
+      closedir(taskdir);
+    }
   }
 };
 
 unordered_map<int, ProcInfo> getProcesses() {
-  vector<char> line(1024);
   unordered_map<int, ProcInfo> ret;
 
   DIR *procdir = opendir("/proc");
@@ -89,11 +127,9 @@
     FILE *fp = fopen((string("/proc/") + entry->d_name + "/stat").c_str(), 
"r");
     if (!fp) continue;
 
-    if (fstat(fileno(fp), &statbuf) == 0) {
-      if (statbuf.st_uid == uid && fgets(line.data(), line.size(), fp) != 
NULL) {
-       ProcInfo pi(line.data());
-       ret[pi.pid] = pi;
-      }
+    if (fstat(fileno(fp), &statbuf) == 0 && statbuf.st_uid == uid) {
+      ProcInfo pi(fp, entry->d_name);
+      ret[pi.pid] = pi;
     }
 
     fclose(fp);
@@ -135,6 +171,9 @@
 
     {
       auto m = getProcesses();
+
+      if (m.count(pid) == 0) throw(runtime_error("Could not retrieve process 
information of oomstaller"));
+
       for(auto e : m) {
        if ((e.second.state != 'R' && e.second.state != 'T' && e.second.state 
!= 'D') ||
            !isTarget(m, &e.second)) continue;
@@ -146,17 +185,23 @@
       }
     }
 
-    long freeMem = readMemInfo("MemAvailable:"), usableMem = freeMem / 
pageSize * 1024 + usedMem;
+    long freeMem = readMemInfo("MemAvailable:"), usableMem = freeMem * 1024 / 
pageSize + usedMem;
+    bool thrashDetected = freeMem < minFreeMem;
 
-    long m = usedMem;
+    unordered_set<int> activePids, removedPids;
+    long m = usedMem, n = proc.size();
+    int nRunningProcs = 0;
     for(auto e : proc) {
+      activePids.insert(e.pid);
       char nextState = '\0';
       if (e.pid == pidLargestRSS) {
        nextState = 'R';
       } else {
-       if (m >= usableMem * memThres || freeMem < minFreeMem) {
+       if (m >= usableMem * memThres || (maxParallel > 0 && n > maxParallel) ||
+           (thrashDetected && maxParallelThrash > 0 && n > maxParallelThrash)) 
{
          nextState = 'T';
          m -= e.rss;
+         n--;
        } else {
          nextState = 'R';
        }
@@ -165,12 +210,29 @@
       if (nextState == 'R') {
        kill(e.pid, SIGCONT);
        stoppedProcs.erase(e.pid);
+       nRunningProcs++;
       } else {
        stoppedProcs.insert(e.pid);
        kill(e.pid, SIGSTOP);
       }
     }
 
+    // Handling of processes that have terminated or slept on its own after 
sending SIGSTOP
+
+    for(auto i : stoppedProcs) if (activePids.count(i) == 0) 
removedPids.insert(i);
+
+    for(auto i : removedPids) {
+      kill(i, SIGCONT);
+      stoppedProcs.erase(i);
+    }
+
+    // Update statistics info
+
+    if (thrashDetected) statInfo[-1]++;
+    statInfo[nRunningProcs]++;
+
+    //
+
     condvar.wait_for(lock, chrono::milliseconds((long)(period * 1000)));
   }
 }
@@ -185,18 +247,33 @@
   condvar.notify_all();
 }
 
-void handler(int n) {
-  auto m = getProcesses();
-  for(auto e : m) {
-    if (e.second.ppid != pid) continue;
-    kill(-e.second.pgrp, SIGCONT);
-    kill(-e.second.pgrp, n);
+void handlerThread(int n) {
+  try {
+    auto m = getProcesses();
+    for(auto e : m) {
+      if (e.second.ppid != pid) continue;
+      kill(-e.second.pgrp, SIGCONT);
+      kill(-e.second.pgrp, n);
+    }
+  } catch(exception &ex) {
+    cerr << ex.what() << endl;
   }
 
   unique_lock<mutex> lock(mtx);
   for(auto e : stoppedProcs) kill(e, SIGCONT);
 }
 
+shared_ptr<thread> handlerTh;
+
+void handler(int n) {
+  signal(SIGINT , SIG_IGN);
+  signal(SIGTERM, SIG_IGN);
+  signal(SIGQUIT, SIG_IGN);
+  signal(SIGHUP , SIG_IGN);
+
+  handlerTh = make_shared<thread>(handlerThread, n);
+}
+
 void showUsage(const string& argv0, const string& mes = "") {
   if (mes != "") cerr << mes << endl << endl;
   cerr << endl;
@@ -208,28 +285,38 @@
   cerr << endl;
   cerr << "DESCRIPTION" << endl;
   cerr << "     This tool monitors the memory usage of each process when 
performing a" << endl;
-  cerr << "     build, and suspends processes as necessary to prevent swapping 
from" << endl;
-  cerr << "     occurring." << endl;
+  cerr << "     build, and suspends processes as necessary to prevent swap 
thrashing" << endl;
+  cerr << "     from occurring." << endl;
   cerr << endl;
-  cerr << "  --thres <percentage>                     default:  75.0" << endl;
+  cerr << "  --thres <percentage>                            default:  75.0" 
<< endl;
   cerr << endl;
   cerr << "     This tool suspends processes so that memory usage by running 
build" << endl;
   cerr << "     processes does not exceed the specified percentage of 
available memory." << endl;
   cerr << endl;
-  cerr << "  --period <seconds>                       default:   1.0" << endl;
+  cerr << "  --max-parallel <number of processes>            default:   0" << 
endl;
+  cerr << endl;
+  cerr << "     Suspends processes so that the number of running build 
processes" << endl;
+  cerr << "     does not exceed the specified number. 0 means no limit. A 
process is" << endl;
+  cerr << "     counted as one process even if it has multiple threads." << 
endl;
+  cerr << endl;
+  cerr << "  --max-parallel-thrash <number of processes>     default:   1" << 
endl;
+  cerr << endl;
+  cerr << "     Specifies the maximum number of processes to run when 
thrashing is" << endl;
+  cerr << "     detected. 0 means no limit." << endl;
+  cerr << endl;
+  cerr << "  --period <seconds>                              default:   1.0" 
<< endl;
   cerr << endl;
   cerr << "     Specifies the interval at which memory usage of each process 
is checked" << endl;
   cerr << "     and processes are controlled." << endl;
   cerr << endl;
-  cerr << "  --thrash <minimum available memory (MB)>  default: 256.0" << endl;
+  cerr << "  --thrash <minimum available memory (MB)>        default: 256.0" 
<< endl;
   cerr << endl;
   cerr << "     If the amount of available memory falls below the specified 
value, it is" << endl;
   cerr << "     assumed that swap thrashing is occurring." << endl;
   cerr << endl;
-  cerr << "  --uid <uid>  default: the UID of the process" << endl;
+  cerr << "  --show-stat" << endl;
   cerr << endl;
-  cerr << "     When oomstaller is executed inside a fakeroot environment, the 
real UID" << endl;
-  cerr << "     has to be provided to work correctly." << endl;
+  cerr << "     Displays statistics when finished." << endl;
   cerr << endl;
   cerr << "TIPS" << endl;
   cerr << "     If you kill this tool with SIGKILL, a large number of build 
processes" << endl;
@@ -244,7 +331,7 @@
   cerr << endl;
   cerr << "     See https://github.com/shibatch/oomstaller"; << endl;
   cerr << endl;
-  cerr << "oomstaller 0.2.0" << endl;
+  cerr << "oomstaller 0.3.0" << endl;
   cerr << endl;
 
   exit(-1);
@@ -259,26 +346,47 @@
       if (nextArg+1 >= argc) showUsage(argv[0]);
       char *p;
       memThres = strtod(argv[nextArg+1], &p) * 0.01;
-      if (p == argv[nextArg+1]) showUsage(argv[0], "A real value is expected 
after --thres.");
+      if (p == argv[nextArg+1] || *p || memThres < 0)
+       showUsage(argv[0], "A non-negative real value is expected after 
--thres.");
+      nextArg++;
+    } else if (string(argv[nextArg]) == "--max-parallel") {
+      if (nextArg+1 >= argc) showUsage(argv[0]);
+      char *p;
+      maxParallel = strtol(argv[nextArg+1], &p, 0);
+      if (p == argv[nextArg+1] || *p || maxParallel < 0)
+       showUsage(argv[0], "A non-negative integer is expected after 
--max-parallel.");
+      nextArg++;
+    } else if (string(argv[nextArg]) == "--max-parallel-thrash") {
+      if (nextArg+1 >= argc) showUsage(argv[0]);
+      char *p;
+      maxParallelThrash = strtol(argv[nextArg+1], &p, 0);
+      if (p == argv[nextArg+1] || *p || maxParallelThrash < 0)
+       showUsage(argv[0], "A non-negative integer is expected after 
--max-parallel-thrash.");
       nextArg++;
     } else if (string(argv[nextArg]) == "--period") {
       if (nextArg+1 >= argc) showUsage(argv[0]);
       char *p;
       period = strtod(argv[nextArg+1], &p);
-      if (p == argv[nextArg+1]) showUsage(argv[0], "A real value is expected 
after --period.");
+      if (p == argv[nextArg+1] || *p || period <= 0)
+       showUsage(argv[0], "A positive value is expected after --period.");
       nextArg++;
     } else if (string(argv[nextArg]) == "--thrash") {
       if (nextArg+1 >= argc) showUsage(argv[0]);
       char *p;
       minFreeMem = strtod(argv[nextArg+1], &p) * 1024;
-      if (p == argv[nextArg+1]) showUsage(argv[0], "A real value is expected 
after --thrash.");
+      if (p == argv[nextArg+1] || *p || minFreeMem < 0)
+       showUsage(argv[0], "A non-negative real value is expected after 
--thrash.");
       nextArg++;
     } else if (string(argv[nextArg]) == "--uid") {
       if (nextArg+1 >= argc) showUsage(argv[0]);
       char *p;
-      uid = strtol(argv[nextArg+1], &p, 0);
-      if (p == argv[nextArg+1]) showUsage(argv[0], "An integer is expected 
after --uid.");
+      long l = strtol(argv[nextArg+1], &p, 0);
+      uid = l;
+      if (p == argv[nextArg+1] || *p || l < 0)
+       showUsage(argv[0], "A non-negative integer is expected after --uid.");
       nextArg++;
+    } else if (string(argv[nextArg]) == "--show-stat") {
+      showStat = true;
     } else if (string(argv[nextArg]).substr(0, 2) == "--") {
       showUsage(argv[0], string("Unrecognized option : ") + argv[nextArg]);
     } else {
@@ -288,6 +396,13 @@
 
   if (nextArg >= argc) showUsage(argv[0], "");
 
+  if (getProcesses().count(pid) == 0) {
+    cerr << argv[0] << " : Could not retrieve process information of 
oomstaller" << endl;
+    exit(-1);
+  }
+
+  auto startTime = chrono::system_clock::now();
+
   signal(SIGINT , handler);
   signal(SIGTERM, handler);
   signal(SIGQUIT, handler);
@@ -312,13 +427,42 @@
     loop(childTh);
   } catch(exception &ex) {
     cerr << argv[0] << " : " << ex.what() << endl;
-    kill(pid, SIGTERM);
+    kill(0, SIGTERM);
   }
 
   childTh->join();
+  if (handlerTh) handlerTh->join();
 
   unique_lock<mutex> lock(mtx);
   for(auto e : stoppedProcs) kill(e, SIGCONT);
 
-  exit(childExitCode);
+  auto endTime = chrono::system_clock::now();
+
+  if (showStat) {
+    if (statInfo[0] > 0) statInfo[0]--;
+    if (statInfo[0] == 0) statInfo.erase(0);
+
+    time_t tStartTime = chrono::system_clock::to_time_t(startTime);
+    time_t tEndTime = chrono::system_clock::to_time_t(endTime);
+
+    cout << endl;
+    cout << "Start   : " << ctime(&tStartTime);
+    cout << "End     : " << ctime(&tEndTime);
+
+    chrono::duration<double> elapsed = endTime - startTime;
+    cout << "Elapsed : " << elapsed.count() << " seconds" << endl;
+
+    set<int> statKeys;
+    for(auto a : statInfo) statKeys.insert(a.first);
+    long pt = 0;
+    for(auto a : statKeys) {
+      if (a == -1) continue;
+      pt += statInfo[a];
+      cout << a << " processes running : " << statInfo[a] << " periods" << 
endl;
+    }
+    cout << "Thrashing detected : " << statInfo[-1] << " periods" << endl;
+    cout << "Total : " << pt << " periods" << endl;
+  }
+
+  return childExitCode;
 }

Reply via email to