def __init__(self, environment=dict()): """ :param environment: dependency container dictionary for the current profiler :param sampling_interval: (required inside environment) delay between profile reports in datetime.timedelta :param killswitch_filepath: (required inside environment) filepath pointing to the killswitch file. This path gets checked every time the profiler samples; the profiler is immediately stopped if this file exists. :param collector: (required inside environment) collector object to handle sample processing :param initial_sampling_interval: (required inside environment) Initial delay signal sampler takes for starting to sample :param profiler_thread_name: (required inside environment) Thread name used for running the report_orchestration_scheduler """ self.timer = environment.get("timer") self.sampler = environment.get("sampler") or Sampler( environment=environment) self.scheduler = Scheduler( command=self._profiling_command, delay_provider=lambda: AgentConfiguration.get().sampling_interval, initial_delay=environment["initial_sampling_interval"], thread_name=environment["profiler_thread_name"]) self.collector = environment["collector"] self.profiler_disabler = environment["profiler_disabler"] self.is_profiling_in_progress = False self._first_execution = True
def around(self): self.ready_queue = Queue() self.done_queue = Queue() self.scheduler = Scheduler(self.running_test_process, thread_name="test_thread") self.scheduler.start() self.ready_queue.get(TEST_TIMEOUT_SECONDS) yield self.done_queue.put(True) self.scheduler.stop()
def test_exception_not_propagated_from_scheduled_thread(self): self.exception_thrown = False def throw_exception(): self.exception_thrown = True raise Exception("testing") scheduler = \ Scheduler(command=throw_exception, thread_name="test_thread") scheduler.start() scheduler._thread.join() assert self.exception_thrown
def around(self): self.ready_queue = Queue() self.done_queue = Queue() def running_test_process(): self.ready_queue.put(True) return self.done_queue.get(TEST_TIMEOUT_SECONDS) self.scheduler = \ Scheduler(running_test_process, thread_name="test_thread") self.scheduler.start() self.ready_queue.get(TEST_TIMEOUT_SECONDS) # finish first execution by pushing into done_queue # the scheduler should go into wait self.done_queue.put(True) # then pause self.scheduler.pause(block=True) yield self.scheduler.stop()
class TestStart: @pytest.fixture(autouse=True) def around(self): self.ready_queue = Queue() self.done_queue = Queue() self.scheduler = Scheduler(self.running_test_process, thread_name="test_thread") self.scheduler.start() self.ready_queue.get(TEST_TIMEOUT_SECONDS) yield self.done_queue.put(True) self.scheduler.stop() def running_test_process(self): self.ready_queue.put(True) self.done_queue.get(TEST_TIMEOUT_SECONDS) def test_function_run_on_new_thread(self): assert (self.scheduler.is_running()) assert (self.scheduler._thread.name == "test_thread")
class TestStop: @before def before(self): self.scheduler = Scheduler(lambda: True, thread_name="test_thread") self.scheduler.start() # Make sure thread is alive assert (self.scheduler.is_running()) def test_thread_terminates_when_called_stop(self): self.scheduler.stop() assert (not self.scheduler.is_running())
class TestPauseAndResume: @pytest.fixture(autouse=True) def around(self): self.ready_queue = Queue() self.done_queue = Queue() def running_test_process(): self.ready_queue.put(True) return self.done_queue.get(TEST_TIMEOUT_SECONDS) self.scheduler = \ Scheduler(running_test_process, thread_name="test_thread") self.scheduler.start() self.ready_queue.get(TEST_TIMEOUT_SECONDS) # finish first execution by pushing into done_queue # the scheduler should go into wait self.done_queue.put(True) # then pause self.scheduler.pause(block=True) yield self.scheduler.stop() def test_pause_when_scheduler_is_paused(self): assert (self.scheduler.is_running()) assert (self.scheduler.is_paused()) def test_resume_when_scheduler_is_running(self): self.scheduler.resume(block=True) assert (self.scheduler.is_running()) assert (not self.scheduler.is_paused())
def test_exception_not_thrown_when_stop_is_called_before_starting( self): scheduler = Scheduler(lambda: True, thread_name="test_thread") scheduler.stop()
def before(self): self.scheduler = Scheduler(lambda: True, thread_name="test_thread") self.scheduler.start() # Make sure thread is alive assert (self.scheduler.is_running())
class ProfilerRunner: """ ProfilerRunner instantiates and orchestrates all components required for running the profiler. It implements and checks the kill switch and cpu limit every time before it samples. This class should only be accessed by Profiler for controlling and monitoring the profiler. NOTE: If CPU usage exceed CPU limit, profiler will be terminated immediately without any attempt to flush the existing data. NOTE: Memory limit check is implemented in LocalAggregator """ def __init__(self, environment=dict()): """ :param environment: dependency container dictionary for the current profiler :param sampling_interval: (required inside environment) delay between profile reports in datetime.timedelta :param killswitch_filepath: (required inside environment) filepath pointing to the killswitch file. This path gets checked every time the profiler samples; the profiler is immediately stopped if this file exists. :param collector: (required inside environment) collector object to handle sample processing :param initial_sampling_interval: (required inside environment) Initial delay signal sampler takes for starting to sample :param profiler_thread_name: (required inside environment) Thread name used for running the report_orchestration_scheduler """ self.timer = environment.get("timer") self.sampler = environment.get("sampler") or Sampler( environment=environment) self.scheduler = Scheduler( command=self._profiling_command, delay_provider=lambda: AgentConfiguration.get().sampling_interval, initial_delay=environment["initial_sampling_interval"], thread_name=environment["profiler_thread_name"]) self.collector = environment["collector"] self.profiler_disabler = environment["profiler_disabler"] self.is_profiling_in_progress = False self._first_execution = True def start(self): """ Start running the profiler. Note: Profiler will not start if killswitch file exists. :return: True if the profiler was started successfully; False otherwise. """ if self.profiler_disabler.should_stop_profiling(): logger.info("Profiler will not start.") return False self.scheduler.start() return True def _refresh_configuration(self): self.collector.refresh_configuration() self.is_profiling_in_progress = AgentConfiguration.get().should_profile if self.is_profiling_in_progress: self.scheduler.update_delay_provider( lambda: AgentConfiguration.get().sampling_interval) else: # if we should not profile we can simply wait for the reporting interval and call again at that time. self.scheduler.update_delay_provider( lambda: AgentConfiguration.get().reporting_interval) def _profiling_command(self): try: if self._first_execution: self.collector.setup() self._first_execution = False return self._run_profiler() except: logger.info( "An unexpected issue caused the profiling command to terminate.", exc_info=True) return False @with_timer("runProfiler") def _run_profiler(self): if self.profiler_disabler.should_stop_profiling( self.collector.profile): return False if not self.is_profiling_in_progress: self._refresh_configuration() # after the refresh we may be working on a profile if self.is_profiling_in_progress: sample = self.sampler.sample() self.collector.add(sample) if self.collector.flush(): self.is_profiling_in_progress = False return True def is_running(self): return self.scheduler.is_running() def is_paused(self): return self.scheduler.is_paused() def stop(self): """ Terminate profiler gracefully. It terminates the profiling thread and flushes existing profile to the backend. """ self.scheduler.stop() self.collector.flush(force=True) self.is_profiling_in_progress = False def resume(self, block=False): """ Will signal the scheduler that profiling should resume. :param block: if True, we will not return from this function before the change is applied, default is False. """ self.collector.profile.resume() self.scheduler.resume(block) def pause(self, block=False): """ Will signal the scheduler that profiling should pause. :param block: if True, we will not return from this function before the change is applied, default is False. """ self.scheduler.pause(block) self.collector.profile.pause()