cmd_report_test.cpp revision eafa7188ac41584555d36ac0c19f3c7fc66d42cc
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 "event_selection_set.h" 28#include "get_test_data.h" 29#include "perf_regs.h" 30#include "read_apk.h" 31#include "test_util.h" 32 33static std::unique_ptr<Command> ReportCmd() { 34 return CreateCommandInstance("report"); 35} 36 37class ReportCommandTest : public ::testing::Test { 38 protected: 39 void Report( 40 const std::string& perf_data, 41 const std::vector<std::string>& add_args = std::vector<std::string>()) { 42 ReportRaw(GetTestData(perf_data), add_args); 43 } 44 45 void ReportRaw( 46 const std::string& perf_data, 47 const std::vector<std::string>& add_args = std::vector<std::string>()) { 48 success = false; 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 TemporaryFile tmp_file; 68 std::string content; 69 std::vector<std::string> lines; 70 bool success; 71}; 72 73TEST_F(ReportCommandTest, no_option) { 74 Report(PERF_DATA); 75 ASSERT_TRUE(success); 76 ASSERT_NE(content.find("GlobalFunc"), std::string::npos); 77} 78 79TEST_F(ReportCommandTest, report_symbol_from_elf_file_with_mini_debug_info) { 80 Report(PERF_DATA_WITH_MINI_DEBUG_INFO); 81 ASSERT_TRUE(success); 82 ASSERT_NE(content.find("GlobalFunc"), std::string::npos); 83} 84 85TEST_F(ReportCommandTest, sort_option_pid) { 86 Report(PERF_DATA, {"--sort", "pid"}); 87 ASSERT_TRUE(success); 88 size_t line_index = 0; 89 while (line_index < lines.size() && 90 lines[line_index].find("Pid") == std::string::npos) { 91 line_index++; 92 } 93 ASSERT_LT(line_index + 2, lines.size()); 94} 95 96TEST_F(ReportCommandTest, sort_option_more_than_one) { 97 Report(PERF_DATA, {"--sort", "comm,pid,dso,symbol"}); 98 ASSERT_TRUE(success); 99 size_t line_index = 0; 100 while (line_index < lines.size() && 101 lines[line_index].find("Overhead") == std::string::npos) { 102 line_index++; 103 } 104 ASSERT_LT(line_index + 1, lines.size()); 105 ASSERT_NE(lines[line_index].find("Command"), std::string::npos); 106 ASSERT_NE(lines[line_index].find("Pid"), std::string::npos); 107 ASSERT_NE(lines[line_index].find("Shared Object"), std::string::npos); 108 ASSERT_NE(lines[line_index].find("Symbol"), std::string::npos); 109 ASSERT_EQ(lines[line_index].find("Tid"), std::string::npos); 110} 111 112TEST_F(ReportCommandTest, children_option) { 113 Report(CALLGRAPH_FP_PERF_DATA, {"--children", "--sort", "symbol"}); 114 ASSERT_TRUE(success); 115 std::unordered_map<std::string, std::pair<double, double>> map; 116 for (size_t i = 0; i < lines.size(); ++i) { 117 char name[1024]; 118 std::pair<double, double> pair; 119 if (sscanf(lines[i].c_str(), "%lf%%%lf%%%s", &pair.first, &pair.second, 120 name) == 3) { 121 map.insert(std::make_pair(name, pair)); 122 } 123 } 124 ASSERT_NE(map.find("GlobalFunc"), map.end()); 125 ASSERT_NE(map.find("main"), map.end()); 126 auto func_pair = map["GlobalFunc"]; 127 auto main_pair = map["main"]; 128 ASSERT_GE(main_pair.first, func_pair.first); 129 ASSERT_GE(func_pair.first, func_pair.second); 130 ASSERT_GE(func_pair.second, main_pair.second); 131} 132 133static bool CheckCalleeMode(std::vector<std::string>& lines) { 134 bool found = false; 135 for (size_t i = 0; i + 2 < lines.size(); ++i) { 136 if (lines[i].find("GlobalFunc") != std::string::npos && 137 lines[i + 1].find('|') != std::string::npos && 138 lines[i + 2].find("main") != std::string::npos) { 139 found = true; 140 break; 141 } 142 } 143 return found; 144} 145 146static bool CheckCallerMode(std::vector<std::string>& lines) { 147 bool found = false; 148 for (size_t i = 0; i + 2 < lines.size(); ++i) { 149 if (lines[i].find("main") != std::string::npos && 150 lines[i + 1].find('|') != std::string::npos && 151 lines[i + 2].find("GlobalFunc") != std::string::npos) { 152 found = true; 153 break; 154 } 155 } 156 return found; 157} 158 159TEST_F(ReportCommandTest, callgraph_option) { 160 Report(CALLGRAPH_FP_PERF_DATA, {"-g"}); 161 ASSERT_TRUE(success); 162 ASSERT_TRUE(CheckCallerMode(lines)); 163 Report(CALLGRAPH_FP_PERF_DATA, {"-g", "callee"}); 164 ASSERT_TRUE(success); 165 ASSERT_TRUE(CheckCalleeMode(lines)); 166 Report(CALLGRAPH_FP_PERF_DATA, {"-g", "caller"}); 167 ASSERT_TRUE(success); 168 ASSERT_TRUE(CheckCallerMode(lines)); 169} 170 171static bool AllItemsWithString(std::vector<std::string>& lines, 172 const std::vector<std::string>& strs) { 173 size_t line_index = 0; 174 while (line_index < lines.size() && 175 lines[line_index].find("Overhead") == std::string::npos) { 176 line_index++; 177 } 178 if (line_index == lines.size() || line_index + 1 == lines.size()) { 179 return false; 180 } 181 line_index++; 182 for (; line_index < lines.size(); ++line_index) { 183 bool exist = false; 184 for (auto& s : strs) { 185 if (lines[line_index].find(s) != std::string::npos) { 186 exist = true; 187 break; 188 } 189 } 190 if (!exist) { 191 return false; 192 } 193 } 194 return true; 195} 196 197TEST_F(ReportCommandTest, pid_filter_option) { 198 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid"}); 199 ASSERT_TRUE(success); 200 ASSERT_FALSE(AllItemsWithString(lines, {"17441"})); 201 ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17443"})); 202 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, 203 {"--sort", "pid", "--pids", "17441"}); 204 ASSERT_TRUE(success); 205 ASSERT_TRUE(AllItemsWithString(lines, {"17441"})); 206 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, 207 {"--sort", "pid", "--pids", "17441,17443"}); 208 ASSERT_TRUE(success); 209 ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17443"})); 210 211 // Test that --pids option is not the same as --tids option. 212 // Thread 17445 and 17441 are in process 17441. 213 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, 214 {"--sort", "tid", "--pids", "17441"}); 215 ASSERT_TRUE(success); 216 ASSERT_NE(content.find("17441"), std::string::npos); 217 ASSERT_NE(content.find("17445"), std::string::npos); 218} 219 220TEST_F(ReportCommandTest, wrong_pid_filter_option) { 221 ASSERT_EXIT( 222 { 223 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--pids", "2,bogus"}); 224 exit(success ? 0 : 1); 225 }, 226 testing::ExitedWithCode(1), "invalid id in --pids option: bogus"); 227} 228 229TEST_F(ReportCommandTest, tid_filter_option) { 230 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid"}); 231 ASSERT_TRUE(success); 232 ASSERT_FALSE(AllItemsWithString(lines, {"17441"})); 233 ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17445"})); 234 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, 235 {"--sort", "tid", "--tids", "17441"}); 236 ASSERT_TRUE(success); 237 ASSERT_TRUE(AllItemsWithString(lines, {"17441"})); 238 Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, 239 {"--sort", "tid", "--tids", "17441,17445"}); 240 ASSERT_TRUE(success); 241 ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17445"})); 242} 243 244TEST_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 id in --tids option: bogus"); 251} 252 253TEST_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 266TEST_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 279TEST_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, 285 {"--sort", "symbol", "--symbols", "func2(int, int)"}); 286 ASSERT_TRUE(success); 287 ASSERT_TRUE(AllItemsWithString(lines, {"func2(int, int)"})); 288 Report(PERF_DATA_WITH_SYMBOLS, 289 {"--sort", "symbol", "--symbols", "main;func2(int, int)"}); 290 ASSERT_TRUE(success); 291 ASSERT_TRUE(AllItemsWithString(lines, {"main", "func2(int, int)"})); 292} 293 294TEST_F(ReportCommandTest, use_branch_address) { 295 Report(BRANCH_PERF_DATA, {"-b", "--sort", "symbol_from,symbol_to"}); 296 std::set<std::pair<std::string, std::string>> hit_set; 297 bool after_overhead = false; 298 for (const auto& line : lines) { 299 if (!after_overhead && line.find("Overhead") != std::string::npos) { 300 after_overhead = true; 301 } else if (after_overhead) { 302 char from[80]; 303 char to[80]; 304 if (sscanf(line.c_str(), "%*f%%%s%s", from, to) == 2) { 305 hit_set.insert(std::make_pair<std::string, std::string>(from, to)); 306 } 307 } 308 } 309 ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>( 310 "GlobalFunc", "CalledFunc")), 311 hit_set.end()); 312 ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>( 313 "CalledFunc", "GlobalFunc")), 314 hit_set.end()); 315} 316 317TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) { 318 Report(NATIVELIB_IN_APK_PERF_DATA); 319 ASSERT_TRUE(success); 320 ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), 321 std::string::npos); 322 ASSERT_NE(content.find("Func2"), std::string::npos); 323} 324 325TEST_F(ReportCommandTest, report_more_than_one_event_types) { 326 Report(PERF_DATA_WITH_TWO_EVENT_TYPES); 327 ASSERT_TRUE(success); 328 ASSERT_NE(content.find("cpu-cycles"), std::string::npos); 329 ASSERT_NE(content.find("cpu-clock"), std::string::npos); 330} 331 332TEST_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 338TEST_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("main"), std::string::npos); 345} 346 347TEST_F(ReportCommandTest, report_sort_vaddr_in_file) { 348 Report(PERF_DATA, {"--sort", "vaddr_in_file"}); 349 ASSERT_TRUE(success); 350 ASSERT_NE(content.find("VaddrInFile"), std::string::npos); 351} 352 353TEST_F(ReportCommandTest, check_build_id) { 354 Report(PERF_DATA_FOR_BUILD_ID_CHECK, 355 {"--symfs", GetTestData(CORRECT_SYMFS_FOR_BUILD_ID_CHECK)}); 356 ASSERT_TRUE(success); 357 ASSERT_NE(content.find("main"), std::string::npos); 358 ASSERT_EXIT( 359 { 360 Report(PERF_DATA_FOR_BUILD_ID_CHECK, 361 {"--symfs", GetTestData(WRONG_SYMFS_FOR_BUILD_ID_CHECK)}); 362 if (!success) { 363 exit(1); 364 } 365 if (content.find("main") != std::string::npos) { 366 exit(2); 367 } 368 exit(0); 369 }, 370 testing::ExitedWithCode(0), "Build id mismatch"); 371} 372 373TEST_F(ReportCommandTest, no_show_ip_option) { 374 Report(PERF_DATA); 375 ASSERT_TRUE(success); 376 ASSERT_EQ(content.find("unknown"), std::string::npos); 377 Report(PERF_DATA, {"--no-show-ip"}); 378 ASSERT_TRUE(success); 379 ASSERT_NE(content.find("unknown"), std::string::npos); 380} 381 382TEST_F(ReportCommandTest, no_symbol_table_warning) { 383 ASSERT_EXIT( 384 { 385 Report(PERF_DATA, 386 {"--symfs", GetTestData(SYMFS_FOR_NO_SYMBOL_TABLE_WARNING)}); 387 if (!success) { 388 exit(1); 389 } 390 if (content.find("GlobalFunc") != std::string::npos) { 391 exit(2); 392 } 393 exit(0); 394 }, 395 testing::ExitedWithCode(0), "elf doesn't contain symbol table"); 396} 397 398TEST_F(ReportCommandTest, read_elf_file_warning) { 399 ASSERT_EXIT( 400 { 401 Report(PERF_DATA, 402 {"--symfs", GetTestData(SYMFS_FOR_READ_ELF_FILE_WARNING)}); 403 if (!success) { 404 exit(1); 405 } 406 if (content.find("GlobalFunc") != std::string::npos) { 407 exit(2); 408 } 409 exit(0); 410 }, 411 testing::ExitedWithCode(0), "elf: Read failed"); 412} 413 414TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) { 415 Report(PERF_DATA_GENERATED_BY_LINUX_PERF); 416 ASSERT_TRUE(success); 417} 418 419#if defined(__linux__) 420 421static std::unique_ptr<Command> RecordCmd() { 422 return CreateCommandInstance("record"); 423} 424 425TEST_F(ReportCommandTest, dwarf_callgraph) { 426 if (IsDwarfCallChainSamplingSupported()) { 427 TemporaryFile tmp_file; 428 ASSERT_TRUE( 429 RecordCmd()->Run({"-g", "-o", tmp_file.path, "sleep", SLEEP_SEC})); 430 ReportRaw(tmp_file.path, {"-g"}); 431 ASSERT_TRUE(success); 432 } else { 433 GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is " 434 "not supported on this device."; 435 } 436} 437 438TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) { 439 // NATIVELIB_IN_APK_PERF_DATA is recorded on arm64, so can only report 440 // callgraph on arm64. 441 if (GetBuildArch() == ARCH_ARM64) { 442 Report(NATIVELIB_IN_APK_PERF_DATA, {"-g"}); 443 ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), 444 std::string::npos); 445 ASSERT_NE(content.find("Func2"), std::string::npos); 446 ASSERT_NE(content.find("Func1"), std::string::npos); 447 ASSERT_NE(content.find("GlobalFunc"), std::string::npos); 448 } else { 449 GTEST_LOG_(INFO) 450 << "This test does nothing as it is only run on arm64 devices"; 451 } 452} 453 454#endif 455