[tor-commits] [nyx/master] Account for subcommands in nyx cpu usage

atagar at torproject.org atagar at torproject.org
Mon Apr 18 20:23:16 UTC 2016


commit 3148caa37ba406610ddb0187468107205581c105
Author: Damian Johnson <atagar at 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





More information about the tor-commits mailing list