xref: /aosp_15_r20/system/extras/simpleperf/cmd_report_test.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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/parseint.h>
24 #include <android-base/strings.h>
25 #include <android-base/test_utils.h>
26 
27 #include "RegEx.h"
28 #include "command.h"
29 #include "get_test_data.h"
30 #include "perf_regs.h"
31 #include "read_apk.h"
32 #include "test_util.h"
33 
34 using namespace simpleperf;
35 
ReportCmd()36 static std::unique_ptr<Command> ReportCmd() {
37   return CreateCommandInstance("report");
38 }
39 
40 // @CddTest = 6.1/C-0-2
41 class ReportCommandTest : public ::testing::Test {
42  protected:
Report(const std::string & perf_data,const std::vector<std::string> & add_args=std::vector<std::string> (),bool with_symfs=true)43   void Report(const std::string& perf_data,
44               const std::vector<std::string>& add_args = std::vector<std::string>(),
45               bool with_symfs = true) {
46     ReportRaw(GetTestData(perf_data), add_args, with_symfs);
47   }
48 
ReportRaw(const std::string & perf_data,const std::vector<std::string> & add_args=std::vector<std::string> (),bool with_symfs=true)49   void ReportRaw(const std::string& perf_data,
50                  const std::vector<std::string>& add_args = std::vector<std::string>(),
51                  bool with_symfs = true) {
52     success = false;
53     TemporaryFile tmp_file;
54     std::vector<std::string> args = {"-i", perf_data, "-o", tmp_file.path};
55 
56     if (with_symfs) {
57       args.emplace_back("--symfs");
58       args.emplace_back(GetTestDataDir());
59     }
60 
61     args.insert(args.end(), add_args.begin(), add_args.end());
62     ASSERT_TRUE(ReportCmd()->Run(args));
63     ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &content));
64     ASSERT_TRUE(!content.empty());
65     std::vector<std::string> raw_lines = android::base::Split(content, "\n");
66     lines.clear();
67     for (const auto& line : raw_lines) {
68       std::string s = android::base::Trim(line);
69       if (!s.empty()) {
70         lines.push_back(s);
71       }
72     }
73     ASSERT_GE(lines.size(), 2u);
74     success = true;
75   }
76 
GetSampleCount()77   size_t GetSampleCount() {
78     auto regex = RegEx::Create(R"(Samples: (\d+))");
79     auto match = regex->SearchAll(content);
80     if (match->IsValid()) {
81       size_t count;
82       if (android::base::ParseUint(match->GetField(1), &count)) {
83         return count;
84       }
85     }
86     return 0;
87   }
88 
89   std::string content;
90   std::vector<std::string> lines;
91   bool success;
92 };
93 
94 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,no_option)95 TEST_F(ReportCommandTest, no_option) {
96   Report(PERF_DATA);
97   ASSERT_TRUE(success);
98   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
99 }
100 
101 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_symbol_from_elf_file_with_mini_debug_info)102 TEST_F(ReportCommandTest, report_symbol_from_elf_file_with_mini_debug_info) {
103   Report(PERF_DATA_WITH_MINI_DEBUG_INFO);
104   ASSERT_TRUE(success);
105   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
106 }
107 
108 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,sort_option_pid)109 TEST_F(ReportCommandTest, sort_option_pid) {
110   Report(PERF_DATA, {"--sort", "pid"});
111   ASSERT_TRUE(success);
112   size_t line_index = 0;
113   while (line_index < lines.size() && lines[line_index].find("Pid") == std::string::npos) {
114     line_index++;
115   }
116   ASSERT_LT(line_index + 2, lines.size());
117 }
118 
119 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,sort_option_more_than_one)120 TEST_F(ReportCommandTest, sort_option_more_than_one) {
121   Report(PERF_DATA, {"--sort", "comm,pid,dso,symbol"});
122   ASSERT_TRUE(success);
123   size_t line_index = 0;
124   while (line_index < lines.size() && lines[line_index].find("Overhead") == std::string::npos) {
125     line_index++;
126   }
127   ASSERT_LT(line_index + 1, lines.size());
128   ASSERT_NE(lines[line_index].find("Command"), std::string::npos);
129   ASSERT_NE(lines[line_index].find("Pid"), std::string::npos);
130   ASSERT_NE(lines[line_index].find("Shared Object"), std::string::npos);
131   ASSERT_NE(lines[line_index].find("Symbol"), std::string::npos);
132   ASSERT_EQ(lines[line_index].find("Tid"), std::string::npos);
133 }
134 
135 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,children_option)136 TEST_F(ReportCommandTest, children_option) {
137   Report(CALLGRAPH_FP_PERF_DATA, {"--children", "--sort", "symbol"});
138   ASSERT_TRUE(success);
139   std::unordered_map<std::string, std::pair<double, double>> map;
140   for (size_t i = 0; i < lines.size(); ++i) {
141     char name[1024];
142     std::pair<double, double> pair;
143     if (sscanf(lines[i].c_str(), "%lf%%%lf%%%s", &pair.first, &pair.second, name) == 3) {
144       map.insert(std::make_pair(name, pair));
145     }
146   }
147   ASSERT_NE(map.find("GlobalFunc"), map.end());
148   ASSERT_NE(map.find("main"), map.end());
149   auto func_pair = map["GlobalFunc"];
150   auto main_pair = map["main"];
151   ASSERT_GE(main_pair.first, func_pair.first);
152   ASSERT_GE(func_pair.first, func_pair.second);
153   ASSERT_GE(func_pair.second, main_pair.second);
154 }
155 
CheckCalleeMode(std::vector<std::string> & lines)156 static bool CheckCalleeMode(std::vector<std::string>& lines) {
157   bool found = false;
158   for (size_t i = 0; i + 1 < lines.size(); ++i) {
159     if (lines[i].find("GlobalFunc") != std::string::npos &&
160         lines[i + 1].find("main") != std::string::npos) {
161       found = true;
162       break;
163     }
164   }
165   return found;
166 }
167 
CheckCallerMode(std::vector<std::string> & lines)168 static bool CheckCallerMode(std::vector<std::string>& lines) {
169   bool found = false;
170   for (size_t i = 0; i + 1 < lines.size(); ++i) {
171     if (lines[i].find("main") != std::string::npos &&
172         lines[i + 1].find("GlobalFunc") != std::string::npos) {
173       found = true;
174       break;
175     }
176   }
177   return found;
178 }
179 
180 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,callgraph_option)181 TEST_F(ReportCommandTest, callgraph_option) {
182   Report(CALLGRAPH_FP_PERF_DATA, {"-g"});
183   ASSERT_TRUE(success);
184   ASSERT_TRUE(CheckCallerMode(lines));
185   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "callee"});
186   ASSERT_TRUE(success);
187   ASSERT_TRUE(CheckCalleeMode(lines));
188   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "caller"});
189   ASSERT_TRUE(success);
190   ASSERT_TRUE(CheckCallerMode(lines));
191 }
192 
AllItemsWithString(std::vector<std::string> & lines,const std::vector<std::string> & strs)193 static bool AllItemsWithString(std::vector<std::string>& lines,
194                                const std::vector<std::string>& strs) {
195   size_t line_index = 0;
196   while (line_index < lines.size() && lines[line_index].find("Overhead") == std::string::npos) {
197     line_index++;
198   }
199   if (line_index == lines.size() || line_index + 1 == lines.size()) {
200     return false;
201   }
202   line_index++;
203   for (; line_index < lines.size(); ++line_index) {
204     bool exist = false;
205     for (auto& s : strs) {
206       if (lines[line_index].find(s) != std::string::npos) {
207         exist = true;
208         break;
209       }
210     }
211     if (!exist) {
212       return false;
213     }
214   }
215   return true;
216 }
217 
218 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,pid_filter_option)219 TEST_F(ReportCommandTest, pid_filter_option) {
220   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid"});
221   ASSERT_TRUE(success);
222   ASSERT_FALSE(AllItemsWithString(lines, {"17441"}));
223   ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17443"}));
224   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid", "--pids", "17441"});
225   ASSERT_TRUE(success);
226   ASSERT_TRUE(AllItemsWithString(lines, {"17441"}));
227   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid", "--pids", "17441,17443"});
228   ASSERT_TRUE(success);
229   ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17443"}));
230 
231   // Test that --pids option is not the same as --tids option.
232   // Thread 17445 and 17441 are in process 17441.
233   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid", "--pids", "17441"});
234   ASSERT_TRUE(success);
235   ASSERT_NE(content.find("17441"), std::string::npos);
236   ASSERT_NE(content.find("17445"), std::string::npos);
237 }
238 
239 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,wrong_pid_filter_option)240 TEST_F(ReportCommandTest, wrong_pid_filter_option) {
241   ASSERT_EXIT(
242       {
243         Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--pids", "2,bogus"});
244         exit(success ? 0 : 1);
245       },
246       testing::ExitedWithCode(1), "invalid pid: bogus");
247 }
248 
249 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,tid_filter_option)250 TEST_F(ReportCommandTest, tid_filter_option) {
251   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid"});
252   ASSERT_TRUE(success);
253   ASSERT_FALSE(AllItemsWithString(lines, {"17441"}));
254   ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17445"}));
255   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid", "--tids", "17441"});
256   ASSERT_TRUE(success);
257   ASSERT_TRUE(AllItemsWithString(lines, {"17441"}));
258   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid", "--tids", "17441,17445"});
259   ASSERT_TRUE(success);
260   ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17445"}));
261 }
262 
263 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,wrong_tid_filter_option)264 TEST_F(ReportCommandTest, wrong_tid_filter_option) {
265   ASSERT_EXIT(
266       {
267         Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--tids", "2,bogus"});
268         exit(success ? 0 : 1);
269       },
270       testing::ExitedWithCode(1), "Invalid tid 'bogus'");
271 }
272 
273 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,comm_filter_option)274 TEST_F(ReportCommandTest, comm_filter_option) {
275   Report(PERF_DATA, {"--sort", "comm"});
276   ASSERT_TRUE(success);
277   ASSERT_FALSE(AllItemsWithString(lines, {"t1"}));
278   ASSERT_FALSE(AllItemsWithString(lines, {"t1", "t2"}));
279   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1"});
280   ASSERT_TRUE(success);
281   ASSERT_TRUE(AllItemsWithString(lines, {"t1"}));
282   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1,t2"});
283   ASSERT_TRUE(success);
284   ASSERT_TRUE(AllItemsWithString(lines, {"t1", "t2"}));
285 }
286 
287 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,dso_filter_option)288 TEST_F(ReportCommandTest, dso_filter_option) {
289   Report(PERF_DATA, {"--sort", "dso"});
290   ASSERT_TRUE(success);
291   ASSERT_FALSE(AllItemsWithString(lines, {"/t1"}));
292   ASSERT_FALSE(AllItemsWithString(lines, {"/t1", "/t2"}));
293   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1"});
294   ASSERT_TRUE(success);
295   ASSERT_TRUE(AllItemsWithString(lines, {"/t1"}));
296   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1,/t2"});
297   ASSERT_TRUE(success);
298   ASSERT_TRUE(AllItemsWithString(lines, {"/t1", "/t2"}));
299 }
300 
301 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,symbol_filter_option)302 TEST_F(ReportCommandTest, symbol_filter_option) {
303   Report(PERF_DATA_WITH_SYMBOLS, {"--sort", "symbol"});
304   ASSERT_TRUE(success);
305   ASSERT_FALSE(AllItemsWithString(lines, {"func2(int, int)"}));
306   ASSERT_FALSE(AllItemsWithString(lines, {"main", "func2(int, int)"}));
307   Report(PERF_DATA_WITH_SYMBOLS, {"--sort", "symbol", "--symbols", "func2(int, int)"});
308   ASSERT_TRUE(success);
309   ASSERT_TRUE(AllItemsWithString(lines, {"func2(int, int)"}));
310   Report(PERF_DATA_WITH_SYMBOLS, {"--sort", "symbol", "--symbols", "main;func2(int, int)"});
311   ASSERT_TRUE(success);
312   ASSERT_TRUE(AllItemsWithString(lines, {"main", "func2(int, int)"}));
313 }
314 
315 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,dso_symbol_filter_with_children_option)316 TEST_F(ReportCommandTest, dso_symbol_filter_with_children_option) {
317   // dso and symbol filter should filter different layers of the callchain separately.
318   Report("perf_display_bitmaps.data", {"--dsos", "/apex/com.android.runtime/lib64/libart.so",
319                                        "--children", "--raw-period", "--sort", "dso"});
320   ASSERT_TRUE(success);
321   ASSERT_NE(content.find("63500000  43250000  /apex/com.android.runtime/lib64/libart.so"),
322             std::string::npos);
323 
324   Report("perf_display_bitmaps.data",
325          {"--symbols", "MterpInvokeVirtual", "--children", "--raw-period", "--sort", "symbol"});
326   ASSERT_TRUE(success);
327   ASSERT_NE(content.find("51500000  2500000  MterpInvokeVirtual"), std::string::npos);
328 }
329 
330 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,use_branch_address)331 TEST_F(ReportCommandTest, use_branch_address) {
332   Report(BRANCH_PERF_DATA, {"-b", "--sort", "symbol_from,symbol_to"});
333   std::set<std::pair<std::string, std::string>> hit_set;
334   bool after_overhead = false;
335   for (const auto& line : lines) {
336     if (!after_overhead && line.find("Overhead") != std::string::npos) {
337       after_overhead = true;
338     } else if (after_overhead) {
339       char from[80];
340       char to[80];
341       if (sscanf(line.c_str(), "%*f%%%s%s", from, to) == 2) {
342         hit_set.insert(std::make_pair<std::string, std::string>(from, to));
343       }
344     }
345   }
346   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>("GlobalFunc", "CalledFunc")),
347             hit_set.end());
348   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>("CalledFunc", "GlobalFunc")),
349             hit_set.end());
350 }
351 
352 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_symbols_of_nativelib_in_apk)353 TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) {
354   Report(NATIVELIB_IN_APK_PERF_DATA);
355   ASSERT_TRUE(success);
356   ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos);
357   ASSERT_NE(content.find("Func2"), std::string::npos);
358 }
359 
360 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_more_than_one_event_types)361 TEST_F(ReportCommandTest, report_more_than_one_event_types) {
362   Report(PERF_DATA_WITH_TWO_EVENT_TYPES);
363   ASSERT_TRUE(success);
364   size_t pos = 0;
365   ASSERT_NE(pos = content.find("cpu-cycles", pos), std::string::npos);
366   ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos);
367   ASSERT_NE(pos = content.find("cpu-clock", pos), std::string::npos);
368   ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos);
369 }
370 
371 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_kernel_symbol)372 TEST_F(ReportCommandTest, report_kernel_symbol) {
373   Report(PERF_DATA_WITH_KERNEL_SYMBOL);
374   ASSERT_TRUE(success);
375   ASSERT_NE(content.find("perf_event_aux"), std::string::npos);
376 }
377 
378 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_dumped_symbols)379 TEST_F(ReportCommandTest, report_dumped_symbols) {
380   Report(PERF_DATA_WITH_SYMBOLS);
381   ASSERT_TRUE(success);
382   ASSERT_NE(content.find("main"), std::string::npos);
383   Report(PERF_DATA_WITH_SYMBOLS_FOR_NONZERO_MINVADDR_DSO);
384   ASSERT_TRUE(success);
385   ASSERT_NE(content.find("memcpy"), std::string::npos);
386 }
387 
388 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_dumped_symbols_with_symfs_dir)389 TEST_F(ReportCommandTest, report_dumped_symbols_with_symfs_dir) {
390   // Check if we can report symbols when they appear both in perf.data and symfs dir.
391   Report(PERF_DATA_WITH_SYMBOLS, {"--symfs", GetTestDataDir()});
392   ASSERT_TRUE(success);
393   ASSERT_NE(content.find("main"), std::string::npos);
394 }
395 
396 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_dumped_symbols_with_symdir)397 TEST_F(ReportCommandTest, report_dumped_symbols_with_symdir) {
398   // Check if we can report symbols by specifying symdir.
399   Report(PERF_DATA, {"--symdir", GetTestDataDir()}, false);
400   ASSERT_TRUE(success);
401   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
402 }
403 
404 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_without_symfs_dir)405 TEST_F(ReportCommandTest, report_without_symfs_dir) {
406   TemporaryFile tmpfile;
407   ASSERT_TRUE(ReportCmd()->Run({"-i", GetTestData(PERF_DATA), "-o", tmpfile.path}));
408 }
409 
410 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_sort_vaddr_in_file)411 TEST_F(ReportCommandTest, report_sort_vaddr_in_file) {
412   Report(PERF_DATA, {"--sort", "vaddr_in_file"});
413   ASSERT_TRUE(success);
414   ASSERT_NE(content.find("VaddrInFile"), std::string::npos);
415 }
416 
417 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,check_build_id)418 TEST_F(ReportCommandTest, check_build_id) {
419   Report(PERF_DATA_FOR_BUILD_ID_CHECK, {"--symfs", GetTestData(CORRECT_SYMFS_FOR_BUILD_ID_CHECK)});
420   ASSERT_TRUE(success);
421   ASSERT_NE(content.find("main"), std::string::npos);
422   ASSERT_EXIT(
423       {
424         Report(PERF_DATA_FOR_BUILD_ID_CHECK,
425                {"--symfs", GetTestData(WRONG_SYMFS_FOR_BUILD_ID_CHECK)});
426         if (!success) {
427           exit(1);
428         }
429         if (content.find("main") != std::string::npos) {
430           exit(2);
431         }
432         exit(0);
433       },
434       testing::ExitedWithCode(0), "failed to read symbols from /elf_for_build_id_check");
435 }
436 
437 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,no_show_ip_option)438 TEST_F(ReportCommandTest, no_show_ip_option) {
439   Report(PERF_DATA);
440   ASSERT_TRUE(success);
441   ASSERT_EQ(content.find("unknown"), std::string::npos);
442   Report(PERF_DATA, {"--no-show-ip"});
443   ASSERT_TRUE(success);
444   ASSERT_NE(content.find("unknown"), std::string::npos);
445 }
446 
447 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,read_elf_file_warning)448 TEST_F(ReportCommandTest, read_elf_file_warning) {
449   ASSERT_EXIT(
450       {
451         Report(PERF_DATA, {"--symfs", GetTestData(SYMFS_FOR_READ_ELF_FILE_WARNING)});
452         if (!success) {
453           exit(1);
454         }
455         if (content.find("GlobalFunc") != std::string::npos) {
456           exit(2);
457         }
458         exit(0);
459       },
460       testing::ExitedWithCode(0), "failed to read symbols from /elf: File not found");
461 }
462 
463 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_data_generated_by_linux_perf)464 TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) {
465   Report(PERF_DATA_GENERATED_BY_LINUX_PERF);
466   ASSERT_TRUE(success);
467 }
468 
469 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,max_stack_and_percent_limit_option)470 TEST_F(ReportCommandTest, max_stack_and_percent_limit_option) {
471   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g"});
472   ASSERT_TRUE(success);
473   ASSERT_NE(content.find("89.03"), std::string::npos);
474 
475   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "0"});
476   ASSERT_TRUE(success);
477   ASSERT_EQ(content.find("89.03"), std::string::npos);
478   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "2"});
479   ASSERT_TRUE(success);
480   ASSERT_NE(content.find("89.03"), std::string::npos);
481 
482   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--percent-limit", "90"});
483   ASSERT_TRUE(success);
484   ASSERT_EQ(content.find("89.03"), std::string::npos);
485   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--percent-limit", "70"});
486   ASSERT_TRUE(success);
487   ASSERT_NE(content.find("89.03"), std::string::npos);
488 }
489 
490 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,percent_limit_option)491 TEST_F(ReportCommandTest, percent_limit_option) {
492   Report(PERF_DATA);
493   ASSERT_TRUE(success);
494   ASSERT_NE(content.find("7.70%"), std::string::npos);
495   ASSERT_NE(content.find("3.23%"), std::string::npos);
496   Report(PERF_DATA, {"--percent-limit", "3.24"});
497   ASSERT_TRUE(success);
498   ASSERT_NE(content.find("7.70%"), std::string::npos);
499   ASSERT_EQ(content.find("3.23%"), std::string::npos);
500 }
501 
502 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,kallsyms_option)503 TEST_F(ReportCommandTest, kallsyms_option) {
504   Report(PERF_DATA, {"--kallsyms", GetTestData("kallsyms")});
505   ASSERT_TRUE(success);
506   ASSERT_NE(content.find("FakeKernelSymbol"), std::string::npos);
507 }
508 
509 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,invalid_perf_data)510 TEST_F(ReportCommandTest, invalid_perf_data) {
511   ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData(INVALID_PERF_DATA)}));
512 }
513 
514 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,raw_period_option)515 TEST_F(ReportCommandTest, raw_period_option) {
516   Report(PERF_DATA, {"--raw-period"});
517   ASSERT_TRUE(success);
518   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
519   ASSERT_EQ(content.find('%'), std::string::npos);
520 }
521 
522 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,full_callgraph_option)523 TEST_F(ReportCommandTest, full_callgraph_option) {
524   Report(CALLGRAPH_FP_PERF_DATA, {"-g"});
525   ASSERT_TRUE(success);
526   ASSERT_NE(content.find("skipped in brief callgraph mode"), std::string::npos);
527   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "--full-callgraph"});
528   ASSERT_TRUE(success);
529   ASSERT_EQ(content.find("skipped in brief callgraph mode"), std::string::npos);
530 }
531 
532 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_offcpu_time)533 TEST_F(ReportCommandTest, report_offcpu_time) {
534   Report(PERF_DATA_WITH_TRACE_OFFCPU, {"--children"});
535   ASSERT_TRUE(success);
536   ASSERT_NE(content.find("Time in ns"), std::string::npos);
537   bool found = false;
538   for (auto& line : lines) {
539     if (line.find("SleepFunction") != std::string::npos) {
540       ASSERT_NE(line.find("38.76%"), std::string::npos);
541       found = true;
542       break;
543     }
544   }
545   ASSERT_TRUE(found);
546 }
547 
548 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_big_trace_data)549 TEST_F(ReportCommandTest, report_big_trace_data) {
550   Report(PERF_DATA_WITH_BIG_TRACE_DATA);
551   ASSERT_TRUE(success);
552 }
553 
554 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,csv_option)555 TEST_F(ReportCommandTest, csv_option) {
556   Report(PERF_DATA, {"--csv"});
557   ASSERT_TRUE(success);
558   ASSERT_NE(content.find("EventCount,EventName"), std::string::npos);
559 
560   Report(CALLGRAPH_FP_PERF_DATA, {"--children", "--csv"});
561   ASSERT_TRUE(success);
562   ASSERT_NE(content.find("AccEventCount,SelfEventCount,EventName"), std::string::npos);
563 }
564 
565 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,csv_separator_option)566 TEST_F(ReportCommandTest, csv_separator_option) {
567   Report(PERF_DATA, {"--csv", "--csv-separator", ";"});
568   ASSERT_TRUE(success);
569   ASSERT_NE(content.find("EventCount;EventName"), std::string::npos);
570   ASSERT_NE(content.find(";cpu-cycles"), std::string::npos);
571 }
572 
573 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,dso_path_for_jit_cache)574 TEST_F(ReportCommandTest, dso_path_for_jit_cache) {
575   Report("perf_with_jit_symbol.data", {"--sort", "dso"});
576   ASSERT_TRUE(success);
577   ASSERT_NE(content.find("[JIT app cache]"), std::string::npos);
578 
579   // Check if we can filter dso by "[JIT app cache]".
580   Report("perf_with_jit_symbol.data", {"--dsos", "[JIT app cache]"});
581   ASSERT_TRUE(success);
582   ASSERT_NE(content.find("[JIT app cache]"), std::string::npos);
583 }
584 
585 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,generic_jit_symbols)586 TEST_F(ReportCommandTest, generic_jit_symbols) {
587   Report("perf_with_generic_git_symbols.data", {"--sort", "symbol"});
588   ASSERT_TRUE(success);
589   ASSERT_NE(std::string::npos, content.find("generic_jit_symbol_one"));
590 }
591 
592 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,cpu_option)593 TEST_F(ReportCommandTest, cpu_option) {
594   Report("perf.data");
595   ASSERT_TRUE(success);
596   ASSERT_EQ(2409, GetSampleCount());
597   Report("perf.data", {"--cpu", "2"});
598   ASSERT_TRUE(success);
599   ASSERT_EQ(603, GetSampleCount());
600   Report("perf.data", {"--cpu", "2-6,16"});
601   ASSERT_TRUE(success);
602   ASSERT_EQ(1806, GetSampleCount());
603   Report("perf.data", {"--cpu", "2-6", "--cpu", "16"});
604   ASSERT_TRUE(success);
605   ASSERT_EQ(1806, GetSampleCount());
606   ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData("perf.data"), "--cpu", "-2"}));
607 }
608 
609 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,print_event_count_option)610 TEST_F(ReportCommandTest, print_event_count_option) {
611   // Report record file not recorded with --add-counter.
612   Report("perf.data", {"--print-event-count"});
613   ASSERT_TRUE(success);
614   ASSERT_NE(content.find("EventCount"), std::string::npos);
615   ASSERT_TRUE(
616       RegEx::Create(R"(325005586\s+elf\s+26083\s+26083\s+/elf\s+GlobalFunc)")->Search(content));
617 
618   // Report record file recorded with --add-counter.
619   const std::string record_file = "perf_with_add_counter.data";
620   Report(record_file, {"--print-event-count"});
621   ASSERT_TRUE(success);
622   ASSERT_TRUE(RegEx::Create(R"(EventCount_cpu-cycles\s+EventCount_instructions)")->Search(content));
623   ASSERT_TRUE(
624       RegEx::Create(R"(175099\s+140443\s+sleep\s+689664\s+689664.+_dl_addr)")->Search(content));
625 
626   // Report accumulated event counts.
627   Report(record_file, {"--print-event-count", "--children"});
628   ASSERT_TRUE(success);
629   ASSERT_TRUE(
630       RegEx::Create(
631           R"(AccEventCount_cpu-cycles\s+SelfEventCount_cpu-cycles\s+AccEventCount_instructions\s+)"
632           R"(SelfEventCount_instructions)")
633           ->Search(content));
634   ASSERT_TRUE(
635       RegEx::Create(R"(175099\s+175099\s+140443\s+140443\s+sleep\s+689664\s+689664.+_dl_addr)")
636           ->Search(content));
637   ASSERT_TRUE(
638       RegEx::Create(R"(366116\s+0\s+297474\s+0\s+sleep\s+689664\s+689664.+__libc_start_main)")
639           ->Search(content));
640 }
641 
642 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,exclude_include_pid_options)643 TEST_F(ReportCommandTest, exclude_include_pid_options) {
644   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid", "--exclude-pid", "17441"});
645   ASSERT_TRUE(success);
646   ASSERT_TRUE(AllItemsWithString(lines, {"17443", "17444"}));
647   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid", "--include-pid", "17441"});
648   ASSERT_TRUE(success);
649   ASSERT_TRUE(AllItemsWithString(lines, {"17441"}));
650 }
651 
652 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,exclude_include_tid_options)653 TEST_F(ReportCommandTest, exclude_include_tid_options) {
654   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
655          {"--sort", "tid", "--exclude-tid", "17441,17443,17444"});
656   ASSERT_TRUE(success);
657   ASSERT_TRUE(AllItemsWithString(lines, {"17445", "17446", "17447"}));
658   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
659          {"--sort", "tid", "--include-tid", "17441,17443,17444"});
660   ASSERT_TRUE(success);
661   ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17443", "17444"}));
662 }
663 
664 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,exclude_include_process_name_options)665 TEST_F(ReportCommandTest, exclude_include_process_name_options) {
666   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "comm", "--exclude-process-name", "t1"});
667   ASSERT_TRUE(success);
668   ASSERT_TRUE(AllItemsWithString(lines, {"simpleperf"}));
669   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "comm", "--include-process-name", "t1"});
670   ASSERT_TRUE(success);
671   ASSERT_TRUE(AllItemsWithString(lines, {"t1"}));
672 }
673 
674 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,exclude_include_thread_name_options)675 TEST_F(ReportCommandTest, exclude_include_thread_name_options) {
676   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "comm", "--exclude-thread-name", "t1"});
677   ASSERT_TRUE(success);
678   ASSERT_TRUE(AllItemsWithString(lines, {"simpleperf"}));
679   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "comm", "--include-thread-name", "t1"});
680   ASSERT_TRUE(success);
681   ASSERT_TRUE(AllItemsWithString(lines, {"t1"}));
682 }
683 
684 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,filter_file_option)685 TEST_F(ReportCommandTest, filter_file_option) {
686   std::string filter_data =
687       "GLOBAL_BEGIN 684943449406175\n"
688       "GLOBAL_END 684943449406176";
689   TemporaryFile tmpfile;
690   ASSERT_TRUE(android::base::WriteStringToFd(filter_data, tmpfile.fd));
691   Report("perf_display_bitmaps.data", {"--filter-file", tmpfile.path});
692   ASSERT_TRUE(success);
693   ASSERT_EQ(GetSampleCount(), 1);
694 
695   // PERF_DATA uses clock perf, which doesn't match the default clock in filter data.
696   CapturedStderr capture;
697   ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData(PERF_DATA), "--filter-file", tmpfile.path}));
698   capture.Stop();
699   ASSERT_NE(capture.str().find("doesn't match clock used in time filter"), std::string::npos);
700 }
701 
702 #if defined(__linux__)
703 #include "event_selection_set.h"
704 
RecordCmd()705 static std::unique_ptr<Command> RecordCmd() {
706   return CreateCommandInstance("record");
707 }
708 
709 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,dwarf_callgraph)710 TEST_F(ReportCommandTest, dwarf_callgraph) {
711   TEST_REQUIRE_HW_COUNTER();
712   OMIT_TEST_ON_NON_NATIVE_ABIS();
713   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
714   std::vector<std::unique_ptr<Workload>> workloads;
715   CreateProcesses(1, &workloads);
716   std::string pid = std::to_string(workloads[0]->GetPid());
717   TemporaryFile tmp_file;
718   ASSERT_TRUE(RecordCmd()->Run({"-p", pid, "-g", "-o", tmp_file.path, "sleep", SLEEP_SEC}));
719   ReportRaw(tmp_file.path, {"-g"});
720   ASSERT_TRUE(success);
721 }
722 
723 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,report_dwarf_callgraph_of_nativelib_in_apk)724 TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) {
725   Report(NATIVELIB_IN_APK_PERF_DATA, {"-g"});
726   ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos);
727   ASSERT_NE(content.find("Func2"), std::string::npos);
728   ASSERT_NE(content.find("Func1"), std::string::npos);
729   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
730 }
731 
732 // @CddTest = 6.1/C-0-2
TEST_F(ReportCommandTest,exclude_kernel_callchain)733 TEST_F(ReportCommandTest, exclude_kernel_callchain) {
734   TEST_REQUIRE_HW_COUNTER();
735   TEST_REQUIRE_HOST_ROOT();
736   OMIT_TEST_ON_NON_NATIVE_ABIS();
737   std::vector<std::unique_ptr<Workload>> workloads;
738   CreateProcesses(1, &workloads);
739   std::string pid = std::to_string(workloads[0]->GetPid());
740   TemporaryFile tmpfile;
741   ASSERT_TRUE(RecordCmd()->Run({"--trace-offcpu", "-e", "cpu-clock:u", "-p", pid, "--duration", "2",
742                                 "-o", tmpfile.path, "-g"}));
743   ReportRaw(tmpfile.path, {"-g"});
744   ASSERT_TRUE(success);
745   ASSERT_EQ(content.find("[kernel.kallsyms]"), std::string::npos);
746 }
747 
748 #endif
749