예제 #1
0
    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 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_it_fails_to_start_a_second_profiler(self):
                assert (not self.second_profiler.start())
    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 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()
예제 #5
0
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_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 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 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.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 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)