1#!/usr/bin/env python3
2#
3# Copyright (C) 2021 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import google.protobuf
18from typing import List, Optional
19
20from binary_cache_builder import BinaryCacheBuilder
21from pprof_proto_generator import load_pprof_profile
22from . test_utils import TestBase, TestHelper
23
24
25class TestPprofProtoGenerator(TestBase):
26    def run_generator(self, options=None, testdata_file='perf_with_interpreter_frames.data'):
27        testdata_path = TestHelper.testdata_path(testdata_file)
28        options = options or []
29        self.run_cmd(['pprof_proto_generator.py', '-i', testdata_path] + options)
30        return self.run_cmd(['pprof_proto_generator.py', '--show'], return_output=True)
31
32    def generate_profile(self, options: Optional[List[str]], testdata_files: List[str]):
33        testdata_paths = [TestHelper.testdata_path(f) for f in testdata_files]
34        options = options or []
35        self.run_cmd(['pprof_proto_generator.py', '-i'] + testdata_paths + options)
36        return load_pprof_profile('pprof.profile')
37
38    def test_show_art_frames(self):
39        art_frame_str = 'art::interpreter::DoCall'
40        # By default, don't show art frames.
41        self.assertNotIn(art_frame_str, self.run_generator())
42        # Use --show_art_frames to show art frames.
43        self.assertIn(art_frame_str, self.run_generator(['--show_art_frames']))
44
45    def test_pid_filter(self):
46        key = 'PlayScene::DoFrame()'  # function in process 10419
47        self.assertIn(key, self.run_generator())
48        self.assertIn(key, self.run_generator(['--pid', '10419']))
49        self.assertIn(key, self.run_generator(['--pid', '10419', '10416']))
50        self.assertNotIn(key, self.run_generator(['--pid', '10416']))
51
52    def test_tid_filter(self):
53        key1 = 'art::ProfileSaver::Run()'  # function in thread 10459
54        key2 = 'PlayScene::DoFrame()'  # function in thread 10463
55        for options in ([], ['--tid', '10459', '10463']):
56            output = self.run_generator(options)
57            self.assertIn(key1, output)
58            self.assertIn(key2, output)
59        output = self.run_generator(['--tid', '10459'])
60        self.assertIn(key1, output)
61        self.assertNotIn(key2, output)
62        output = self.run_generator(['--tid', '10463'])
63        self.assertNotIn(key1, output)
64        self.assertIn(key2, output)
65
66    def test_comm_filter(self):
67        key1 = 'art::ProfileSaver::Run()'  # function in thread 'Profile Saver'
68        key2 = 'PlayScene::DoFrame()'  # function in thread 'e.sample.tunnel'
69        for options in ([], ['--comm', 'Profile Saver', 'e.sample.tunnel']):
70            output = self.run_generator(options)
71            self.assertIn(key1, output)
72            self.assertIn(key2, output)
73        output = self.run_generator(['--comm', 'Profile Saver'])
74        self.assertIn(key1, output)
75        self.assertNotIn(key2, output)
76        output = self.run_generator(['--comm', 'e.sample.tunnel'])
77        self.assertNotIn(key1, output)
78        self.assertIn(key2, output)
79
80    def test_build_id(self):
81        """ Test the build ids generated are not padded with zeros. """
82        self.assertIn('build_id: e3e938cc9e40de2cfe1a5ac7595897de(', self.run_generator())
83
84    def test_location_address(self):
85        """ Test if the address of a location is within the memory range of the corresponding
86            mapping.
87        """
88        profile = self.generate_profile(None, ['perf_with_interpreter_frames.data'])
89        # pylint: disable=no-member
90        for location in profile.location:
91            mapping = profile.mapping[location.mapping_id - 1]
92            self.assertLessEqual(mapping.memory_start, location.address)
93            self.assertGreaterEqual(mapping.memory_limit, location.address)
94
95    def test_multiple_perf_data(self):
96        """ Test reporting multiple recording file. """
97        profile1 = self.generate_profile(None, ['aggregatable_perf1.data'])
98        profile2 = self.generate_profile(None, ['aggregatable_perf2.data'])
99        profile_both = self.generate_profile(
100            None, ['aggregatable_perf1.data', 'aggregatable_perf2.data'])
101        # pylint: disable=no-member
102        self.assertGreater(len(profile_both.sample), len(profile1.sample))
103        self.assertGreater(len(profile_both.sample), len(profile2.sample))
104
105    def test_proguard_mapping_file(self):
106        """ Test --proguard-mapping-file option. """
107        testdata_file = 'perf_need_proguard_mapping.data'
108        proguard_mapping_file = TestHelper.testdata_path('proguard_mapping.txt')
109        original_methodname = 'androidx.fragment.app.FragmentActivity.startActivityForResult'
110        # Can't show original method name without proguard mapping file.
111        self.assertNotIn(original_methodname, self.run_generator(testdata_file=testdata_file))
112        # Show original method name with proguard mapping file.
113        self.assertIn(original_methodname, self.run_generator(
114            ['--proguard-mapping-file', proguard_mapping_file], testdata_file))
115
116    def test_use_binary_cache(self):
117        testdata_file = TestHelper.testdata_path('runtest_two_functions_arm64_perf.data')
118
119        # Build binary_cache.
120        binary_cache_builder = BinaryCacheBuilder(TestHelper.ndk_path, False)
121        binary_cache_builder.build_binary_cache(testdata_file, [TestHelper.testdata_dir])
122
123        # Generate profile.
124        output = self.run_generator(testdata_file=testdata_file)
125        self.assertIn('simpleperf_runtest_two_functions_arm64', output)
126        self.assertIn('two_functions.cpp', output)
127