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