OSDN Git Service

simpleperf: Omit dwarf-callgraph tests running on arm translation tools.
[android-x86/system-extras.git] / simpleperf / cmd_report_test.cpp
1 /*
2  * Copyright (C) 2015 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 <set>
20 #include <unordered_map>
21
22 #include <android-base/file.h>
23 #include <android-base/strings.h>
24 #include <android-base/test_utils.h>
25
26 #include "command.h"
27 #include "get_test_data.h"
28 #include "perf_regs.h"
29 #include "read_apk.h"
30 #include "test_util.h"
31
32 static std::unique_ptr<Command> ReportCmd() {
33   return CreateCommandInstance("report");
34 }
35
36 class ReportCommandTest : public ::testing::Test {
37  protected:
38   void Report(
39       const std::string& perf_data,
40       const std::vector<std::string>& add_args = std::vector<std::string>()) {
41     ReportRaw(GetTestData(perf_data), add_args);
42   }
43
44   void ReportRaw(
45       const std::string& perf_data,
46       const std::vector<std::string>& add_args = std::vector<std::string>()) {
47     success = false;
48     TemporaryFile tmp_file;
49     std::vector<std::string> args = {
50         "-i", perf_data, "--symfs", GetTestDataDir(), "-o", tmp_file.path};
51     args.insert(args.end(), add_args.begin(), add_args.end());
52     ASSERT_TRUE(ReportCmd()->Run(args));
53     ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &content));
54     ASSERT_TRUE(!content.empty());
55     std::vector<std::string> raw_lines = android::base::Split(content, "\n");
56     lines.clear();
57     for (const auto& line : raw_lines) {
58       std::string s = android::base::Trim(line);
59       if (!s.empty()) {
60         lines.push_back(s);
61       }
62     }
63     ASSERT_GE(lines.size(), 2u);
64     success = true;
65   }
66
67   std::string content;
68   std::vector<std::string> lines;
69   bool success;
70 };
71
72 TEST_F(ReportCommandTest, no_option) {
73   Report(PERF_DATA);
74   ASSERT_TRUE(success);
75   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
76 }
77
78 TEST_F(ReportCommandTest, report_symbol_from_elf_file_with_mini_debug_info) {
79   Report(PERF_DATA_WITH_MINI_DEBUG_INFO);
80   ASSERT_TRUE(success);
81   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
82 }
83
84 TEST_F(ReportCommandTest, sort_option_pid) {
85   Report(PERF_DATA, {"--sort", "pid"});
86   ASSERT_TRUE(success);
87   size_t line_index = 0;
88   while (line_index < lines.size() &&
89          lines[line_index].find("Pid") == std::string::npos) {
90     line_index++;
91   }
92   ASSERT_LT(line_index + 2, lines.size());
93 }
94
95 TEST_F(ReportCommandTest, sort_option_more_than_one) {
96   Report(PERF_DATA, {"--sort", "comm,pid,dso,symbol"});
97   ASSERT_TRUE(success);
98   size_t line_index = 0;
99   while (line_index < lines.size() &&
100          lines[line_index].find("Overhead") == std::string::npos) {
101     line_index++;
102   }
103   ASSERT_LT(line_index + 1, lines.size());
104   ASSERT_NE(lines[line_index].find("Command"), std::string::npos);
105   ASSERT_NE(lines[line_index].find("Pid"), std::string::npos);
106   ASSERT_NE(lines[line_index].find("Shared Object"), std::string::npos);
107   ASSERT_NE(lines[line_index].find("Symbol"), std::string::npos);
108   ASSERT_EQ(lines[line_index].find("Tid"), std::string::npos);
109 }
110
111 TEST_F(ReportCommandTest, children_option) {
112   Report(CALLGRAPH_FP_PERF_DATA, {"--children", "--sort", "symbol"});
113   ASSERT_TRUE(success);
114   std::unordered_map<std::string, std::pair<double, double>> map;
115   for (size_t i = 0; i < lines.size(); ++i) {
116     char name[1024];
117     std::pair<double, double> pair;
118     if (sscanf(lines[i].c_str(), "%lf%%%lf%%%s", &pair.first, &pair.second,
119                name) == 3) {
120       map.insert(std::make_pair(name, pair));
121     }
122   }
123   ASSERT_NE(map.find("GlobalFunc"), map.end());
124   ASSERT_NE(map.find("main"), map.end());
125   auto func_pair = map["GlobalFunc"];
126   auto main_pair = map["main"];
127   ASSERT_GE(main_pair.first, func_pair.first);
128   ASSERT_GE(func_pair.first, func_pair.second);
129   ASSERT_GE(func_pair.second, main_pair.second);
130 }
131
132 static bool CheckCalleeMode(std::vector<std::string>& lines) {
133   bool found = false;
134   for (size_t i = 0; i + 1 < lines.size(); ++i) {
135     if (lines[i].find("GlobalFunc") != std::string::npos &&
136         lines[i + 1].find("main") != std::string::npos) {
137       found = true;
138       break;
139     }
140   }
141   return found;
142 }
143
144 static bool CheckCallerMode(std::vector<std::string>& lines) {
145   bool found = false;
146   for (size_t i = 0; i + 1 < lines.size(); ++i) {
147     if (lines[i].find("main") != std::string::npos &&
148         lines[i + 1].find("GlobalFunc") != std::string::npos) {
149       found = true;
150       break;
151     }
152   }
153   return found;
154 }
155
156 TEST_F(ReportCommandTest, callgraph_option) {
157   Report(CALLGRAPH_FP_PERF_DATA, {"-g"});
158   ASSERT_TRUE(success);
159   ASSERT_TRUE(CheckCallerMode(lines));
160   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "callee"});
161   ASSERT_TRUE(success);
162   ASSERT_TRUE(CheckCalleeMode(lines));
163   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "caller"});
164   ASSERT_TRUE(success);
165   ASSERT_TRUE(CheckCallerMode(lines));
166 }
167
168 static bool AllItemsWithString(std::vector<std::string>& lines,
169                                const std::vector<std::string>& strs) {
170   size_t line_index = 0;
171   while (line_index < lines.size() &&
172          lines[line_index].find("Overhead") == std::string::npos) {
173     line_index++;
174   }
175   if (line_index == lines.size() || line_index + 1 == lines.size()) {
176     return false;
177   }
178   line_index++;
179   for (; line_index < lines.size(); ++line_index) {
180     bool exist = false;
181     for (auto& s : strs) {
182       if (lines[line_index].find(s) != std::string::npos) {
183         exist = true;
184         break;
185       }
186     }
187     if (!exist) {
188       return false;
189     }
190   }
191   return true;
192 }
193
194 TEST_F(ReportCommandTest, pid_filter_option) {
195   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid"});
196   ASSERT_TRUE(success);
197   ASSERT_FALSE(AllItemsWithString(lines, {"17441"}));
198   ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17443"}));
199   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
200          {"--sort", "pid", "--pids", "17441"});
201   ASSERT_TRUE(success);
202   ASSERT_TRUE(AllItemsWithString(lines, {"17441"}));
203   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
204          {"--sort", "pid", "--pids", "17441,17443"});
205   ASSERT_TRUE(success);
206   ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17443"}));
207
208   // Test that --pids option is not the same as --tids option.
209   // Thread 17445 and 17441 are in process 17441.
210   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
211          {"--sort", "tid", "--pids", "17441"});
212   ASSERT_TRUE(success);
213   ASSERT_NE(content.find("17441"), std::string::npos);
214   ASSERT_NE(content.find("17445"), std::string::npos);
215 }
216
217 TEST_F(ReportCommandTest, wrong_pid_filter_option) {
218   ASSERT_EXIT(
219       {
220         Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--pids", "2,bogus"});
221         exit(success ? 0 : 1);
222       },
223       testing::ExitedWithCode(1), "invalid id in --pids option: bogus");
224 }
225
226 TEST_F(ReportCommandTest, tid_filter_option) {
227   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid"});
228   ASSERT_TRUE(success);
229   ASSERT_FALSE(AllItemsWithString(lines, {"17441"}));
230   ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17445"}));
231   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
232          {"--sort", "tid", "--tids", "17441"});
233   ASSERT_TRUE(success);
234   ASSERT_TRUE(AllItemsWithString(lines, {"17441"}));
235   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
236          {"--sort", "tid", "--tids", "17441,17445"});
237   ASSERT_TRUE(success);
238   ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17445"}));
239 }
240
241 TEST_F(ReportCommandTest, wrong_tid_filter_option) {
242   ASSERT_EXIT(
243       {
244         Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--tids", "2,bogus"});
245         exit(success ? 0 : 1);
246       },
247       testing::ExitedWithCode(1), "invalid id in --tids option: bogus");
248 }
249
250 TEST_F(ReportCommandTest, comm_filter_option) {
251   Report(PERF_DATA, {"--sort", "comm"});
252   ASSERT_TRUE(success);
253   ASSERT_FALSE(AllItemsWithString(lines, {"t1"}));
254   ASSERT_FALSE(AllItemsWithString(lines, {"t1", "t2"}));
255   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1"});
256   ASSERT_TRUE(success);
257   ASSERT_TRUE(AllItemsWithString(lines, {"t1"}));
258   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1,t2"});
259   ASSERT_TRUE(success);
260   ASSERT_TRUE(AllItemsWithString(lines, {"t1", "t2"}));
261 }
262
263 TEST_F(ReportCommandTest, dso_filter_option) {
264   Report(PERF_DATA, {"--sort", "dso"});
265   ASSERT_TRUE(success);
266   ASSERT_FALSE(AllItemsWithString(lines, {"/t1"}));
267   ASSERT_FALSE(AllItemsWithString(lines, {"/t1", "/t2"}));
268   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1"});
269   ASSERT_TRUE(success);
270   ASSERT_TRUE(AllItemsWithString(lines, {"/t1"}));
271   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1,/t2"});
272   ASSERT_TRUE(success);
273   ASSERT_TRUE(AllItemsWithString(lines, {"/t1", "/t2"}));
274 }
275
276 TEST_F(ReportCommandTest, symbol_filter_option) {
277   Report(PERF_DATA_WITH_SYMBOLS, {"--sort", "symbol"});
278   ASSERT_TRUE(success);
279   ASSERT_FALSE(AllItemsWithString(lines, {"func2(int, int)"}));
280   ASSERT_FALSE(AllItemsWithString(lines, {"main", "func2(int, int)"}));
281   Report(PERF_DATA_WITH_SYMBOLS,
282          {"--sort", "symbol", "--symbols", "func2(int, int)"});
283   ASSERT_TRUE(success);
284   ASSERT_TRUE(AllItemsWithString(lines, {"func2(int, int)"}));
285   Report(PERF_DATA_WITH_SYMBOLS,
286          {"--sort", "symbol", "--symbols", "main;func2(int, int)"});
287   ASSERT_TRUE(success);
288   ASSERT_TRUE(AllItemsWithString(lines, {"main", "func2(int, int)"}));
289 }
290
291 TEST_F(ReportCommandTest, use_branch_address) {
292   Report(BRANCH_PERF_DATA, {"-b", "--sort", "symbol_from,symbol_to"});
293   std::set<std::pair<std::string, std::string>> hit_set;
294   bool after_overhead = false;
295   for (const auto& line : lines) {
296     if (!after_overhead && line.find("Overhead") != std::string::npos) {
297       after_overhead = true;
298     } else if (after_overhead) {
299       char from[80];
300       char to[80];
301       if (sscanf(line.c_str(), "%*f%%%s%s", from, to) == 2) {
302         hit_set.insert(std::make_pair<std::string, std::string>(from, to));
303       }
304     }
305   }
306   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>(
307                 "GlobalFunc", "CalledFunc")),
308             hit_set.end());
309   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>(
310                 "CalledFunc", "GlobalFunc")),
311             hit_set.end());
312 }
313
314 TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) {
315   Report(NATIVELIB_IN_APK_PERF_DATA);
316   ASSERT_TRUE(success);
317   ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)),
318             std::string::npos);
319   ASSERT_NE(content.find("Func2"), std::string::npos);
320 }
321
322 TEST_F(ReportCommandTest, report_more_than_one_event_types) {
323   Report(PERF_DATA_WITH_TWO_EVENT_TYPES);
324   ASSERT_TRUE(success);
325   size_t pos = 0;
326   ASSERT_NE(pos = content.find("cpu-cycles", pos), std::string::npos);
327   ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos);
328   ASSERT_NE(pos = content.find("cpu-clock", pos), std::string::npos);
329   ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos);
330 }
331
332 TEST_F(ReportCommandTest, report_kernel_symbol) {
333   Report(PERF_DATA_WITH_KERNEL_SYMBOL);
334   ASSERT_TRUE(success);
335   ASSERT_NE(content.find("perf_event_aux"), std::string::npos);
336 }
337
338 TEST_F(ReportCommandTest, report_dumped_symbols) {
339   Report(PERF_DATA_WITH_SYMBOLS);
340   ASSERT_TRUE(success);
341   ASSERT_NE(content.find("main"), std::string::npos);
342   Report(PERF_DATA_WITH_SYMBOLS_FOR_NONZERO_MINVADDR_DSO);
343   ASSERT_TRUE(success);
344   ASSERT_NE(content.find("memcpy"), std::string::npos);
345 }
346
347 TEST_F(ReportCommandTest, report_dumped_symbols_with_symfs_dir) {
348   // Check if we can report symbols when they appear both in perf.data and symfs dir.
349   Report(PERF_DATA_WITH_SYMBOLS, {"--symfs", GetTestDataDir()});
350   ASSERT_TRUE(success);
351   ASSERT_NE(content.find("main"), std::string::npos);
352 }
353
354 TEST_F(ReportCommandTest, report_sort_vaddr_in_file) {
355   Report(PERF_DATA, {"--sort", "vaddr_in_file"});
356   ASSERT_TRUE(success);
357   ASSERT_NE(content.find("VaddrInFile"), std::string::npos);
358 }
359
360 TEST_F(ReportCommandTest, check_build_id) {
361   Report(PERF_DATA_FOR_BUILD_ID_CHECK,
362          {"--symfs", GetTestData(CORRECT_SYMFS_FOR_BUILD_ID_CHECK)});
363   ASSERT_TRUE(success);
364   ASSERT_NE(content.find("main"), std::string::npos);
365   ASSERT_EXIT(
366       {
367         Report(PERF_DATA_FOR_BUILD_ID_CHECK,
368                {"--symfs", GetTestData(WRONG_SYMFS_FOR_BUILD_ID_CHECK)});
369         if (!success) {
370           exit(1);
371         }
372         if (content.find("main") != std::string::npos) {
373           exit(2);
374         }
375         exit(0);
376       },
377       testing::ExitedWithCode(0), "Build id mismatch");
378 }
379
380 TEST_F(ReportCommandTest, no_show_ip_option) {
381   Report(PERF_DATA);
382   ASSERT_TRUE(success);
383   ASSERT_EQ(content.find("unknown"), std::string::npos);
384   Report(PERF_DATA, {"--no-show-ip"});
385   ASSERT_TRUE(success);
386   ASSERT_NE(content.find("unknown"), std::string::npos);
387 }
388
389 TEST_F(ReportCommandTest, no_symbol_table_warning) {
390   ASSERT_EXIT(
391       {
392         Report(PERF_DATA,
393                {"--symfs", GetTestData(SYMFS_FOR_NO_SYMBOL_TABLE_WARNING)});
394         if (!success) {
395           exit(1);
396         }
397         if (content.find("GlobalFunc") != std::string::npos) {
398           exit(2);
399         }
400         exit(0);
401       },
402       testing::ExitedWithCode(0), "elf doesn't contain symbol table");
403 }
404
405 TEST_F(ReportCommandTest, read_elf_file_warning) {
406   ASSERT_EXIT(
407       {
408         Report(PERF_DATA,
409                {"--symfs", GetTestData(SYMFS_FOR_READ_ELF_FILE_WARNING)});
410         if (!success) {
411           exit(1);
412         }
413         if (content.find("GlobalFunc") != std::string::npos) {
414           exit(2);
415         }
416         exit(0);
417       },
418       testing::ExitedWithCode(0), "elf: Read failed");
419 }
420
421 TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) {
422   Report(PERF_DATA_GENERATED_BY_LINUX_PERF);
423   ASSERT_TRUE(success);
424 }
425
426 TEST_F(ReportCommandTest, max_stack_and_percent_limit_option) {
427   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g"});
428   ASSERT_TRUE(success);
429   ASSERT_NE(content.find("89.03"), std::string::npos);
430
431   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "0"});
432   ASSERT_TRUE(success);
433   ASSERT_EQ(content.find("89.03"), std::string::npos);
434   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "2"});
435   ASSERT_TRUE(success);
436   ASSERT_NE(content.find("89.03"), std::string::npos);
437
438   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT,
439          {"-g", "--percent-limit", "90"});
440   ASSERT_TRUE(success);
441   ASSERT_EQ(content.find("89.03"), std::string::npos);
442   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT,
443          {"-g", "--percent-limit", "70"});
444   ASSERT_TRUE(success);
445   ASSERT_NE(content.find("89.03"), std::string::npos);
446 }
447
448 TEST_F(ReportCommandTest, kallsyms_option) {
449   Report(PERF_DATA, {"--kallsyms", GetTestData("kallsyms")});
450   ASSERT_TRUE(success);
451   ASSERT_NE(content.find("FakeKernelSymbol"), std::string::npos);
452 }
453
454 TEST_F(ReportCommandTest, invalid_perf_data) {
455   ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData(INVALID_PERF_DATA)}));
456 }
457
458 TEST_F(ReportCommandTest, raw_period_option) {
459   Report(PERF_DATA, {"--raw-period"});
460   ASSERT_TRUE(success);
461   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
462   ASSERT_EQ(content.find("%"), std::string::npos);
463 }
464
465 TEST_F(ReportCommandTest, full_callgraph_option) {
466   Report(CALLGRAPH_FP_PERF_DATA, {"-g"});
467   ASSERT_TRUE(success);
468   ASSERT_NE(content.find("skipped in brief callgraph mode"), std::string::npos);
469   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "--full-callgraph"});
470   ASSERT_TRUE(success);
471   ASSERT_EQ(content.find("skipped in brief callgraph mode"), std::string::npos);
472 }
473
474 #if defined(__linux__)
475 #include "event_selection_set.h"
476
477 static std::unique_ptr<Command> RecordCmd() {
478   return CreateCommandInstance("record");
479 }
480
481 TEST_F(ReportCommandTest, dwarf_callgraph) {
482   OMIT_TEST_ON_NON_NATIVE_ABIS();
483   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
484   std::vector<std::unique_ptr<Workload>> workloads;
485   CreateProcesses(1, &workloads);
486   std::string pid = std::to_string(workloads[0]->GetPid());
487   TemporaryFile tmp_file;
488   ASSERT_TRUE(
489       RecordCmd()->Run({"-p", pid, "-g", "-o", tmp_file.path, "sleep", SLEEP_SEC}));
490   ReportRaw(tmp_file.path, {"-g"});
491   ASSERT_TRUE(success);
492 }
493
494 TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) {
495   // NATIVELIB_IN_APK_PERF_DATA is recorded on arm64, so can only report
496   // callgraph on arm64.
497   if (GetBuildArch() == ARCH_ARM64) {
498     Report(NATIVELIB_IN_APK_PERF_DATA, {"-g"});
499     ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)),
500               std::string::npos);
501     ASSERT_NE(content.find("Func2"), std::string::npos);
502     ASSERT_NE(content.find("Func1"), std::string::npos);
503     ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
504   } else {
505     GTEST_LOG_(INFO)
506         << "This test does nothing as it is only run on arm64 devices";
507   }
508 }
509
510 #endif