2015-10-26 50 views
3

我正在尝试使用SIGVTALRM来快照我的Python代码,但它似乎并没有在诸如time.sleep()和套接字操作之类的阻止操作中发射。为什么SIGVTALRM在time.sleep()内触发?

这是为什么?有什么办法可以解决这个问题吗,所以我可以在阻止操作时收集样本?

我也试过使用ITIMER_PROF/SIGPROFITIMER_REAL/SIGALRM,两者似乎都产生了类似的结果。

我与测试代码如下,输出是一样的东西:

$ python profiler-test.py 
<module>(__main__:1);test_sampling_profiler(__main__:53): 1 
<module>(__main__:1);test_sampling_profiler(__main__:53);busyloop(__main__:48): 1509 

注意,timesleep功能不显示在所有。

测试代码:

import time 
import signal 
import collections 


class SamplingProfiler(object): 
    def __init__(self, interval=0.001, logger=None): 
     self.interval = interval 
     self.running = False 
     self.counter = collections.Counter() 

    def _sample(self, signum, frame): 
     if not self.running: 
      return 

     stack = [] 
     while frame is not None: 
      formatted_frame = "%s(%s:%s)" %(
       frame.f_code.co_name, 
       frame.f_globals.get('__name__'), 
       frame.f_code.co_firstlineno, 
      ) 
      stack.append(formatted_frame) 
      frame = frame.f_back 

     formatted_stack = ';'.join(reversed(stack)) 
     self.counter[formatted_stack] += 1 
     signal.setitimer(signal.ITIMER_VIRTUAL, self.interval, 0) 

    def start(self): 
     if self.running: 
      return 
     signal.signal(signal.SIGVTALRM, self._sample) 
     signal.setitimer(signal.ITIMER_VIRTUAL, self.interval, 0) 
     self.running = True 

    def stop(self): 
     if not self.running: 
      return 
     self.running = False 
     signal.signal(signal.SIGVTALRM, signal.SIG_IGN) 

    def flush(self): 
     res = self.counter 
     self.counter = collections.Counter() 
     return res 

def busyloop(): 
    start = time.time() 
    while time.time() - start < 5: 
     pass 

def timesleep(): 
    time.sleep(5) 

def test_sampling_profiler(): 
    p = SamplingProfiler() 
    p.start() 
    busyloop() 
    timesleep() 
    p.stop() 
    print "\n".join("%s: %s" %x for x in sorted(p.flush().items())) 

if __name__ == "__main__": 
    test_sampling_profiler() 
+0

time.sleep使os暂停程序线程n秒。所以在这个时候Python不会做任何事情。 – palsch

回答

1

不知道为什么time.sleep这样工作(难道是用SIGALRM本身知道何时才能恢复?),但Popen.wait不阻断信号,因此最坏的情况下你可以调用OS进入睡眠状态。

0

另一种方法是使用一个单独的线程来触发采样:

import sys 
import threading 
import time 
import collections 


class SamplingProfiler(object): 
    def __init__(self, interval=0.001): 
     self.interval = interval 
     self.running = False 
     self.counter = collections.Counter() 
     self.thread = threading.Thread(target=self._sample) 

    def _sample(self): 
     while self.running: 
      next_wakeup_time = time.time() + self.interval 
      for thread_id, frame in sys._current_frames().items(): 
       if thread_id == self.thread.ident: 
        continue 
       stack = [] 
       while frame is not None: 
        formatted_frame = "%s(%s:%s)" % (
         frame.f_code.co_name, 
         frame.f_globals.get('__name__'), 
         frame.f_code.co_firstlineno, 
        ) 
        stack.append(formatted_frame) 
        frame = frame.f_back 

       formatted_stack = ';'.join(reversed(stack)) 
       self.counter[formatted_stack] += 1 
      sleep_time = next_wakeup_time - time.time() 
      if sleep_time > 0: 
       time.sleep(sleep_time) 

    def start(self): 
     if self.running: 
      return 
     self.running = True 
     self.thread.start() 

    def stop(self): 
     if not self.running: 
      return 
     self.running = False 

    def flush(self): 
     res = self.counter 
     self.counter = collections.Counter() 
     return res 

def busyloop(): 
    start = time.time() 
    while time.time() - start < 5: 
     pass 

def timesleep(): 
    time.sleep(5) 

def test_sampling_profiler(): 
    p = SamplingProfiler() 
    p.start() 
    busyloop() 
    timesleep() 
    p.stop() 
    print "\n".join("%s: %s" %x for x in sorted(p.flush().items())) 

if __name__ == "__main__": 
    test_sampling_profiler() 

在做这种方式的结果是:

$ python profiler-test.py 
<module>(__main__:1);test_sampling_profiler(__main__:62);busyloop(__main__:54): 2875 
<module>(__main__:1);test_sampling_profiler(__main__:62);start(__main__:37);start(threading:717);wait(threading:597);wait(threading:309): 1 
<module>(__main__:1);test_sampling_profiler(__main__:62);timesleep(__main__:59): 4280 

还没完全公平,但总比没有样品更好完全在睡梦中。

0

一个sleep()期间没有SIGVTALRM并不让我感到吃惊,因为ITIMER_VIRTUAL "runs only when the process is executing." (顺便说一句,CPython的在非Windows平台上实现了在select()方面time.sleep()。)

如果是单纯的SIGALRM,但是,我期待一个信号中断,事实上,我看到一个:

<module>(__main__:1);test_sampling_profiler(__main__:62);busyloop(__main__:54): 4914 
<module>(__main__:1);test_sampling_profiler(__main__:62);timesleep(__main__:59): 1 

我有所改变的代码,但你的想法:

class SamplingProfiler(object): 

    TimerSigs = { 
     signal.ITIMER_PROF : signal.SIGPROF, 
     signal.ITIMER_REAL : signal.SIGALRM, 
     signal.ITIMER_VIRTUAL : signal.SIGVTALRM, 
    } 

    def __init__(self, interval=0.001, timer = signal.ITIMER_REAL): # CHANGE 
     self.interval = interval 
     self.running = False 
     self.counter = collections.Counter() 
     self.timer = timer     # CHANGE 
     self.signal = self.TimerSigs[timer] # CHANGE 
    ....