From cae78d70415923c0deabd518f23dc534f11cfacd Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Mon, 08 Jan 2007 17:06:59 +0000 Subject: Rework the console to use autoactivation, move it in services/ --- (limited to 'services') diff --git a/services/Makefile.am b/services/Makefile.am index 9cf9b95..ceb990c 100644 --- a/services/Makefile.am +++ b/services/Makefile.am @@ -1 +1 @@ -SUBDIRS = presence nm clipboard datastore +SUBDIRS = presence nm clipboard datastore console diff --git a/services/console/Makefile.am b/services/console/Makefile.am new file mode 100644 index 0000000..fe6aee0 --- /dev/null +++ b/services/console/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = interface lib + +servicedir = $(datadir)/dbus-1/services +service_in_files = org.laptop.sugar.Console.service.in +service_DATA = $(service_in_files:.service.in=.service) + +$(service_DATA): $(service_in_files) Makefile + @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ + +sugardir = $(pkgdatadir)/shell/console +sugar_PYTHON = \ + __init__.py \ + console.py + +bin_SCRIPTS = sugar-console + +DISTCLEANFILES = $(service_DATA) diff --git a/services/console/__init__.py b/services/console/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/services/console/__init__.py @@ -0,0 +1 @@ + diff --git a/services/console/console.py b/services/console/console.py new file mode 100755 index 0000000..1b2a371 --- /dev/null +++ b/services/console/console.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +# Copyright (C) 2006, Eduardo Silva (edsiper@gmail.com). +# +# 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 dbus +import dbus.glib +import dbus.service +import os +import sys +import gtk +import gobject + +sys.path.append(os.path.dirname(__file__) + '/lib') +sys.path.append(os.path.dirname(__file__) + '/interface') + +CONSOLE_BUS = 'org.laptop.sugar.Console' +CONSOLE_PATH = '/org/laptop/sugar/Console' +CONSOLE_IFACE = 'org.laptop.sugar.Console' + +class Console: + + def __init__(self): + + # Main Window + self.window = gtk.Window() + self.window.set_title('Developer console') + self.window.connect("delete-event", self._delete_event_cb) + + self.default_width = gtk.gdk.screen_width() * 95 / 100 + self.default_height = gtk.gdk.screen_height() * 95 / 100 + + self.window.set_default_size(self.default_width, self.default_height) + + self.window.realize() + self.window.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + + # Notebook + self.notebook = gtk.Notebook() + + self._load_interface('xo', 'XO Resources') + self._load_interface('memphis', 'Memphis') + self._load_interface('logviewer', 'Log Viewer') + self._load_interface('terminal', 'Terminal') + + main_hbox = gtk.HBox() + main_hbox.pack_start(self.notebook, True, True, 0) + main_hbox.show() + + self.notebook.show() + self.window.add(main_hbox) + + def _load_interface(self, interface, label): + mod = __import__(interface) + widget = mod.Interface().widget + widget.show() + + self.notebook.append_page(widget, gtk.Label(label)) + + def _delete_event_cb(self, window, gdkevent): + window.hide() + return True + +class Service(dbus.service.Object): + def __init__(self, bus, object_path=CONSOLE_PATH): + dbus.service.Object.__init__(self, bus, object_path) + self._console = Console() + + @dbus.service.method(CONSOLE_IFACE) + def show(self): + self._console.window.present() + +bus = dbus.SessionBus() +name = dbus.service.BusName(CONSOLE_BUS, bus) +obj = Service(name) + +gtk.main() diff --git a/services/console/interface/Makefile.am b/services/console/interface/Makefile.am new file mode 100644 index 0000000..2654a4b --- /dev/null +++ b/services/console/interface/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = memphis logviewer terminal xo + +sugardir = $(pkgdatadir)/shell/console/interface +sugar_PYTHON = \ + __init__.py + diff --git a/services/console/interface/__init__.py b/services/console/interface/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/services/console/interface/__init__.py diff --git a/services/console/interface/logviewer/Makefile.am b/services/console/interface/logviewer/Makefile.am new file mode 100644 index 0000000..e6a6979 --- /dev/null +++ b/services/console/interface/logviewer/Makefile.am @@ -0,0 +1,4 @@ +sugardir = $(pkgdatadir)/shell/console/interface/logviewer +sugar_PYTHON = \ + __init__.py \ + logviewer.py diff --git a/services/console/interface/logviewer/__init__.py b/services/console/interface/logviewer/__init__.py new file mode 100644 index 0000000..614ed67 --- /dev/null +++ b/services/console/interface/logviewer/__init__.py @@ -0,0 +1 @@ +from logviewer import Interface diff --git a/services/console/interface/logviewer/logviewer.py b/services/console/interface/logviewer/logviewer.py new file mode 100644 index 0000000..6bd4543 --- /dev/null +++ b/services/console/interface/logviewer/logviewer.py @@ -0,0 +1,173 @@ +#!/usr/bin/python + +# Copyright (C) 2006, 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 + + +# Rewritten by Eduardo Silva, edsiper@gmail.com + +import os + +import pygtk +pygtk.require('2.0') +import gtk +import gobject + +from sugar import env + +class MultiLogView(gtk.VBox): + def __init__(self, path): + self._active_log = None + self._iters = [] + + # Creating Main treeview with Actitivities list + tv_menu = gtk.TreeView() + tv_menu.connect('cursor-changed', self._load_log) + tv_menu.set_rules_hint(True) + + # Set width + box_width = gtk.gdk.screen_width() * 80 / 100 + tv_menu.set_size_request(box_width*25/100, 0) + + self.store_menu = gtk.TreeStore(str) + tv_menu.set_model(self.store_menu) + + self._add_column(tv_menu, 'Sugar logs', 0) + self._logs_path = os.path.join(env.get_profile_path(), 'logs') + self._activity = {} + + # Activities menu + self.hbox = gtk.HBox(False, 3) + self.hbox.pack_start(tv_menu, True, True, 0) + + # Activity log, set width + self._view = LogView() + self._view.set_size_request(box_width*75/100, 0) + + self.hbox.pack_start(self._view, True, True, 0) + self.hbox.show_all() + + gobject.timeout_add(1000, self._update, tv_menu) + + # Load the log information in View (textview) + def _load_log(self, treeview): + treeselection = treeview.get_selection() + + treestore, iter = treeselection.get_selected() + + # Get current selection + act_log = self.store_menu.get_value(iter, 0) + + # Set buffer and scroll down + self._view.textview.set_buffer(self._activity[act_log]) + self._view.textview.scroll_to_mark(self._activity[act_log].get_insert(), 0); + self._active_log = act_log + + def _update(self, tv_menu): + # Searching log files + for logfile in os.listdir(self._logs_path): + + if not self._activity.has_key(logfile): + self._add_activity(logfile) + full_log_path = os.path.join(self._logs_path, logfile) + model = LogBuffer(full_log_path) + self._activity[logfile] = model + + self._activity[logfile].update() + written = self._activity[logfile]._written + + # Load the first iter + if self._active_log == None: + self._active_log = logfile + iter = tv_menu.get_model().get_iter_root() + tv_menu.get_selection().select_iter(iter) + self._load_log(tv_menu) + + if written > 0 and self._active_log == logfile: + self._view.textview.scroll_to_mark(self._activity[logfile].get_insert(), 0); + + return True + + def _add_activity(self, name): + self._insert_row(self.store_menu, None, name) + + # Add a new column to the main treeview, (code from Memphis) + def _add_column(self, treeview, column_name, index): + cell = gtk.CellRendererText() + col_tv = gtk.TreeViewColumn(column_name, cell, text=index) + col_tv.set_resizable(True) + col_tv.set_property('clickable', True) + + treeview.append_column(col_tv) + + # Set the last column index added + self.last_col_index = index + + # Insert a Row in our TreeView + def _insert_row(self, store, parent, name): + iter = store.insert_before(parent, None) + index = 0 + store.set_value(iter, index , name) + + return iter + +class LogBuffer(gtk.TextBuffer): + def __init__(self, logfile): + gtk.TextBuffer.__init__(self) + + self._logfile = logfile + self._pos = 0 + self.update() + + def update(self): + f = open(self._logfile, 'r') + + init_pos = self._pos + + f.seek(self._pos) + self.insert(self.get_end_iter(), f.read()) + self._pos = f.tell() + + f.close() + + self._written = (self._pos - init_pos) + return True + +class LogView(gtk.ScrolledWindow): + def __init__(self): + gtk.ScrolledWindow.__init__(self) + + self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + + self.textview = gtk.TextView() + self.textview.set_wrap_mode(gtk.WRAP_WORD) + + # Set background color + bgcolor = gtk.gdk.color_parse("#FFFFFF") + self.textview.modify_base(gtk.STATE_NORMAL, bgcolor) + + self.textview.set_editable(False) + + self.add(self.textview) + self.textview.show() + +class Interface: + + def __init__(self): + path = None + viewer = MultiLogView(path) + self.widget = viewer.hbox + diff --git a/services/console/interface/memphis/Makefile.am b/services/console/interface/memphis/Makefile.am new file mode 100644 index 0000000..319dad6 --- /dev/null +++ b/services/console/interface/memphis/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = plugins + +sugardir = $(pkgdatadir)/shell/console/interface/memphis +sugar_PYTHON = \ + __init__.py \ + memphis.py \ + plugin.py + diff --git a/services/console/interface/memphis/__init__.py b/services/console/interface/memphis/__init__.py new file mode 100644 index 0000000..145d9d4 --- /dev/null +++ b/services/console/interface/memphis/__init__.py @@ -0,0 +1 @@ +from memphis import Interface diff --git a/services/console/interface/memphis/memphis.py b/services/console/interface/memphis/memphis.py new file mode 100644 index 0000000..8fbc85b --- /dev/null +++ b/services/console/interface/memphis/memphis.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python + +# Copyright (C) 2006, Eduardo Silva (edsiper@gmail.com). +# +# 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 sys +import os +import string +import wnck +import plugin + +from procmem import proc + +try: + import gtk + import gtk.gdk + import gobject +except: + sys.exit(1) + +class Interface: + + store_data_types = [] + store_data_types_details = [] + + def __init__(self): + + # Our GtkTree (Treeview) + self.treeview = gtk.TreeView() + self.treeview.show() + + self.button_start = gtk.Button('Start Memphis') + self.button_stop = gtk.Button('Stop Memphis') + + fixed = gtk.Fixed() + fixed.add(self.button_start) + fixed.add(self.button_stop) + + vbox = gtk.VBox(False) + vbox.set_border_width(5) + vbox.pack_start(fixed, True, True, 0) + + # Our GtkTree (Treeview) + self.treeview = gtk.TreeView() + t_width = gtk.gdk.screen_width() + t_height = gtk.gdk.screen_height() * 83 / 100 + + self.treeview.set_size_request(t_width, t_height) + vbox.pack_start(self.treeview, True, True, 0) + vbox.show_all() + self.widget = vbox + + # Loading plugins + self.plg = plugin.Plugin() + + # TOP data types (columns) + self.store_data_types = [] + + for plg in self.plg.list: + plg_data = plg.INTERNALS + + # Give plugin object to plugin + plg.INTERNALS['Plg'] = self.plg + + # Creating a store model and loading process data to Treeview + # self.store_data_types, ex [int, str, str, str, int,...] + #self.store = gtk.TreeStore(*self.store_data_types) + self.data = Data(self, self.treeview, self.plg.list) + + self.button_stop.hide() + self.button_start.connect('clicked', self.data._start_memphis) + self.button_stop.connect('clicked', self.data._stop_memphis) + +class Data: + + last_col_index = 0 + + store_data_cols = [] + store_data_types = [] + store_data_types_details = [] + + _running_status = False + + def __init__(self, interface, treeview, plg_list): + + self.interface = interface + + # Top data types + self.plg_list = plg_list + + for plg in self.plg_list: + + if plg.INTERNALS['top_data'] != None: + last_dt = len(self.store_data_types) + + if last_dt > 0: + last_dt -= 1 + + len_dt = len(plg.INTERNALS['top_data']) + + self.store_data_types_details.append({"plugin": plg, "init": last_dt, "end": last_dt + len_dt}) + + for dt in plg.INTERNALS['top_data']: + self.store_data_types.append(dt) + + for col in plg.INTERNALS['top_cols']: + self.store_data_cols.append(col) + + # Set global treeview + self.treeview = treeview + + # Basic columns + index = 0 + for column_name in self.store_data_cols: + self.add_column(column_name, index) + index += 1 + + self.store = gtk.TreeStore(*self.store_data_types) + treeview.set_model(self.store) + + def _start_memphis(self, button): + + # Update information every 1.5 second + button.hide() + self.interface.button_stop.show() + self._running_status = True + gobject.timeout_add(1500, self.load_data, self.treeview) + + def _stop_memphis(self, button): + + self._running_status = False + button.hide() + self.interface.button_start.show() + + # Add a new column to the main treeview + def add_column(self, column_name, index): + cell = gtk.CellRendererText() + col_tv = gtk.TreeViewColumn(column_name, cell, text=index) + col_tv.set_resizable(True) + col_tv.connect('clicked', self.sort_column_clicked) + col_tv.set_property('clickable', True) + + self.treeview.append_column(col_tv) + + # Set the last column index added + self.last_col_index = index + + # Sorting + def sort_column_clicked(self, TreeViewColumn): + cols = self.treeview.get_columns() + + # Searching column index + index = 0 + for col in cols: + if col == TreeViewColumn: + break + + index += 1 + + self.store.set_sort_column_id(index, gtk.SORT_DESCENDING) + + def load_data(self, treeview): + self.store.clear() + + # Getting procfs data + self.procdata = proc.ProcInfo() + self.process_list = [] + + pids = [] + screen = wnck.screen_get_default() + windows = screen.get_windows() + + current_pid = os.getpid() + + for win in windows: + pid = int(win.get_pid()) + if current_pid != pid: + pids.append(pid) + + self.process_list = set(pids) + + # Sort rows using pid + #self.process_list.sort(key=operator.itemgetter('pid')) + self.process_iter = [] + + for pid in self.process_list: + pi = self.build_row(self.store, None, self.procdata, pid) + self.process_iter.append(pi) + + treeview.set_rules_hint(True) + treeview.expand_all() + + return self._running_status + + def build_row(self, store, parent_iter, proc_data, pid): + data = [] + + pinfo = proc_data.MemoryInfo(pid) + + # Look for plugins that need to update the top data treeview + for plg in self.plg_list: + plg_data = [] + + if plg.INTERNALS['top_data'] != None: + # data = [xxx, yyy,zzz,...] + plg_data = plg.info.plg_on_top_data_refresh(plg, pinfo) + + for field in plg_data: + data.append(field) + + pi = self.insert_row(store, parent_iter, data) + + return pi + + # Insert a Row in our TreeView + def insert_row(self, store, parent, row_data): + iter = store.insert_after(parent, None) + + index = 0 + + for data in row_data: + store.set_value(iter, index , data) + index += 1 + + return iter diff --git a/services/console/interface/memphis/plugin.py b/services/console/interface/memphis/plugin.py new file mode 100644 index 0000000..5ee8eb7 --- /dev/null +++ b/services/console/interface/memphis/plugin.py @@ -0,0 +1,48 @@ +############################################### +# Memphis Plugin Support +############################################### + +import sys, os +from procmem import proc, proc_smaps, analysis + +class Plugin: + + # Plugin list + list = [] + proc = proc.ProcInfo() + + internal_plugin = "memphis_init" + plg_path = os.path.dirname(os.path.abspath(__file__)) + "/plugins" + + # Frequency timer, managed by main program + freq_timer = 0 + + def __init__(self): + + sys.path.insert(0, self.plg_path) + + # Including memphis plugin + self.list.append(__import__(self.internal_plugin)) + + if os.path.isdir(self.plg_path): + # around dir entries + for plg in os.listdir(self.plg_path): + + if plg == self.internal_plugin: + continue + + if os.path.isdir(self.plg_path + "/" + plg): + p = __import__(plg) + self.list.append(__import__(plg)) + + # Parse /proc/PID/smaps information + def proc_get_smaps(self, pid): + return proc_smaps.ProcSmaps(pid) + + # Parse /proc/PID/maps information + def proc_get_maps(self, pid): + return proc_smaps.ProcMaps(pid) + + def proc_analysis(self, pid): + return analysis.Analysis(pid) + diff --git a/services/console/interface/memphis/plugins/Makefile.am b/services/console/interface/memphis/plugins/Makefile.am new file mode 100644 index 0000000..2c6b1dd --- /dev/null +++ b/services/console/interface/memphis/plugins/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = clean_size cpu dirty_size memphis_init + +sugardir = $(pkgdatadir)/shell/console/interface/memphis/plugins +sugar_PYTHON = diff --git a/services/console/interface/memphis/plugins/clean_size/Makefile.am b/services/console/interface/memphis/plugins/clean_size/Makefile.am new file mode 100644 index 0000000..33565f5 --- /dev/null +++ b/services/console/interface/memphis/plugins/clean_size/Makefile.am @@ -0,0 +1,6 @@ + +sugardir = $(pkgdatadir)/shell/console/interface/memphis/plugins/clean_size +sugar_PYTHON = \ + README \ + __init__.py \ + info.py diff --git a/services/console/interface/memphis/plugins/clean_size/README b/services/console/interface/memphis/plugins/clean_size/README new file mode 100644 index 0000000..3dd3ae3 --- /dev/null +++ b/services/console/interface/memphis/plugins/clean_size/README @@ -0,0 +1,2 @@ +This plugin give support to get the clean size memory usage +by process using the /proc/PID/maps file. diff --git a/services/console/interface/memphis/plugins/clean_size/__init__.py b/services/console/interface/memphis/plugins/clean_size/__init__.py new file mode 100644 index 0000000..75ce1d1 --- /dev/null +++ b/services/console/interface/memphis/plugins/clean_size/__init__.py @@ -0,0 +1,16 @@ + +import info + +INTERNALS = { + # Basic information + 'PLGNAME': "Clean Size", + 'TABNAME': None, + 'AUTHOR': "Eduardo Silva", + 'DESC': "Print the approx real memory usage", + + # Plugin API + 'Plg': None, # Plugin object + + 'top_data': [int], # Top data types needed by memphis core plugin + 'top_cols': ["Approx Real Usage (kb)"] + } diff --git a/services/console/interface/memphis/plugins/clean_size/info.py b/services/console/interface/memphis/plugins/clean_size/info.py new file mode 100644 index 0000000..25ed044 --- /dev/null +++ b/services/console/interface/memphis/plugins/clean_size/info.py @@ -0,0 +1,15 @@ +########################################################### +# Main function: +# ----------------- +# self: self plugin object +# mself: memphis object / principal class +# pinfo: row with information about current tracing process +############################################################ + +def plg_on_top_data_refresh(self, pinfo): + + # Get clean size + maps = self.INTERNALS['Plg'].proc_get_maps(pinfo['pid']) + + size = (maps.clean_size/1024) + return [size] diff --git a/services/console/interface/memphis/plugins/cpu/Makefile.am b/services/console/interface/memphis/plugins/cpu/Makefile.am new file mode 100644 index 0000000..58e9eab --- /dev/null +++ b/services/console/interface/memphis/plugins/cpu/Makefile.am @@ -0,0 +1,6 @@ + +sugardir = $(pkgdatadir)/shell/console/interface/memphis/plugins/cpu +sugar_PYTHON = \ + README \ + __init__.py \ + info.py diff --git a/services/console/interface/memphis/plugins/cpu/README b/services/console/interface/memphis/plugins/cpu/README new file mode 100644 index 0000000..9c7d6f3 --- /dev/null +++ b/services/console/interface/memphis/plugins/cpu/README @@ -0,0 +1,2 @@ +This plugin give support to draw the Virtual Memory Size +usage by the current tracing process. diff --git a/services/console/interface/memphis/plugins/cpu/__init__.py b/services/console/interface/memphis/plugins/cpu/__init__.py new file mode 100644 index 0000000..e22a413 --- /dev/null +++ b/services/console/interface/memphis/plugins/cpu/__init__.py @@ -0,0 +1,23 @@ +import os +import info + +INTERNALS = { + 'PLGNAME': "cpu", + 'TABNAME': None, + 'AUTHOR': "Eduardo Silva", + 'DESC': "Print CPU usage", + + # Plugin API + 'Plg': None, # Plugin object + 'current_plg': None, # Current plugin object + 'current_page': None, # Current page number + + # Top process view requirements + 'top_data': [int], # Top data types needed by memphis core plugin + 'top_cols': ["%CPU "] # Column names + } + +# Get CPU frequency +cpu_hz = os.sysconf(2) + +pids_ujiffies = {} diff --git a/services/console/interface/memphis/plugins/cpu/info.py b/services/console/interface/memphis/plugins/cpu/info.py new file mode 100644 index 0000000..9cb1ad4 --- /dev/null +++ b/services/console/interface/memphis/plugins/cpu/info.py @@ -0,0 +1,48 @@ +########################################################### +# Main function: +# ----------------- +# self: self plugin object +# mself: memphis object / principal class +# pinfo: row with information about current tracing process +############################################################ + +def plg_on_top_data_refresh(self, pinfo): + PI = self.INTERNALS['Plg'].proc + + pid = pinfo['pid'] + + # Get JIFFIES CPU usage + used_jiffies = pinfo['utime'] + pinfo['stime'] + last_ujiffies = get_pid_ujiffies(self, pid) + + cpu_usage = PI.get_CPU_usage(self.cpu_hz, used_jiffies, pinfo['start_time']) + + # Get PERCENT CPU usage + if last_ujiffies == 0.0: + pcpu = 0.0 + set_pid_ujiffies(self, pid, cpu_usage['used_jiffies']) + data = [int(pcpu)] + return data + + used_jiffies = cpu_usage['used_jiffies'] - last_ujiffies + + # Available jiffies are + avail_jiffies = (500/1000.0)*self.cpu_hz # 500 = 0.5 second + pcpu = ((used_jiffies*100)/avail_jiffies) + + set_pid_ujiffies(self, pid, cpu_usage['used_jiffies']) + + data = [int(pcpu)] + return data + +def get_pid_ujiffies(self, pid): + + if pid in self.pids_ujiffies: + return self.pids_ujiffies[pid] + else: + set_pid_ujiffies(self, pid, 0) + return self.pids_ujiffies[pid] + +def set_pid_ujiffies(self, pid, ujiffies): + self.pids_ujiffies[pid] = ujiffies + diff --git a/services/console/interface/memphis/plugins/dirty_size/Makefile.am b/services/console/interface/memphis/plugins/dirty_size/Makefile.am new file mode 100644 index 0000000..47c5298 --- /dev/null +++ b/services/console/interface/memphis/plugins/dirty_size/Makefile.am @@ -0,0 +1,6 @@ + +sugardir = $(pkgdatadir)/shell/console/interface/memphis/plugins/dirty_size +sugar_PYTHON = \ + README \ + __init__.py \ + info.py diff --git a/services/console/interface/memphis/plugins/dirty_size/README b/services/console/interface/memphis/plugins/dirty_size/README new file mode 100644 index 0000000..ee4d1a5 --- /dev/null +++ b/services/console/interface/memphis/plugins/dirty_size/README @@ -0,0 +1,2 @@ +This plugin give support to get the public and shared dirty memory usage +by process using the /proc/PID/smaps file. diff --git a/services/console/interface/memphis/plugins/dirty_size/__init__.py b/services/console/interface/memphis/plugins/dirty_size/__init__.py new file mode 100644 index 0000000..f8e9e0a --- /dev/null +++ b/services/console/interface/memphis/plugins/dirty_size/__init__.py @@ -0,0 +1,17 @@ + +import info + + +INTERNALS = { + # Basic information + 'PLGNAME': "Dirty Size", + 'TABNAME': None, # No tabbed plugin + 'AUTHOR': "Eduardo Silva", + 'DESC': "Get dirty size memory usage", + + # Plugin API + 'Plg': None, # Plugin object + + 'top_data': [int], # Top data types needed by memphis core plugin + 'top_cols': ["PDRSS (kb)"] + } diff --git a/services/console/interface/memphis/plugins/dirty_size/info.py b/services/console/interface/memphis/plugins/dirty_size/info.py new file mode 100644 index 0000000..54a2e7e --- /dev/null +++ b/services/console/interface/memphis/plugins/dirty_size/info.py @@ -0,0 +1,20 @@ +########################################################### +# Main function: +# ----------------- +# self: self plugin object +# mself: memphis object / principal class +# pinfo: row with information about current tracing process +############################################################ + + +def plg_on_top_data_refresh(self, ppinfo): + + dirty_sizes = get_dirty(self, ppinfo['pid']) + + # memhis need an array + return [dirty_sizes['private']] + +def get_dirty(pself, pid): + ProcAnalysis = pself.INTERNALS['Plg'].proc_analysis(pid) + + return ProcAnalysis.DirtyRSS() diff --git a/services/console/interface/memphis/plugins/memphis_init/Makefile.am b/services/console/interface/memphis/plugins/memphis_init/Makefile.am new file mode 100644 index 0000000..53b398a --- /dev/null +++ b/services/console/interface/memphis/plugins/memphis_init/Makefile.am @@ -0,0 +1,6 @@ + +sugardir = $(pkgdatadir)/shell/console/interface/memphis/plugins/memphis_init +sugar_PYTHON = \ + README \ + __init__.py \ + info.py diff --git a/services/console/interface/memphis/plugins/memphis_init/README b/services/console/interface/memphis/plugins/memphis_init/README new file mode 100644 index 0000000..9c7d6f3 --- /dev/null +++ b/services/console/interface/memphis/plugins/memphis_init/README @@ -0,0 +1,2 @@ +This plugin give support to draw the Virtual Memory Size +usage by the current tracing process. diff --git a/services/console/interface/memphis/plugins/memphis_init/__init__.py b/services/console/interface/memphis/plugins/memphis_init/__init__.py new file mode 100644 index 0000000..f5ada7e --- /dev/null +++ b/services/console/interface/memphis/plugins/memphis_init/__init__.py @@ -0,0 +1,15 @@ +import info + +INTERNALS = { + 'PLGNAME': "memphis", + 'TABNAME': None, + 'AUTHOR': "Eduardo Silva", + 'DESC': "Print basic process information", + + # Plugin API + 'Plg': None, # Plugin object + + # Top process view requirements + 'top_data': [int, str, str], # Top data types needed by memphis core plugin + 'top_cols': ["PID", "Process Name", "Status"] # Column names + } diff --git a/services/console/interface/memphis/plugins/memphis_init/info.py b/services/console/interface/memphis/plugins/memphis_init/info.py new file mode 100644 index 0000000..6e524c7 --- /dev/null +++ b/services/console/interface/memphis/plugins/memphis_init/info.py @@ -0,0 +1,13 @@ +########################################################### +# Main function: +# ----------------- +# self: self plugin object +# mself: memphis object / principal class +# pinfo: row with information about current tracing process +############################################################ + +def plg_on_top_data_refresh(self, ppinfo): + + data = [ppinfo['pid'], ppinfo['name'], ppinfo['state_name']] + + return data diff --git a/services/console/interface/terminal/Makefile.am b/services/console/interface/terminal/Makefile.am new file mode 100644 index 0000000..8df4650 --- /dev/null +++ b/services/console/interface/terminal/Makefile.am @@ -0,0 +1,5 @@ +sugardir = $(pkgdatadir)/shell/console/interface/terminal +sugar_PYTHON = \ + __init__.py \ + terminal.py + diff --git a/services/console/interface/terminal/__init__.py b/services/console/interface/terminal/__init__.py new file mode 100644 index 0000000..eb78b86 --- /dev/null +++ b/services/console/interface/terminal/__init__.py @@ -0,0 +1 @@ +from terminal import Interface \ No newline at end of file diff --git a/services/console/interface/terminal/terminal.py b/services/console/interface/terminal/terminal.py new file mode 100644 index 0000000..3621fb2 --- /dev/null +++ b/services/console/interface/terminal/terminal.py @@ -0,0 +1,247 @@ +#!/usr/bin/python + +# Copyright (C) 2006, 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 + + +# Modified by Eduardo Silva, edsiper@gmail.com + +import ConfigParser +import os.path + +import gtk +import vte +import pango + +import sugar.env + +class Terminal(gtk.HBox): + def __init__(self): + gtk.HBox.__init__(self, False, 4) + + self._vte = vte.Terminal() + self._configure_vte() + self._vte.set_size(30, 5) + self._vte.set_size_request(200, 450) + self._vte.show() + self.pack_start(self._vte) + + self._scrollbar = gtk.VScrollbar(self._vte.get_adjustment()) + self._scrollbar.show() + self.pack_start(self._scrollbar, False, False, 0) + + self._vte.connect("child-exited", lambda term: term.fork_command()) + + self._vte.fork_command() + + def _configure_vte(self): + conf = ConfigParser.ConfigParser() + + conf_file = os.path.join(sugar.env.get_profile_path(), 'terminalrc') + + if os.path.isfile(conf_file): + f = open(conf_file, 'r') + conf.readfp(f) + f.close() + else: + conf.add_section('terminal') + + if conf.has_option('terminal', 'font'): + font = conf.get('terminal', 'font') + else: + font = 'Monospace 8' + conf.set('terminal', 'font', font) + self._vte.set_font(pango.FontDescription(font)) + + if conf.has_option('terminal', 'fg_color'): + fg_color = conf.get('terminal', 'fg_color') + else: + fg_color = '#000000' + conf.set('terminal', 'fg_color', fg_color) + if conf.has_option('terminal', 'bg_color'): + bg_color = conf.get('terminal', 'bg_color') + else: + bg_color = '#FFFFFF' + conf.set('terminal', 'bg_color', bg_color) + self._vte.set_colors(gtk.gdk.color_parse (fg_color), + gtk.gdk.color_parse (bg_color), + []) + + if conf.has_option('terminal', 'cursor_blink'): + blink = conf.getboolean('terminal', 'cursor_blink') + else: + blink = False + conf.set('terminal', 'cursor_blink', blink) + + self._vte.set_cursor_blinks(blink) + + if conf.has_option('terminal', 'bell'): + bell = conf.getboolean('terminal', 'bell') + else: + bell = False + conf.set('terminal', 'bell', bell) + self._vte.set_audible_bell(bell) + + if conf.has_option('terminal', 'scrollback_lines'): + scrollback_lines = conf.getint('terminal', 'scrollback_lines') + else: + scrollback_lines = 1000 + conf.set('terminal', 'scrollback_lines', scrollback_lines) + + self._vte.set_scrollback_lines(scrollback_lines) + + self._vte.set_allow_bold(True) + + if conf.has_option('terminal', 'scroll_on_keystroke'): + scroll_key = conf.getboolean('terminal', 'scroll_on_keystroke') + else: + scroll_key = False + conf.set('terminal', 'scroll_on_keystroke', scroll_key) + self._vte.set_scroll_on_keystroke(scroll_key) + + if conf.has_option('terminal', 'scroll_on_output'): + scroll_output = conf.getboolean('terminal', 'scroll_on_output') + else: + scroll_output = False + conf.set('terminal', 'scroll_on_output', scroll_output) + self._vte.set_scroll_on_output(scroll_output) + + if conf.has_option('terminal', 'emulation'): + emulation = conf.get('terminal', 'emulation') + else: + emulation = 'xterm' + conf.set('terminal', 'emulation', emulation) + self._vte.set_emulation(emulation) + + if conf.has_option('terminal', 'visible_bell'): + visible_bell = conf.getboolean('terminal', 'visible_bell') + else: + visible_bell = False + conf.set('terminal', 'visible_bell', visible_bell) + self._vte.set_visible_bell(visible_bell) + + conf.write(open(conf_file, 'w')) + + def on_gconf_notification(self, client, cnxn_id, entry, what): + self.reconfigure_vte() + + def on_vte_button_press(self, term, event): + if event.button == 3: + self.do_popup(event) + return True + + def on_vte_popup_menu(self, term): + pass + +class Multiple: + + page_number = 0 + + def __init__(self): + self.notebook = gtk.Notebook() + t_width = gtk.gdk.screen_width() + t_height = gtk.gdk.screen_height() * 83 / 100 + self.notebook.set_size_request(t_width, t_height) + + self.add_new_terminal() + + open_terminal = gtk.Button('Open a new terminal') + open_terminal.connect("clicked", self.add_new_terminal) + open_terminal.show() + + self.notebook.show() + + self.main_vbox = gtk.VBox(False, 3) + self.main_vbox.pack_start(open_terminal, True, True, 2) + self.main_vbox.pack_start(self.notebook, True, True, 2) + + self.main_vbox.show_all() + + # Remove a page from the notebook + def close_terminal(self, button, child): + page = self.notebook.page_num(child) + + if page != -1: + self.notebook.remove_page(page) + + + pages = self.notebook.get_n_pages() + if pages <= 0: + self.page_number = 0 + self.add_new_terminal() + + # Need to refresh the widget -- + # This forces the widget to redraw itself. + self.notebook.queue_draw_area(0, 0, -1, -1) + + def add_icon_to_button(self, button): + iconBox = gtk.HBox(False, 0) + image = gtk.Image() + image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) + gtk.Button.set_relief(button, gtk.RELIEF_NONE) + + settings = gtk.Widget.get_settings (button) + (w,h) = gtk.icon_size_lookup_for_settings (settings, gtk.ICON_SIZE_MENU) + gtk.Widget.set_size_request (button, w + 4, h + 4) + image.show() + iconBox.pack_start(image, True, False, 0) + button.add(iconBox) + iconBox.show() + + def add_new_terminal(self, *arguments, **keywords): + self.page_number += 1 + + terminal = Terminal() + terminal.show() + + eventBox = self.create_custom_tab("Term %d" % self.page_number, terminal) + self.notebook.append_page(terminal, eventBox) + + # Set the new page + pages = gtk.Notebook.get_n_pages(self.notebook) + self.notebook.set_current_page(pages - 1) + return True + + def create_custom_tab(self, text, child): + eventBox = gtk.EventBox() + tabBox = gtk.HBox(False, 2) + tabLabel = gtk.Label(text) + + tabButton = gtk.Button() + tabButton.connect('clicked', self.close_terminal, child) + + # Add a picture on a button + self.add_icon_to_button(tabButton) + iconBox = gtk.HBox(False, 0) + + eventBox.show() + tabButton.show() + tabLabel.show() + + tabBox.pack_start(tabLabel, False) + tabBox.pack_start(tabButton, False) + + tabBox.show_all() + eventBox.add(tabBox) + + return eventBox + +class Interface: + + def __init__(self): + multiple = Multiple() + self.widget = multiple.main_vbox + diff --git a/services/console/interface/xo/Makefile.am b/services/console/interface/xo/Makefile.am new file mode 100644 index 0000000..b2035dd --- /dev/null +++ b/services/console/interface/xo/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pkgdatadir)/shell/console/interface/xo +sugar_PYTHON = \ + __init__.py \ + drwarea.py \ + xo.py + diff --git a/services/console/interface/xo/__init__.py b/services/console/interface/xo/__init__.py new file mode 100644 index 0000000..6610a0d --- /dev/null +++ b/services/console/interface/xo/__init__.py @@ -0,0 +1 @@ +from xo import Interface diff --git a/services/console/interface/xo/drwarea.py b/services/console/interface/xo/drwarea.py new file mode 100644 index 0000000..b0e598a --- /dev/null +++ b/services/console/interface/xo/drwarea.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com). +# +# 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 + +############################################# +# Drawing area tools +############################################# + +import gtk +import gtk.gdk + +class Drawing_Area_Tools: + + height = 0 + width = 0 + + margin = 5 # Left and bottom margin + + range_x = [] + range_y = [] + + def __init__(self, drwarea): + drwarea_size = drwarea.get_size_request() + + self.width = drwarea_size[0] + self.height = drwarea_size[1] + + # print "width %i" % self.width + # print "height %i" % self.height + + self.range_x = {'from': self.margin+2, 'to': self.width - (self.margin+2)} + self.range_y = {'from': self.margin+2, 'to': self.height - (self.margin+2)} + + def draw_line(self, context, from_x, from_y, to_x, to_y): + context.move_to(from_x, from_y) + context.line_to(to_x, to_y) + + def draw_border_lines(self, context): + context.set_source_rgb(1, 1, 1) + self.draw_line(context, self.margin, self.margin, self.margin, self.height - self.margin) + self.draw_line(context, self.margin, self.height - self.margin - 1, self.width - self.margin, self.height - self.margin - 1) + context.stroke() + + # Draw a grid + def draw_grid(self, context, init_x, init_y, end_x, end_y): + + x_range = (end_x - init_x) + 5 + y_range = (end_y - init_y) + 1 + + current_y = init_y + context.set_line_width(0.3) + + for y in range(y_range): + if (y%20) == 0: + context.move_to(init_x, y) + context.line_to(end_x, y) + + for x in range(x_range): + if (x%20) == 0: + context.move_to(x, init_y) + context.line_to(x, end_y) + + context.stroke() diff --git a/services/console/interface/xo/xo.py b/services/console/interface/xo/xo.py new file mode 100644 index 0000000..3a3d668 --- /dev/null +++ b/services/console/interface/xo/xo.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python + +# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com). +# +# 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 gtk, gobject +import gtk.gdk +import cairo +import string +import drwarea + + +class CPU_Usage: + + CPU_HZ = 0 + last_jiffies = 0 + times = 0 + + def __init__(self): + self.CPU_hz = os.sysconf(2) + + def _get_CPU_data(self): + # Uptime info + stat_file = "/proc/stat" + + try: + infile = file(stat_file, "r") + except: + print "Error trying uptime file" + return -1 + + stat_line = infile.readline() + cpu_info = string.split(stat_line, ' ') + infile.close() + + return cpu_info + + def _get_CPU_usage(self): + + cpu_info = self._get_CPU_data() + + used_jiffies = (int(cpu_info[2]) + int(cpu_info[3]) + int(cpu_info[4])) + + if self.times ==0: + self.last_jiffies = used_jiffies + self.times +=1 + return True + + new_ujiffies = (used_jiffies - self.last_jiffies) + new_ajiffies = ((self.frequency/1000) * self.CPU_hz) + + if new_ajiffies <= 0: + pcpu = 0.0 + else: + pcpu = ((new_ujiffies*100)/new_ajiffies) + + if pcpu >100: + pcpu = 100 + + self.times +=1 + self.last_jiffies = used_jiffies + + return pcpu + +class Interface: + + context = None + frequency_timer = 1 + graph_offset = 7 + + def __init__(self): + + self.drw_width = gtk.gdk.screen_width() * 90 / 100 + self.drw_height = gtk.gdk.screen_height() * 20 / 100 + self.y_cpu = self.drw_height - self.graph_offset + self.drw_buffer = [] + + drawingarea = gtk.DrawingArea() + drawingarea.set_size_request(self.drw_width, self.drw_height) + drawingarea.connect("expose-event", self.do_expose) + + self.dat = drwarea.Drawing_Area_Tools(drawingarea) + + fixed = gtk.Fixed(); + fixed.set_border_width(10) + fixed.add(drawingarea) + + self.frame = gtk.Frame('System CPU Usage: 0%') + self.frame.set_border_width(10) + self.frame.add(fixed) + + self.widget = self.hbox = gtk.HBox(False, 3) + self.hbox.pack_start(self.frame, True, True, 0) + self.hbox.show_all() + + DRW_CPU = CPU_Usage() + DRW_CPU.frequency = 1000 # 1 Second + + gobject.timeout_add(DRW_CPU.frequency, self._draw_cpu_usage, DRW_CPU, drawingarea) + + def _draw_cpu_usage(self, DRW_CPU, drwarea): + # End of the graph ? + if ((self.frequency_timer + 1)*self.graph_offset) >= (self.drw_width - self.graph_offset): + self.frequency_timer = 1 + self.drw_buffer = [] + self.do_expose(drwarea, None) + + context = drwarea.window.cairo_create() + + from_x = self.frequency_timer * self.graph_offset + from_y = self.y_cpu + + self.frequency_timer += 1 + + pcpu = DRW_CPU._get_CPU_usage() + + self.drw_buffer.append(pcpu) + + to_x = self.frequency_timer * self.graph_offset + self.y_cpu = to_y = self._get_y_cpu(pcpu) + + # Context properties + context.set_line_width(2) + context.set_source_rgb(0,1,0) + + cpu_label = str(round(pcpu, 4)) + self.frame.set_label('System CPU Usage: ' + cpu_label + ' %') + + self.dat.draw_line(context, from_x, from_y, to_x, to_y) + context.stroke() + + return True + + def _get_y_cpu(self, pcpu): + + height = (self.dat.range_y['to']) - (self.dat.range_y['from']) + + # Get percent of cpu usage + y_value = (height - ((pcpu*height)/100) + 4) + + return int(y_value) + + def do_expose(self, widget, event): + + self.context = widget.window.cairo_create() + self.context.rectangle(0, 0, self.dat.width - 1, self.dat.height - 1) + + self.context.set_source_rgb (0,0,0) + self.context.fill_preserve() + + # Drawing horizontal and vertical border lines + self.dat.draw_border_lines(self.context) + + # Drawing grid + line_margin = self.dat.margin + self.context.set_source_rgb(1, 1, 1) + self.context.set_line_width(1) + self.dat.draw_grid(self.context, line_margin + 1, line_margin + 1, self.dat.width - line_margin - 2, self.dat.height - line_margin - 2) + self.context.stroke() + + self._draw_buffer(widget) + return False + + def _draw_buffer(self, drwarea): + freq = 1 # Frequency timer + last_y = self.drw_height - self.graph_offset + + context = drwarea.window.cairo_create() + for pcpu in self.drw_buffer: + + from_x = freq * self.graph_offset + from_y = last_y + + freq+=1 + + to_x = freq * self.graph_offset + last_y = to_y = self._get_y_cpu(pcpu) + + # Context properties + context.set_line_width(2) + context.set_source_rgb(0,1,0) + + self.dat.draw_line(context, from_x, from_y, to_x, to_y) + context.stroke() + diff --git a/services/console/lib/Makefile.am b/services/console/lib/Makefile.am new file mode 100644 index 0000000..16782d7 --- /dev/null +++ b/services/console/lib/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = procmem + +sugardir = $(pkgdatadir)/shell/console/lib +sugar_PYTHON = diff --git a/services/console/lib/procmem/Makefile.am b/services/console/lib/procmem/Makefile.am new file mode 100644 index 0000000..257c13f --- /dev/null +++ b/services/console/lib/procmem/Makefile.am @@ -0,0 +1,8 @@ + +sugardir = $(pkgdatadir)/shell/console/lib/procmem + +sugar_PYTHON = \ + __init__.py \ + proc.py \ + proc_smaps.py \ + analysis.py diff --git a/services/console/lib/procmem/__init__.py b/services/console/lib/procmem/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/services/console/lib/procmem/__init__.py diff --git a/services/console/lib/procmem/analysis.py b/services/console/lib/procmem/analysis.py new file mode 100644 index 0000000..d2a247a --- /dev/null +++ b/services/console/lib/procmem/analysis.py @@ -0,0 +1,30 @@ +import proc, proc_smaps + +class Analysis: + + pid = 0 + + def __init__(self, pid): + self.pid = pid + + def DirtyRSS(self): + smaps = proc_smaps.ProcSmaps(self.pid) + dirty = [] + + private = 0 + shared = 0 + + for map in smaps.mappings: + private += map.private_dirty + shared += map.shared_dirty + + dirty = {"private": int(private), "shared": int(shared)} + + return dirty + + def ApproxRealMemoryUsage(self): + maps = proc_smaps.ProcMaps(self.pid) + size = (maps.clean_size/1024) + + return size + \ No newline at end of file diff --git a/services/console/lib/procmem/proc.py b/services/console/lib/procmem/proc.py new file mode 100644 index 0000000..729aa13 --- /dev/null +++ b/services/console/lib/procmem/proc.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python + +import sys, os +import string + +class ProcInfo: + + dir_path = "/proc/" # Our cute Proc File System + status_file = "status" + stat_file = "stat" + + proc_list = [] # Our PID list :D + proc_info = [] # + + def __init__(self): + self.proc_list = self.Get_PID_List() + + # Returns Process List + def Get_PID_List(self): + list = [] + + # Exists our procfs ? + if os.path.isdir(self.dir_path): + # around dir entries + for f in os.listdir(self.dir_path): + if os.path.isdir(self.dir_path+f) & str.isdigit(f): + list.append(int(f)) + + return list + + def MemoryInfo(self, pid): + # Path + pidfile = self.dir_path + str(pid) + "/stat" + try: + infile = open(pidfile, "r") + except: + print "Error trying " + pidfile + return None + + # Parsing data , check 'man 5 proc' for details + data = infile.read().split() + + infile.close() + + state_dic = { + 'R': 'Running', + 'S': 'Sleeping', + 'D': 'Disk sleep', + 'Z': 'Zombie', + 'T': 'Traced/Stopped', + 'W': 'Paging' + } + + # user and group owners + pidstat = os.stat(pidfile) + + info = { + 'pid': int(data[0]), # Process ID + 'name': data[1].strip('()'), # Process name + 'state': data[2], # Process State, ex: R|S|D|Z|T|W + 'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc + 'ppid': int(data[3]), # Parent process ID + 'utime': int(data[13]), # Used jiffies in user mode + 'stime': int(data[14]), # Used jiffies in kernel mode + 'start_time': int(data[21]), # Process time from system boot (jiffies) + 'vsize': int(data[22]), # Virtual memory size used (bytes) + 'rss': int(data[23])*4, # Resident Set Size (bytes) + 'user_id': pidstat.st_uid, # process owner + 'group_id': pidstat.st_gid # owner group + } + + return info + + + # Returns the CPU usage expressed in Jiffies + def get_CPU_usage(self, cpu_hz, used_jiffies, start_time): + + # Uptime info + uptime_file = self.dir_path + "/uptime" + try: + infile = file(uptime_file, "r") + except: + print "Error trying uptime file" + return None + + uptime_line = infile.readline() + uptime = string.split(uptime_line, " ",2) + + infile.close() + + # System uptime, from /proc/uptime + uptime = float(uptime[0]) + + # Jiffies + avail_jiffies = (uptime * cpu_hz) - start_time + + cpu_usage = {'used_jiffies': used_jiffies, 'avail_jiffies': avail_jiffies} + + return cpu_usage + diff --git a/services/console/lib/procmem/proc_smaps.py b/services/console/lib/procmem/proc_smaps.py new file mode 100644 index 0000000..9416c52 --- /dev/null +++ b/services/console/lib/procmem/proc_smaps.py @@ -0,0 +1,129 @@ +#################################################################### +# This class open the /proc/PID/maps and /proc/PID/smaps files +# to get useful information about the real memory usage +#################################################################### +#!/usr/bin/env python + +import os + +# Parse the /proc/PID/smaps file +class ProcSmaps: + + mappings = [] # Devices information + + def __init__(self, pid): + + smapfile = "/proc/%s/smaps" % pid + self.mappings = [] + + # Coded by Federico Mena (script) + try: + infile = open(smapfile, "r") + input = infile.read() + infile.close() + except: + print "Error trying " + smapfile + return + + lines = input.splitlines() + + 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 + + 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]) + name = name.strip () + + mapping = Mapping (size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name) + self.mappings.append (mapping) + + num_lines -= 7 + line_idx += 7 + + # 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: + def __init__ (self, size, rss, shared_clean, shared_dirty, private_clean, private_dirty, 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.permissions = permissions + 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 diff --git a/services/console/org.laptop.sugar.Console.service.in b/services/console/org.laptop.sugar.Console.service.in new file mode 100644 index 0000000..90882fe --- /dev/null +++ b/services/console/org.laptop.sugar.Console.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name = org.laptop.Console +Exec = @bindir@/sugar-console + diff --git a/services/console/sugar-console b/services/console/sugar-console new file mode 100755 index 0000000..b971fb4 --- /dev/null +++ b/services/console/sugar-console @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import pygtk +pygtk.require('2.0') + +import os, sys +from sugar import env + +sys.path.insert(0, os.path.join(env.get_data_dir(), 'services/console')) + +import console -- cgit v0.9.1