Documented pdm.perf as well.
[pdm.git] / pdm / perf.py
CommitLineData
5463509c
FT
1"""Python Daemon Management -- PERF utilities
2
3This module serves two purposes: It has a few utility classes
4for implementing PERF interfaces in common ways, and uses those
5classes to implement some standard PERF objects that can be used by
6PERF clients connecting to any PERF server.
7
8See the documentation for pdm.srv.perf for a description of the
9various PERF interfaces.
10
11It contains two named PERF objects:
12
13 * sysres -- A directory containing the following objects pertaining
14 to the resource usage of the server process:
15 * realtime -- An attribute returning the amount of real time
16 since the PDM module was imported (which likely
17 coincides with the amount of time the server process
18 has been running).
19 * cputime -- An attribute returning the amount of CPU time
20 consumed by the server process (in both user and
21 kernel mode).
22 * utime -- An attribute returning the amount of CPU time the
23 server process has spent in user mode.
24 * stime -- An attribute returning the amount of CPU time the
25 server process has spent in kernel mode.
26 * maxrss -- An attribute returning the largest resident set size
27 the server process has used during its lifetime.
28 * rusage -- An attribute returning the current rusage of the
29 server process.
30* sysinfo -- A directory containing the following objects pertaining
31 to the environment of the server process:
32 * pid -- An attribute returning the PID of the server process.
33 * uname -- An attribute returning the uname information of the
34 system.
35 * hostname -- An attribute returning the hostname of the system.
36 * platform -- An attribute returning the Python build platform.
37"""
38
87b07b36 39import os, sys, resource, time, socket, threading
7f97a47e 40
5463509c
FT
41__all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj",
42 "staticdir", "event", "procevent", "startevent",
43 "finishevent"]
44
7f97a47e 45class attrinfo(object):
5463509c
FT
46 """The return value of the `attrinfo' method on `attr' objects as
47 described in pdm.srv.perf.
48
49 Currently contains a single data field, `desc', which should have
50 a human-readable description of the purpose of the attribute.
51 """
7f97a47e
FT
52 def __init__(self, desc = None):
53 self.desc = desc
54
55class perfobj(object):
56 def __init__(self, *args, **kwargs):
57 super(perfobj, self).__init__()
58
59 def pdm_protocols(self):
60 return []
61
62class simpleattr(perfobj):
5463509c
FT
63 """An implementation of the `attr' interface, which is initialized
64 with a function, and returns whatever that function returns when
65 read.
66 """
7f97a47e
FT
67 def __init__(self, func, info = None, *args, **kwargs):
68 super(simpleattr, self).__init__(*args, **kwargs)
69 self.func = func
70 if info is None:
71 info = attrinfo()
72 self.info = info
73
74 def readattr(self):
75 return self.func()
76
77 def attrinfo(self):
78 return self.info
79
80 def pdm_protocols(self):
81 return super(simpleattr, self).pdm_protocols() + ["attr"]
82
83class valueattr(perfobj):
5463509c
FT
84 """An implementation of the `attr' interface, which is initialized
85 with a single value, and returns that value when read. Subsequent
86 updates to the value are reflected in subsequent reads.
87 """
7f97a47e
FT
88 def __init__(self, init, info = None, *args, **kwargs):
89 super(valueattr, self).__init__(*args, **kwargs)
90 self.value = init
91 if info is None:
92 info = attrinfo()
93 self.info = info
94
95 def readattr(self):
96 return self.value
97
98 def attrinfo(self):
99 return self.info
100
101 def pdm_protocols(self):
102 return super(valueattr, self).pdm_protocols() + ["attr"]
103
7f97a47e 104class eventobj(perfobj):
5463509c
FT
105 """An implementation of the `event' interface. It keeps track of
106 subscribers, and will multiplex any event to all current
107 subscribers when submitted with the `notify' method.
108 """
7f97a47e
FT
109 def __init__(self, *args, **kwargs):
110 super(eventobj, self).__init__(*args, **kwargs)
111 self.subscribers = set()
112
113 def subscribe(self, cb):
114 if cb in self.subscribers:
115 raise ValueError("Already subscribed")
116 self.subscribers.add(cb)
117
118 def unsubscribe(self, cb):
119 self.subscribers.remove(cb)
120
121 def notify(self, event):
5463509c 122 """Notify all subscribers with the given event object."""
7f97a47e
FT
123 for cb in self.subscribers:
124 try:
125 cb(event)
126 except: pass
127
128 def pdm_protocols(self):
129 return super(eventobj, self).pdm_protocols() + ["event"]
130
131class staticdir(perfobj):
5463509c
FT
132 """An implementation of the `dir' interface. Put other PERF
133 objects in it using the normal dict assignment syntax, and it will
134 return them to requesting clients.
135 """
7f97a47e
FT
136 def __init__(self, *args, **kwargs):
137 super(staticdir, self).__init__(*args, **kwargs)
138 self.map = {}
139
140 def __setitem__(self, name, ob):
141 self.map[name] = ob
142
143 def __delitem__(self, name):
144 del self.map[name]
145
146 def __getitem__(self, name):
147 return self.map[name]
148
149 def get(self, name, default = None):
150 return self.map.get(name, default)
151
152 def listdir(self):
153 return self.map.keys()
154
155 def lookup(self, name):
156 return self.map[name]
157
158 def pdm_protocols(self):
159 return super(staticdir, self).pdm_protocols() + ["dir"]
160
bcb622d6 161class event(object):
5463509c
FT
162 """This class should be subclassed by all event objects sent via
163 the `event' interface. Its main utility is that it keeps track of
164 the time it was created, so that listening clients can precisely
165 measure the time between event notifications.
166
167 Subclasses should make sure to call the __init__ method if they
168 override it.
169 """
bcb622d6
FT
170 def __init__(self):
171 self.time = time.time()
172
87b07b36
FT
173idlock = threading.Lock()
174procevid = 0
709446f6
FT
175
176def getprocid():
177 global procevid
178 idlock.acquire()
179 try:
180 ret = procevid
181 procevid += 1
182 return ret
183 finally:
184 idlock.release()
185
186class procevent(event):
5463509c
FT
187 """A subclass of the `event' class meant to group several events
188 related to the same process. Create a new process by creating (a
189 subclass of) the `startevent' class, and subsequent events in the
190 same process by passing that startevent as the `id' parameter.
191
192 It works by having `startevent' allocate a unique ID for each
193 process, and every other procevent initializing from that
194 startevent copying the ID. The data field `id' contains the ID so
195 that it can be compared by clients.
196
197 An example of such a process might be a client connection, where a
198 `startevent' is emitted when a client connects, another subclass
199 of `procevent' emitted when the client runs a command, and a
200 `finishevent' emitted when the connection is closed.
201 """
709446f6
FT
202 def __init__(self, id):
203 super(procevent, self).__init__()
204 if isinstance(id, procevent):
205 self.id = id.id
206 else:
207 self.id = id
208
209class startevent(procevent):
5463509c 210 """A subclass of `procevent'. See its documentation for details."""
87b07b36 211 def __init__(self):
709446f6
FT
212 super(startevent, self).__init__(getprocid())
213
214class finishevent(procevent):
5463509c
FT
215 """A subclass of `procevent'. Intended to be emitted when a
216 process finishes and terminates. The `aborted' field can be used
217 to indicate whether the process completed successfully, if such a
218 distinction is meaningful. The `start' parameter should be the
219 `startevent' instance used when the process was initiated."""
0f1f2a1b 220 def __init__(self, start, aborted = False):
709446f6 221 super(finishevent, self).__init__(start)
87b07b36
FT
222 self.aborted = aborted
223
7f97a47e
FT
224sysres = staticdir()
225itime = time.time()
226ires = resource.getrusage(resource.RUSAGE_SELF)
227def ct():
228 ru = resource.getrusage(resource.RUSAGE_SELF)
229 return (ru.ru_utime - ires.ru_utime) + (ru.ru_stime - ires.ru_stime)
230sysres["realtime"] = simpleattr(func = lambda: time.time() - itime)
231sysres["cputime"] = simpleattr(func = ct)
232sysres["utime"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_utime - ires.ru_utime)
233sysres["stime"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_stime - ires.ru_stime)
234sysres["maxrss"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
235sysres["rusage"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF))
236
237sysinfo = staticdir()
238sysinfo["pid"] = simpleattr(func = os.getpid)
239sysinfo["uname"] = simpleattr(func = os.uname)
240sysinfo["hostname"] = simpleattr(func = socket.gethostname)
241sysinfo["platform"] = valueattr(init = sys.platform)