def before(self): self.profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ 'initial_sampling_interval': timedelta(), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }) self.client_stubber = Stubber( self.profiler._profiler_runner.collector.reporter. codeguru_client_builder.codeguru_client) report_expected_params = { 'agentProfile': ANY, 'contentType': 'application/json', 'profilingGroupName': DUMMY_TEST_PROFILING_GROUP_NAME } self.client_stubber.add_response( 'configure_agent', {'configuration': { 'shouldProfile': True, 'periodInSeconds': 100 }}) self.client_stubber.add_response('post_agent_profile', {}, report_expected_params) yield self.profiler.stop()
def test_exceptions_are_caught_and_do_not_propagate(self): Profiler( profiling_group_name="test-application", environment_override={ "profiler_runner_factory": throw_exception }, ) Profiler(profiling_group_name=None)
def test_report_via_invalid_reporting_mode(self): self.profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ "reporting_mode": "invalid_reporting_mode", }) self.profiler.start() assert self.profiler.is_running() is False
class TestWhenAnotherInstanceAlreadyStarted: @pytest.fixture(autouse=True) def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.profiler_runner.start = Mock(return_value=True) self.profiler_runner.is_running = Mock(return_value=False) self.first_profiler = Profiler( profiling_group_name="pg_name", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory(self.profiler_runner) }) self.first_profiler.start() self.second_profiler = Profiler( profiling_group_name="test-application", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory(self.profiler_runner) }) yield self.first_profiler.stop() self.second_profiler.stop() def test_stopping_first_instance_allows_next_profiler_to_start( self): self.first_profiler.stop() assert self.second_profiler.start()
def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.profiler_runner.pause = Mock() self.profiler = Profiler(profiling_group_name="test-application", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory( self.profiler_runner) }) yield self.profiler.stop()
def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.profiler_runner.is_running = Mock(return_value=False) self.profiler_runner.start = throw_exception self.profiler = Profiler( profiling_group_name="test-application", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory(self.profiler_runner) }) yield self.profiler.stop()
def test_profiler_does_not_start(self): with tempfile.NamedTemporaryFile() as killswitch_file: profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ "killswitch_filepath": killswitch_file.name }) try: assert profiler.start() is False finally: profiler.stop()
def before(self): self.timer = Timer() self.profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ "timer": self.timer, "cpu_limit_percentage": 40, "sampling_interval": timedelta(seconds=0.01), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }, ) yield self.profiler.stop()
class TestEndToEndProfiling: @before def before(self): self.profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ 'initial_sampling_interval': timedelta(), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }) self.client_stubber = Stubber( self.profiler._profiler_runner.collector.reporter. codeguru_client_builder.codeguru_client) report_expected_params = { 'agentProfile': ANY, 'contentType': 'application/json', 'profilingGroupName': DUMMY_TEST_PROFILING_GROUP_NAME } self.client_stubber.add_response( 'configure_agent', {'configuration': { 'shouldProfile': True, 'periodInSeconds': 100 }}) self.client_stubber.add_response('post_agent_profile', {}, report_expected_params) yield self.profiler.stop() def test_report_when_stopped(self): with \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration.is_under_min_reporting_time", return_value=False), \ patch( "codeguru_profiler_agent.sdk_reporter.sdk_reporter.SdkReporter.check_create_pg_called_during_submit_profile", return_value=False): with self.client_stubber: self.profiler.start() self.profiler.stop() # stop will trigger the report # check that we have reported. assert_no_pending_responses will fail if post_agent_profile is not called # with the expected parameters. self.client_stubber.assert_no_pending_responses() def test_report_via_invalid_reporting_mode(self): self.profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ "reporting_mode": "invalid_reporting_mode", }) self.profiler.start() assert self.profiler.is_running() is False
class TestExceptionHandling: @pytest.fixture(autouse=True) def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.profiler_runner.is_running = Mock(return_value=False) self.profiler_runner.start = throw_exception self.profiler = Profiler( profiling_group_name="test-application", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory(self.profiler_runner) }) yield self.profiler.stop() def test_exceptions_are_caught_and_do_not_propagate(self): assert (not self.profiler.start())
class TestWhenStartWasNotCalled: @pytest.fixture(autouse=True) def around(self): self.profiler = Profiler( profiling_group_name="test-application") yield def test_it_returns_true(self): assert self.profiler.stop()
def test_it_raises_a_value_error(self): with pytest.raises(ValueError): Profiler( profiling_group_name="unit-test", environment_override={ "allow_top_level_exceptions": True, "reporting_mode": "wrong-reporting-mode" }, )
def before(self): temporary_directory = tempfile.mkdtemp() self.temp_filepath = str( Path(temporary_directory, 'test_profiler_stops_after_killswitch_was_detected')) self.profiler = Profiler( profiling_group_name="test-application", environment_override={ "cpu_limit_percentage": None, "killswitch_filepath": self.temp_filepath, "sampling_interval": timedelta(seconds=0.1), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }) yield shutil.rmtree(temporary_directory) self.profiler.stop()
def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.profiler_runner.start = Mock(return_value=True) self.profiler_runner.is_running = Mock(return_value=False) self.first_profiler = Profiler( profiling_group_name="pg_name", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory(self.profiler_runner) }) self.first_profiler.start() self.second_profiler = Profiler( profiling_group_name="test-application", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory(self.profiler_runner) }) yield self.first_profiler.stop() self.second_profiler.stop()
class TestWhenRunnerIsRunning: @pytest.fixture(autouse=True) def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.profiler_runner.is_running = Mock(return_value=True) self.profiler_runner.resume = Mock() self.profiler = Profiler( profiling_group_name="test-application", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory(self.profiler_runner) }) yield self.profiler.stop() def test_it_returns_true(self): assert self.profiler.start() def test_it_calls_resume(self): self.profiler.start() assert self.profiler_runner.resume.called
class TestCPULimitReachedDuringProfiling: @before def before(self): self.timer = Timer() self.profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ "timer": self.timer, "cpu_limit_percentage": 40, "sampling_interval": timedelta(seconds=0.01), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }, ) yield self.profiler.stop() def test_profiler_terminates(self): self.profiler.start() assert self.profiler.is_running() # With sampling_interval to be 0.01 seconds, having runProfiler as 0.5 seconds should breach # the cpu limit. We need to sample 20 times before we check the CPU limit for i in range(20): self.timer.record('sampleAndAggregate', 0.5) assert wait_for(lambda: not self.profiler.is_running(), timeout_seconds=5)
class TestKillSwitchActivatesDuringExecution: @before def before(self): temporary_directory = tempfile.mkdtemp() self.temp_filepath = str( Path(temporary_directory, 'test_profiler_stops_after_killswitch_was_detected')) self.profiler = Profiler( profiling_group_name="test-application", environment_override={ "cpu_limit_percentage": None, "killswitch_filepath": self.temp_filepath, "sampling_interval": timedelta(seconds=0.1), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }) yield shutil.rmtree(temporary_directory) self.profiler.stop() def test_profiler_stops_after_killswitch_was_detected(self): self.profiler.start() assert self.profiler.is_running() is True Path(self.temp_filepath).touch() # Force the killswitch check happens immediately self.profiler._profiler_runner_instance.profiler_disabler.killswitch.last_check_for_file_time = None assert (wait_for(lambda: not self.profiler.is_running(), timeout_seconds=5))
def before(self): self.mock_reporter = MagicMock(name="reporter", spec=SdkReporter) self.mock_profile = MagicMock(name="profile", spec=Profile) self.mock_profile_factory = MagicMock(name="profile_factory", spec=Profile, return_value=self.mock_profile) self.timer = mock_timer() self.time_now = CURRENT_TIME_FOR_TESTING_SECOND self.clock = lambda: self.time_now self.reporting_interval = DEFAULT_REPORTING_INTERVAL self.environment = { "profiling_group_name": TEST_PROFILING_GROUP_NAME, "sampling_interval": timedelta(seconds=TEST_SAMPLING_INTERVAL), "host_weight": TEST_HOST_WEIGHT, "profile_factory": self.mock_profile_factory, "errors_metadata": ERRORS_METADATA, "memory_limit_bytes": DEFAULT_MEMORY_LIMIT_BYTES, "clock": self.clock, "timer": self.timer, } self.configuration = { "reporter": self.mock_reporter, "environment": self.environment, } AgentConfiguration.set( AgentConfiguration( should_profile=True, sampling_interval=timedelta(seconds=TEST_SAMPLING_INTERVAL), minimum_time_reporting=INITIAL_MINIMUM_REPORTING_INTERVAL, reporting_interval=self.reporting_interval, max_stack_depth=999, cpu_limit_percentage=10)) assert len(self.environment.keys()) == 8 self.profiler = Profiler( profiling_group_name=TEST_PROFILING_GROUP_NAME, environment_override=self.environment) assert len(self.environment.keys()) == 7 self.subject = LocalAggregator(**self.configuration) self.mock_profile_factory.reset_mock() self.timer.reset_mock() def move_clock_to(duration_timedelta): self.time_now = \ CURRENT_TIME_FOR_TESTING_SECOND + duration_timedelta.total_seconds() self.move_clock_to = move_clock_to
def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.session = Mock() self.session.__repr__ = \ Mock(return_value="Session(region_name='eu-west-2', profile_name='alternate_credentials')") self.profiler = Profiler(profiling_group_name="test-application", region_name="ap-southeast-1", aws_session=self.session, environment_override={ "sampling_interval": timedelta(seconds=0.01), "profiler_runner_factory": mock_profiler_runner_factory( self.profiler_runner) }) yield
def test_checks_number_of_params(self): environment = { 'reporting_mode': 'codeguru_service', 'excluded_threads': set(), 'should_profile': True, 'sampling_interval': timedelta(seconds=1), 'reporting_interval': timedelta(minutes=10), 'minimum_time_reporting': timedelta(minutes=1), 'max_stack_depth': 1000, 'cpu_limit_percentage': 10, 'memory_limit_bytes': 1024, 'host_weight': 1.0, 'max_threads': 100 } assert len(environment.keys()) == 11 Profiler(profiling_group_name="test-application", environment_override=environment) assert len(environment.keys()) == 5
def test_it_samples_and_saves_a_profile_to_a_file(self): with \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration.is_under_min_reporting_time", return_value=False): file_prefix = str(Path(self.temporary_directory, FILE_PREFIX)) test_start_time = time_utils.current_milli_time() profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, environment_override={ "initial_sampling_interval": timedelta(), "reporting_mode": "file", "file_prefix": file_prefix, 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }) try: profiler.start() finally: profiler.stop() test_end_time = time_utils.current_milli_time() resulting_profile_path = str( Path(self.temporary_directory, os.listdir(self.temporary_directory)[0])) with (open(resulting_profile_path)) as profiling_result_file: resulting_json = json.loads(profiling_result_file.read()) self.assert_valid_agent_metadata(resulting_json["agentMetadata"]) assert test_start_time <= resulting_json[ "start"] <= resulting_json["end"] <= test_end_time assert frames_in_callgraph_are_in_expected_order( resulting_json["callgraph"], "test.help_utils:HelperThreadRunner:dummy_parent_method", "test.help_utils:HelperThreadRunner:dummy_method")
class TestPause: @pytest.fixture(autouse=True) def around(self): self.profiler_runner = Mock(spec_set=ProfilerRunner) self.profiler_runner.pause = Mock() self.profiler = Profiler(profiling_group_name="test-application", environment_override={ "profiler_runner_factory": mock_profiler_runner_factory( self.profiler_runner) }) yield self.profiler.stop() def test_it_returns_true(self): assert self.profiler.pause() def test_it_calls_pause_on_runner(self): self.profiler.pause() assert self.profiler_runner.pause.called def test_exceptions_are_caught_and_do_not_propagate(self): self.profiler_runner.pause = throw_exception assert (not self.profiler.pause())
def test_live_profiling(self): with \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration.is_under_min_reporting_time", return_value=False), \ patch( "codeguru_profiler_agent.sdk_reporter.sdk_reporter.SdkReporter.check_create_pg_called_during_submit_profile", return_value=False), \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration._is_reporting_interval_smaller_than_minimum_allowed", return_value=False): profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, region_name='eu-west-2', environment_override={ "initial_sampling_interval": timedelta(), "sampling_interval": timedelta(seconds=1), "reporting_interval": timedelta(seconds=2), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }) client = profiler._profiler_runner.collector.reporter.codeguru_client_builder.codeguru_client aggregator = profiler._profiler_runner.collector assert AgentConfiguration.get().sampling_interval == timedelta( seconds=1) assert AgentConfiguration.get().reporting_interval == timedelta( seconds=2) with \ patch.object(client, "post_agent_profile", wraps=client.post_agent_profile) as wrapped_post_agent_profile, \ patch.object(client, "configure_agent", wraps=client.configure_agent) as wrapped_configure_agent, \ patch.object(aggregator, "add", wraps=aggregator.add) as wrapped_add, \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration.is_under_min_reporting_time", return_value=False), \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration._is_reporting_interval_smaller_than_minimum_allowed", return_value=False): wrapped_configure_agent.return_value = { "configuration": { "agentParameters": { "SamplingIntervalInMilliseconds": "100", "MinimumTimeForReportingInMilliseconds": "1000", "MaxStackDepth": "1000", "MemoryUsageLimitPercent": "29" }, "periodInSeconds": 2, "shouldProfile": True } } try: start_status = profiler.start() assert start_status assert profiler.is_running() time.sleep(4) finally: profiler.stop() # We should see at least 2 samples in 4 seconds as the sequence should happen in the order of # initial delay (1 second) # After 1 second, no flush -> sample # After 2 seconds, it attempt to flush (possibly succeed) -> sample/ no sample # After 3 seconds, it attempt to flush (must succeed if it did not flush before) -> no sample/ sample # After 4 seconds, no flush -> sample (if profiler has not stopped yet) assert wrapped_add.call_count >= 2 assert wrapped_post_agent_profile.call_count >= 1 assert wrapped_configure_agent.call_count >= 1 assert AgentConfiguration.get().sampling_interval == timedelta( seconds=1) assert AgentConfiguration.get( ).reporting_interval == timedelta(seconds=2)
def test_live_profiling(self): with \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration.is_under_min_reporting_time", return_value=False), \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration._is_reporting_interval_smaller_than_minimum_allowed", return_value=False): profiler = Profiler( profiling_group_name=DUMMY_TEST_PROFILING_GROUP_NAME, region_name='eu-west-2', environment_override={ "initial_sampling_interval": timedelta(), "sampling_interval": timedelta(seconds=1), "reporting_interval": timedelta(seconds=2), 'agent_metadata': AgentMetadata(fleet_info=DefaultFleetInfo()) }) client = profiler._profiler_runner.collector.reporter.codeguru_client_builder.codeguru_client aggregator = profiler._profiler_runner.collector assert AgentConfiguration.get().sampling_interval == timedelta( seconds=1) assert AgentConfiguration.get().reporting_interval == timedelta( seconds=2) with \ patch.object(client, "post_agent_profile", wraps=client.post_agent_profile) as wrapped_post_agent_profile, \ patch.object(client, "configure_agent", wraps=client.configure_agent) as wrapped_configure_agent, \ patch.object(aggregator, "add", wraps=aggregator.add) as wrapped_add, \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration.is_under_min_reporting_time", return_value=False), \ patch( "codeguru_profiler_agent.reporter.agent_configuration.AgentConfiguration._is_reporting_interval_smaller_than_minimum_allowed", return_value=False): wrapped_configure_agent.return_value = { "configuration": { "agentParameters": { "SamplingIntervalInMilliseconds": "100", "MinimumTimeForReportingInMilliseconds": "1000", "MaxStackDepth": "1000", "MemoryUsageLimitPercent": "29" }, "periodInSeconds": 2, "shouldProfile": True } } try: start_status = profiler.start() assert start_status assert profiler.is_running() time.sleep(3) finally: profiler.stop() assert wrapped_add.call_count >= 3 assert wrapped_post_agent_profile.call_count >= 1 assert wrapped_configure_agent.call_count >= 1 assert AgentConfiguration.get().sampling_interval == timedelta( seconds=1) assert AgentConfiguration.get( ).reporting_interval == timedelta(seconds=2)
def around(self): self.profiler = Profiler( profiling_group_name="test-application") yield
def test_it_does_propagate_a_value_error(self): environment = {"reporting_interval": timedelta(seconds=29)} Profiler(profiling_group_name="test-application", environment_override=environment)