Merge pull request #3 from Ved-vampir/master
perprocess cpu usage added
diff --git a/sensors/host1_config.json b/sensors/host1_config.json
index 450db06..5f9d2e8 100644
--- a/sensors/host1_config.json
+++ b/sensors/host1_config.json
@@ -9,5 +9,11 @@
"allowed_prefixes": ["cpu"]
},
"system-ram": {
+ },
+ "perprocess-cpu": {
+ "allowed_prefixes": ["ceph"]
+ },
+ "perprocess-ram": {
+ "allowed_prefixes": ["ceph"]
}
}
diff --git a/sensors/main.py b/sensors/main.py
index 8668518..4f0a8d1 100644
--- a/sensors/main.py
+++ b/sensors/main.py
@@ -10,6 +10,8 @@
import net_sensors
import syscpu_sensors
import sysram_sensors
+import pscpu_sensors
+import psram_sensors
from utils import SensorInfo
from daemonize import Daemonize
diff --git a/sensors/ps_mem.py b/sensors/ps_mem.py
new file mode 100644
index 0000000..2e0fed8
--- /dev/null
+++ b/sensors/ps_mem.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+
+# Based on ps_mem.py:
+# Licence: LGPLv2
+# Author: P@draigBrady.com
+# Source: http://www.pixelbeat.org/scripts/ps_mem.py
+# http://github.com/pixelb/scripts/commits/master/scripts/ps_mem.py
+
+import errno
+import os
+import sys
+
+
+
+class Proc:
+ def __init__(self):
+ uname = os.uname()
+ if uname[0] == "FreeBSD":
+ self.proc = '/compat/linux/proc'
+ else:
+ self.proc = '/proc'
+
+ def path(self, *args):
+ return os.path.join(self.proc, *(str(a) for a in args))
+
+ def open(self, *args):
+ try:
+ return open(self.path(*args))
+ except (IOError, OSError):
+ val = sys.exc_info()[1]
+ if (val.errno == errno.ENOENT or # kernel thread or process gone
+ val.errno == errno.EPERM):
+ raise LookupError
+ raise
+
+
+def kernel_ver():
+ """ Return (major,minor,release)"""
+ proc = Proc()
+ kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3]
+ last = len(kv)
+ if last == 2:
+ kv.append('0')
+ last -= 1
+ while last > 0:
+ for char in "-_":
+ kv[last] = kv[last].split(char)[0]
+ try:
+ int(kv[last])
+ except:
+ kv[last] = 0
+ last -= 1
+ return (int(kv[0]), int(kv[1]), int(kv[2]))
+
+
+#Note shared is always a subset of rss (trs is not always)
+def getMemStats(pid):
+ """ Return memory data of pid in format (Private, Shared) """
+
+ PAGESIZE = os.sysconf("SC_PAGE_SIZE") / 1024 #KiB
+ proc = Proc()
+
+ Private_lines = []
+ Shared_lines = []
+ Pss_lines = []
+
+ Rss = (int(proc.open(pid, 'statm').readline().split()[1])
+ * PAGESIZE)
+
+ if os.path.exists(proc.path(pid, 'smaps')): #stat
+ for line in proc.open(pid, 'smaps').readlines(): #open
+ # Note we checksum smaps as maps is usually but
+ # not always different for separate processes.
+ 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 avg error due to trunctation
+ Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines])
+ Shared = Pss - Private
+ elif (2, 6, 1) <= kernel_ver() <= (2, 6, 9):
+ Shared = 0 #lots of overestimation, but what can we do?
+ Private = Rss
+ else:
+ Shared = int(proc.open(pid, 'statm').readline().split()[2])
+ Shared *= PAGESIZE
+ Private = Rss - Shared
+ return (Private, Shared)
diff --git a/sensors/pscpu_sensors.py b/sensors/pscpu_sensors.py
new file mode 100644
index 0000000..4e1849f
--- /dev/null
+++ b/sensors/pscpu_sensors.py
@@ -0,0 +1,64 @@
+import os
+import time
+
+from discover import provides
+from utils import SensorInfo, get_pid_name, get_pid_list
+
+
+
+@provides("perprocess-cpu")
+def pscpu_stat(disallowed_prefixes=None, allowed_prefixes=None):
+ results = {}
+ pid_stat0 = {}
+ sys_stat0 = {}
+ pid_list = get_pid_list(disallowed_prefixes, allowed_prefixes)
+
+ for pid in pid_list:
+ try:
+ pid_stat0[pid] = pid_stat(pid)
+ sys_stat0[pid] = sys_stat()
+ except IOError:
+ # may be, proc has already terminated
+ continue
+
+ time.sleep(1)
+
+ for pid in pid_list:
+ try:
+ dev_name = get_pid_name(pid)
+
+ pid_stat1 = pid_stat(pid)
+ sys_stat1 = sys_stat()
+ cpu = (pid_stat1 - pid_stat0[pid]) / (sys_stat1 - sys_stat0[pid])
+
+ sensor_name = "{0}.{1}".format(dev_name, pid)
+ results[sensor_name] = SensorInfo(cpu*100, False)
+ except IOError:
+ # may be, proc has already terminated
+ continue
+ return results
+
+
+def pid_stat(pid):
+ """ Return total cpu usage time from process"""
+ # read /proc/pid/stat
+ with open(os.path.join('/proc/', pid, 'stat'), 'r') as pidfile:
+ proctimes = pidfile.readline().split()
+ # get utime from /proc/<pid>/stat, 14 item
+ utime = proctimes[13]
+ # get stime from proc/<pid>/stat, 15 item
+ stime = proctimes[14]
+ # count total process used time
+ proctotal = int(utime) + int(stime)
+ return float(proctotal)
+
+
+def sys_stat():
+ """ Return total system cpu usage time"""
+ with open('/proc/stat', 'r') as procfile:
+ cputimes = procfile.readline().split()[1:]
+ cputotal = 0
+ # count from /proc/stat sum
+ for i in cputimes:
+ cputotal = cputotal + int(i)
+ return float(cputotal)
diff --git a/sensors/psram_sensors.py b/sensors/psram_sensors.py
new file mode 100644
index 0000000..db36710
--- /dev/null
+++ b/sensors/psram_sensors.py
@@ -0,0 +1,38 @@
+from ps_mem import getMemStats
+
+from discover import provides
+from utils import SensorInfo, get_pid_name
+
+
+
+@provides("perprocess-ram")
+def psram_stat(disallowed_prefixes=None, allowed_prefixes=None):
+ results = {}
+ pid_list = get_pid_list(disallowed_prefixes, allowed_prefixes)
+ print pid_list
+ for pid in pid_list:
+ try:
+ dev_name = get_pid_name(pid)
+
+ private, shared = getMemStats(pid)
+ total = private + shared
+ sys_total = get_ram_size()
+ usage = float(total) / float(sys_total)
+
+ sensor_name = "{0}.{1}".format(dev_name, pid)
+
+ results[sensor_name + ".private_mem"] = SensorInfo(private, False)
+ results[sensor_name + ".shared_mem"] = SensorInfo(shared, False)
+ results[sensor_name + ".used_mem"] = SensorInfo(total, False)
+ results[sensor_name + ".mem_usage_percent"] = SensorInfo(usage*100, False)
+ except IOError:
+ # permission denied or proc die
+ continue
+ return results
+
+
+def get_ram_size():
+ """ Return RAM size in Kb"""
+ with open("/proc/meminfo") as proc:
+ mem_total = proc.readline().split()
+ return mem_total[1]
diff --git a/sensors/utils.py b/sensors/utils.py
index 5af0a2a..546dee7 100644
--- a/sensors/utils.py
+++ b/sensors/utils.py
@@ -1,3 +1,5 @@
+import os
+
from collections import namedtuple
SensorInfo = namedtuple("SensorInfo", ['value', 'is_accumulated'])
@@ -17,6 +19,42 @@
return dev_ok
+def get_pid_list(disallowed_prefixes, allowed_prefixes):
+ """ Return pid list from list of pids and names """
+ # exceptions
+ but = disallowed_prefixes if disallowed_prefixes is not None else []
+ if allowed_prefixes is None:
+ # if nothing setted - all ps will be returned except setted
+ result = [pid
+ for pid in os.listdir('/proc')
+ if pid.isdigit() and pid not in but]
+ else:
+ result = []
+ for pid in os.listdir('/proc'):
+ if pid.isdigit() and pid not in but:
+ name = get_pid_name(pid)
+ if pid in allowed_prefixes or \
+ any(name.startswith(val) for val in allowed_prefixes):
+ print name
+ # this is allowed pid?
+ result.append(pid)
+ return result
+
+
+def get_pid_name(pid):
+ """ Return name by pid """
+ try:
+ with open(os.path.join('/proc/', pid, 'cmdline'), 'r') as pidfile:
+ try:
+ cmd = pidfile.readline().split()[0]
+ return os.path.basename(cmd).rstrip('\x00')
+ except IndexError:
+ # no cmd returned
+ return "no_name"
+ except IOError:
+ return "no_such_process"
+
+
def delta(func, only_upd=True):
prev = {}
while True: