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; }