summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2008-02-25 18:21:52 -0300
committerArnaldo Carvalho de Melo <acme@redhat.com>2008-02-25 18:21:52 -0300
commitbcb8b4401c721056ff5bb902f228b9b451a6985f (patch)
treea0daccd3b924962b7d51813970e20fe98f05a842
downloadpython-linux-procfs-bcb8b4401c721056ff5bb902f228b9b451a6985f.tar.gz
Created repo from files previously in ait.git
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--MANIFEST7
-rw-r--r--procfs/__init__.py11
-rwxr-xr-xprocfs/procfs.py437
-rwxr-xr-xprocfs/sysctl.py52
-rwxr-xr-xprocfs/utilist.py38
-rw-r--r--rpm/SPECS/python-linux-procfs.spec37
-rwxr-xr-xsetup.py26
7 files changed, 608 insertions, 0 deletions
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..7b0ecde
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,7 @@
+MANIFEST
+procfs/__init__.py
+procfs/procfs.py
+procfs/sysctl.py
+procfs/utilist.py
+rpm/SPECS/python-linux-procfs.spec
+setup.py
diff --git a/procfs/__init__.py b/procfs/__init__.py
new file mode 100644
index 0000000..8e3a449
--- /dev/null
+++ b/procfs/__init__.py
@@ -0,0 +1,11 @@
+"""
+Copyright (c) 2008 Red Hat Inc.
+
+Abstractions to extract information from the Linux kernel /proc files.
+"""
+__author__ = "Arnaldo Carvalho de Melo <acme@redhat.com>"
+__license__ = "GPLv2 License"
+
+from procfs import *
+from sysctl import *
+from utilist import *
diff --git a/procfs/procfs.py b/procfs/procfs.py
new file mode 100755
index 0000000..102503b
--- /dev/null
+++ b/procfs/procfs.py
@@ -0,0 +1,437 @@
+#! /usr/bin/python
+# -*- python -*-
+# -*- coding: utf-8 -*-
+
+import os, time, utilist
+
+VERSION="0.1"
+
+def process_cmdline(pid_info):
+ if pid_info.has_key("cmdline"):
+ return reduce(lambda a, b: a + " %s" % b, pid_info["cmdline"]).strip()
+
+ return pid_info["stat"]["comm"]
+
+class pidstats:
+ proc_stat_fields = [ "pid", "comm", "state", "ppid", "pgrp", "session",
+ "tty_nr", "tpgid", "flags", "minflt", "cminflt",
+ "majflt", "cmajflt", "utime", "stime", "cutime",
+ "cstime", "priority", "nice", "num_threads",
+ "itrealvalue", "starttime", "vsize", "rss",
+ "rlim", "startcode", "endcode", "startstack",
+ "kstkesp", "kstkeip", "signal", "blocked",
+ "sigignore", "sigcatch", "wchan", "nswap",
+ "cnswap", "exit_signal", "processor",
+ "rt_priority", "policy",
+ "delayacct_blkio_ticks" ]
+
+ def __init__(self, basedir = "/proc"):
+ self.basedir = basedir
+ self.processes = {}
+ self.reload()
+
+ def __getitem__(self, key):
+ return self.processes[key]
+
+ def __delitem__(self, key):
+ # not clear on why this can fail, but it can
+ try:
+ del self.processes[key]
+ except:
+ pass
+
+ def keys(self):
+ return self.processes.keys()
+
+ def has_key(self, key):
+ return self.processes.has_key(key)
+
+ def read_stat_entry(self, pid):
+ f = open("%s/%d/stat" % (self.basedir, pid))
+ tags = {}
+ fields = f.readline().strip().split()
+ nr_fields = min(len(fields), len(self.proc_stat_fields))
+ for i in range(nr_fields):
+ tags[self.proc_stat_fields[i]] = fields[i]
+ tags["comm"] = tags["comm"].strip('()')
+ f.close()
+ return tags
+
+ def read_status_entry(self, pid):
+ f = open("%s/%d/status" % (self.basedir, pid))
+ tags = {}
+ for line in f.readlines():
+ fields = line.split(":")
+ tags[fields[0]] = fields[1].strip()
+ f.close()
+ return tags
+
+ def reload(self):
+ del self.processes
+ self.processes = {}
+ pids = os.listdir(self.basedir)
+ for spid in pids:
+ try:
+ pid = int(spid)
+ except:
+ continue
+
+ self.processes[pid] = {}
+ try:
+ self.processes[pid]["stat"] = self.read_stat_entry(pid)
+ except:
+ del self.processes[pid]
+ continue
+
+ try:
+ self.processes[pid]["status"] = self.read_status_entry(pid)
+ except:
+ del self.processes[pid]
+
+ def reload_threads(self):
+ for pid in self.processes.keys():
+ threads = pidstats("/proc/%d/task/" % pid)
+ # remove thread leader
+ del threads[pid]
+ if not threads.keys():
+ continue
+ self.processes[pid]["threads"] = threads
+
+ def load_cmdline(self):
+ for pid in self.processes.keys():
+ if self.processes[pid].has_key("cmdline"):
+ continue
+ f = file("/proc/%d/cmdline" % pid)
+ line = f.readline()
+ if line:
+ self.processes[pid]["cmdline"] = line.strip().split('\0')
+ f.close()
+
+ def find_by_name(self, name):
+ name = name[:15]
+ pids = []
+ for pid in self.processes.keys():
+ if self.processes[pid]["stat"]["comm"] == name:
+ pids.append(pid)
+ return pids
+
+ def find_by_regex(self, regex):
+ pids = []
+ for pid in self.processes.keys():
+ if regex.match(self.processes[pid]["stat"]["comm"]):
+ pids.append(pid)
+ return pids
+
+ def find_by_cmdline_regex(self, regex):
+ pids = []
+ for pid in self.processes.keys():
+ if regex.match(process_cmdline(self.processes[pid])):
+ pids.append(pid)
+ return pids
+
+ def get_per_cpu_rtprios(self, basename):
+ cpu = 0
+ priorities=""
+ processed_pids = []
+ while True:
+ name = "%s/%d" % (basename, cpu)
+ pids = self.find_by_name(name)
+ if not pids or len([n for n in pids if n not in processed_pids]) == 0:
+ break
+ for pid in pids:
+ priorities += "%s," % self.processes[pid]["stat"]["rt_priority"]
+ processed_pids += pids
+ cpu += 1
+
+ priorities = priorities.strip(',')
+ return priorities
+
+ def get_rtprios(self, name):
+ cpu = 0
+ priorities=""
+ processed_pids = []
+ while True:
+ pids = self.find_by_name(name)
+ if not pids or len([n for n in pids if n not in processed_pids]) == 0:
+ break
+ for pid in pids:
+ priorities += "%s," % self.processes[pid]["stat"]["rt_priority"]
+ processed_pids += pids
+ cpu += 1
+
+ priorities = priorities.strip(',')
+ return priorities
+
+class interrupts:
+ def __init__(self):
+ self.interrupts = {}
+ self.reload()
+
+ def __getitem__(self, key):
+ return self.interrupts[str(key)]
+
+ def keys(self):
+ return self.interrupts.keys()
+
+ def has_key(self, key):
+ return self.interrupts.has_key(str(key))
+
+ def reload(self):
+ del self.interrupts
+ self.interrupts = {}
+ f = open("/proc/interrupts")
+
+ for line in f.readlines():
+ line = line.strip()
+ fields = line.split()
+ if fields[0][:3] == "CPU":
+ self.nr_cpus = len(fields)
+ continue
+ irq = fields[0].strip(":")
+ self.interrupts[irq] = {}
+ self.interrupts[irq] = self.parse_entry(fields[1:], line)
+ try:
+ nirq = int(irq)
+ except:
+ continue
+ self.interrupts[irq]["affinity"] = self.parse_affinity(nirq)
+
+ f.close()
+
+ def parse_entry(self, fields, line):
+ dict = {}
+ dict["cpu"] = []
+ dict["cpu"].append(int(fields[0]))
+ nr_fields = len(fields)
+ if nr_fields >= self.nr_cpus:
+ dict["cpu"] += [int(i) for i in fields[1:self.nr_cpus]]
+ if nr_fields > self.nr_cpus:
+ dict["type"] = fields[self.nr_cpus]
+ # look if there are users (interrupts 3 and 4 haven't)
+ if nr_fields > self.nr_cpus + 1:
+ dict["users"] = [a.strip() for a in line[line.index(fields[self.nr_cpus + 1]):].split(',')]
+ else:
+ dict["users"] = []
+ return dict
+
+ def parse_affinity(self, irq):
+ if os.getuid() != 0:
+ return
+ try:
+ f = file("/proc/irq/%s/smp_affinity" % irq)
+ line = f.readline()
+ f.close()
+ return utilist.bitmasklist(line, self.nr_cpus)
+ except IOError:
+ return [ 0, ]
+
+ def find_by_user(self, user):
+ for i in self.interrupts.keys():
+ if self.interrupts[i].has_key("users") and \
+ user in self.interrupts[i]["users"]:
+ return i
+ return None
+
+class cmdline:
+ def __init__(self):
+ self.options = {}
+ self.parse()
+
+ def parse(self):
+ f = file("/proc/cmdline")
+ for option in f.readline().strip().split():
+ fields = option.split("=")
+ if len(fields) == 1:
+ self.options[fields[0]] = True
+ else:
+ self.options[fields[0]] = fields[1]
+
+ f.close()
+
+class cpuinfo:
+ def __init__(self):
+ self.tags = {}
+ self.nr_cpus = 0
+ self.parse()
+
+ def __getitem__(self, key):
+ return self.tags[key.lower()]
+
+ def keys(self):
+ return self.tags.keys()
+
+ def parse(self):
+ f = file("/proc/cpuinfo")
+ for line in f.readlines():
+ line = line.strip()
+ if len(line) == 0:
+ continue
+ fields = line.split(":")
+ tagname = fields[0].strip().lower()
+ if tagname == "processor":
+ self.nr_cpus += 1
+ continue
+ elif tagname == "core id":
+ continue
+ self.tags[tagname] = fields[1].strip()
+
+ f.close()
+
+class smaps_lib:
+ def __init__(self, lines):
+ fields = lines[0].split()
+ self.vm_start, self.vm_end = map(lambda a: int(a, 16), fields[0].split("-"))
+ self.perms = fields[1]
+ self.offset = int(fields[2], 16)
+ self.major, self.minor = fields[3].split(":")
+ self.inode = int(fields[4])
+ if len(fields) > 5:
+ self.name = fields[5]
+ else:
+ self.name = None
+ self.tags = {}
+ for line in lines[1:]:
+ fields = line.split()
+ self.tags[fields[0][:-1].lower()] = int(fields[1])
+
+ def __getitem__(self, key):
+ return self.tags[key.lower()]
+
+ def keys(self):
+ return self.tags.keys()
+
+
+class smaps:
+ def __init__(self, pid):
+ self.pid = pid
+ self.entries = []
+ self.reload()
+
+ def parse_entry(self, f, line):
+ lines = []
+ if not line:
+ line = f.readline().strip()
+ if not line:
+ return
+ lines.append(line)
+ while True:
+ line = f.readline()
+ if not line:
+ break
+ line = line.strip()
+ if len(line.split()) < 4:
+ lines.append(line)
+ else:
+ break
+ self.entries.append(smaps_lib(lines))
+ return line
+
+ def reload(self):
+ f = file("/proc/%d/smaps" % self.pid)
+ line = None
+ while True:
+ line = self.parse_entry(f, line)
+ if not line:
+ break
+ f.close()
+ self.nr_entries = len(self.entries)
+
+ def find_by_name_fragment(self, fragment):
+ result = []
+ for i in range(self.nr_entries):
+ if self.entries[i].name and \
+ self.entries[i].name.find(fragment) >= 0:
+ result.append(self.entries[i])
+
+ return result
+
+class cpustat:
+ def __init__(self, fields):
+ self.name = fields[0]
+ (self.user,
+ self.nice,
+ self.system,
+ self.idle,
+ self.iowait,
+ self.irq,
+ self.softirq,
+ self.steal) = [int(i) for i in fields[1:9]]
+ if len(fields) > 8:
+ self.guest = int(fields[8])
+
+class cpusstats:
+ def __init__(self):
+ self.entries = []
+ self.time = None
+ self.hertz = os.sysconf(2)
+ self.reload()
+
+ def __iter__(self):
+ return iter(self.entries)
+
+ def __getitem__(self, key):
+ return self.entries[key]
+
+ def __len__(self):
+ return len(self.entries)
+
+ def reload(self):
+ last_entries = self.entries
+ self.entries = []
+ f = file("/proc/stat")
+ for line in f.readlines():
+ fields = line.strip().split()
+ if fields[0][:3].lower() != "cpu":
+ continue
+ self.entries.append(cpustat(fields))
+ f.close()
+ last_time = self.time
+ self.time = time.time()
+ if len(last_entries) > 0:
+ delta_sec = self.time - last_time
+ interval_hz = delta_sec * self.hertz
+ for cpu in range(len(last_entries)):
+ curr = self.entries[cpu]
+ prev = last_entries[cpu]
+ delta = (curr.user - prev.user) + \
+ (curr.nice - prev.nice) + \
+ (curr.system - prev.system)
+ curr.usage = (delta / interval_hz) * 100
+ if curr.usage > 100:
+ curr.usage = 100
+
+if __name__ == '__main__':
+ import sys
+
+ ints = interrupts()
+
+ for i in ints.interrupts.keys():
+ print "%s: %s" % (i, ints.interrupts[i])
+
+ options = cmdline()
+ for o in options.options.keys():
+ print "%s: %s" % (o, options.options[o])
+
+ cpu = cpuinfo()
+ print "\ncpuinfo data: %d processors" % cpu.nr_cpus
+ for tag in cpu.keys():
+ print "%s=%s" % (tag, cpu[tag])
+
+ print "smaps:\n" + ("-" * 40)
+ s = smaps(int(sys.argv[1]))
+ for i in range(s.nr_entries):
+ print "%#x %s" % (s.entries[i].vm_start, s.entries[i].name)
+ print "-" * 40
+ for a in s.find_by_name_fragment(sys.argv[2]):
+ print a["Size"]
+
+ ps = pidstats()
+ print ps[1]
+
+ cs = cpusstats()
+ while True:
+ time.sleep(1)
+ cs.reload()
+ for cpu in cs:
+ print "%s: %d" % (cpu.name, cpu.usage)
+ print "-" * 10
diff --git a/procfs/sysctl.py b/procfs/sysctl.py
new file mode 100755
index 0000000..c49bc54
--- /dev/null
+++ b/procfs/sysctl.py
@@ -0,0 +1,52 @@
+#! /usr/bin/python
+# -*- python -*-
+# -*- coding: utf-8 -*-
+
+class sysctl:
+ def __init__(self):
+ self.cache = {}
+
+ def __getitem__(self, key):
+ if not self.cache.has_key(key):
+ value = self.read(key)
+ if value == None:
+ return None
+ self.cache[key] = value
+
+ return self.cache[key]
+
+ def __setitem__(self, key, value):
+ oldvalue = self[key]
+
+ if oldvalue == None:
+ raise IOError
+ elif oldvalue != value:
+ self.write(key, value)
+ self.cache[key] = value
+
+ def keys(self):
+ return self.cache.keys()
+
+ def read(self, key):
+ try:
+ f = file("/proc/sys/%s" % key.replace(".", "/"))
+ except:
+ return None
+ value = f.readline().strip()
+ f.close()
+ return value
+
+ def write(self, key, value):
+ try:
+ f = file("/proc/sys/%s" % key.replace(".", "/"), "w")
+ except:
+ return
+ f.write(value)
+ f.close()
+
+ def refresh(self):
+ for key in self.cache():
+ del self.cache[key]
+ value = self.read(key)
+ if value != None:
+ self.cache[key] = value
diff --git a/procfs/utilist.py b/procfs/utilist.py
new file mode 100755
index 0000000..23fa171
--- /dev/null
+++ b/procfs/utilist.py
@@ -0,0 +1,38 @@
+#! /usr/bin/python
+# -*- python -*-
+# -*- coding: utf-8 -*-
+
+def hexbitmask(l, nr_entries):
+ hexbitmask = []
+ bit = 0
+ mask = 0
+ for entry in range(nr_entries):
+ if entry in l:
+ mask |= (1 << bit)
+ bit += 1
+ if bit == 32:
+ bit = 0
+ hexbitmask.insert(0, mask)
+ mask = 0
+
+ if bit < 32 and mask != 0:
+ hexbitmask.insert(0, mask)
+
+ return hexbitmask
+
+def bitmasklist(line, nr_entries):
+ fields = line.strip().split(",")
+ bitmasklist = []
+ entry = 0
+ for i in range(len(fields) - 1, -1, -1):
+ mask = int(fields[i], 16)
+ while mask != 0:
+ if mask & 1:
+ bitmasklist.append(entry)
+ mask >>= 1
+ entry += 1
+ if entry == nr_entries:
+ break
+ if entry == nr_entries:
+ break
+ return bitmasklist
diff --git a/rpm/SPECS/python-linux-procfs.spec b/rpm/SPECS/python-linux-procfs.spec
new file mode 100644
index 0000000..6379619
--- /dev/null
+++ b/rpm/SPECS/python-linux-procfs.spec
@@ -0,0 +1,37 @@
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+
+Name: python-linux-procfs
+Version: 0.1
+Release: 1%{?dist}
+License: GPLv2
+Summary: Linux /proc abstraction classes
+Group: Application/System
+Source: http://userweb.kernel.org/~acme/python-linux-procfs/%{name}-%{version}.tar.bz2
+BuildArch: noarch
+BuildRequires: python-devel
+BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
+
+%description
+Abstractions to extract information from the Linux kernel /proc files.
+
+%prep
+%setup -q -c -n %{name}-%{version}
+
+%build
+%{__python} setup.py build
+
+%install
+rm -rf %{buildroot}
+%{__python} setup.py install --skip-build --root %{buildroot}
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(0755,root,root,0755)
+%{python_sitelib}/procfs/
+%{python_sitelib}/*.egg-info
+
+%changelog
+* Mon Feb 25 2008 Arnaldo Carvalho de Melo <acme@redhat.com> - 0.1-1
+- package created
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..9e3d9e5
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+from distutils.sysconfig import get_python_lib
+from distutils.core import setup
+from os.path import isfile, join
+import glob
+import os
+
+if isfile("MANIFEST"):
+ os.unlink("MANIFEST")
+
+# Get PYTHONLIB with no prefix so --prefix installs work.
+PYTHONLIB = join(get_python_lib(standard_lib=1, prefix=''), 'site-packages')
+
+setup(name="python-linux-procfs",
+ version = "0.1",
+ description = "Linux /proc abstraction classes",
+ author = "Arnaldo Carvalho de Melo",
+ author_email = "acme@redhat.com",
+ url = "http://userweb.kernel.org/python-linux-procfs",
+ license = "GPLv2",
+ long_description =
+"""\
+Abstractions to extract information from the Linux kernel /proc files.
+""",
+ packages = ["procfs"],
+ )