2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <gtest/gtest.h>
20 #include <unordered_map>
22 #include <android-base/file.h>
23 #include <android-base/strings.h>
24 #include <android-base/test_utils.h>
27 #include "get_test_data.h"
28 #include "perf_regs.h"
30 #include "test_util.h"
32 static std::unique_ptr<Command> ReportCmd() {
33 return CreateCommandInstance("report");
36 class ReportCommandTest : public ::testing::Test {
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);
45 const std::string& perf_data,
46 const std::vector<std::string>& add_args = std::vector<std::string>()) {
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");
57 for (const auto& line : raw_lines) {
58 std::string s = android::base::Trim(line);
63 ASSERT_GE(lines.size(), 2u);
68 std::vector<std::string> lines;
72 TEST_F(ReportCommandTest, no_option) {
75 ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
78 TEST_F(ReportCommandTest, report_symbol_from_elf_file_with_mini_debug_info) {
79 Report(PERF_DATA_WITH_MINI_DEBUG_INFO);
81 ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
84 TEST_F(ReportCommandTest, sort_option_pid) {
85 Report(PERF_DATA, {"--sort", "pid"});
87 size_t line_index = 0;
88 while (line_index < lines.size() &&
89 lines[line_index].find("Pid") == std::string::npos) {
92 ASSERT_LT(line_index + 2, lines.size());
95 TEST_F(ReportCommandTest, sort_option_more_than_one) {
96 Report(PERF_DATA, {"--sort", "comm,pid,dso,symbol"});
98 size_t line_index = 0;
99 while (line_index < lines.size() &&
100 lines[line_index].find("Overhead") == std::string::npos) {
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);
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) {
117 std::pair<double, double> pair;
118 if (sscanf(lines[i].c_str(), "%lf%%%lf%%%s", &pair.first, &pair.second,
120 map.insert(std::make_pair(name, pair));
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);
132 static bool CheckCalleeMode(std::vector<std::string>& lines) {
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) {
144 static bool CheckCallerMode(std::vector<std::string>& lines) {
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) {
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));
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) {
175 if (line_index == lines.size() || line_index + 1 == lines.size()) {
179 for (; line_index < lines.size(); ++line_index) {
181 for (auto& s : strs) {
182 if (lines[line_index].find(s) != std::string::npos) {
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"}));
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);
217 TEST_F(ReportCommandTest, wrong_pid_filter_option) {
220 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--pids", "2,bogus"});
221 exit(success ? 0 : 1);
223 testing::ExitedWithCode(1), "invalid id in --pids option: bogus");
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"}));
241 TEST_F(ReportCommandTest, wrong_tid_filter_option) {
244 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--tids", "2,bogus"});
245 exit(success ? 0 : 1);
247 testing::ExitedWithCode(1), "invalid id in --tids option: bogus");
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"}));
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"}));
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)"}));
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) {
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));
306 ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>(
307 "GlobalFunc", "CalledFunc")),
309 ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>(
310 "CalledFunc", "GlobalFunc")),
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)),
319 ASSERT_NE(content.find("Func2"), std::string::npos);
322 TEST_F(ReportCommandTest, report_more_than_one_event_types) {
323 Report(PERF_DATA_WITH_TWO_EVENT_TYPES);
324 ASSERT_TRUE(success);
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);
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);
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);
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);
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);
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);
367 Report(PERF_DATA_FOR_BUILD_ID_CHECK,
368 {"--symfs", GetTestData(WRONG_SYMFS_FOR_BUILD_ID_CHECK)});
372 if (content.find("main") != std::string::npos) {
377 testing::ExitedWithCode(0), "Build id mismatch");
380 TEST_F(ReportCommandTest, no_show_ip_option) {
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);
389 TEST_F(ReportCommandTest, no_symbol_table_warning) {
393 {"--symfs", GetTestData(SYMFS_FOR_NO_SYMBOL_TABLE_WARNING)});
397 if (content.find("GlobalFunc") != std::string::npos) {
402 testing::ExitedWithCode(0), "elf doesn't contain symbol table");
405 TEST_F(ReportCommandTest, read_elf_file_warning) {
409 {"--symfs", GetTestData(SYMFS_FOR_READ_ELF_FILE_WARNING)});
413 if (content.find("GlobalFunc") != std::string::npos) {
418 testing::ExitedWithCode(0), "elf: Read failed");
421 TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) {
422 Report(PERF_DATA_GENERATED_BY_LINUX_PERF);
423 ASSERT_TRUE(success);
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);
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);
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);
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);
454 TEST_F(ReportCommandTest, invalid_perf_data) {
455 ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData(INVALID_PERF_DATA)}));
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);
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);
474 #if defined(__linux__)
475 #include "event_selection_set.h"
477 static std::unique_ptr<Command> RecordCmd() {
478 return CreateCommandInstance("record");
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;
489 RecordCmd()->Run({"-p", pid, "-g", "-o", tmp_file.path, "sleep", SLEEP_SEC}));
490 ReportRaw(tmp_file.path, {"-g"});
491 ASSERT_TRUE(success);
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)),
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);
506 << "This test does nothing as it is only run on arm64 devices";