commit 3148caa37ba406610ddb0187468107205581c105 Author: Damian Johnson atagar@torproject.org Date: Wed Apr 13 10:10:04 2016 -0700
Account for subcommands in nyx cpu usage
Shelling out to other commands isn't accounted for in os.times(). As such wrapping stem's call() function so shelling out is accounted for in the nyx cpu usage we show. --- nyx/panel/header.py | 53 ++++++++++++++++++++++++++++++++++++---------------- test/panel/header.py | 13 ++++++++++--- 2 files changed, 47 insertions(+), 19 deletions(-)
diff --git a/nyx/panel/header.py b/nyx/panel/header.py index 998a20d..f29d232 100644 --- a/nyx/panel/header.py +++ b/nyx/panel/header.py @@ -12,14 +12,17 @@ import time import threading
import stem +import stem.control +import stem.util.proc +import stem.util.str_tools +import stem.util.system
import nyx.controller import nyx.panel import nyx.popups import nyx.tracker
-from stem.control import Listener, State -from stem.util import conf, log, proc, str_tools, system +from stem.util import conf, log from nyx import msg, tor_controller
from nyx.curses import RED, GREEN, YELLOW, CYAN, WHITE, BOLD, HIGHLIGHT @@ -28,6 +31,13 @@ MIN_DUAL_COL_WIDTH = 141 # minimum width where we'll show two columns SHOW_FD_THRESHOLD = 60 # show file descriptor usage if usage is over this percentage UPDATE_RATE = 5 # rate in seconds at which we refresh
+# Tracks total time spent shelling out to other commands like 'ps' and +# 'netstat', so we can account for it as part of our cpu time. + +SYSTEM_CALL_TIME = 0.0 +SYSTEM_CALL_TIME_LOCK = threading.RLock() +SYSTEM_CALL_ORIG = stem.util.system.call + CONFIG = conf.config_dict('nyx', { 'attr.flag_colors': {}, 'attr.version_status_colors': {}, @@ -35,6 +45,21 @@ CONFIG = conf.config_dict('nyx', { })
+def call_wrapper(*args, **kwargs): + global SYSTEM_CALL_TIME + + start_time = time.time() + + try: + return SYSTEM_CALL_ORIG(*args, **kwargs) + finally: + with SYSTEM_CALL_TIME_LOCK: + SYSTEM_CALL_TIME += time.time() - start_time + + +stem.util.system.call = call_wrapper + + class HeaderPanel(nyx.panel.Panel, threading.Thread): """ Top area containing tor settings and system information. @@ -232,7 +257,7 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread): def reset_listener(self, controller, event_type, _): self._update()
- if event_type == State.CLOSED: + if event_type == stem.control.State.CLOSED: log.notice('Tor control port closed')
def _update(self): @@ -282,10 +307,10 @@ class Sampling(object):
pid = controller.get_pid('') tor_resources = nyx.tracker.get_resource_tracker().get_value() - nyx_total_cpu_time = sum(os.times()[:3]) + nyx_total_cpu_time = sum(os.times()[:3], SYSTEM_CALL_TIME)
- or_listeners = controller.get_listeners(Listener.OR, []) - control_listeners = controller.get_listeners(Listener.CONTROL, []) + or_listeners = controller.get_listeners(stem.control.Listener.OR, []) + control_listeners = controller.get_listeners(stem.control.Listener.CONTROL, [])
if controller.get_conf('HashedControlPassword', None): auth_type = 'password' @@ -295,18 +320,14 @@ class Sampling(object): auth_type = 'open'
try: - fd_used = proc.file_descriptors_used(pid) + fd_used = stem.util.proc.file_descriptors_used(pid) except IOError: fd_used = None
if last_sampling: nyx_cpu_delta = nyx_total_cpu_time - last_sampling.nyx_total_cpu_time nyx_time_delta = retrieved - last_sampling.retrieved - - python_cpu_time = nyx_cpu_delta / nyx_time_delta - sys_call_cpu_time = 0.0 # TODO: add a wrapper around call() to get this - - nyx_cpu = python_cpu_time + sys_call_cpu_time + nyx_cpu = nyx_cpu_delta / nyx_time_delta else: nyx_cpu = 0.0
@@ -334,14 +355,14 @@ class Sampling(object):
'auth_type': auth_type, 'pid': pid, - 'start_time': system.start_time(pid), + 'start_time': stem.util.system.start_time(pid), 'fd_limit': int(controller.get_info('process/descriptor-limit', '-1')), 'fd_used': fd_used,
'nyx_total_cpu_time': nyx_total_cpu_time, 'tor_cpu': '%0.1f' % (100 * tor_resources.cpu_sample), 'nyx_cpu': '%0.1f' % (nyx_cpu), - 'memory': str_tools.size_label(tor_resources.memory_bytes) if tor_resources.memory_bytes > 0 else 0, + 'memory': stem.util.str_tools.size_label(tor_resources.memory_bytes) if tor_resources.memory_bytes > 0 else 0, 'memory_percent': '%0.1f' % (100 * tor_resources.memory_percent),
'hostname': os.uname()[1], @@ -354,7 +375,7 @@ class Sampling(object): formatted_msg = message.format(**self._attr)
if crop_width is not None: - formatted_msg = str_tools.crop(formatted_msg, crop_width) + formatted_msg = stem.util.str_tools.crop(formatted_msg, crop_width)
return formatted_msg
@@ -444,7 +465,7 @@ def _draw_resource_usage(subwindow, x, y, width, vals, pause_time): else: now = time.time()
- uptime = str_tools.short_time_label(now - vals.start_time) + uptime = stem.util.str_tools.short_time_label(now - vals.start_time) else: uptime = ''
diff --git a/test/panel/header.py b/test/panel/header.py index 7ea2510..6e0f275 100644 --- a/test/panel/header.py +++ b/test/panel/header.py @@ -5,10 +5,12 @@ Unit tests for nyx.panel.header. import time import unittest
-import nyx.panel.header import stem.control import stem.exit_policy import stem.version +import stem.util.system + +import nyx.panel.header import test
from test import require_curses @@ -16,13 +18,18 @@ from mock import patch, Mock
class TestHeader(unittest.TestCase): + def test_system_call_time_tracked(self): + initial = nyx.panel.header.SYSTEM_CALL_TIME + stem.util.system.call('sleep 0.5') + self.assertTrue(nyx.panel.header.SYSTEM_CALL_TIME - initial > 0.4) + @patch('nyx.panel.header.tor_controller') @patch('nyx.tracker.get_resource_tracker') @patch('time.time', Mock(return_value = 1234.5)) @patch('os.times', Mock(return_value = (0.08, 0.03, 0.0, 0.0, 18759021.31))) @patch('os.uname', Mock(return_value = ('Linux', 'odin', '3.5.0-54-generic', '#81~precise1-Ubuntu SMP Tue Jul 15 04:05:58 UTC 2014', 'i686'))) - @patch('nyx.panel.header.system.start_time', Mock(return_value = 5678)) - @patch('nyx.panel.header.proc.file_descriptors_used', Mock(return_value = 89)) + @patch('stem.util.system.start_time', Mock(return_value = 5678)) + @patch('stem.util.proc.file_descriptors_used', Mock(return_value = 89)) def test_sample(self, resource_tracker_mock, tor_controller_mock): tor_controller_mock().is_alive.return_value = True tor_controller_mock().connection_time.return_value = 567.8
tor-commits@lists.torproject.org