1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
## -*- coding: utf-8 -*-
##
## Process lister (control TBA)
##
## Copyright 2007, Red Hat, Inc
## Michael DeHaan <mdehaan@redhat.com>
##
## This software may be freely redistributed under the terms of the GNU
## general public license.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
# other modules
import sub_process
import codes
# our modules
import func_module
# =================================
class ProcessModule(func_module.FuncModule):
version = "0.0.1"
api_version = "0.0.1"
description = "Process related reporting and control."
def info(self, flags="-auxh"):
"""
Returns a struct of hardware information. By default, this pulls down
all of the devices. If you don't care about them, set with_devices to
False.
"""
flags.replace(";", "") # prevent stupidity
cmd = sub_process.Popen(["/bin/ps", flags], executable="/bin/ps",
stdout=sub_process.PIPE,
stderr=sub_process.PIPE,
shell=False)
data, error = cmd.communicate()
# We can get warnings for odd formatting. warnings != errors.
if error and error[:7] != "Warning":
raise codes.FuncException(error.split('\n')[0])
results = []
for x in data.split("\n"):
tokens = x.split()
results.append(tokens)
return results
def mem(self):
"""
Returns a list of per-program memory usage.
Private + Shared = RAM used Program
[["39.4 MiB", "10.3 MiB", "49.8 MiB", "Xorg"],
["42.2 MiB", "12.4 MiB", "54.6 MiB", "nautilus"],
["52.3 MiB", "10.8 MiB", "63.0 MiB", "liferea-bin"]
["171.6 MiB", "11.9 MiB", "183.5 MiB", "firefox-bin"]]
Taken from the ps_mem.py script written by Pádraig Brady.
http://www.pixelbeat.org/scripts/ps_mem.py
"""
import os
our_pid=os.getpid()
results = []
global have_pss
have_pss=0
def kernel_ver():
""" (major,minor,release) """
kv=open("/proc/sys/kernel/osrelease").readline().split(".")[:3]
for char in "-_":
kv[2]=kv[2].split(char)[0]
return (int(kv[0]), int(kv[1]), int(kv[2]))
kv=kernel_ver()
def getMemStats(pid):
""" return Private,Shared """
global have_pss
Private_lines=[]
Shared_lines=[]
Pss_lines=[]
pagesize=os.sysconf("SC_PAGE_SIZE")/1024 #KiB
Rss=int(open("/proc/"+str(pid)+"/statm").readline().split()[1])*pagesize
if os.path.exists("/proc/"+str(pid)+"/smaps"): #stat
for line in open("/proc/"+str(pid)+"/smaps").readlines(): #open
if line.startswith("Shared"):
Shared_lines.append(line)
elif line.startswith("Private"):
Private_lines.append(line)
elif line.startswith("Pss"):
have_pss=1
Pss_lines.append(line)
Shared=sum([int(line.split()[1]) for line in Shared_lines])
Private=sum([int(line.split()[1]) for line in Private_lines])
#Note Shared + Private = Rss above
#The Rss in smaps includes video card mem etc.
if have_pss:
pss_adjust=0.5 #add 0.5KiB as this average error due to trunctation
Pss=sum([float(line.split()[1])+pss_adjust for line in Pss_lines])
Shared = Pss - Private
elif (2,6,1) <= kv <= (2,6,9):
Shared=0 #lots of overestimation, but what can we do?
Private = Rss
else:
Shared=int(open("/proc/"+str(pid)+"/statm").readline().split()[2])*pagesize
Private = Rss - Shared
return (Private, Shared)
def getCmdName(pid):
cmd = file("/proc/%d/status" % pid).readline()[6:-1]
exe = os.path.basename(os.path.realpath("/proc/%d/exe" % pid))
if exe.startswith(cmd):
cmd=exe #show non truncated version
#Note because we show the non truncated name
#one can have separated programs as follows:
#584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash)
# 56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin
return cmd
cmds={}
shareds={}
count={}
for pid in os.listdir("/proc/"):
try:
pid = int(pid) #note Thread IDs not listed in /proc/ which is good
if pid == our_pid: continue
except:
continue
try:
cmd = getCmdName(pid)
except:
#permission denied or
#kernel threads don't have exe links or
#process gone
continue
try:
private, shared = getMemStats(pid)
except:
continue #process gone
if shareds.get(cmd):
if have_pss: #add shared portion of PSS together
shareds[cmd]+=shared
elif shareds[cmd] < shared: #just take largest shared val
shareds[cmd]=shared
else:
shareds[cmd]=shared
cmds[cmd]=cmds.setdefault(cmd,0)+private
if count.has_key(cmd):
count[cmd] += 1
else:
count[cmd] = 1
#Add shared mem for each program
total=0
for cmd in cmds.keys():
cmds[cmd]=cmds[cmd]+shareds[cmd]
total+=cmds[cmd] #valid if PSS available
sort_list = cmds.items()
sort_list.sort(lambda x,y:cmp(x[1],y[1]))
sort_list=filter(lambda x:x[1],sort_list) #get rid of zero sized processes
#The following matches "du -h" output
def human(num, power="Ki"):
powers=["Ki","Mi","Gi","Ti"]
while num >= 1000: #4 digits
num /= 1024.0
power=powers[powers.index(power)+1]
return "%.1f %s" % (num,power)
def cmd_with_count(cmd, count):
if count>1:
return "%s (%u)" % (cmd, count)
else:
return cmd
for cmd in sort_list:
results.append([
"%sB" % human(cmd[1]-shareds[cmd[0]]),
"%sB" % human(shareds[cmd[0]]),
"%sB" % human(cmd[1]),
"%s" % cmd_with_count(cmd[0], count[cmd[0]])
])
if have_pss:
results.append(["", "", "", "%sB" % human(total)])
return results
memory = mem
def kill(self,pid,signal="TERM"):
if pid == "0":
raise codes.FuncException("Killing pid group 0 not permitted")
if signal == "":
# this is default /bin/kill behaviour,
# it claims, but enfore it anyway
signal = "-TERM"
if signal[0] != "-":
signal = "-%s" % signal
rc = sub_process.call(["/bin/kill",signal, pid],
executable="/bin/kill", shell=False)
print rc
return rc
def pkill(self,name,level=""):
# example killall("thunderbird","-9")
rc = sub_process.call(["/usr/bin/pkill", name, level],
executable="/usr/bin/pkill", shell=False)
return rc
|