Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
This commit is contained in:
commit
88d196d089
@ -111,10 +111,10 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
_REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
|
_REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
self._redraw_activity_ring()
|
|
||||||
if self._redraw_id is None:
|
if self._redraw_id is None:
|
||||||
self._redraw_id = gobject.timeout_add(self._REDRAW_TIMEOUT,
|
self._redraw_id = gobject.timeout_add(self._REDRAW_TIMEOUT,
|
||||||
self._redraw_activity_ring)
|
self._redraw_activity_ring)
|
||||||
|
self._redraw_activity_ring()
|
||||||
|
|
||||||
def suspend(self):
|
def suspend(self):
|
||||||
if self._redraw_id is not None:
|
if self._redraw_id is not None:
|
||||||
|
44
shell/view/home/activitiesdonut.py
Normal file → Executable file
44
shell/view/home/activitiesdonut.py
Normal file → Executable file
@ -30,7 +30,7 @@ from sugar.graphics.palette import Palette
|
|||||||
from sugar.graphics import style
|
from sugar.graphics import style
|
||||||
from sugar.graphics import xocolor
|
from sugar.graphics import xocolor
|
||||||
from sugar import profile
|
from sugar import profile
|
||||||
from proc_smaps import ProcSmaps
|
import proc_smaps
|
||||||
|
|
||||||
# TODO: rgb_to_html and html_to_rgb are useful elsewhere
|
# TODO: rgb_to_html and html_to_rgb are useful elsewhere
|
||||||
# we should put this in a common module
|
# we should put this in a common module
|
||||||
@ -186,6 +186,7 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
self._activities = []
|
self._activities = []
|
||||||
self._shell = shell
|
self._shell = shell
|
||||||
self._angles = []
|
self._angles = []
|
||||||
|
self._shell_mappings = proc_smaps.get_shared_mapping_names(os.getpid())
|
||||||
|
|
||||||
self._model = shell.get_model().get_home()
|
self._model = shell.get_model().get_home()
|
||||||
self._model.connect('activity-added', self._activity_added_cb)
|
self._model.connect('activity-added', self._activity_added_cb)
|
||||||
@ -282,23 +283,11 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _update_activity_sizes(self):
|
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
|
# Get the memory mappings of each process that hosts an
|
||||||
# activity, and count how many activity instances each
|
# activity, and count how many activity instances each
|
||||||
# activity process hosts, and how many processes are mapping
|
# activity process hosts, and how many processes are mapping
|
||||||
# each shared library, etc
|
# each shared library, etc
|
||||||
process_smaps = {}
|
process_mappings = {}
|
||||||
num_activities = {}
|
num_activities = {}
|
||||||
num_mappings = {}
|
num_mappings = {}
|
||||||
unknown_size_activities = 0
|
unknown_size_activities = 0
|
||||||
@ -314,15 +303,14 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
smaps = ProcSmaps(pid)
|
mappings = proc_smaps.get_mappings(pid, self._shell_mappings)
|
||||||
self._subtract_mappings(smaps, shell_mappings)
|
for mapping in mappings:
|
||||||
for mapping in smaps.mappings:
|
if mapping.shared > 0:
|
||||||
if mapping.shared_clean > 0 or mapping.shared_dirty > 0:
|
|
||||||
if num_mappings.has_key(mapping.name):
|
if num_mappings.has_key(mapping.name):
|
||||||
num_mappings[mapping.name] += 1
|
num_mappings[mapping.name] += 1
|
||||||
else:
|
else:
|
||||||
num_mappings[mapping.name] = 1
|
num_mappings[mapping.name] = 1
|
||||||
process_smaps[pid] = smaps
|
process_mappings[pid] = mappings
|
||||||
num_activities[pid] = 1
|
num_activities[pid] = 1
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logging.warn('ActivitiesDonut: could not read /proc/%s/smaps: %r'
|
logging.warn('ActivitiesDonut: could not read /proc/%s/smaps: %r'
|
||||||
@ -333,16 +321,16 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
total_activity_size = 0
|
total_activity_size = 0
|
||||||
for activity in self._model:
|
for activity in self._model:
|
||||||
pid = activity.get_pid()
|
pid = activity.get_pid()
|
||||||
if not process_smaps.has_key(pid):
|
if not process_mappings.has_key(pid):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
smaps = process_smaps[pid]
|
mappings = process_mappings[pid]
|
||||||
size = 0
|
size = 0
|
||||||
for mapping in smaps.mappings:
|
for mapping in mappings:
|
||||||
size += mapping.private_clean + mapping.private_dirty
|
size += mapping.private
|
||||||
if mapping.shared_clean + mapping.shared_dirty > 0:
|
if mapping.shared > 0:
|
||||||
num = num_mappings[mapping.name]
|
num = num_mappings[mapping.name]
|
||||||
size += (mapping.shared_clean + mapping.shared_dirty) / num
|
size += mapping.shared / num
|
||||||
process_size[pid] = size
|
process_size[pid] = size
|
||||||
total_activity_size += size / num_activities[pid]
|
total_activity_size += size / num_activities[pid]
|
||||||
|
|
||||||
@ -406,12 +394,6 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
if icon.size > _MIN_WEDGE_SIZE:
|
if icon.size > _MIN_WEDGE_SIZE:
|
||||||
icon.size -= (icon.size - _MIN_WEDGE_SIZE) * reduction
|
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):
|
def _compute_angles(self):
|
||||||
self._angles = []
|
self._angles = []
|
||||||
if len(self._activities) == 0:
|
if len(self._activities) == 0:
|
||||||
|
225
shell/view/home/proc_smaps.py
Normal file → Executable file
225
shell/view/home/proc_smaps.py
Normal file → Executable file
@ -1,149 +1,106 @@
|
|||||||
####################################################################
|
# Copyright (C) 2007 Red Hat, Inc.
|
||||||
# This class open the /proc/PID/maps and /proc/PID/smaps files
|
#
|
||||||
# to get useful information about the real memory usage
|
# 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 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
|
def get_shared_mapping_names(pid):
|
||||||
class ProcSmaps:
|
"""Returns a set of the files for which PID has a shared mapping"""
|
||||||
|
|
||||||
mappings = [] # Devices information
|
|
||||||
|
|
||||||
def __init__(self, pid):
|
mappings = set()
|
||||||
global _smaps_has_references
|
infile = open("/proc/%s/maps" % pid, "r")
|
||||||
if _smaps_has_references is None:
|
for line in infile:
|
||||||
_smaps_has_references = os.path.isfile('/proc/%s/clear_refs' %
|
# sharable mappings are non-anonymous and either read-only
|
||||||
os.getpid())
|
# (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
|
_smaps_lines_per_entry = None
|
||||||
self.mappings = []
|
|
||||||
|
|
||||||
# Coded by Federico Mena (script)
|
|
||||||
infile = open(smapfile, "r")
|
|
||||||
input = infile.read()
|
|
||||||
infile.close()
|
|
||||||
|
|
||||||
lines = input.splitlines()
|
|
||||||
|
|
||||||
num_lines = len (lines)
|
def get_mappings(pid, ignored_shared_mappings):
|
||||||
line_idx = 0
|
"""Returns a list of (name, private, shared) tuples describing the
|
||||||
|
memory mappings of PID. Shared mappings named in
|
||||||
|
ignored_shared_mappings are ignored
|
||||||
|
"""
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
mappings = []
|
||||||
|
|
||||||
# 08065000-08067000 rw-p 0001c000 03:01 147613 /opt/gnome/bin/evolution-2.6
|
smapfile = "/proc/%s/smaps" % pid
|
||||||
# Size: 8 kB
|
infile = open(smapfile, "r")
|
||||||
# Rss: 8 kB
|
input = infile.read()
|
||||||
# Shared_Clean: 0 kB
|
infile.close()
|
||||||
# Shared_Dirty: 0 kB
|
lines = input.splitlines()
|
||||||
# Private_Clean: 8 kB
|
|
||||||
# Private_Dirty: 0 kB
|
|
||||||
# Referenced: 4 kb -> Introduced in kernel 2.6.22
|
|
||||||
|
|
||||||
while num_lines > 0:
|
for line_idx in range(0, len(lines), _smaps_lines_per_entry):
|
||||||
fields = lines[line_idx].split (" ", 5)
|
name_idx = lines[line_idx].find('/')
|
||||||
if len (fields) == 6:
|
if name_idx == -1:
|
||||||
(offsets, permissions, bin_permissions, device, inode, name) = fields
|
name = None
|
||||||
else:
|
else:
|
||||||
(offsets, permissions, bin_permissions, device, inode) = fields
|
name = lines[line_idx][name_idx:]
|
||||||
name = ""
|
|
||||||
|
|
||||||
size = self.parse_smaps_size_line (lines[line_idx + 1])
|
private_clean = int(lines[line_idx + 5][14:-3])
|
||||||
rss = self.parse_smaps_size_line (lines[line_idx + 2])
|
private_dirty = int(lines[line_idx + 6][14:-3])
|
||||||
shared_clean = self.parse_smaps_size_line (lines[line_idx + 3])
|
if name in ignored_shared_mappings:
|
||||||
shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4])
|
shared_clean = 0
|
||||||
private_clean = self.parse_smaps_size_line (lines[line_idx + 5])
|
shared_dirty = 0
|
||||||
private_dirty = self.parse_smaps_size_line (lines[line_idx + 6])
|
else:
|
||||||
if _smaps_has_references:
|
shared_clean = int(lines[line_idx + 3][14:-3])
|
||||||
referenced = self.parse_smaps_size_line (lines[line_idx + 7])
|
shared_dirty = int(lines[line_idx + 4][14:-3])
|
||||||
else:
|
|
||||||
referenced = None
|
|
||||||
name = name.strip ()
|
|
||||||
|
|
||||||
mapping = Mapping (size, rss, shared_clean, shared_dirty, \
|
mapping = Mapping(name, private, shared)
|
||||||
private_clean, private_dirty, referenced, permissions, name)
|
mappings.append (mapping)
|
||||||
self.mappings.append (mapping)
|
|
||||||
|
|
||||||
if _smaps_has_references:
|
return mappings
|
||||||
num_lines -= 8
|
|
||||||
line_idx += 8
|
|
||||||
else:
|
|
||||||
num_lines -= 7
|
|
||||||
line_idx += 7
|
|
||||||
|
|
||||||
if _smaps_has_references:
|
|
||||||
self._clear_reference(pid)
|
|
||||||
|
|
||||||
def _clear_reference(self, pid):
|
|
||||||
os.system("echo 1 > /proc/%s/clear_refs" % pid)
|
|
||||||
|
|
||||||
# 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])
|
|
||||||
|
|
||||||
class Mapping:
|
class Mapping:
|
||||||
def __init__ (self, size, rss, shared_clean, shared_dirty, \
|
def __init__ (self, name, private, shared):
|
||||||
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
|
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.private = private
|
||||||
# Parse /proc/PID/maps file to get the clean memory usage by process,
|
self.shared = shared
|
||||||
# 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
|
|
||||||
|
Loading…
Reference in New Issue
Block a user