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
17from simpleperf_report_lib import ReportLib
18from . test_utils import TestBase, TestHelper
19
20
21class TestReportLib(TestBase):
22    def setUp(self):
23        super(TestReportLib, self).setUp()
24        self.report_lib = ReportLib()
25        self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_symbols.data'))
26
27    def tearDown(self):
28        self.report_lib.Close()
29        super(TestReportLib, self).tearDown()
30
31    def test_build_id(self):
32        build_id = self.report_lib.GetBuildIdForPath('/data/t2')
33        self.assertEqual(build_id, '0x70f1fe24500fc8b0d9eb477199ca1ca21acca4de')
34
35    def test_symbol(self):
36        found_func2 = False
37        while self.report_lib.GetNextSample():
38            symbol = self.report_lib.GetSymbolOfCurrentSample()
39            if symbol.symbol_name == 'func2(int, int)':
40                found_func2 = True
41                self.assertEqual(symbol.symbol_addr, 0x4004ed)
42                self.assertEqual(symbol.symbol_len, 0x14)
43        self.assertTrue(found_func2)
44
45    def test_sample(self):
46        found_sample = False
47        while self.report_lib.GetNextSample():
48            sample = self.report_lib.GetCurrentSample()
49            if sample.ip == 0x4004ff and sample.time == 7637889424953:
50                found_sample = True
51                self.assertEqual(sample.pid, 15926)
52                self.assertEqual(sample.tid, 15926)
53                self.assertEqual(sample.thread_comm, 't2')
54                self.assertEqual(sample.cpu, 5)
55                self.assertEqual(sample.period, 694614)
56                event = self.report_lib.GetEventOfCurrentSample()
57                self.assertEqual(event.name, 'cpu-cycles')
58                callchain = self.report_lib.GetCallChainOfCurrentSample()
59                self.assertEqual(callchain.nr, 0)
60        self.assertTrue(found_sample)
61
62    def test_meta_info(self):
63        self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_trace_offcpu.data'))
64        meta_info = self.report_lib.MetaInfo()
65        self.assertTrue("simpleperf_version" in meta_info)
66        self.assertEqual(meta_info["system_wide_collection"], "false")
67        self.assertEqual(meta_info["trace_offcpu"], "true")
68        self.assertEqual(meta_info["event_type_info"], "cpu-cycles,0,0\nsched:sched_switch,2,47")
69        self.assertTrue("product_props" in meta_info)
70
71    def test_event_name_from_meta_info(self):
72        self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_tracepoint_event.data'))
73        event_names = set()
74        while self.report_lib.GetNextSample():
75            event_names.add(self.report_lib.GetEventOfCurrentSample().name)
76        self.assertTrue('sched:sched_switch' in event_names)
77        self.assertTrue('cpu-cycles' in event_names)
78
79    def test_record_cmd(self):
80        self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_trace_offcpu.data'))
81        self.assertEqual(self.report_lib.GetRecordCmd(),
82                         "/data/local/tmp/simpleperf record --trace-offcpu --duration 2 -g " +
83                         "./simpleperf_runtest_run_and_sleep64")
84
85    def test_offcpu(self):
86        self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_trace_offcpu.data'))
87        total_period = 0
88        sleep_function_period = 0
89        sleep_function_name = "SleepFunction(unsigned long long)"
90        while self.report_lib.GetNextSample():
91            sample = self.report_lib.GetCurrentSample()
92            total_period += sample.period
93            if self.report_lib.GetSymbolOfCurrentSample().symbol_name == sleep_function_name:
94                sleep_function_period += sample.period
95                continue
96            callchain = self.report_lib.GetCallChainOfCurrentSample()
97            for i in range(callchain.nr):
98                if callchain.entries[i].symbol.symbol_name == sleep_function_name:
99                    sleep_function_period += sample.period
100                    break
101            self.assertEqual(self.report_lib.GetEventOfCurrentSample().name, 'cpu-cycles')
102        sleep_percentage = float(sleep_function_period) / total_period
103        self.assertGreater(sleep_percentage, 0.30)
104
105    def test_show_art_frames(self):
106        def has_art_frame(report_lib):
107            report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_interpreter_frames.data'))
108            result = False
109            while report_lib.GetNextSample():
110                callchain = report_lib.GetCallChainOfCurrentSample()
111                for i in range(callchain.nr):
112                    if callchain.entries[i].symbol.symbol_name == 'artMterpAsmInstructionStart':
113                        result = True
114                        break
115            report_lib.Close()
116            return result
117
118        report_lib = ReportLib()
119        self.assertFalse(has_art_frame(report_lib))
120        report_lib = ReportLib()
121        report_lib.ShowArtFrames(False)
122        self.assertFalse(has_art_frame(report_lib))
123        report_lib = ReportLib()
124        report_lib.ShowArtFrames(True)
125        self.assertTrue(has_art_frame(report_lib))
126
127    def test_merge_java_methods(self):
128        def parse_dso_names(report_lib):
129            dso_names = set()
130            report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_interpreter_frames.data'))
131            while report_lib.GetNextSample():
132                dso_names.add(report_lib.GetSymbolOfCurrentSample().dso_name)
133                callchain = report_lib.GetCallChainOfCurrentSample()
134                for i in range(callchain.nr):
135                    dso_names.add(callchain.entries[i].symbol.dso_name)
136            report_lib.Close()
137            has_jit_symfiles = any('TemporaryFile-' in name for name in dso_names)
138            has_jit_cache = '[JIT cache]' in dso_names
139            return has_jit_symfiles, has_jit_cache
140
141        report_lib = ReportLib()
142        self.assertEqual(parse_dso_names(report_lib), (False, True))
143
144        report_lib = ReportLib()
145        report_lib.MergeJavaMethods(True)
146        self.assertEqual(parse_dso_names(report_lib), (False, True))
147
148        report_lib = ReportLib()
149        report_lib.MergeJavaMethods(False)
150        self.assertEqual(parse_dso_names(report_lib), (True, False))
151
152    def test_jited_java_methods(self):
153        report_lib = ReportLib()
154        report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_jit_symbol.data'))
155        has_jit_cache = False
156        while report_lib.GetNextSample():
157            if report_lib.GetSymbolOfCurrentSample().dso_name == '[JIT app cache]':
158                has_jit_cache = True
159            callchain = report_lib.GetCallChainOfCurrentSample()
160            for i in range(callchain.nr):
161                if callchain.entries[i].symbol.dso_name == '[JIT app cache]':
162                    has_jit_cache = True
163        report_lib.Close()
164        self.assertTrue(has_jit_cache)
165
166    def test_tracing_data(self):
167        self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_with_tracepoint_event.data'))
168        has_tracing_data = False
169        while self.report_lib.GetNextSample():
170            event = self.report_lib.GetEventOfCurrentSample()
171            tracing_data = self.report_lib.GetTracingDataOfCurrentSample()
172            if event.name == 'sched:sched_switch':
173                self.assertIsNotNone(tracing_data)
174                self.assertIn('prev_pid', tracing_data)
175                self.assertIn('next_comm', tracing_data)
176                if tracing_data['prev_pid'] == 9896 and tracing_data['next_comm'] == 'swapper/4':
177                    has_tracing_data = True
178            else:
179                self.assertIsNone(tracing_data)
180        self.assertTrue(has_tracing_data)
181
182    def test_dynamic_field_in_tracing_data(self):
183        self.report_lib.SetRecordFile(TestHelper.testdata_path(
184            'perf_with_tracepoint_event_dynamic_field.data'))
185        has_dynamic_field = False
186        while self.report_lib.GetNextSample():
187            event = self.report_lib.GetEventOfCurrentSample()
188            tracing_data = self.report_lib.GetTracingDataOfCurrentSample()
189            if event.name == 'kprobes:myopen':
190                self.assertIsNotNone(tracing_data)
191                self.assertIn('name', tracing_data)
192                if tracing_data['name'] == '/sys/kernel/debug/tracing/events/kprobes/myopen/format':
193                    has_dynamic_field = True
194            else:
195                self.assertIsNone(tracing_data)
196        self.assertTrue(has_dynamic_field)
197
198    def test_add_proguard_mapping_file(self):
199        with self.assertRaises(ValueError):
200            self.report_lib.AddProguardMappingFile('non_exist_file')
201        proguard_mapping_file = TestHelper.testdata_path('proguard_mapping.txt')
202        self.report_lib.AddProguardMappingFile(proguard_mapping_file)
203