Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/shell
diff options
context:
space:
mode:
authorDan Winship <dwinship@redhat.com>2007-09-11 15:36:33 (GMT)
committer Dan Winship <dwinship@redhat.com>2007-09-11 15:42:22 (GMT)
commit8a733eea5d7ada6d625c934fb44e691312763b28 (patch)
treeaea0af6f29dc7ff2f8f966abd07cdab4c33d5c7e /shell
parent59708891e85930efbe077f2d6a566ed813cb20b6 (diff)
redo the smaps-parsing code to be a little more efficient. part of #3096
Diffstat (limited to 'shell')
-rwxr-xr-x[-rw-r--r--]shell/view/home/activitiesdonut.py44
-rwxr-xr-x[-rw-r--r--]shell/view/home/proc_smaps.py225
2 files changed, 104 insertions, 165 deletions
diff --git a/shell/view/home/activitiesdonut.py b/shell/view/home/activitiesdonut.py
index aa0adde..9ac7621 100644..100755
--- a/shell/view/home/activitiesdonut.py
+++ b/shell/view/home/activitiesdonut.py
@@ -30,7 +30,7 @@ from sugar.graphics.palette import Palette
from sugar.graphics import style
from sugar.graphics import xocolor
from sugar import profile
-from proc_smaps import ProcSmaps
+import proc_smaps
# TODO: rgb_to_html and html_to_rgb are useful elsewhere
# we should put this in a common module
@@ -186,6 +186,7 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
self._activities = []
self._shell = shell
self._angles = []
+ self._shell_mappings = proc_smaps.get_shared_mapping_names(os.getpid())
self._model = shell.get_model().get_home()
self._model.connect('activity-added', self._activity_added_cb)
@@ -282,23 +283,11 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
return True
def _update_activity_sizes(self):
- # First, get the shell's memory mappings; this memory won't be
- # counted against the memory used by activities, since it
- # would still be in use even if all activities exited.
- shell_mappings = {}
- try:
- shell_smaps = ProcSmaps(os.getpid())
- for mapping in shell_smaps.mappings:
- if mapping.shared_clean > 0 or mapping.shared_dirty > 0:
- shell_mappings[mapping.name] = mapping
- except Exception, e:
- logging.warn('ActivitiesDonut: could not read own smaps: %r' % e)
-
# Get the memory mappings of each process that hosts an
# activity, and count how many activity instances each
# activity process hosts, and how many processes are mapping
# each shared library, etc
- process_smaps = {}
+ process_mappings = {}
num_activities = {}
num_mappings = {}
unknown_size_activities = 0
@@ -314,15 +303,14 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
continue
try:
- smaps = ProcSmaps(pid)
- self._subtract_mappings(smaps, shell_mappings)
- for mapping in smaps.mappings:
- if mapping.shared_clean > 0 or mapping.shared_dirty > 0:
+ mappings = proc_smaps.get_mappings(pid, self._shell_mappings)
+ for mapping in mappings:
+ if mapping.shared > 0:
if num_mappings.has_key(mapping.name):
num_mappings[mapping.name] += 1
else:
num_mappings[mapping.name] = 1
- process_smaps[pid] = smaps
+ process_mappings[pid] = mappings
num_activities[pid] = 1
except Exception, e:
logging.warn('ActivitiesDonut: could not read /proc/%s/smaps: %r'
@@ -333,16 +321,16 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
total_activity_size = 0
for activity in self._model:
pid = activity.get_pid()
- if not process_smaps.has_key(pid):
+ if not process_mappings.has_key(pid):
continue
- smaps = process_smaps[pid]
+ mappings = process_mappings[pid]
size = 0
- for mapping in smaps.mappings:
- size += mapping.private_clean + mapping.private_dirty
- if mapping.shared_clean + mapping.shared_dirty > 0:
+ for mapping in mappings:
+ size += mapping.private
+ if mapping.shared > 0:
num = num_mappings[mapping.name]
- size += (mapping.shared_clean + mapping.shared_dirty) / num
+ size += mapping.shared / num
process_size[pid] = size
total_activity_size += size / num_activities[pid]
@@ -406,12 +394,6 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
if icon.size > _MIN_WEDGE_SIZE:
icon.size -= (icon.size - _MIN_WEDGE_SIZE) * reduction
- def _subtract_mappings(self, smaps, mappings_to_remove):
- for mapping in smaps.mappings:
- if mappings_to_remove.has_key(mapping.name):
- mapping.shared_clean = 0
- mapping.shared_dirty = 0
-
def _compute_angles(self):
self._angles = []
if len(self._activities) == 0:
diff --git a/shell/view/home/proc_smaps.py b/shell/view/home/proc_smaps.py
index c7a81ec..47a5fe5 100644..100755
--- a/shell/view/home/proc_smaps.py
+++ b/shell/view/home/proc_smaps.py
@@ -1,149 +1,106 @@
-####################################################################
-# This class open the /proc/PID/maps and /proc/PID/smaps files
-# to get useful information about the real memory usage
-####################################################################
+# Copyright (C) 2007 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+# USA
import os
-import logging
-_smaps_has_references = None
+# /proc/PID/maps consists of a number of lines like this:
+# 00400000-004b1000 r-xp 00000000 fd:00 5767206 /bin/bash
+# 006b1000-006bb000 rw-p 000b1000 fd:00 5767206 /bin/bash
+# 006bb000-006c0000 rw-p 006bb000 00:00 0
+# ...
+# The fields are: address, permissions, offset, device, inode, and
+# (for non-anonymous mappings) pathname.
+#
+# /proc/PID/smaps gives additional information for each mapping:
+# 00400000-004b1000 r-xp 00000000 fd:00 5767206 /bin/bash
+# Size: 708 kB
+# Rss: 476 kB
+# Shared_Clean: 468 kB
+# Shared_Dirty: 0 kB
+# Private_Clean: 8 kB
+# Private_Dirty: 0 kB
+# Referenced: 0 kb
+#
+# The "Referenced" line only appears in kernel 2.6.22 and later.
-# Parse the /proc/PID/smaps file
-class ProcSmaps:
-
- mappings = [] # Devices information
+def get_shared_mapping_names(pid):
+ """Returns a set of the files for which PID has a shared mapping"""
- def __init__(self, pid):
- global _smaps_has_references
- if _smaps_has_references is None:
- _smaps_has_references = os.path.isfile('/proc/%s/clear_refs' %
- os.getpid())
+ mappings = set()
+ infile = open("/proc/%s/maps" % pid, "r")
+ for line in infile:
+ # sharable mappings are non-anonymous and either read-only
+ # (permissions "r-..") or writable but explicitly marked
+ # shared ("rw.s")
+ fields = line.split()
+ if len(fields) < 6 or not fields[5].startswith('/'):
+ continue
+ if fields[1][0] != 'r' or (fields[1][1] == 'w' and fields[1][3] != 's'):
+ continue
+ mappings.add(fields[5])
+ infile.close()
+ return mappings
- smapfile = "/proc/%s/smaps" % pid
- self.mappings = []
-
- # Coded by Federico Mena (script)
- infile = open(smapfile, "r")
- input = infile.read()
- infile.close()
-
- lines = input.splitlines()
+_smaps_lines_per_entry = None
- num_lines = len (lines)
- line_idx = 0
-
- # 08065000-08067000 rw-p 0001c000 03:01 147613 /opt/gnome/bin/evolution-2.6
- # Size: 8 kB
- # Rss: 8 kB
- # Shared_Clean: 0 kB
- # Shared_Dirty: 0 kB
- # Private_Clean: 8 kB
- # Private_Dirty: 0 kB
- # Referenced: 4 kb -> Introduced in kernel 2.6.22
+def get_mappings(pid, ignored_shared_mappings):
+ """Returns a list of (name, private, shared) tuples describing the
+ memory mappings of PID. Shared mappings named in
+ ignored_shared_mappings are ignored
+ """
- while num_lines > 0:
- fields = lines[line_idx].split (" ", 5)
- if len (fields) == 6:
- (offsets, permissions, bin_permissions, device, inode, name) = fields
- else:
- (offsets, permissions, bin_permissions, device, inode) = fields
- name = ""
-
- size = self.parse_smaps_size_line (lines[line_idx + 1])
- rss = self.parse_smaps_size_line (lines[line_idx + 2])
- shared_clean = self.parse_smaps_size_line (lines[line_idx + 3])
- shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4])
- private_clean = self.parse_smaps_size_line (lines[line_idx + 5])
- private_dirty = self.parse_smaps_size_line (lines[line_idx + 6])
- if _smaps_has_references:
- referenced = self.parse_smaps_size_line (lines[line_idx + 7])
- else:
- referenced = None
- name = name.strip ()
+ global _smaps_lines_per_entry
+ if _smaps_lines_per_entry is None:
+ if os.path.isfile('/proc/%s/clear_refs' % os.getpid()):
+ _smaps_lines_per_entry = 8
+ else:
+ _smaps_lines_per_entry = 7
- mapping = Mapping (size, rss, shared_clean, shared_dirty, \
- private_clean, private_dirty, referenced, permissions, name)
- self.mappings.append (mapping)
+ mappings = []
+
+ smapfile = "/proc/%s/smaps" % pid
+ infile = open(smapfile, "r")
+ input = infile.read()
+ infile.close()
+ lines = input.splitlines()
- if _smaps_has_references:
- num_lines -= 8
- line_idx += 8
- else:
- num_lines -= 7
- line_idx += 7
-
- if _smaps_has_references:
- self._clear_reference(pid)
+ for line_idx in range(0, len(lines), _smaps_lines_per_entry):
+ name_idx = lines[line_idx].find('/')
+ if name_idx == -1:
+ name = None
+ else:
+ name = lines[line_idx][name_idx:]
+
+ private_clean = int(lines[line_idx + 5][14:-3])
+ private_dirty = int(lines[line_idx + 6][14:-3])
+ if name in ignored_shared_mappings:
+ shared_clean = 0
+ shared_dirty = 0
+ else:
+ shared_clean = int(lines[line_idx + 3][14:-3])
+ shared_dirty = int(lines[line_idx + 4][14:-3])
- def _clear_reference(self, pid):
- os.system("echo 1 > /proc/%s/clear_refs" % pid)
+ mapping = Mapping(name, private, shared)
+ mappings.append (mapping)
- # Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field
- def parse_smaps_size_line (self, line):
- # Rss: 8 kB
- fields = line.split ()
- return int(fields[1])
+ return mappings
class Mapping:
- def __init__ (self, size, rss, shared_clean, shared_dirty, \
- private_clean, private_dirty, referenced, permissions, name):
- self.size = size
- self.rss = rss
- self.shared_clean = shared_clean
- self.shared_dirty = shared_dirty
- self.private_clean = private_clean
- self.private_dirty = private_dirty
- self.referenced = referenced
- self.permissions = permissions
+ def __init__ (self, name, private, shared):
self.name = name
-
-# Parse /proc/PID/maps file to get the clean memory usage by process,
-# we avoid lines with backed-files
-class ProcMaps:
-
- clean_size = 0
-
- def __init__(self, pid):
- mapfile = "/proc/%s/maps" % pid
-
- try:
- infile = open(mapfile, "r")
- except:
- print "Error trying " + mapfile
- return None
-
- sum = 0
- to_data_do = {
- "[anon]": self.parse_size_line,
- "[heap]": self.parse_size_line
- }
-
- for line in infile:
- arr = line.split()
-
- # Just parse writable mapped areas
- if arr[1][1] != "w":
- continue
-
- if len(arr) == 6:
- # if we got a backed-file we skip this info
- if os.path.isfile(arr[5]):
- continue
- else:
- line_size = to_data_do.get(arr[5], self.skip)(line)
- sum += line_size
- else:
- line_size = self.parse_size_line(line)
- sum += line_size
-
- infile.close()
- self.clean_size = sum
-
- def skip(self, line):
- return 0
-
- # Parse a maps line and return the mapped size
- def parse_size_line(self, line):
- start, end = line.split()[0].split('-')
- size = int(end, 16) - int(start, 16)
- return size
+ self.private = private
+ self.shared = shared