Commit ade3f247 authored by Luke Macken's avatar Luke Macken

Graph CPU & memory usage.

parent 00ddfe03
...@@ -28,6 +28,7 @@ import psutil ...@@ -28,6 +28,7 @@ import psutil
import logging import logging
import keyword import keyword
import tokenize import tokenize
import threading
from meliae import loader from meliae import loader
from gi.repository import GLib, GObject, Pango, Gtk, WebKit from gi.repository import GLib, GObject, Pango, Gtk, WebKit
...@@ -39,9 +40,17 @@ log = logging.getLogger('pyrasite') ...@@ -39,9 +40,17 @@ log = logging.getLogger('pyrasite')
socket_families = dict([(getattr(socket, k), k) for k in dir(socket) socket_families = dict([(getattr(socket, k), k) for k in dir(socket)
if k.startswith('AF_')]) if k.startswith('AF_')])
socket_types = dict([(getattr(socket, k), k) for k in dir(socket) socket_types = dict([(getattr(socket, k), k) for k in dir(socket)
if k.startswith('SOCK_')]) if k.startswith('SOCK_')])
POLL_INTERVAL = 3
cpu_intervals = []
cpu_details = ''
mem_intervals = []
mem_details = ''
read_count = read_bytes = write_count = write_bytes = 0
class Process(pyrasite.PyrasiteIPC, GObject.GObject): class Process(pyrasite.PyrasiteIPC, GObject.GObject):
""" """
...@@ -82,6 +91,7 @@ class PyrasiteWindow(Gtk.Window): ...@@ -82,6 +91,7 @@ class PyrasiteWindow(Gtk.Window):
self.processes = {} self.processes = {}
self.pid = None # Currently selected pid self.pid = None # Currently selected pid
self.resource_thread = None
self.set_title('Pyrasite v%s' % pyrasite.__version__) self.set_title('Pyrasite v%s' % pyrasite.__version__)
self.set_default_size (600, 400) self.set_default_size (600, 400)
...@@ -275,22 +285,10 @@ class PyrasiteWindow(Gtk.Window): ...@@ -275,22 +285,10 @@ class PyrasiteWindow(Gtk.Window):
def generate_description(self, proc, title): def generate_description(self, proc, title):
p = psutil.Process(proc.pid) p = psutil.Process(proc.pid)
cputimes = p.get_cpu_times()
meminfo = p.get_memory_info()
io = p.get_io_counters() io = p.get_io_counters()
self.info_html = """ self.info_html = """
<?xml version="1.0" encoding="UTF-8"?> <html><head>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<style> <style>
body {font: normal 12px/150%% Arial, Helvetica, sans-serif;} body {font: normal 12px/150%% Arial, Helvetica, sans-serif;}
.grid table { border-collapse: collapse; text-align: left; width: 100%%; } .grid table { border-collapse: collapse; text-align: left; width: 100%%; }
...@@ -305,34 +303,39 @@ class PyrasiteWindow(Gtk.Window): ...@@ -305,34 +303,39 @@ class PyrasiteWindow(Gtk.Window):
.grid table tbody .alt td { background: #E1EEf4; color: #00557F; } .grid table tbody .alt td { background: #E1EEf4; color: #00557F; }
.grid table tbody td:first-child { border: none; } .grid table tbody td:first-child { border: none; }
</style> </style>
</head> </head>
<body> <body>
<h2>%(title)s</h2> <h2>%(title)s</h2>
<ul> <div class="grid">
Sparkline: <table>
<span id="cpu_graph" class="cpu_graph">Loading</span> <thead>
<br/> <tr><th>CPU: <span id="cpu_details"/></th>
<li><b>CPU:</b> %(cpu)0.2f%% (%(cpu_user)s user, %(cpu_sys)s system)</li> <th>Memory: <span id="mem_details"/></th></tr>
<li><b>Memory:</b> %(mem)0.2f%% (%(mem_rss)s RSS, %(mem_vms)s VMS)</li> </thead>
<li><b>Read count:</b> %(read_count)s</li> <tbody>
<li><b>Read bytes:</b> %(read_bytes)s</li> <tr><td><span id="cpu_graph" class="cpu_graph"></span></td>
<li><b>Write count:</b> %(write_count)s</li> <td><span id="mem_graph" class="mem_graph"></span></td></tr>
<li><b>Write bytes:</b> %(write_bytes)s</li> </tbody>
</ul> </table>
""" % dict( </div>
title = proc.title, """ % dict(title = proc.title)
cpu = p.get_cpu_percent(interval=1.0),
cpu_user = cputimes.user, self.info_html += """
cpu_sys = cputimes.system, <h3>I/O Counters</h3>
mem = p.get_memory_percent(), <div class="grid">
mem_rss = humanize_bytes(meminfo.rss), <table>
mem_vms = humanize_bytes(meminfo.vms), <thead><tr><th></th><th>Count</th><th>Size</th></tr></thead>
read_count = io.read_count, <tbody>
read_bytes = humanize_bytes(io.read_bytes), <tr><td>Read</td><td><span id="read_count">%s</span></td>
write_count = io.write_count, <td><span id="read_size">%s</span></td></tr>
write_bytes = humanize_bytes(io.write_bytes), <tr><td>Write</td><td><span id="write_count">%s</span></td>
) <td><span id="write_size">%s</span></td></tr>
</tbody>
</table>
</div>
""" % (io.read_count, humanize_bytes(io.read_bytes),
io.write_count, humanize_bytes(io.write_bytes))
open_files = p.get_open_files() open_files = p.get_open_files()
if open_files: if open_files:
...@@ -415,25 +418,41 @@ class PyrasiteWindow(Gtk.Window): ...@@ -415,25 +418,41 @@ class PyrasiteWindow(Gtk.Window):
self.details_view.load_string(self.details_html, "text/html", "utf-8", '#') self.details_view.load_string(self.details_html, "text/html", "utf-8", '#')
def test(): global cpu_intervals, mem_intervals, cpu_details, mem_details
# Inject jQuery cpu_intervals = [p.get_cpu_percent(interval=1.0)]
mem_intervals = [p.get_memory_info().rss]
if not self.resource_thread:
self.resource_thread = ResourceUsagePoller(proc.pid)
#self.resource_thread.process = p
self.resource_thread.daemon = True
self.resource_thread.info_view = self.info_view
self.resource_thread.start()
self.resource_thread.process = p
def poll_resource_usage():
self.info_view.execute_script("""
jQuery('#cpu_graph').sparkline(%s, {'height': 50});
jQuery('#mem_graph').sparkline(%s, {'height': 50, lineColor: '#f00',
fillColor: '#ffa', minSpotColor: false, maxSpotColor: false,
spotColor: '#77f', spotRadius: 3});
jQuery('#cpu_details').text('%s');
jQuery('#mem_details').text('%s');
""" % (cpu_intervals, mem_intervals, cpu_details, mem_details))
return True
GObject.timeout_add(500, self.inject_js)
GObject.timeout_add(3500, poll_resource_usage)
def inject_js(self):
log.debug("Injecting jQuery")
jquery = file('jquery-1.7.1.min.js') jquery = file('jquery-1.7.1.min.js')
self.info_view.execute_script(jquery.read()) self.info_view.execute_script(jquery.read())
jquery.close() jquery.close()
# Inject Sparkline
sparkline = file('jquery.sparkline.min.js') sparkline = file('jquery.sparkline.min.js')
self.info_view.execute_script(sparkline.read()) self.info_view.execute_script(sparkline.read())
sparkline.close() sparkline.close()
self.info_view.execute_script("""
jQuery(document).ready(function() {
jQuery('#cpu_graph').sparkline([10,8,3,7,4,4,1]);
});
""")
GObject.timeout_add(500, test)
def update_progress(self, fraction, text=None): def update_progress(self, fraction, text=None):
if text: if text:
self.progress.set_text(text + '...') self.progress.set_text(text + '...')
...@@ -640,6 +659,7 @@ class PyrasiteWindow(Gtk.Window): ...@@ -640,6 +659,7 @@ class PyrasiteWindow(Gtk.Window):
end_iter.set_line(erow-1) end_iter.set_line(erow-1)
end_iter.set_line_offset(ecol) end_iter.set_line_offset(ecol)
try:
for x in tokenize.generate_tokens(InputStream(data).readline): for x in tokenize.generate_tokens(InputStream(data).readline):
# x has 5-tuples # x has 5-tuples
tok_type, tok_str = x[0], x[1] tok_type, tok_str = x[0], x[1]
...@@ -688,6 +708,8 @@ class PyrasiteWindow(Gtk.Window): ...@@ -688,6 +708,8 @@ class PyrasiteWindow(Gtk.Window):
if is_decorator is True: if is_decorator is True:
is_decorator = False is_decorator = False
except Exception, e:
log.exception(str(e))
def close(self): def close(self):
self.progress.show() self.progress.show()
...@@ -720,8 +742,43 @@ class InputStream(object): ...@@ -720,8 +742,43 @@ class InputStream(object):
return line return line
class ResourceUsagePoller(threading.Thread):
"""A thread for polling a processes CPU & memory usage"""
process = None
def __init__(self, pid):
super(ResourceUsagePoller, self).__init__()
self.process = psutil.Process(pid)
def run(self):
global cpu_intervals, mem_intervals, cpu_details, mem_details
global read_count, read_bytes, write_count, write_bytes
while True:
if self.process:
if len(cpu_intervals) >= 100:
cpu_intervals = cpu_intervals[1:100]
mem_intervals = mem_intervals[1:100]
cpu_intervals.append(
self.process.get_cpu_percent(interval=POLL_INTERVAL))
mem_intervals.append(self.process.get_memory_info().rss)
cputimes = self.process.get_cpu_times()
cpu_details = '%0.2f%% (%s user, %s system)' % (
cpu_intervals[-1], cputimes.user, cputimes.system)
meminfo = self.process.get_memory_info()
mem_details = '%0.2f%% (%s RSS, %s VMS)' % (
self.process.get_memory_percent(),
humanize_bytes(meminfo.rss),
humanize_bytes(cputimes.system))
io = self.process.get_io_counters()
read_count = io.read_count
read_bytes = humanize_bytes(io.read_bytes)
write_count = io.write_count
write_bytes = humanize_bytes(io.write_bytes)
def main(): def main():
mainloop = GLib.MainLoop() mainloop = GLib.MainLoop()
GObject.threads_init()
window = PyrasiteWindow() window = PyrasiteWindow()
window.show() window.show()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment