[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r23975: {arm} Querying connection information via the proc contents. This (in arm/trunk: . src src/interface src/util)
Author: atagar
Date: 2010-12-23 06:22:55 +0000 (Thu, 23 Dec 2010)
New Revision: 23975
Added:
arm/trunk/src/util/procTools.py
Modified:
arm/trunk/README
arm/trunk/armrc.sample
arm/trunk/src/interface/controller.py
arm/trunk/src/settings.cfg
arm/trunk/src/starter.py
arm/trunk/src/test.py
arm/trunk/src/util/__init__.py
arm/trunk/src/util/connections.py
arm/trunk/src/util/sysTools.py
Log:
Querying connection information via the proc contents.
This runs around 90% faster than netstat, lsof, or any other connection resolver. I've also added (and tested) utilities that can be used to replace netstat queries, though arm isn't configured yet to use them.
Modified: arm/trunk/README
===================================================================
--- arm/trunk/README 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/README 2010-12-23 06:22:55 UTC (rev 23975)
@@ -144,6 +144,7 @@
hostnames.py - service providing nonblocking reverse dns lookups
log.py - aggregator for application events
panel.py - wrapper for safely working with curses subwindows
+ procTools.py - queries process & system information from /proc contents
sysTools.py - helper for system calls, providing client side caching
torConfig.py - functions for working with the torrc and config options
torTools.py - TorCtl wrapper, providing caching and derived information
Modified: arm/trunk/armrc.sample
===================================================================
--- arm/trunk/armrc.sample 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/armrc.sample 2010-12-23 06:22:55 UTC (rev 23975)
@@ -195,6 +195,7 @@
log.torSetConf INFO
log.torEventTypeUnrecognized NOTICE
log.torPrefixPathInvalid NOTICE
+log.procCallMade DEBUG
log.sysCallMade DEBUG
log.sysCallCached NONE
log.sysCallFailed INFO
Modified: arm/trunk/src/interface/controller.py
===================================================================
--- arm/trunk/src/interface/controller.py 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/src/interface/controller.py 2010-12-23 06:22:55 UTC (rev 23975)
@@ -1426,7 +1426,7 @@
panels["conn"].sortConnections()
elif page == 1 and (key == ord('u') or key == ord('U')):
# provides menu to pick identification resolving utility
- optionTypes = [None, connections.CMD_NETSTAT, connections.CMD_SOCKSTAT, connections.CMD_LSOF, connections.CMD_SS, connections.CMD_BSD_SOCKSTAT, connections.CMD_BSD_PROCSTAT]
+ optionTypes = [None, connections.CMD_PROC, connections.CMD_NETSTAT, connections.CMD_SOCKSTAT, connections.CMD_LSOF, connections.CMD_SS, connections.CMD_BSD_SOCKSTAT, connections.CMD_BSD_PROCSTAT]
options = ["auto"] + [connections.CMD_STR[util] for util in optionTypes[1:]]
initialSelection = connections.getResolver("tor").overwriteResolver # enums correspond to indices
Modified: arm/trunk/src/settings.cfg
===================================================================
--- arm/trunk/src/settings.cfg 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/src/settings.cfg 2010-12-23 06:22:55 UTC (rev 23975)
@@ -39,6 +39,7 @@
# (Network is unreachable; NOROUTE; count 47; recommendation warn)
# [WARN] 4 unknown, 1 missing key, 3 good, 0 bad, 1 no signature, 4 required
# [ARM_DEBUG] refresh rate: 0.001 seconds
+# [ARM_DEBUG] proc call (process connections): /proc/net/[tcp|udp] (runtime: 0.0018)
# [ARM_DEBUG] system call: ps -p 2354 -o %cpu,rss,%mem,etime (runtime: 0.02)
# [ARM_DEBUG] system call: netstat -npt | grep 2354/tor (runtime: 0.02)
# [ARM_DEBUG] recreating panel 'graph' with the dimensions of 14/124
@@ -66,6 +67,13 @@
msg.WARN Problem bootstrapping. Stuck at
msg.WARN *missing key,
msg.ARM_DEBUG refresh rate:
+msg.ARM_DEBUG proc call (process cwd):
+msg.ARM_DEBUG proc call (process memory usage):
+msg.ARM_DEBUG proc call (process command
+msg.ARM_DEBUG proc call (process utime
+msg.ARM_DEBUG proc call (process stime
+msg.ARM_DEBUG proc call (process start time
+msg.ARM_DEBUG proc call (process connections):
msg.ARM_DEBUG system call: ps
msg.ARM_DEBUG system call: netstat
msg.ARM_DEBUG recreating panel '
Modified: arm/trunk/src/starter.py
===================================================================
--- arm/trunk/src/starter.py 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/src/starter.py 2010-12-23 06:22:55 UTC (rev 23975)
@@ -19,6 +19,7 @@
import util.hostnames
import util.log
import util.panel
+import util.procTools
import util.sysTools
import util.torConfig
import util.torTools
@@ -218,7 +219,7 @@
config.update(CONFIG)
# loads user preferences for utilities
- for utilModule in (util.conf, util.connections, util.hostnames, util.log, util.panel, util.sysTools, util.torConfig, util.torTools, util.uiTools):
+ for utilModule in (util.conf, util.connections, util.hostnames, util.log, util.panel, util.procTools, util.sysTools, util.torConfig, util.torTools, util.uiTools):
utilModule.loadConfig(config)
# overwrites undefined parameters with defaults
Modified: arm/trunk/src/test.py
===================================================================
--- arm/trunk/src/test.py 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/src/test.py 2010-12-23 06:22:55 UTC (rev 23975)
@@ -67,7 +67,7 @@
# provide the selection options
printDivider()
print("Select a resolver:")
- for i in range(1, 7):
+ for i in range(1, 8):
print(" %i. %s" % (i, connections.CMD_STR[i]))
print(" q. Go back to the main menu")
@@ -76,7 +76,7 @@
printDivider()
break
- if userSelection.isdigit() and int(userSelection) in range(1, 7):
+ if userSelection.isdigit() and int(userSelection) in range(1, 8):
try:
resolver = int(userSelection)
startTime = time.time()
Modified: arm/trunk/src/util/__init__.py
===================================================================
--- arm/trunk/src/util/__init__.py 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/src/util/__init__.py 2010-12-23 06:22:55 UTC (rev 23975)
@@ -4,5 +4,5 @@
and safely working with curses (hiding some of the gory details).
"""
-__all__ = ["conf", "connections", "hostnames", "log", "panel", "sysTools", "torConfig", "torTools", "uiTools"]
+__all__ = ["conf", "connections", "hostnames", "log", "panel", "procTools", "sysTools", "torConfig", "torTools", "uiTools"]
Modified: arm/trunk/src/util/connections.py
===================================================================
--- arm/trunk/src/util/connections.py 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/src/util/connections.py 2010-12-23 06:22:55 UTC (rev 23975)
@@ -18,15 +18,15 @@
"""
import os
-import sys
import time
import threading
-from util import log, sysTools
+from util import log, procTools, sysTools
# enums for connection resolution utilities
-CMD_NETSTAT, CMD_SOCKSTAT, CMD_LSOF, CMD_SS, CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT = range(1, 7)
-CMD_STR = {CMD_NETSTAT: "netstat",
+CMD_PROC, CMD_NETSTAT, CMD_SOCKSTAT, CMD_LSOF, CMD_SS, CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT = range(1, 8)
+CMD_STR = {CMD_PROC: "proc",
+ CMD_NETSTAT: "netstat",
CMD_SS: "ss",
CMD_LSOF: "lsof",
CMD_SOCKSTAT: "sockstat",
@@ -105,7 +105,8 @@
# if the pid was undefined then match any in that field
processPid = "[0-9]*"
- if resolutionCmd == CMD_NETSTAT: return RUN_NETSTAT % (processPid, processName)
+ if resolutionCmd == CMD_PROC: return ""
+ elif resolutionCmd == CMD_NETSTAT: return RUN_NETSTAT % (processPid, processName)
elif resolutionCmd == CMD_SS: return RUN_SS % (processName, processPid)
elif resolutionCmd == CMD_LSOF: return RUN_LSOF % (processName, processPid)
elif resolutionCmd == CMD_SOCKSTAT: return RUN_SOCKSTAT % (processName, processPid)
@@ -130,46 +131,56 @@
processPid - process ID (this helps improve accuracy)
"""
-
- # raises an IOError if the command fails or isn't available
- cmd = getResolverCommand(resolutionCmd, processName, processPid)
- results = sysTools.call(cmd)
-
- if not results: raise IOError("No results found using: %s" % cmd)
-
- # parses results for the resolution command
- conn = []
- for line in results:
- if resolutionCmd == CMD_LSOF:
- # Different versions of lsof have different numbers of columns, so
- # stripping off the optional 'established' entry so we can just use
- # the last one.
- comp = line.replace("(ESTABLISHED)", "").strip().split()
- else: comp = line.split()
+ if resolutionCmd == CMD_PROC:
+ # Attempts resolution via checking the proc contents.
+ if not processPid:
+ raise ValueError("proc resolution requires a pid")
- if resolutionCmd == CMD_NETSTAT:
- localIp, localPort = comp[3].split(":")
- foreignIp, foreignPort = comp[4].split(":")
- elif resolutionCmd == CMD_SS:
- localIp, localPort = comp[4].split(":")
- foreignIp, foreignPort = comp[5].split(":")
- elif resolutionCmd == CMD_LSOF:
- local, foreign = comp[-1].split("->")
- localIp, localPort = local.split(":")
- foreignIp, foreignPort = foreign.split(":")
- elif resolutionCmd == CMD_SOCKSTAT:
- localIp, localPort = comp[4].split(":")
- foreignIp, foreignPort = comp[5].split(":")
- elif resolutionCmd == CMD_BSD_SOCKSTAT:
- localIp, localPort = comp[5].split(":")
- foreignIp, foreignPort = comp[6].split(":")
- elif resolutionCmd == CMD_BSD_PROCSTAT:
- localIp, localPort = comp[9].split(":")
- foreignIp, foreignPort = comp[10].split(":")
+ try:
+ return procTools.getConnections(processPid)
+ except Exception, exc:
+ raise IOError(str(exc))
+ else:
+ # Queries a resolution utility (netstat, lsof, etc). This raises an
+ # IOError if the command fails or isn't available.
+ cmd = getResolverCommand(resolutionCmd, processName, processPid)
+ results = sysTools.call(cmd)
- conn.append((localIp, localPort, foreignIp, foreignPort))
-
- return conn
+ if not results: raise IOError("No results found using: %s" % cmd)
+
+ # parses results for the resolution command
+ conn = []
+ for line in results:
+ if resolutionCmd == CMD_LSOF:
+ # Different versions of lsof have different numbers of columns, so
+ # stripping off the optional 'established' entry so we can just use
+ # the last one.
+ comp = line.replace("(ESTABLISHED)", "").strip().split()
+ else: comp = line.split()
+
+ if resolutionCmd == CMD_NETSTAT:
+ localIp, localPort = comp[3].split(":")
+ foreignIp, foreignPort = comp[4].split(":")
+ elif resolutionCmd == CMD_SS:
+ localIp, localPort = comp[4].split(":")
+ foreignIp, foreignPort = comp[5].split(":")
+ elif resolutionCmd == CMD_LSOF:
+ local, foreign = comp[-1].split("->")
+ localIp, localPort = local.split(":")
+ foreignIp, foreignPort = foreign.split(":")
+ elif resolutionCmd == CMD_SOCKSTAT:
+ localIp, localPort = comp[4].split(":")
+ foreignIp, foreignPort = comp[5].split(":")
+ elif resolutionCmd == CMD_BSD_SOCKSTAT:
+ localIp, localPort = comp[5].split(":")
+ foreignIp, foreignPort = comp[6].split(":")
+ elif resolutionCmd == CMD_BSD_PROCSTAT:
+ localIp, localPort = comp[9].split(":")
+ foreignIp, foreignPort = comp[10].split(":")
+
+ conn.append((localIp, localPort, foreignIp, foreignPort))
+
+ return conn
def isResolverAlive(processName, processPid = ""):
"""
@@ -226,8 +237,12 @@
"""
if osType == None: osType = os.uname()[0]
- if osType == "FreeBSD": return [CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT, CMD_LSOF]
- else: return [CMD_NETSTAT, CMD_SOCKSTAT, CMD_LSOF, CMD_SS]
+ if osType == "FreeBSD":
+ return [CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT, CMD_LSOF]
+ elif osType == "Linux":
+ return [CMD_PROC, CMD_NETSTAT, CMD_SOCKSTAT, CMD_LSOF, CMD_SS]
+ else:
+ return [CMD_NETSTAT, CMD_SOCKSTAT, CMD_LSOF, CMD_SS]
class ConnectionResolver(threading.Thread):
"""
@@ -292,7 +307,7 @@
self.defaultRate = CONFIG["queries.connections.minRate"]
self.lastLookup = -1
self.overwriteResolver = None
- self.defaultResolver = CMD_NETSTAT
+ self.defaultResolver = CMD_PROC
osType = os.uname()[0]
self.resolverOptions = getSystemResolvers(osType)
@@ -303,7 +318,7 @@
# sets the default resolver to be the first found in the system's PATH
# (left as netstat if none are found)
for resolver in self.resolverOptions:
- if sysTools.isAvailable(CMD_STR[resolver]):
+ if resolver == CMD_PROC or sysTools.isAvailable(CMD_STR[resolver]):
self.defaultResolver = resolver
break
Added: arm/trunk/src/util/procTools.py
===================================================================
--- arm/trunk/src/util/procTools.py (rev 0)
+++ arm/trunk/src/util/procTools.py 2010-12-23 06:22:55 UTC (rev 23975)
@@ -0,0 +1,269 @@
+"""
+Helper functions for querying process and system information from the /proc
+contents. Fetching information this way provides huge performance benefits
+over lookups via system utilities (ps, netstat, etc). For instance, resolving
+connections this way cuts the runtime by around 90% verses the alternatives.
+These functions may not work on all platforms (only Linux?).
+
+All functions raise IOErrors if unable to read their respective proc files.
+
+The method for reading these files (and some of the code) are borrowed from
+psutil:
+https://code.google.com/p/psutil/
+which was written by Jay Loden, Dave Daeschler, Giampaolo Rodola' and is under
+the BSD license.
+"""
+
+import os
+import sys
+import time
+import socket
+import base64
+
+from util import log
+
+# TODO: need to log the runtimes for proc queries
+# TODO: need to update the header panel time every second to give a more fluid appearance
+# TODO: finish integrating proc connection selection into arm's interface
+# TODO: integrate ps resultion into arm's header panel
+
+# cached system values
+SYS_START_TIME, SYS_PHYSICAL_MEMORY = None, None
+CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
+STAT_COMMAND, STAT_CPU_UTIME, STAT_CPU_STIME, STAT_START_TIME = range(4)
+
+CONFIG = {"log.procCallMade": log.DEBUG}
+
+def loadConfig(config):
+ config.update(CONFIG)
+
+def getSystemStartTime():
+ """
+ Provides the unix time (seconds since epoch) when the system started.
+ """
+
+ global SYS_START_TIME
+ if not SYS_START_TIME:
+ startTime = time.time()
+ statFile = open('/proc/stat')
+ statLines = statFile.readlines()
+ statFile.close()
+
+ for line in statLines:
+ if line.startswith('btime'):
+ SYS_START_TIME = float(line.strip().split()[1])
+ break
+
+ _logProcRuntime("system start time", "/proc/stat[btime]", startTime)
+
+ return SYS_START_TIME
+
+def getPhysicalMemory():
+ """
+ Provides the total physical memory on the system in bytes.
+ """
+
+ global SYS_PHYSICAL_MEMORY
+ if not SYS_PHYSICAL_MEMORY:
+ startTime = time.time()
+ memFile = open('/proc/meminfo')
+ memLines = memFile.readlines()
+ memFile.close()
+
+ for line in memLines:
+ if line.startswith('MemTotal:'):
+ SYS_PHYSICAL_MEMORY = int(line.split()[1]) * 1024
+
+ _logProcRuntime("system physical memory", "/proc/meminfo[MemTotal]", startTime)
+
+ return SYS_PHYSICAL_MEMORY
+
+def getCwd(pid):
+ """
+ Provides the current working directory for the given process.
+
+ Arguments:
+ pid - queried process
+ """
+
+ startTime = time.time()
+ if pid == 0: cwd = ""
+ else: cwd = os.readlink("/proc/%s/cwd" % pid)
+ _logProcRuntime("cwd", "/proc/%s/cwd" % pid, startTime)
+ return cwd
+
+def getMemoryUsage(pid):
+ """
+ Provides the memory usage in bytes for the given process of the form:
+ (residentSize, virtualSize)
+
+ Arguments:
+ pid - queried process
+ """
+
+ # checks if this is the kernel process
+ if pid == 0: return (0, 0)
+
+ startTime = time.time()
+ statusFile = open("/proc/%s/status" % pid)
+ statusFileLines = statusFile.readlines()
+ statusFile.close()
+
+ residentSize, virtualSize = None, None
+ for line in statusFileLines:
+ if line.startswith("VmRSS"):
+ residentSize = int(line.split()[1]) * 1024
+ if virtualSize != None: break
+ elif line.startswith("VmSize:"):
+ virtualSize = int(line.split()[1]) * 1024
+ if residentSize != None: break
+
+ _logProcRuntime("memory usage", "/proc/%s/status[VmRSS|VmSize]" % pid, startTime)
+ return (residentSize, virtualSize)
+
+def getStats(pid, *statTypes):
+ """
+ Provides process specific information. Options are:
+ STAT_COMMAND command name under which the process is running
+ STAT_CPU_UTIME total user time spent on the process
+ STAT_CPU_STIME total system time spent on the process
+ STAT_START_TIME when this process began, in unix time
+
+ Arguments:
+ pid - queried process
+ statTypes - information to be provided back
+ """
+
+ startTime = time.time()
+ statFilePath = "/proc/%s/stat" % pid
+ statFile = open(statFilePath)
+ statContents = statFile.read().strip()
+ statFile.close()
+
+ # contents are of the form:
+ # 8438 (tor) S 8407 8438 8407 34818 8438 4202496...
+ statComp = []
+ cmdStart, cmdEnd = statContents.find("("), statContents.find(")")
+
+ if cmdStart != -1 and cmdEnd != -1:
+ statComp.append(statContents[:cmdStart])
+ statComp.append(statContents[cmdStart + 1:cmdEnd])
+ statComp += statContents[cmdEnd + 1:].split()
+
+ if len(statComp) != 44:
+ raise IOError("stat file had an unexpected format: %s" % statFilePath)
+
+ results, queriedStats = [], []
+ for statType in statTypes:
+ if statType == STAT_COMMAND:
+ queriedStats.append("command")
+ if pid == 0: results.append("sched")
+ else: results.append(statComp[1])
+ elif statType == STAT_CPU_UTIME:
+ queriedStats.append("utime")
+ if pid == 0: results.append("0")
+ else: results.append(str(float(statComp[13]) / CLOCK_TICKS))
+ elif statType == STAT_CPU_STIME:
+ queriedStats.append("stime")
+ if pid == 0: results.append("0")
+ else: results.append(str(float(statComp[14]) / CLOCK_TICKS))
+ elif statType == STAT_START_TIME:
+ queriedStats.append("start time")
+ if pid == 0: return getSystemStartTime()
+ else:
+ # According to documentation, starttime is in field 21 and the unit is
+ # jiffies (clock ticks). We divide it for clock ticks, then add the
+ # uptime to get the seconds since the epoch.
+ pStartTime = float(statComp[21]) / CLOCK_TICKS
+ results.append(str(pStartTime + getSystemStartTime()))
+
+ _logProcRuntime("process %s" % ", ".join(queriedStats), "/proc/%s/stat" % pid, startTime)
+ return results
+
+def getConnections(pid):
+ """
+ Provides a listing of connection tuples of the form:
+ [(local_ipAddr1, local_port1, foreign_ipAddr1, foreign_port1), ...]
+
+ If the information about a connection can't be queried (often due to
+ permission issues) then it's excluded from the listing.
+
+ Arguments:
+ pid - ID of the process to be resolved
+ """
+
+ if pid == "0": return []
+
+ # fetches the inode numbers for socket file descriptors
+ startTime = time.time()
+ inodes = []
+ for fd in os.listdir("/proc/%s/fd" % pid):
+ try:
+ # File descriptor link, such as 'socket:[30899]'
+ fdName = os.readlink("/proc/%s/fd/%s" % (pid, fd))
+
+ if fdName.startswith('socket:['):
+ inodes.append(fdName[8:-1])
+ except OSError:
+ pass # most likely couldn't be read due to permissions
+
+ if not inodes:
+ # unable to fetch any connections for this process
+ return []
+
+ # check for the connection information from the /proc/net contents
+ conn = []
+ for procFilePath in ("/proc/net/tcp", "/proc/net/udp"):
+ procFile = open(procFilePath)
+ procFile.readline() # skip the first line
+
+ for line in procFile:
+ _, lAddr, fAddr, status, _, _, _, _, _, inode = line.split()[:10]
+
+ if inode in inodes:
+ # if a tcp connection, skip if it isn't yet established
+ if procFilePath.endswith("/tcp") and status != "01":
+ continue
+
+ localIp, localPort = _decodeProcAddressEncoding(lAddr)
+ foreignIp, foreignPort = _decodeProcAddressEncoding(fAddr)
+ conn.append((localIp, localPort, foreignIp, foreignPort))
+
+ procFile.close()
+
+ _logProcRuntime("process connections", "/proc/net/[tcp|udp]", startTime)
+
+ return conn
+
+def _decodeProcAddressEncoding(addr):
+ """
+ Translates an address entry in the /proc/net/* contents to a human readable
+ form, for instance:
+ "0500000A:0016" -> ("10.0.0.5", "22")
+
+ Reference:
+ http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html
+
+ Arguments:
+ addr - proc address entry to be decoded
+ """
+
+ ip, port = addr.split(':')
+
+ # the port is represented as a two-byte hexadecimal number
+ port = str(int(port, 16))
+
+ if sys.version_info >= (3,):
+ ip = ip.encode('ascii')
+
+ # The IPv4 address portion is a little-endian four-byte hexadecimal number.
+ # That is, the least significant byte is listed first, so we need to reverse
+ # the order of the bytes to convert it to an IP address.
+ ip = socket.inet_ntop(socket.AF_INET, base64.b16decode(ip)[::-1])
+
+ return (ip, port)
+
+def _logProcRuntime(parameter, procLocation, startTime):
+ msg = "proc call (%s): %s (runtime: %0.4f)" % (parameter, procLocation, time.time() - startTime)
+ log.log(CONFIG["log.procCallMade"], msg)
+
Modified: arm/trunk/src/util/sysTools.py
===================================================================
--- arm/trunk/src/util/sysTools.py 2010-12-22 07:11:24 UTC (rev 23974)
+++ arm/trunk/src/util/sysTools.py 2010-12-23 06:22:55 UTC (rev 23975)
@@ -3,11 +3,10 @@
"""
import os
-import sys
import time
import threading
-from util import log
+from util import log, procTools
# mapping of commands to if they're available or not
CMD_AVAILABLE_CACHE = {}
@@ -94,26 +93,11 @@
return PROCESS_NAME_CACHE[pid]
processName, raisedExc = "", None
- if sys.platform.lower().startswith("linux"):
- if pid == 0:
- # special case for the kernel process
- processName = "sched"
- else:
- try:
- statFilePath = "/proc/%s/stat" % pid
- statFile = open(statFilePath)
- statContents = statFile.read()
- statFile.close()
-
- # contents are of the form:
- # 8438 (tor) S 8407 8438 8407 34818 8438 4202496...
- start, end = statContents.find("("), statContents.find(")")
- if start != -1 and end != -1:
- processName = statContents[start + 1:end]
- else:
- raise IOError("stat file had an unexpected format: %s" % statFilePath)
- except IOError, exc:
- raisedExc = exc
+ if os.uname()[0] == "Linux":
+ try:
+ processName = procTools.getStats(pid, procTools.STAT_COMMAND)[0]
+ except IOError, exc:
+ raisedExc = exc
if raisedExc:
if default == None: raise raisedExc