Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(379)

Unified Diff: third_party/logilab/logilab/common/proc.py

Issue 1920403002: [content/test/gpu] Run pylint check of gpu tests in unittest instead of PRESUBMIT (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update path to LICENSE.txt of logilab/README.chromium Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/logilab/logilab/common/optparser.py ('k') | third_party/logilab/logilab/common/pyro_ext.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/logilab/logilab/common/proc.py
diff --git a/third_party/logilab/logilab/common/proc.py b/third_party/logilab/logilab/common/proc.py
new file mode 100644
index 0000000000000000000000000000000000000000..c27356c6b88b286775998ab414de95541f7eb5f5
--- /dev/null
+++ b/third_party/logilab/logilab/common/proc.py
@@ -0,0 +1,277 @@
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of logilab-common.
+#
+# logilab-common is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option) any
+# later version.
+#
+# logilab-common is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with logilab-common. If not, see <http://www.gnu.org/licenses/>.
+"""module providing:
+* process information (linux specific: rely on /proc)
+* a class for resource control (memory / time / cpu time)
+
+This module doesn't work on windows platforms (only tested on linux)
+
+:organization: Logilab
+
+
+
+"""
+__docformat__ = "restructuredtext en"
+
+import os
+import stat
+from resource import getrlimit, setrlimit, RLIMIT_CPU, RLIMIT_AS
+from signal import signal, SIGXCPU, SIGKILL, SIGUSR2, SIGUSR1
+from threading import Timer, currentThread, Thread, Event
+from time import time
+
+from logilab.common.tree import Node
+
+class NoSuchProcess(Exception): pass
+
+def proc_exists(pid):
+ """check the a pid is registered in /proc
+ raise NoSuchProcess exception if not
+ """
+ if not os.path.exists('/proc/%s' % pid):
+ raise NoSuchProcess()
+
+PPID = 3
+UTIME = 13
+STIME = 14
+CUTIME = 15
+CSTIME = 16
+VSIZE = 22
+
+class ProcInfo(Node):
+ """provide access to process information found in /proc"""
+
+ def __init__(self, pid):
+ self.pid = int(pid)
+ Node.__init__(self, self.pid)
+ proc_exists(self.pid)
+ self.file = '/proc/%s/stat' % self.pid
+ self.ppid = int(self.status()[PPID])
+
+ def memory_usage(self):
+ """return the memory usage of the process in Ko"""
+ try :
+ return int(self.status()[VSIZE])
+ except IOError:
+ return 0
+
+ def lineage_memory_usage(self):
+ return self.memory_usage() + sum([child.lineage_memory_usage()
+ for child in self.children])
+
+ def time(self, children=0):
+ """return the number of jiffies that this process has been scheduled
+ in user and kernel mode"""
+ status = self.status()
+ time = int(status[UTIME]) + int(status[STIME])
+ if children:
+ time += int(status[CUTIME]) + int(status[CSTIME])
+ return time
+
+ def status(self):
+ """return the list of fields found in /proc/<pid>/stat"""
+ return open(self.file).read().split()
+
+ def name(self):
+ """return the process name found in /proc/<pid>/stat
+ """
+ return self.status()[1].strip('()')
+
+ def age(self):
+ """return the age of the process
+ """
+ return os.stat(self.file)[stat.ST_MTIME]
+
+class ProcInfoLoader:
+ """manage process information"""
+
+ def __init__(self):
+ self._loaded = {}
+
+ def list_pids(self):
+ """return a list of existent process ids"""
+ for subdir in os.listdir('/proc'):
+ if subdir.isdigit():
+ yield int(subdir)
+
+ def load(self, pid):
+ """get a ProcInfo object for a given pid"""
+ pid = int(pid)
+ try:
+ return self._loaded[pid]
+ except KeyError:
+ procinfo = ProcInfo(pid)
+ procinfo.manager = self
+ self._loaded[pid] = procinfo
+ return procinfo
+
+
+ def load_all(self):
+ """load all processes information"""
+ for pid in self.list_pids():
+ try:
+ procinfo = self.load(pid)
+ if procinfo.parent is None and procinfo.ppid:
+ pprocinfo = self.load(procinfo.ppid)
+ pprocinfo.append(procinfo)
+ except NoSuchProcess:
+ pass
+
+
+try:
+ class ResourceError(BaseException):
+ """Error raise when resource limit is reached"""
+ limit = "Unknown Resource Limit"
+except NameError:
+ class ResourceError(Exception):
+ """Error raise when resource limit is reached"""
+ limit = "Unknown Resource Limit"
+
+
+class XCPUError(ResourceError):
+ """Error raised when CPU Time limit is reached"""
+ limit = "CPU Time"
+
+class LineageMemoryError(ResourceError):
+ """Error raised when the total amount of memory used by a process and
+ it's child is reached"""
+ limit = "Lineage total Memory"
+
+class TimeoutError(ResourceError):
+ """Error raised when the process is running for to much time"""
+ limit = "Real Time"
+
+# Can't use subclass because the StandardError MemoryError raised
+RESOURCE_LIMIT_EXCEPTION = (ResourceError, MemoryError)
+
+
+class MemorySentinel(Thread):
+ """A class checking a process don't use too much memory in a separated
+ daemonic thread
+ """
+ def __init__(self, interval, memory_limit, gpid=os.getpid()):
+ Thread.__init__(self, target=self._run, name="Test.Sentinel")
+ self.memory_limit = memory_limit
+ self._stop = Event()
+ self.interval = interval
+ self.setDaemon(True)
+ self.gpid = gpid
+
+ def stop(self):
+ """stop ap"""
+ self._stop.set()
+
+ def _run(self):
+ pil = ProcInfoLoader()
+ while not self._stop.isSet():
+ if self.memory_limit <= pil.load(self.gpid).lineage_memory_usage():
+ os.killpg(self.gpid, SIGUSR1)
+ self._stop.wait(self.interval)
+
+
+class ResourceController:
+
+ def __init__(self, max_cpu_time=None, max_time=None, max_memory=None,
+ max_reprieve=60):
+ if SIGXCPU == -1:
+ raise RuntimeError("Unsupported platform")
+ self.max_time = max_time
+ self.max_memory = max_memory
+ self.max_cpu_time = max_cpu_time
+ self._reprieve = max_reprieve
+ self._timer = None
+ self._msentinel = None
+ self._old_max_memory = None
+ self._old_usr1_hdlr = None
+ self._old_max_cpu_time = None
+ self._old_usr2_hdlr = None
+ self._old_sigxcpu_hdlr = None
+ self._limit_set = 0
+ self._abort_try = 0
+ self._start_time = None
+ self._elapse_time = 0
+
+ def _hangle_sig_timeout(self, sig, frame):
+ raise TimeoutError()
+
+ def _hangle_sig_memory(self, sig, frame):
+ if self._abort_try < self._reprieve:
+ self._abort_try += 1
+ raise LineageMemoryError("Memory limit reached")
+ else:
+ os.killpg(os.getpid(), SIGKILL)
+
+ def _handle_sigxcpu(self, sig, frame):
+ if self._abort_try < self._reprieve:
+ self._abort_try += 1
+ raise XCPUError("Soft CPU time limit reached")
+ else:
+ os.killpg(os.getpid(), SIGKILL)
+
+ def _time_out(self):
+ if self._abort_try < self._reprieve:
+ self._abort_try += 1
+ os.killpg(os.getpid(), SIGUSR2)
+ if self._limit_set > 0:
+ self._timer = Timer(1, self._time_out)
+ self._timer.start()
+ else:
+ os.killpg(os.getpid(), SIGKILL)
+
+ def setup_limit(self):
+ """set up the process limit"""
+ assert currentThread().getName() == 'MainThread'
+ os.setpgrp()
+ if self._limit_set <= 0:
+ if self.max_time is not None:
+ self._old_usr2_hdlr = signal(SIGUSR2, self._hangle_sig_timeout)
+ self._timer = Timer(max(1, int(self.max_time) - self._elapse_time),
+ self._time_out)
+ self._start_time = int(time())
+ self._timer.start()
+ if self.max_cpu_time is not None:
+ self._old_max_cpu_time = getrlimit(RLIMIT_CPU)
+ cpu_limit = (int(self.max_cpu_time), self._old_max_cpu_time[1])
+ self._old_sigxcpu_hdlr = signal(SIGXCPU, self._handle_sigxcpu)
+ setrlimit(RLIMIT_CPU, cpu_limit)
+ if self.max_memory is not None:
+ self._msentinel = MemorySentinel(1, int(self.max_memory) )
+ self._old_max_memory = getrlimit(RLIMIT_AS)
+ self._old_usr1_hdlr = signal(SIGUSR1, self._hangle_sig_memory)
+ as_limit = (int(self.max_memory), self._old_max_memory[1])
+ setrlimit(RLIMIT_AS, as_limit)
+ self._msentinel.start()
+ self._limit_set += 1
+
+ def clean_limit(self):
+ """reinstall the old process limit"""
+ if self._limit_set > 0:
+ if self.max_time is not None:
+ self._timer.cancel()
+ self._elapse_time += int(time())-self._start_time
+ self._timer = None
+ signal(SIGUSR2, self._old_usr2_hdlr)
+ if self.max_cpu_time is not None:
+ setrlimit(RLIMIT_CPU, self._old_max_cpu_time)
+ signal(SIGXCPU, self._old_sigxcpu_hdlr)
+ if self.max_memory is not None:
+ self._msentinel.stop()
+ self._msentinel = None
+ setrlimit(RLIMIT_AS, self._old_max_memory)
+ signal(SIGUSR1, self._old_usr1_hdlr)
+ self._limit_set -= 1
« no previous file with comments | « third_party/logilab/logilab/common/optparser.py ('k') | third_party/logilab/logilab/common/pyro_ext.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698