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#include <algorithm> 19#include <cctype> 20#include <string> 21#include <regex> 22#include <stdio.h> 23#include <sys/types.h> 24#include <sys/stat.h> 25#include <fcntl.h> 26 27#include <android-base/stringprintf.h> 28#include <cutils/properties.h> 29 30#include "perfprofdcore.h" 31#include "configreader.h" 32#include "perfprofdutils.h" 33#include "perfprofdmockutils.h" 34 35#include "perf_profile.pb.h" 36#include "google/protobuf/text_format.h" 37 38// 39// Set to argv[0] on startup 40// 41static const char *executable_path; 42 43// 44// test_dir is the directory containing the test executable and 45// any files associated with the test (will be created by the harness). 46// 47// dest_dir is a subdirectory of test_dir that we'll create on the fly 48// at the start of each testpoint (into which new files can be written), 49// then delete at end of testpoint. 50// 51static std::string test_dir; 52static std::string dest_dir; 53 54// Path to perf executable on device 55#define PERFPATH "/system/bin/perf" 56 57// Temporary config file that we will emit for the daemon to read 58#define CONFIGFILE "perfprofd.conf" 59 60static std::string encoded_file_path(int seq) 61{ 62 return android::base::StringPrintf("%s/perf.data.encoded.%d", 63 dest_dir.c_str(), seq); 64} 65 66class PerfProfdTest : public testing::Test { 67 protected: 68 virtual void SetUp() { 69 mock_perfprofdutils_init(); 70 create_dest_dir(); 71 yesclean(); 72 } 73 74 virtual void TearDown() { 75 mock_perfprofdutils_finish(); 76 } 77 78 void noclean() { 79 clean_ = false; 80 } 81 void yesclean() { 82 clean_ = true; 83 } 84 85 private: 86 bool clean_; 87 88 void create_dest_dir() { 89 setup_dirs(); 90 ASSERT_FALSE(dest_dir == ""); 91 if (clean_) { 92 std::string cmd("rm -rf "); 93 cmd += dest_dir; 94 system(cmd.c_str()); 95 } 96 std::string cmd("mkdir -p "); 97 cmd += dest_dir; 98 system(cmd.c_str()); 99 } 100 101 void setup_dirs() 102 { 103 if (test_dir == "") { 104 ASSERT_TRUE(executable_path != nullptr); 105 std::string s(executable_path); 106 auto found = s.find_last_of('/'); 107 test_dir = s.substr(0,found); 108 dest_dir = test_dir; 109 dest_dir += "/tmp"; 110 } 111 } 112 113}; 114 115static bool bothWhiteSpace(char lhs, char rhs) 116{ 117 return (std::isspace(lhs) && std::isspace(rhs)); 118} 119 120// 121// Squeeze out repeated whitespace from expected/actual logs. 122// 123static std::string squeezeWhite(const std::string &str, 124 const char *tag, 125 bool dump=false) 126{ 127 if (dump) { fprintf(stderr, "raw %s is %s\n", tag, str.c_str()); } 128 std::string result(str); 129 std::replace(result.begin(), result.end(), '\n', ' '); 130 auto new_end = std::unique(result.begin(), result.end(), bothWhiteSpace); 131 result.erase(new_end, result.end()); 132 while (result.begin() != result.end() && std::isspace(*result.rbegin())) { 133 result.pop_back(); 134 } 135 if (dump) { fprintf(stderr, "squeezed %s is %s\n", tag, result.c_str()); } 136 return result; 137} 138 139// 140// Replace all occurrences of a string with another string. 141// 142static std::string replaceAll(const std::string &str, 143 const std::string &from, 144 const std::string &to) 145{ 146 std::string ret = ""; 147 size_t pos = 0; 148 while (pos < str.size()) { 149 size_t found = str.find(from, pos); 150 if (found == std::string::npos) { 151 ret += str.substr(pos); 152 break; 153 } 154 ret += str.substr(pos, found - pos) + to; 155 pos = found + from.size(); 156 } 157 return ret; 158} 159 160// 161// Replace occurrences of special variables in the string. 162// 163static std::string expandVars(const std::string &str) { 164#ifdef __LP64__ 165 return replaceAll(str, "$NATIVE_TESTS", "/data/nativetest64"); 166#else 167 return replaceAll(str, "$NATIVE_TESTS", "/data/nativetest"); 168#endif 169} 170 171/// 172/// Helper class to kick off a run of the perfprofd daemon with a specific 173/// config file. 174/// 175class PerfProfdRunner { 176 public: 177 PerfProfdRunner() 178 : config_path_(test_dir) 179 { 180 config_path_ += "/" CONFIGFILE; 181 } 182 183 ~PerfProfdRunner() 184 { 185 remove_processed_file(); 186 } 187 188 void addToConfig(const std::string &line) 189 { 190 config_text_ += line; 191 config_text_ += "\n"; 192 } 193 194 void remove_semaphore_file() 195 { 196 std::string semaphore(test_dir); 197 semaphore += "/" SEMAPHORE_FILENAME; 198 unlink(semaphore.c_str()); 199 } 200 201 void create_semaphore_file() 202 { 203 std::string semaphore(test_dir); 204 semaphore += "/" SEMAPHORE_FILENAME; 205 close(open(semaphore.c_str(), O_WRONLY|O_CREAT, 0600)); 206 } 207 208 void write_processed_file(int start_seq, int end_seq) 209 { 210 std::string processed = test_dir + "/" PROCESSED_FILENAME; 211 FILE *fp = fopen(processed.c_str(), "w"); 212 for (int i = start_seq; i < end_seq; i++) { 213 fprintf(fp, "%d\n", i); 214 } 215 fclose(fp); 216 } 217 218 void remove_processed_file() 219 { 220 std::string processed = test_dir + "/" PROCESSED_FILENAME; 221 unlink(processed.c_str()); 222 } 223 224 int invoke() 225 { 226 static const char *argv[3] = { "perfprofd", "-c", "" }; 227 argv[2] = config_path_.c_str(); 228 229 writeConfigFile(config_path_, config_text_); 230 231 // execute daemon main 232 return perfprofd_main(3, (char **) argv); 233 } 234 235 private: 236 std::string config_path_; 237 std::string config_text_; 238 239 void writeConfigFile(const std::string &config_path, 240 const std::string &config_text) 241 { 242 FILE *fp = fopen(config_path.c_str(), "w"); 243 ASSERT_TRUE(fp != nullptr); 244 fprintf(fp, "%s\n", config_text.c_str()); 245 fclose(fp); 246 } 247}; 248 249//...................................................................... 250 251static void readEncodedProfile(const char *testpoint, 252 wireless_android_play_playlog::AndroidPerfProfile &encodedProfile) 253{ 254 struct stat statb; 255 int perf_data_stat_result = stat(encoded_file_path(0).c_str(), &statb); 256 ASSERT_NE(-1, perf_data_stat_result); 257 258 // read 259 std::string encoded; 260 encoded.resize(statb.st_size); 261 FILE *ifp = fopen(encoded_file_path(0).c_str(), "r"); 262 ASSERT_NE(nullptr, ifp); 263 size_t items_read = fread((void*) encoded.data(), statb.st_size, 1, ifp); 264 ASSERT_EQ(1, items_read); 265 fclose(ifp); 266 267 // decode 268 encodedProfile.ParseFromString(encoded); 269} 270 271static std::string encodedLoadModuleToString(const wireless_android_play_playlog::LoadModule &lm) 272{ 273 std::stringstream ss; 274 ss << "name: \"" << lm.name() << "\"\n"; 275 if (lm.build_id() != "") { 276 ss << "build_id: \"" << lm.build_id() << "\"\n"; 277 } 278 return ss.str(); 279} 280 281static std::string encodedModuleSamplesToString(const wireless_android_play_playlog::LoadModuleSamples &mod) 282{ 283 std::stringstream ss; 284 285 ss << "load_module_id: " << mod.load_module_id() << "\n"; 286 for (size_t k = 0; k < mod.address_samples_size(); k++) { 287 const auto &sample = mod.address_samples(k); 288 ss << " address_samples {\n"; 289 for (size_t l = 0; l < mod.address_samples(k).address_size(); 290 l++) { 291 auto address = mod.address_samples(k).address(l); 292 ss << " address: " << address << "\n"; 293 } 294 ss << " count: " << sample.count() << "\n"; 295 ss << " }\n"; 296 } 297 return ss.str(); 298} 299 300#define RAW_RESULT(x) #x 301 302// 303// Check to see if the log messages emitted by the daemon 304// match the expected result. By default we use a partial 305// match, e.g. if we see the expected excerpt anywhere in the 306// result, it's a match (for exact match, set exact to true) 307// 308static void compareLogMessages(const std::string &actual, 309 const std::string &expected, 310 const char *testpoint, 311 bool exactMatch=false) 312{ 313 std::string sqexp = squeezeWhite(expected, "expected"); 314 std::string sqact = squeezeWhite(actual, "actual"); 315 if (exactMatch) { 316 EXPECT_STREQ(sqexp.c_str(), sqact.c_str()); 317 } else { 318 std::size_t foundpos = sqact.find(sqexp); 319 bool wasFound = true; 320 if (foundpos == std::string::npos) { 321 std::cerr << testpoint << ": expected result not found\n"; 322 std::cerr << " Actual: \"" << sqact << "\"\n"; 323 std::cerr << " Expected: \"" << sqexp << "\"\n"; 324 wasFound = false; 325 } 326 EXPECT_TRUE(wasFound); 327 } 328} 329 330TEST_F(PerfProfdTest, TestUtil) 331{ 332 EXPECT_EQ("", replaceAll("", "", "")); 333 EXPECT_EQ("zzbc", replaceAll("abc", "a", "zz")); 334 EXPECT_EQ("azzc", replaceAll("abc", "b", "zz")); 335 EXPECT_EQ("abzz", replaceAll("abc", "c", "zz")); 336 EXPECT_EQ("xxyyzz", replaceAll("abc", "abc", "xxyyzz")); 337} 338 339TEST_F(PerfProfdTest, MissingGMS) 340{ 341 // 342 // AWP requires cooperation between the daemon and the GMS core 343 // piece. If we're running on a device that has an old or damaged 344 // version of GMS core, then the config directory we're interested in 345 // may not be there. This test insures that the daemon does the 346 // right thing in this case. 347 // 348 PerfProfdRunner runner; 349 runner.addToConfig("only_debug_build=0"); 350 runner.addToConfig("trace_config_read=0"); 351 runner.addToConfig("config_directory=/does/not/exist"); 352 runner.addToConfig("main_loop_iterations=1"); 353 runner.addToConfig("use_fixed_seed=1"); 354 runner.addToConfig("collection_interval=100"); 355 356 // Kick off daemon 357 int daemon_main_return_code = runner.invoke(); 358 359 // Check return code from daemon 360 EXPECT_EQ(0, daemon_main_return_code); 361 362 // Verify log contents 363 const std::string expected = RAW_RESULT( 364 I: sleep 90 seconds 365 W: unable to open config directory /does/not/exist: (No such file or directory) 366 I: profile collection skipped (missing config directory) 367 ); 368 369 // check to make sure entire log matches 370 compareLogMessages(mock_perfprofdutils_getlogged(), 371 expected, "MissingGMS"); 372} 373 374 375TEST_F(PerfProfdTest, MissingOptInSemaphoreFile) 376{ 377 // 378 // Android device owners must opt in to "collect and report usage 379 // data" in order for us to be able to collect profiles. The opt-in 380 // check is performed in the GMS core component; if the check 381 // passes, then it creates a semaphore file for the daemon to pick 382 // up on. 383 // 384 PerfProfdRunner runner; 385 runner.addToConfig("only_debug_build=0"); 386 std::string cfparam("config_directory="); cfparam += test_dir; 387 runner.addToConfig(cfparam); 388 std::string ddparam("destination_directory="); ddparam += dest_dir; 389 runner.addToConfig(ddparam); 390 runner.addToConfig("main_loop_iterations=1"); 391 runner.addToConfig("use_fixed_seed=1"); 392 runner.addToConfig("collection_interval=100"); 393 394 runner.remove_semaphore_file(); 395 396 // Kick off daemon 397 int daemon_main_return_code = runner.invoke(); 398 399 // Check return code from daemon 400 EXPECT_EQ(0, daemon_main_return_code); 401 402 // Verify log contents 403 const std::string expected = RAW_RESULT( 404 I: profile collection skipped (missing semaphore file) 405 ); 406 // check to make sure log excerpt matches 407 compareLogMessages(mock_perfprofdutils_getlogged(), 408 expected, "MissingOptInSemaphoreFile"); 409} 410 411TEST_F(PerfProfdTest, MissingPerfExecutable) 412{ 413 // 414 // Perfprofd uses the 'simpleperf' tool to collect profiles 415 // (although this may conceivably change in the future). This test 416 // checks to make sure that if 'simpleperf' is not present we bail out 417 // from collecting profiles. 418 // 419 PerfProfdRunner runner; 420 runner.addToConfig("only_debug_build=0"); 421 runner.addToConfig("trace_config_read=1"); 422 std::string cfparam("config_directory="); cfparam += test_dir; 423 runner.addToConfig(cfparam); 424 std::string ddparam("destination_directory="); ddparam += dest_dir; 425 runner.addToConfig(ddparam); 426 runner.addToConfig("main_loop_iterations=1"); 427 runner.addToConfig("use_fixed_seed=1"); 428 runner.addToConfig("collection_interval=100"); 429 runner.addToConfig("perf_path=/does/not/exist"); 430 431 // Create semaphore file 432 runner.create_semaphore_file(); 433 434 // Kick off daemon 435 int daemon_main_return_code = runner.invoke(); 436 437 // Check return code from daemon 438 EXPECT_EQ(0, daemon_main_return_code); 439 440 // expected log contents 441 const std::string expected = RAW_RESULT( 442 I: profile collection skipped (missing 'perf' executable) 443 ); 444 // check to make sure log excerpt matches 445 compareLogMessages(mock_perfprofdutils_getlogged(), 446 expected, "MissingPerfExecutable"); 447} 448 449TEST_F(PerfProfdTest, BadPerfRun) 450{ 451 // 452 // Perf tools tend to be tightly coupled with a specific kernel 453 // version -- if things are out of sync perf could fail or 454 // crash. This test makes sure that we detect such a case and log 455 // the error. 456 // 457 PerfProfdRunner runner; 458 runner.addToConfig("only_debug_build=0"); 459 std::string cfparam("config_directory="); cfparam += test_dir; 460 runner.addToConfig(cfparam); 461 std::string ddparam("destination_directory="); ddparam += dest_dir; 462 runner.addToConfig(ddparam); 463 runner.addToConfig("main_loop_iterations=1"); 464 runner.addToConfig("use_fixed_seed=1"); 465 runner.addToConfig("collection_interval=100"); 466 runner.addToConfig("perf_path=/system/bin/false"); 467 468 // Create semaphore file 469 runner.create_semaphore_file(); 470 471 // Kick off daemon 472 int daemon_main_return_code = runner.invoke(); 473 474 // Check return code from daemon 475 EXPECT_EQ(0, daemon_main_return_code); 476 477 // Verify log contents 478 const std::string expected = RAW_RESULT( 479 I: profile collection failed (perf record returned bad exit status) 480 ); 481 482 // check to make sure log excerpt matches 483 compareLogMessages(mock_perfprofdutils_getlogged(), 484 expected, "BadPerfRun"); 485} 486 487TEST_F(PerfProfdTest, ConfigFileParsing) 488{ 489 // 490 // Gracefully handly malformed items in the config file 491 // 492 PerfProfdRunner runner; 493 runner.addToConfig("only_debug_build=0"); 494 runner.addToConfig("main_loop_iterations=1"); 495 runner.addToConfig("collection_interval=100"); 496 runner.addToConfig("use_fixed_seed=1"); 497 runner.addToConfig("destination_directory=/does/not/exist"); 498 499 // assorted bad syntax 500 runner.addToConfig("collection_interval=0"); 501 runner.addToConfig("collection_interval=-1"); 502 runner.addToConfig("collection_interval=2"); 503 runner.addToConfig("nonexistent_key=something"); 504 runner.addToConfig("no_equals_stmt"); 505 506 // Kick off daemon 507 int daemon_main_return_code = runner.invoke(); 508 509 // Check return code from daemon 510 EXPECT_EQ(0, daemon_main_return_code); 511 512 // Verify log contents 513 const std::string expected = RAW_RESULT( 514 W: line 6: specified value 0 for 'collection_interval' outside permitted range [100 4294967295] (ignored) 515 W: line 7: malformed unsigned value (ignored) 516 W: line 8: specified value 2 for 'collection_interval' outside permitted range [100 4294967295] (ignored) 517 W: line 9: unknown option 'nonexistent_key' ignored 518 W: line 10: line malformed (no '=' found) 519 ); 520 521 // check to make sure log excerpt matches 522 compareLogMessages(mock_perfprofdutils_getlogged(), 523 expected, "ConfigFileParsing"); 524} 525 526TEST_F(PerfProfdTest, ProfileCollectionAnnotations) 527{ 528 unsigned util1 = collect_cpu_utilization(); 529 EXPECT_LE(util1, 100); 530 EXPECT_GE(util1, 0); 531 532 // NB: expectation is that when we run this test, the device will be 533 // completed booted, will be on charger, and will not have the camera 534 // active. 535 EXPECT_FALSE(get_booting()); 536 EXPECT_TRUE(get_charging()); 537 EXPECT_FALSE(get_camera_active()); 538} 539 540TEST_F(PerfProfdTest, BasicRunWithCannedPerf) 541{ 542 // 543 // Verify the portion of the daemon that reads and encodes 544 // perf.data files. Here we run the encoder on a canned perf.data 545 // file and verify that the resulting protobuf contains what 546 // we think it should contain. 547 // 548 std::string input_perf_data(test_dir); 549 input_perf_data += "/canned.perf.data"; 550 551 // Set up config to avoid these annotations (they are tested elsewhere) 552 ConfigReader config; 553 config.overrideUnsignedEntry("collect_cpu_utilization", 0); 554 config.overrideUnsignedEntry("collect_charging_state", 0); 555 config.overrideUnsignedEntry("collect_camera_active", 0); 556 557 // Kick off encoder and check return code 558 PROFILE_RESULT result = 559 encode_to_proto(input_perf_data, encoded_file_path(0).c_str(), config, 0); 560 EXPECT_EQ(OK_PROFILE_COLLECTION, result); 561 562 // Read and decode the resulting perf.data.encoded file 563 wireless_android_play_playlog::AndroidPerfProfile encodedProfile; 564 readEncodedProfile("BasicRunWithCannedPerf", 565 encodedProfile); 566 567 // Expect 45 programs 568 EXPECT_EQ(45, encodedProfile.programs_size()); 569 570 // Check a couple of load modules 571 { const auto &lm0 = encodedProfile.load_modules(0); 572 std::string act_lm0 = encodedLoadModuleToString(lm0); 573 std::string sqact0 = squeezeWhite(act_lm0, "actual for lm 0"); 574 const std::string expected_lm0 = RAW_RESULT( 575 name: "/data/app/com.google.android.apps.plus-1/lib/arm/libcronet.so" 576 ); 577 std::string sqexp0 = squeezeWhite(expected_lm0, "expected_lm0"); 578 EXPECT_STREQ(sqexp0.c_str(), sqact0.c_str()); 579 } 580 { const auto &lm9 = encodedProfile.load_modules(9); 581 std::string act_lm9 = encodedLoadModuleToString(lm9); 582 std::string sqact9 = squeezeWhite(act_lm9, "actual for lm 9"); 583 const std::string expected_lm9 = RAW_RESULT( 584 name: "/system/lib/libandroid_runtime.so" build_id: "8164ed7b3a8b8f5a220d027788922510" 585 ); 586 std::string sqexp9 = squeezeWhite(expected_lm9, "expected_lm9"); 587 EXPECT_STREQ(sqexp9.c_str(), sqact9.c_str()); 588 } 589 590 // Examine some of the samples now 591 { const auto &p1 = encodedProfile.programs(0); 592 const auto &lm1 = p1.modules(0); 593 std::string act_lm1 = encodedModuleSamplesToString(lm1); 594 std::string sqact1 = squeezeWhite(act_lm1, "actual for lm1"); 595 const std::string expected_lm1 = RAW_RESULT( 596 load_module_id: 9 address_samples { address: 296100 count: 1 } 597 ); 598 std::string sqexp1 = squeezeWhite(expected_lm1, "expected_lm1"); 599 EXPECT_STREQ(sqexp1.c_str(), sqact1.c_str()); 600 } 601 { const auto &p1 = encodedProfile.programs(2); 602 const auto &lm2 = p1.modules(0); 603 std::string act_lm2 = encodedModuleSamplesToString(lm2); 604 std::string sqact2 = squeezeWhite(act_lm2, "actual for lm2"); 605 const std::string expected_lm2 = RAW_RESULT( 606 load_module_id: 2 607 address_samples { address: 28030244 count: 1 } 608 address_samples { address: 29657840 count: 1 } 609 ); 610 std::string sqexp2 = squeezeWhite(expected_lm2, "expected_lm2"); 611 EXPECT_STREQ(sqexp2.c_str(), sqact2.c_str()); 612 } 613} 614 615TEST_F(PerfProfdTest, CallchainRunWithCannedPerf) 616{ 617 // This test makes sure that the perf.data converter 618 // can handle call chains. 619 // 620 std::string input_perf_data(test_dir); 621 input_perf_data += "/callchain.canned.perf.data"; 622 623 // Set up config to avoid these annotations (they are tested elsewhere) 624 ConfigReader config; 625 config.overrideUnsignedEntry("collect_cpu_utilization", 0); 626 config.overrideUnsignedEntry("collect_charging_state", 0); 627 config.overrideUnsignedEntry("collect_camera_active", 0); 628 629 // Kick off encoder and check return code 630 PROFILE_RESULT result = 631 encode_to_proto(input_perf_data, encoded_file_path(0).c_str(), config, 0); 632 EXPECT_EQ(OK_PROFILE_COLLECTION, result); 633 634 // Read and decode the resulting perf.data.encoded file 635 wireless_android_play_playlog::AndroidPerfProfile encodedProfile; 636 readEncodedProfile("BasicRunWithCannedPerf", 637 encodedProfile); 638 639 640 // Expect 3 programs 8 load modules 641 EXPECT_EQ(3, encodedProfile.programs_size()); 642 EXPECT_EQ(8, encodedProfile.load_modules_size()); 643 644 // Check a couple of load modules 645 { const auto &lm0 = encodedProfile.load_modules(0); 646 std::string act_lm0 = encodedLoadModuleToString(lm0); 647 std::string sqact0 = squeezeWhite(act_lm0, "actual for lm 0"); 648 const std::string expected_lm0 = RAW_RESULT( 649 name: "/system/bin/dex2oat" 650 build_id: "ee12bd1a1de39422d848f249add0afc4" 651 ); 652 std::string sqexp0 = squeezeWhite(expected_lm0, "expected_lm0"); 653 EXPECT_STREQ(sqexp0.c_str(), sqact0.c_str()); 654 } 655 { const auto &lm1 = encodedProfile.load_modules(1); 656 std::string act_lm1 = encodedLoadModuleToString(lm1); 657 std::string sqact1 = squeezeWhite(act_lm1, "actual for lm 1"); 658 const std::string expected_lm1 = RAW_RESULT( 659 name: "/system/bin/linker" 660 build_id: "a36715f673a4a0aa76ef290124c516cc" 661 ); 662 std::string sqexp1 = squeezeWhite(expected_lm1, "expected_lm1"); 663 EXPECT_STREQ(sqexp1.c_str(), sqact1.c_str()); 664 } 665 666 // Examine some of the samples now 667 { const auto &p0 = encodedProfile.programs(0); 668 const auto &lm1 = p0.modules(0); 669 std::string act_lm1 = encodedModuleSamplesToString(lm1); 670 std::string sqact1 = squeezeWhite(act_lm1, "actual for lm1"); 671 const std::string expected_lm1 = RAW_RESULT( 672 load_module_id: 0 673 address_samples { address: 108552 count: 2 } 674 ); 675 std::string sqexp1 = squeezeWhite(expected_lm1, "expected_lm1"); 676 EXPECT_STREQ(sqexp1.c_str(), sqact1.c_str()); 677 } 678 { const auto &p4 = encodedProfile.programs(2); 679 const auto &lm2 = p4.modules(1); 680 std::string act_lm2 = encodedModuleSamplesToString(lm2); 681 std::string sqact2 = squeezeWhite(act_lm2, "actual for lm2"); 682 const std::string expected_lm2 = RAW_RESULT( 683 load_module_id: 2 address_samples { address: 403913 count: 1 } address_samples { address: 840761 count: 1 } address_samples { address: 846481 count: 1 } address_samples { address: 999053 count: 1 } address_samples { address: 1012959 count: 1 } address_samples { address: 1524309 count: 1 } address_samples { address: 1580779 count: 1 } address_samples { address: 4287986288 count: 1 } 684 ); 685 std::string sqexp2 = squeezeWhite(expected_lm2, "expected_lm2"); 686 EXPECT_STREQ(sqexp2.c_str(), sqact2.c_str()); 687 } 688} 689 690TEST_F(PerfProfdTest, BasicRunWithLivePerf) 691{ 692 // 693 // Basic test to exercise the main loop of the daemon. It includes 694 // a live 'perf' run 695 // 696 PerfProfdRunner runner; 697 runner.addToConfig("only_debug_build=0"); 698 std::string ddparam("destination_directory="); ddparam += dest_dir; 699 runner.addToConfig(ddparam); 700 std::string cfparam("config_directory="); cfparam += test_dir; 701 runner.addToConfig(cfparam); 702 runner.addToConfig("main_loop_iterations=1"); 703 runner.addToConfig("use_fixed_seed=12345678"); 704 runner.addToConfig("max_unprocessed_profiles=100"); 705 runner.addToConfig("collection_interval=9999"); 706 runner.addToConfig("sample_duration=2"); 707 708 // Create semaphore file 709 runner.create_semaphore_file(); 710 711 // Kick off daemon 712 int daemon_main_return_code = runner.invoke(); 713 714 // Check return code from daemon 715 EXPECT_EQ(0, daemon_main_return_code); 716 717 // Read and decode the resulting perf.data.encoded file 718 wireless_android_play_playlog::AndroidPerfProfile encodedProfile; 719 readEncodedProfile("BasicRunWithLivePerf", encodedProfile); 720 721 // Examine what we get back. Since it's a live profile, we can't 722 // really do much in terms of verifying the contents. 723 EXPECT_LT(0, encodedProfile.programs_size()); 724 725 // Verify log contents 726 const std::string expected = RAW_RESULT( 727 I: starting Android Wide Profiling daemon 728 I: config file path set to $NATIVE_TESTS/perfprofd_test/perfprofd.conf 729 I: random seed set to 12345678 730 I: sleep 674 seconds 731 I: initiating profile collection 732 I: profile collection complete 733 I: sleep 9325 seconds 734 I: finishing Android Wide Profiling daemon 735 ); 736 // check to make sure log excerpt matches 737 compareLogMessages(mock_perfprofdutils_getlogged(), 738 expandVars(expected), "BasicRunWithLivePerf", true); 739} 740 741TEST_F(PerfProfdTest, MultipleRunWithLivePerf) 742{ 743 // 744 // Basic test to exercise the main loop of the daemon. It includes 745 // a live 'perf' run 746 // 747 PerfProfdRunner runner; 748 runner.addToConfig("only_debug_build=0"); 749 std::string ddparam("destination_directory="); ddparam += dest_dir; 750 runner.addToConfig(ddparam); 751 std::string cfparam("config_directory="); cfparam += test_dir; 752 runner.addToConfig(cfparam); 753 runner.addToConfig("main_loop_iterations=3"); 754 runner.addToConfig("use_fixed_seed=12345678"); 755 runner.addToConfig("collection_interval=9999"); 756 runner.addToConfig("sample_duration=2"); 757 runner.write_processed_file(1, 2); 758 759 // Create semaphore file 760 runner.create_semaphore_file(); 761 762 // Kick off daemon 763 int daemon_main_return_code = runner.invoke(); 764 765 // Check return code from daemon 766 EXPECT_EQ(0, daemon_main_return_code); 767 768 // Read and decode the resulting perf.data.encoded file 769 wireless_android_play_playlog::AndroidPerfProfile encodedProfile; 770 readEncodedProfile("BasicRunWithLivePerf", encodedProfile); 771 772 // Examine what we get back. Since it's a live profile, we can't 773 // really do much in terms of verifying the contents. 774 EXPECT_LT(0, encodedProfile.programs_size()); 775 776 // Examine that encoded.1 file is removed while encoded.{0|2} exists. 777 EXPECT_EQ(0, access(encoded_file_path(0).c_str(), F_OK)); 778 EXPECT_NE(0, access(encoded_file_path(1).c_str(), F_OK)); 779 EXPECT_EQ(0, access(encoded_file_path(2).c_str(), F_OK)); 780 781 // Verify log contents 782 const std::string expected = RAW_RESULT( 783 I: starting Android Wide Profiling daemon 784 I: config file path set to $NATIVE_TESTS/perfprofd_test/perfprofd.conf 785 I: random seed set to 12345678 786 I: sleep 674 seconds 787 I: initiating profile collection 788 I: profile collection complete 789 I: sleep 9325 seconds 790 I: sleep 4974 seconds 791 I: initiating profile collection 792 I: profile collection complete 793 I: sleep 5025 seconds 794 I: sleep 501 seconds 795 I: initiating profile collection 796 I: profile collection complete 797 I: sleep 9498 seconds 798 I: finishing Android Wide Profiling daemon 799 ); 800 // check to make sure log excerpt matches 801 compareLogMessages(mock_perfprofdutils_getlogged(), 802 expandVars(expected), "BasicRunWithLivePerf", true); 803} 804 805TEST_F(PerfProfdTest, CallChainRunWithLivePerf) 806{ 807 // 808 // Callchain profiles are only supported on certain devices. 809 // For now this test is stubbed out except when run on "angler". 810 // 811 char propBuf[PROPERTY_VALUE_MAX]; 812 propBuf[0] = '\0'; 813 property_get("ro.hardware", propBuf, ""); 814 if (strcmp(propBuf, "angler")) { 815 return; 816 } 817 818 // 819 // Collect a callchain profile, so as to exercise the code in 820 // perf_data post-processing that digests callchains. 821 // 822 PerfProfdRunner runner; 823 std::string ddparam("destination_directory="); ddparam += dest_dir; 824 runner.addToConfig(ddparam); 825 std::string cfparam("config_directory="); cfparam += test_dir; 826 runner.addToConfig(cfparam); 827 runner.addToConfig("main_loop_iterations=1"); 828 runner.addToConfig("use_fixed_seed=12345678"); 829 runner.addToConfig("max_unprocessed_profiles=100"); 830 runner.addToConfig("collection_interval=9999"); 831 runner.addToConfig("stack_profile=1"); 832 runner.addToConfig("sample_duration=2"); 833 834 // Create semaphore file 835 runner.create_semaphore_file(); 836 837 // Kick off daemon 838 int daemon_main_return_code = runner.invoke(); 839 840 // Check return code from daemon 841 EXPECT_EQ(0, daemon_main_return_code); 842 843 // Read and decode the resulting perf.data.encoded file 844 wireless_android_play_playlog::AndroidPerfProfile encodedProfile; 845 readEncodedProfile("CallChainRunWithLivePerf", encodedProfile); 846 847 // Examine what we get back. Since it's a live profile, we can't 848 // really do much in terms of verifying the contents. 849 EXPECT_LT(0, encodedProfile.programs_size()); 850 851 // Verify log contents 852 const std::string expected = RAW_RESULT( 853 I: starting Android Wide Profiling daemon 854 I: config file path set to $NATIVE_TESTS/perfprofd_test/perfprofd.conf 855 I: random seed set to 12345678 856 I: sleep 674 seconds 857 I: initiating profile collection 858 I: profile collection complete 859 I: sleep 9325 seconds 860 I: finishing Android Wide Profiling daemon 861 ); 862 // check to make sure log excerpt matches 863 compareLogMessages(mock_perfprofdutils_getlogged(), 864 expandVars(expected), "CallChainRunWithLivePerf", true); 865} 866 867int main(int argc, char **argv) { 868 executable_path = argv[0]; 869 // switch to / before starting testing (perfprofd 870 // should be location-independent) 871 chdir("/"); 872 testing::InitGoogleTest(&argc, argv); 873 return RUN_ALL_TESTS(); 874} 875