Change in vdsm[master]: profile: Allow disabling threads when using the decorator

nsoffer at redhat.com nsoffer at redhat.com
Mon May 19 23:12:07 UTC 2014


Nir Soffer has uploaded a new change for review.

Change subject: profile: Allow disabling threads when using the decorator
......................................................................

profile: Allow disabling threads when using the decorator

yappi supports profiling all threads (default) or only the thread where
the profiler was started from. This patch add this option to the profile
decorator. This is is probably not going to be used very much, but
useful when you need it.

New threading tests verify that yappi indeed work correctly with other
threads, even those that started before yappi was started.

Change-Id: I6608a831e0ac2d347d9b2f00458e570da31fdafd
Signed-off-by: Nir Soffer <nsoffer at redhat.com>
---
M lib/vdsm/profile.py
M tests/profileTests.py
2 files changed, 83 insertions(+), 6 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/94/27894/1

diff --git a/lib/vdsm/profile.py b/lib/vdsm/profile.py
index d9069f5..8344e28 100644
--- a/lib/vdsm/profile.py
+++ b/lib/vdsm/profile.py
@@ -33,10 +33,13 @@
 # Import yappi lazily when profile is started
 yappi = None
 
+# Defaults
+
 _FILENAME = os.path.join(constants.P_VDSM_RUN, 'vdsmd.prof')
 _FORMAT = config.get('vars', 'profile_format')
 _BUILTINS = config.getboolean('vars', 'profile_builtins')
 _CLOCK = config.get('vars', 'profile_clock')
+_THREADS = True
 
 _lock = threading.Lock()
 
@@ -48,7 +51,7 @@
 def start():
     """ Starts application wide profiling """
     if is_enabled():
-        _start_profiling(_BUILTINS, _CLOCK)
+        _start_profiling(_CLOCK, _BUILTINS, _THREADS)
 
 
 def stop():
@@ -66,7 +69,7 @@
         return yappi and yappi.is_running()
 
 
-def profile(filename, format=_FORMAT, builtins=_BUILTINS, clock=_CLOCK):
+def profile(filename, format=_FORMAT, clock=_CLOCK, builtins=_BUILTINS, threads=_THREADS):
     """
     Profile decorated function, saving profile to filename using format.
 
@@ -76,7 +79,7 @@
     def decorator(f):
         @wraps(f)
         def wrapper(*a, **kw):
-            _start_profiling(builtins, clock)
+            _start_profiling(clock, builtins, threads)
             try:
                 return f(*a, **kw)
             finally:
@@ -85,7 +88,7 @@
     return decorator
 
 
-def _start_profiling(builtins, clock):
+def _start_profiling(clock, builtins, threads):
     global yappi
     logging.debug("Starting profiling")
     with _lock:
@@ -96,7 +99,7 @@
         if yappi.is_running():
             raise Error('Profiler is already running')
         yappi.set_clock_type(clock)
-        yappi.start(builtins=builtins)
+        yappi.start(builtins=builtins, profile_threads=threads)
 
 
 def _stop_profiling(filename, format):
diff --git a/tests/profileTests.py b/tests/profileTests.py
index 2501620..2bdc691 100644
--- a/tests/profileTests.py
+++ b/tests/profileTests.py
@@ -23,6 +23,7 @@
 import os
 import pstats
 import time
+import threading
 
 from vdsm import profile
 from vdsm import config
@@ -259,6 +260,75 @@
     def wall_clock(self):
         time.sleep(0.1)
 
+
+class ThreadsProfileTests(ProfileTests):
+
+    def setUp(self):
+        self.thread = None
+        self.ready = threading.Event()
+
+    @MonkeyPatch(profile, 'config', make_config(enable='false'))
+    def test_new_threads(self):
+        # The easy case - threads started after yappi was started
+        requires_yappi()
+        self.new_threads()
+        stats = open_ystats(FILENAME)
+        name = function_name(self.worker_function)
+        func = find_function(stats, __file__, name)
+        self.assertEquals(func.ncall, 1)
+
+    @MonkeyPatch(profile, 'config', make_config(enable='false'))
+    def test_running_threads(self):
+        # The harder case - threads started before yappi was started
+        requires_yappi()
+        self.start_thread()
+        self.running_threads()
+        stats = open_ystats(FILENAME)
+        name = function_name(self.worker_function)
+        func = find_function(stats, __file__, name)
+        self.assertEquals(func.ncall, 1)
+
+    @MonkeyPatch(profile, 'config', make_config(enable='false'))
+    def test_without_threads(self):
+        requires_yappi()
+        self.without_threads()
+        stats = open_ystats(FILENAME)
+        name = function_name(self.worker_function)
+        self.assertRaises(NotFound, find_function, stats, __file__, name)
+
+    @profile.profile(FILENAME, format="ystat", threads=True)
+    def new_threads(self):
+        self.start_thread()
+        self.ready.set()
+        self.join_thread()
+
+    @profile.profile(FILENAME, format="ystat", threads=True)
+    def running_threads(self):
+        self.ready.set()
+        self.join_thread()
+
+    @profile.profile(FILENAME, format="ystat", threads=False)
+    def without_threads(self):
+        self.start_thread()
+        self.ready.set()
+        self.join_thread()
+
+    def start_thread(self):
+        self.thread = threading.Thread(target=self.worker)
+        self.thread.daemon = True
+        self.thread.start()
+
+    def join_thread(self):
+        self.thread.join()
+
+    def worker(self):
+        self.ready.wait()
+        self.worker_function()
+
+    def worker_function(self):
+        pass
+
+
 # Helpers
 
 def open_ystats(filename):
@@ -274,11 +344,15 @@
     return False
 
 
+class NotFound(Exception):
+    pass
+
+
 def find_function(ystats, module, name):
     for func in ystats:
         if func.module == module and func.name == name:
             return func
-    raise Exception('No such function: %s(%s)' % (module, name))
+    raise NotFound('No such function: %s(%s)' % (module, name))
 
 
 def function_name(meth):


-- 
To view, visit http://gerrit.ovirt.org/27894
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6608a831e0ac2d347d9b2f00458e570da31fdafd
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer at redhat.com>


More information about the vdsm-patches mailing list