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