1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <gtest/gtest.h>
18 
19 #include <android-base/file.h>
20 #include <android-base/strings.h>
21 
22 #include "RegEx.h"
23 #include "command.h"
24 #include "get_test_data.h"
25 
26 using namespace simpleperf;
27 
ReportSampleCmd()28 static std::unique_ptr<Command> ReportSampleCmd() {
29   return CreateCommandInstance("report-sample");
30 }
31 
32 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,text)33 TEST(cmd_report_sample, text) {
34   ASSERT_TRUE(ReportSampleCmd()->Run({"-i", GetTestData(PERF_DATA_WITH_SYMBOLS)}));
35 }
36 
37 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,output_option)38 TEST(cmd_report_sample, output_option) {
39   TemporaryFile tmpfile;
40   ASSERT_TRUE(
41       ReportSampleCmd()->Run({"-i", GetTestData(PERF_DATA_WITH_SYMBOLS), "-o", tmpfile.path}));
42 }
43 
44 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,show_callchain_option)45 TEST(cmd_report_sample, show_callchain_option) {
46   TemporaryFile tmpfile;
47   ASSERT_TRUE(ReportSampleCmd()->Run(
48       {"-i", GetTestData(CALLGRAPH_FP_PERF_DATA), "-o", tmpfile.path, "--show-callchain"}));
49 }
50 
GetProtobufReport(const std::string & test_data_file,std::string * protobuf_report,const std::vector<std::string> & extra_args={})51 static void GetProtobufReport(const std::string& test_data_file, std::string* protobuf_report,
52                               const std::vector<std::string>& extra_args = {}) {
53   TemporaryFile tmpfile;
54   TemporaryFile tmpfile2;
55   std::vector<std::string> args = {"-i", GetTestData(test_data_file), "-o", tmpfile.path,
56                                    "--protobuf"};
57   args.insert(args.end(), extra_args.begin(), extra_args.end());
58   ASSERT_TRUE(ReportSampleCmd()->Run(args));
59   ASSERT_TRUE(
60       ReportSampleCmd()->Run({"--dump-protobuf-report", tmpfile.path, "-o", tmpfile2.path}));
61   ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, protobuf_report));
62 }
63 
64 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,protobuf_option)65 TEST(cmd_report_sample, protobuf_option) {
66   std::string data;
67   GetProtobufReport(PERF_DATA_WITH_SYMBOLS, &data);
68   ASSERT_NE(data.find("magic: SIMPLEPERF"), std::string::npos);
69   ASSERT_NE(data.find("version: 1"), std::string::npos);
70   ASSERT_NE(data.find("file:"), std::string::npos);
71 }
72 
73 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,no_skipped_file_id)74 TEST(cmd_report_sample, no_skipped_file_id) {
75   std::string data;
76   GetProtobufReport(PERF_DATA_WITH_WRONG_IP_IN_CALLCHAIN, &data);
77   // If wrong ips in callchain are omitted, "unknown" file path will not be generated.
78   ASSERT_EQ(data.find("unknown"), std::string::npos);
79 }
80 
81 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,sample_has_event_count)82 TEST(cmd_report_sample, sample_has_event_count) {
83   std::string data;
84   GetProtobufReport(PERF_DATA_WITH_SYMBOLS, &data);
85   ASSERT_NE(data.find("event_count:"), std::string::npos);
86 }
87 
88 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,has_thread_record)89 TEST(cmd_report_sample, has_thread_record) {
90   std::string data;
91   GetProtobufReport(PERF_DATA_WITH_SYMBOLS, &data);
92   ASSERT_NE(data.find("thread:"), std::string::npos);
93   ASSERT_NE(data.find("thread_name: t2"), std::string::npos);
94 }
95 
96 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,trace_offcpu)97 TEST(cmd_report_sample, trace_offcpu) {
98   std::string data;
99   GetProtobufReport("perf_with_trace_offcpu_v2.data", &data);
100   ASSERT_NE(data.find("event_type: sched:sched_switch"), std::string::npos);
101   ASSERT_NE(data.find("trace_offcpu: true"), std::string::npos);
102   std::vector<std::vector<std::string>> cases = {
103       {"context_switch:", "switch_on: true", "time: 676374949239318", "thread_id: 6525"},
104       {"context_switch:", "switch_on: false", "time: 676374953363850", "thread_id: 6525"},
105   };
106   for (auto& test_case : cases) {
107     auto regex = RegEx::Create(android::base::Join(test_case, R"((\s|\n|\r)+)"));
108     ASSERT_TRUE(regex->Search(data));
109   }
110 }
111 
112 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,have_clear_callchain_end_in_protobuf_output)113 TEST(cmd_report_sample, have_clear_callchain_end_in_protobuf_output) {
114   std::string data;
115   GetProtobufReport("perf_with_trace_offcpu_v2.data", &data, {"--show-callchain"});
116   ASSERT_NE(data.find("__libc_init"), std::string::npos);
117   ASSERT_EQ(data.find("_start_main"), std::string::npos);
118 }
119 
120 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,app_device_info_in_meta_info)121 TEST(cmd_report_sample, app_device_info_in_meta_info) {
122   std::string data;
123   GetProtobufReport("perf_with_meta_info.data", &data);
124   ASSERT_NE(data.find("app_package_name: com.google.sample.tunnel"), std::string::npos);
125   ASSERT_NE(data.find("app_type: debuggable"), std::string::npos);
126   ASSERT_NE(data.find("android_sdk_version: 30"), std::string::npos);
127   ASSERT_NE(data.find("android_build_type: userdebug"), std::string::npos);
128 }
129 
130 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,remove_unknown_kernel_symbols)131 TEST(cmd_report_sample, remove_unknown_kernel_symbols) {
132   std::string data;
133   // Test --remove-unknown-kernel-symbols on perf.data with kernel_symbols_available=false.
134   GetProtobufReport(PERF_DATA_WITH_KERNEL_SYMBOLS_AVAILABLE_FALSE, &data, {"--show-callchain"});
135   ASSERT_NE(data.find("time: 1368182962424044"), std::string::npos);
136   ASSERT_NE(data.find("path: [kernel.kallsyms]"), std::string::npos);
137   ASSERT_NE(data.find("path: /system/lib64/libc.so"), std::string::npos);
138   GetProtobufReport(PERF_DATA_WITH_KERNEL_SYMBOLS_AVAILABLE_FALSE, &data,
139                     {"--show-callchain", "--remove-unknown-kernel-symbols"});
140   // The sample dumped at time 1368182962424044 shouldn't be removed. Because it has user space
141   // callchains.
142   ASSERT_NE(data.find("time: 1368182962424044"), std::string::npos);
143   // Kernel callchains shouldn't be removed.
144   ASSERT_EQ(data.find("path: [kernel.kallsyms]"), std::string::npos);
145   // User space callchains still exist.
146   ASSERT_NE(data.find("path: /system/lib64/libc.so"), std::string::npos);
147 
148   // Test --remove-unknown-kernel-symbols on perf.data with kernel_symbols_available=true.
149   GetProtobufReport(PERF_DATA_WITH_KERNEL_SYMBOLS_AVAILABLE_TRUE, &data, {"--show-callchain"});
150   ASSERT_NE(data.find("time: 1368297633794862"), std::string::npos);
151   ASSERT_NE(data.find("path: [kernel.kallsyms]"), std::string::npos);
152   ASSERT_NE(data.find("symbol: binder_ioctl_write_read"), std::string::npos);
153   ASSERT_NE(data.find("path: /system/lib64/libc.so"), std::string::npos);
154   GetProtobufReport(PERF_DATA_WITH_KERNEL_SYMBOLS_AVAILABLE_TRUE, &data,
155                     {"--show-callchain", "--remove-unknown-kernel-symbols"});
156   ASSERT_NE(data.find("time: 1368297633794862"), std::string::npos);
157   ASSERT_NE(data.find("path: [kernel.kallsyms]"), std::string::npos);
158   ASSERT_NE(data.find("symbol: binder_ioctl_write_read"), std::string::npos);
159   ASSERT_NE(data.find("path: /system/lib64/libc.so"), std::string::npos);
160 }
161 
162 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,show_art_frames_option)163 TEST(cmd_report_sample, show_art_frames_option) {
164   std::string data;
165   GetProtobufReport(PERF_DATA_WITH_INTERPRETER_FRAMES, &data, {"--show-callchain"});
166   ASSERT_EQ(data.find("artMterpAsmInstructionStart"), std::string::npos);
167   GetProtobufReport(PERF_DATA_WITH_INTERPRETER_FRAMES, &data,
168                     {"--show-callchain", "--show-art-frames"});
169   ASSERT_NE(data.find("artMterpAsmInstructionStart"), std::string::npos);
170 }
171 
172 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,show_execution_type_option)173 TEST(cmd_report_sample, show_execution_type_option) {
174   std::string data;
175   GetProtobufReport("perf_display_bitmaps.data", &data,
176                     {"--show-callchain", "--show-execution-type"});
177   ASSERT_NE(data.find("execution_type: interpreted_jvm_method"), std::string::npos);
178   // We convert JIT frames to map to dex files. So there is no file named jit_app_cache in the
179   // report. But the execution type of a JIT frame isn't changed.
180   ASSERT_EQ(data.find("jit_app_cache"), std::string::npos);
181   ASSERT_NE(data.find("execution_type: jit_jvm_method"), std::string::npos);
182   // art_method is shown only when --show-art-frames is used.
183   ASSERT_EQ(data.find("execution_type: art_method"), std::string::npos);
184 
185   GetProtobufReport("perf_display_bitmaps.data", &data,
186                     {"--show-callchain", "--show-execution-type", "--show-art-frames"});
187   ASSERT_NE(data.find("execution_type: art_method"), std::string::npos);
188 }
189 
190 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,show_symbols_before_and_after_demangle)191 TEST(cmd_report_sample, show_symbols_before_and_after_demangle) {
192   std::string data;
193   GetProtobufReport(PERF_DATA_WITH_INTERPRETER_FRAMES, &data, {"--show-callchain"});
194   ASSERT_NE(data.find("symbol: android::hardware::IPCThreadState::talkWithDriver(bool)"),
195             std::string::npos);
196   ASSERT_NE(data.find("mangled_symbol: _ZN7android8hardware14IPCThreadState14talkWithDriverEb"),
197             std::string::npos);
198 }
199 
200 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,symdir_option)201 TEST(cmd_report_sample, symdir_option) {
202   std::string data;
203   GetProtobufReport(PERF_DATA_FOR_BUILD_ID_CHECK, &data);
204   ASSERT_EQ(data.find("symbol: main"), std::string::npos);
205   GetProtobufReport(PERF_DATA_FOR_BUILD_ID_CHECK, &data,
206                     {"--symdir", GetTestDataDir() + CORRECT_SYMFS_FOR_BUILD_ID_CHECK});
207   ASSERT_NE(data.find("symbol: main"), std::string::npos);
208 }
209 
210 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,show_art_jni_methods)211 TEST(cmd_report_sample, show_art_jni_methods) {
212   std::string data;
213   GetProtobufReport("perf_display_bitmaps.data", &data, {"--show-callchain"});
214   ASSERT_NE(data.find("art::Method_invoke"), std::string::npos);
215   // Don't show art_jni_trampoline.
216   ASSERT_EQ(data.find("art_jni_trampoline"), std::string::npos);
217 }
218 
219 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,show_unwinding_result)220 TEST(cmd_report_sample, show_unwinding_result) {
221   std::string data;
222   GetProtobufReport("perf_with_failed_unwinding_debug_info.data", &data,
223                     {"--show-callchain", "--remove-gaps", "0"});
224   ASSERT_NE(data.find("error_code: ERROR_INVALID_MAP"), std::string::npos);
225 }
226 
227 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,proguard_mapping_file_option)228 TEST(cmd_report_sample, proguard_mapping_file_option) {
229   std::string data;
230   // Symbols aren't de-obfuscated without proguard mapping file.
231   GetProtobufReport("perf_need_proguard_mapping.data", &data,
232                     {"--show-callchain", "--remove-gaps", "0"});
233   ASSERT_EQ(data.find("androidx.fragment.app.FragmentActivity.startActivityForResult"),
234             std::string::npos);
235   ASSERT_EQ(data.find("com.example.android.displayingbitmaps.ui.ImageGridFragment.onItemClick"),
236             std::string::npos);
237   // Symbols are de-obfuscated with proguard mapping file.
238   GetProtobufReport("perf_need_proguard_mapping.data", &data,
239                     {"--show-callchain", "--proguard-mapping-file",
240                      GetTestData("proguard_mapping.txt"), "--remove-gaps", "0"});
241   ASSERT_NE(data.find("androidx.fragment.app.FragmentActivity.startActivityForResult"),
242             std::string::npos);
243   ASSERT_NE(data.find("com.example.android.displayingbitmaps.ui.ImageGridFragment.onItemClick"),
244             std::string::npos);
245 }
246 
247 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,exclude_include_pid_options)248 TEST(cmd_report_sample, exclude_include_pid_options) {
249   std::string data;
250   GetProtobufReport("perf_display_bitmaps.data", &data, {"--exclude-pid", "31850"});
251   ASSERT_EQ(data.find("thread_id: 31850"), std::string::npos);
252 
253   GetProtobufReport("perf_display_bitmaps.data", &data, {"--include-pid", "31850"});
254   ASSERT_NE(data.find("thread_id: 31850"), std::string::npos);
255 }
256 
257 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,exclude_include_tid_options)258 TEST(cmd_report_sample, exclude_include_tid_options) {
259   std::string data;
260   GetProtobufReport("perf_display_bitmaps.data", &data, {"--exclude-tid", "31881"});
261   ASSERT_EQ(data.find("thread_id: 31881"), std::string::npos);
262 
263   GetProtobufReport("perf_display_bitmaps.data", &data, {"--include-tid", "31881"});
264   ASSERT_NE(data.find("thread_id: 31881"), std::string::npos);
265 }
266 
267 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,exclude_include_process_name_options)268 TEST(cmd_report_sample, exclude_include_process_name_options) {
269   std::string data;
270   GetProtobufReport("perf_display_bitmaps.data", &data,
271                     {"--exclude-process-name", "com.example.android.displayingbitmaps"});
272   ASSERT_EQ(data.find("thread_id: 31881"), std::string::npos);
273 
274   GetProtobufReport("perf_display_bitmaps.data", &data,
275                     {"--include-process-name", "com.example.android.displayingbitmaps"});
276   ASSERT_NE(data.find("thread_id: 31881"), std::string::npos);
277 }
278 
279 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,exclude_include_thread_name_options)280 TEST(cmd_report_sample, exclude_include_thread_name_options) {
281   std::string data;
282   GetProtobufReport("perf_display_bitmaps.data", &data,
283                     {"--exclude-thread-name", "com.example.android.displayingbitmaps"});
284   ASSERT_EQ(data.find("thread_id: 31850"), std::string::npos);
285 
286   GetProtobufReport("perf_display_bitmaps.data", &data,
287                     {"--include-thread-name", "com.example.android.displayingbitmaps"});
288   ASSERT_NE(data.find("thread_id: 31850"), std::string::npos);
289 }
290 
291 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,filter_file_option)292 TEST(cmd_report_sample, filter_file_option) {
293   std::string filter_data =
294       "GLOBAL_BEGIN 684943449406175\n"
295       "GLOBAL_END 684943449406176";
296   TemporaryFile tmpfile;
297   ASSERT_TRUE(android::base::WriteStringToFd(filter_data, tmpfile.fd));
298   std::string data;
299   GetProtobufReport("perf_display_bitmaps.data", &data, {"--filter-file", tmpfile.path});
300   ASSERT_NE(data.find("thread_id: 31881"), std::string::npos);
301   ASSERT_EQ(data.find("thread_id: 31850"), std::string::npos);
302 }
303 
304 // @CddTest = 6.1/C-0-2
TEST(cmd_report_sample,remove_gaps_option)305 TEST(cmd_report_sample, remove_gaps_option) {
306   auto get_sample_count = [](const std::string& s) {
307     size_t count = 0;
308     ssize_t pos = 0;
309     while ((pos = s.find("\nsample ", pos)) != s.npos) {
310       ++count;
311       pos += strlen("\nsample ");
312     }
313     return count;
314   };
315 
316   std::string data;
317   // By default, `--remove-gaps 3` is used.
318   GetProtobufReport("perf_display_bitmaps.data", &data, {"--show-callchain"});
319   ASSERT_EQ(get_sample_count(data), 517);
320   ASSERT_NE(data.find("sample_count: 517"), std::string::npos) << data;
321   // We can disable removing gaps by using `--remove-gaps 0`.
322   GetProtobufReport("perf_display_bitmaps.data", &data, {"--show-callchain", "--remove-gaps", "0"});
323   ASSERT_EQ(get_sample_count(data), 525);
324   ASSERT_NE(data.find("sample_count: 525"), std::string::npos);
325 }
326