HWRF  trunk@4391
rusage.py
1 """!This module allows querying resource usage and limits, as well as
2 setting resource limits. It is a wrapper around the Python resource
3 module.
4 
5 Setting resource limits:
6 @code
7  use logging, produtil.rusage
8  logger=logging.logger("rusage")
9  produtil.rusage.setrlimit(logger,data=1e9,nofile=500,aspace=2e9,stack=5e8)
10 @endcode
11 
12 Printing resource limits to a logger:
13 @code
14  use logging, produtil.rusage
15  logger=logging.logger("rusage")
16  u.produtil.rusage.getrlimit(logger) # writes the limits to the logger
17  # Limits are also in the returned object "u"
18  # Send None instead of logger to avoid logging.
19 @endcode
20 """
21 
22 import resource, logging, StringIO, time
23 
24 ##@var rtypemap
25 # Maps the name used in this module for each resource class to the
26 # name used by the Python resource module
27 rtypemap=dict(core=resource.RLIMIT_CORE,
28  cpu=resource.RLIMIT_CPU,
29  fsize=resource.RLIMIT_FSIZE,
30  data=resource.RLIMIT_DATA,
31  stack=resource.RLIMIT_STACK,
32  rss=resource.RLIMIT_RSS,
33  nproc=resource.RLIMIT_NPROC,
34  nofile=resource.RLIMIT_NOFILE,
35  #ofile=resource.RLIMIT_OFILE,
36  memlock=resource.RLIMIT_MEMLOCK,
37  #vmem=resource.RLIMIT_VMEM,
38  aspace=resource.RLIMIT_AS)
39 """Maps the name used in this module for each resource class, to the
40 name used by the Python resource module."""
41 
42 ##@var rnamemap
43 # Maps the name used in this module for each resource class, to a
44 # short human-readable string explaining the resource's meaning.
45 rnamemap=dict(core='core file size',
46  cpu='cpu usage',
47  fsize='max. file size',
48  data='max. heap size',
49  stack='max. stack size',
50  rss='max. resident set size',
51  nproc='max. processes',
52  nofile='max. open files',
53  #ofile='max. open files (BSD)',
54  memlock='max. locked memory',
55  #vmem='max. virtual memory',
56  aspace='max. address space')
57 """Maps the name used in this module for each resource class, to a
58 short human-readable string explaining the resource's meaning."""
59 
60 def setrlimit(logger=None, ignore=False, hard=False, **kwargs):
61  """!Sets resource limits.
62 
63  @param ignore If ignore=True, ignores any errors from
64  getrlimit or setrlimit.
65  @param hard If hard=True, attempts to set hard
66  limits, which generally requires administrator privileges.
67  @param logger The logger argument sets the logger (default:
68  produtil.setrlimit logging domain).
69  @param kwargs The kwargs should be a list of resource limits.
70  Accepted resource limits:
71  * core = core file size (RLIMIT_CORE)
72  * cpu = max. cpu usage (RLIMIT_CPU)
73  * fsize = max. file size (RLIMIT_FSIZE)
74  * data = max. heap size (RLIMIT_DATA)
75  * stack = max. stack size (RLIMIT_STACK)
76  * rss = max. resident set size (RLIMIT_RSS)
77  * nproc = max. processes (RLIMIT_NPROC)
78  * nofile = max. open files (RLIMIT_NOFILE or RLIMIT_OFILE)
79  * memlock= max locked memory (RLIMIT_MEMLOCK)
80  * aspace = max. address space (RLIMIT_AS)
81  See "man setrlimit" for details."""
82  if logger is None: logger=logging.getLogger('produtil.setrlimit')
83  for k,v in kwargs.iteritems():
84  try:
85  (softL,hardL)=resource.getrlimit(rtypemap[k])
86  if hard:
87  hardL=kwargs[k]
88  softL=kwargs[k]
89  if logger is not None:
90  logger.info('Requesting %s (%s) soft=%s hard=%s'
91  %(k,rnamemap[k],softL,hardL))
92  resource.setrlimit(rtypemap[k],(softL,hardL))
93  except (resource.error,EnvironmentError,ValueError,TypeError,KeyError) as e:
94  if logger is not None:
95  logger.warning("%s: cannot set limit: %s"%(k,str(e)),exc_info=True)
96  if not ignore: raise
97 
98 class RLimit(object):
99  """!Gets the resource limits set on this process:
100  core, cpu, fsize, data, stack, rss, nproc, nofile, memlock, aspace
101  Each is set to a tuple containing the soft and hard limit."""
102  def __init__(self,logger=None):
103  """!RLimit constructor
104  @param logger a logging.Logger for log messages."""
105  if logger is None:
106  logger=logging.getLogger('produtil.getrlimit')
107  for (name,limit) in rtypemap.iteritems():
108  try:
109  r=resource.getrlimit(limit)
110  if r is None:
111  logger.warning('%s: cannot get limit: '
112  'getrlimit returned None.'%(name,))
113  self.__dict__['_limits_'+name]=r
114  except (resource.error,ValueError) as e:
115  logger.warning('%s: cannot get limit: %s'%(name,str(e)),
116  exc_info=True)
117  def __str__(self):
118  """!Creates a multi-line string representation of the resource
119  limits."""
120  out=StringIO.StringIO()
121  for k,v in self.__dict__.iteritems():
122  kk=k[8:]
123  if k[0:8]=='_limits_':
124  (soft,hard)=v
125  if(soft<0): soft=hard
126  if soft<0:
127  out.write('%7s - %25s = (unlimited)\n'%(kk,rnamemap[kk]))
128  else:
129  out.write('%7s - %25s = %g\n'%(kk,rnamemap[kk],soft))
130  return out.getvalue()
131 
132 def getrlimit(logger=None):
133  """!Gets the current resource limits. If logger is not None,
134  sends the limits to the logger at level INFO.
135  @returns a RLimit object with resource information"""
136  rlimit=RLimit(logger=logger)
137  if logger is not None:
138  for line in str(rlimit).splitlines():
139  logger.info(line)
140  return rlimit
141 
142 ##@var rusage_keys
143 # A tuple containing all rusage keys.
144 rusage_keys=('ru_utime','ru_stime','ru_maxrss','ru_ixrss','ru_idrss',
145  'ru_isrss','ru_minflt','ru_majflt','ru_nswap','ru_inblock',
146  'ru_oublock','ru_msgsnd','ru_msgrcv','ru_nsignals',
147  'ru_nvcsw','ru_nivcsw')
148 """Tuple containing all rusage keys."""
149 
150 ##@var rusage_meanings
151 # A mapping from rusage key to a human-readable explanation of the meaning.
152 rusage_meanings=dict(ru_utime='time in user mode',
153  ru_stime='time in system mode',
154  ru_maxrss='maximum resident set size',
155  ru_ixrss='shared memory size',
156  ru_idrss='unshared memory size',
157  ru_isrss='unshared stack size',
158  ru_minflt='page faults not requiring I/O',
159  ru_majflt='page faults requiring I/O',
160  ru_nswap='number of swap outs',
161  ru_inblock='block input operations',
162  ru_oublock='block output operations',
163  ru_msgsnd='messages sent',
164  ru_msgrcv='messages received',
165  ru_nsignals='signals received',
166  ru_nvcsw='voluntary context switches',
167  ru_nivcsw='involuntary context switches')
168 """A mapping from rusage key to a human-readable explanation of the
169 meaning."""
170 
171 class RUsageReport(Exception):
172  """!Raised when caller makes an RUsage, and tries to generate its
173  report, before calling its __enter__ or __exit__ routines."""
174 
175 class RUsage(object):
176  """!Contains resource usage (rusage) information that can be used
177  with a Python "with" construct to collect the resources utilized
178  by a block of code, or group of subprocesses executing during that
179  block.
180 
181  Example:
182  @code
183  with produtil.rusage.RUsage(logger=logging.getLogger("usage")):
184  ... do things ...
185  ... stop doing things ...
186  @endcode
187 
188  Just after the "with" block exits, the resource usage is printed
189  to the given logger. The information can be retained for
190  inspection instead:
191  @code
192  u=produtil.rusage.RUsage(logger=logging.getLogger("usage"))
193  with u:
194  ... do things ...
195  ... stop doing things ...
196  # u.rusage_before is a dict of resource usage before the block
197  # u.time_before contains the time before the block
198  # u.rusage_after contains the resource usage at the end of the block
199  # u.time_after contains the time after the block
200  @endcode
201 
202  Note that the logger is optional: without it, nothing is logged."""
203  def __init__(self,who=resource.RUSAGE_CHILDREN,logger=None):
204  """!Creates an RUsage object for input to a "with" statement.
205 
206  @param who Pass who=resource.RUSAGE_SELF to get usage on this
207  process or rusage.RUSAGE_CHILDREN (the default) to get
208  resource usage on child processes.
209  @param logger a logging.Logger for log messages"""
210  self.logger=logger
211  self.rusage_before=None
212  self.rusage_after=None
213  self.time_before=None
214  self.time_after=None
215  self._pagesize=resource.getpagesize()
216  self._who=who
217  self._report=None
218  ##@var logger
219  # The logging.Logger for log messages
220 
221  ##@var rusage_before
222  # Resource usage before monitoring began
223 
224  ##@var rusage_after
225  # The resource usage after monitoring ended
226 
227  ##@var time_before
228  # The current time before usage monitoring began
229 
230  ##@var time_after
231  # The current time after monitoring ended.
232 
233  @property
234  def who(self):
235  """!The "who" parameter to the constructor, which selects
236  whether the usage measured should be of the child processes
237  (RUSAGE_CHILDREN) or this process (RUSAGE_SELF) . See
238  __init__ for details."""
239  return self._who
240  @property
241  def pagesize(self):
242  """!System page size in bytes from resource.getpagesize().
243  This is needed to interpret return values."""
244  return self._pagesize
245  def __enter__(self):
246  """!Gets the resource usage and time at the top of the "with"
247  block. This function is called automatically by the Python
248  interpreter at the beginning of a "with" block."""
249  self.rusage_before=resource.getrusage(self._who)
250  self.time_before=float(time.time())
251  def __exit__(self, type, value, tb):
252  """!Gets the resource usage and time at the end of a "with"
253  block. This is called automatically by Python at the end of a
254  "with" block.
255  @param type,value,tb exception information"""
256  self.time_after=float(time.time())
257  self.rusage_after=resource.getrusage(self._who)
258  if self.logger is not None:
259  for line in self.report().splitlines():
260  self.logger.info(line)
261  def report(self):
262  """!Generates a string report of the resource usage utilized.
263  Accessible via str(self)."""
264  if self._report is None:
265  s=StringIO.StringIO()
266  b=self.rusage_before
267  a=self.rusage_after
268  if a is None or b is None:
269  raise RUsageNotRun("You cannot generate an RUsage report until you run RUsage.")
270  dt=self.time_after-self.time_before
271  for k in rusage_keys:
272  if hasattr(a,k) and hasattr(b,k):
273  s.write('%11s - %29s = %g\n'%(k,rusage_meanings[k],
274  getattr(a,k)-getattr(b,k)))
275  s.write('%11s - %29s = %g\n'%('ru_walltime','wallclock time',dt))
276  self._report=s.getvalue()
277  return self._report
278  def __str__(self):
279  """!Generates a string report of the resource usage utilized."""
280  if self.rusage_before is None or self.rusage_after is None:
281  return '(uninitialized RUsage report)'
282  else:
283  if self._report is None: self.report()
284  return self._report
285 
286 ##@var produtil.rusage.rusage
287 # Alias for produtil.rusage.RUsage
288 rusage=RUsage
289 """A synonym for RUsage"""
Raised when caller makes an RUsage, and tries to generate its report, before calling its enter or exi...
Definition: rusage.py:171
def __enter__(self)
Gets the resource usage and time at the top of the "with" block.
Definition: rusage.py:245
def getrlimit
Gets the current resource limits.
Definition: rusage.py:132
def __init__
Creates an RUsage object for input to a "with" statement.
Definition: rusage.py:203
def pagesize(self)
System page size in bytes from resource.getpagesize().
Definition: rusage.py:241
logger
The logging.Logger for log messages.
Definition: rusage.py:210
def __str__(self)
Generates a string report of the resource usage utilized.
Definition: rusage.py:278
def __init__
RLimit constructor.
Definition: rusage.py:102
def __str__(self)
Creates a multi-line string representation of the resource limits.
Definition: rusage.py:117
def __exit__(self, type, value, tb)
Gets the resource usage and time at the end of a "with" block.
Definition: rusage.py:251
def report(self)
Generates a string report of the resource usage utilized.
Definition: rusage.py:261
def who(self)
The "who" parameter to the constructor, which selects whether the usage measured should be of the chi...
Definition: rusage.py:234
time_before
The current time before usage monitoring began.
Definition: rusage.py:213
rusage_before
Resource usage before monitoring began.
Definition: rusage.py:211
Gets the resource limits set on this process: core, cpu, fsize, data, stack, rss, nproc...
Definition: rusage.py:98
def setrlimit(logger=None, ignore=False, hard=False, kwargs)
Sets resource limits.
Definition: rusage.py:60
time_after
The current time after monitoring ended.
Definition: rusage.py:214
rusage_after
The resource usage after monitoring ended.
Definition: rusage.py:212