1/* 2** 3** Copyright 2015, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18#include <assert.h> 19#include <dirent.h> 20#include <errno.h> 21#include <fcntl.h> 22#include <signal.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <sys/stat.h> 27#include <sys/types.h> 28#include <sys/wait.h> 29#include <time.h> 30#include <unistd.h> 31#include <string> 32#include <sstream> 33#include <map> 34#include <set> 35#include <cctype> 36 37#include <android-base/file.h> 38#include <android-base/stringprintf.h> 39#include <cutils/properties.h> 40 41#include "perfprofdcore.h" 42#include "perfprofdutils.h" 43#include "perf_data_converter.h" 44#include "cpuconfig.h" 45#include "configreader.h" 46 47// 48// Perf profiling daemon -- collects system-wide profiles using 49// 50// simpleperf record -a 51// 52// and encodes them so that they can be uploaded by a separate service. 53// 54 55//...................................................................... 56 57// 58// Output file from 'perf record'. 59// 60#define PERF_OUTPUT "perf.data" 61 62// 63// This enum holds the results of the "should we profile" configuration check. 64// 65typedef enum { 66 67 // All systems go for profile collection. 68 DO_COLLECT_PROFILE, 69 70 // The selected configuration directory doesn't exist. 71 DONT_PROFILE_MISSING_CONFIG_DIR, 72 73 // Destination directory does not contain the semaphore file that 74 // the perf profile uploading service creates when it determines 75 // that the user has opted "in" for usage data collection. No 76 // semaphore -> no user approval -> no profiling. 77 DONT_PROFILE_MISSING_SEMAPHORE, 78 79 // No perf executable present 80 DONT_PROFILE_MISSING_PERF_EXECUTABLE, 81 82 // We're running in the emulator, perf won't be able to do much 83 DONT_PROFILE_RUNNING_IN_EMULATOR 84 85} CKPROFILE_RESULT; 86 87// 88// Are we running in the emulator? If so, stub out profile collection 89// Starts as uninitialized (-1), then set to 1 or 0 at init time. 90// 91static int running_in_emulator = -1; 92 93// 94// Is this a debug build ('userdebug' or 'eng')? 95// Starts as uninitialized (-1), then set to 1 or 0 at init time. 96// 97static int is_debug_build = -1; 98 99// 100// Random number generator seed (set at startup time). 101// 102static unsigned short random_seed[3]; 103 104// 105// SIGHUP handler. Sending SIGHUP to the daemon can be used to break it 106// out of a sleep() call so as to trigger a new collection (debugging) 107// 108static void sig_hup(int /* signum */) 109{ 110 W_ALOGW("SIGHUP received"); 111} 112 113// 114// Parse command line args. Currently you can supply "-c P" to set 115// the path of the config file to P. 116// 117static void parse_args(int argc, char** argv) 118{ 119 int ac; 120 121 for (ac = 1; ac < argc; ++ac) { 122 if (!strcmp(argv[ac], "-c")) { 123 if (ac >= argc-1) { 124 W_ALOGE("malformed command line: -c option requires argument)"); 125 continue; 126 } 127 ConfigReader::setConfigFilePath(argv[ac+1]); 128 ++ac; 129 } else { 130 W_ALOGE("malformed command line: unknown option or arg %s)", argv[ac]); 131 continue; 132 } 133 } 134} 135 136// 137// Convert a CKPROFILE_RESULT to a string 138// 139const char *ckprofile_result_to_string(CKPROFILE_RESULT result) 140{ 141 switch (result) { 142 case DO_COLLECT_PROFILE: 143 return "DO_COLLECT_PROFILE"; 144 case DONT_PROFILE_MISSING_CONFIG_DIR: 145 return "missing config directory"; 146 case DONT_PROFILE_MISSING_SEMAPHORE: 147 return "missing semaphore file"; 148 case DONT_PROFILE_MISSING_PERF_EXECUTABLE: 149 return "missing 'perf' executable"; 150 case DONT_PROFILE_RUNNING_IN_EMULATOR: 151 return "running in emulator"; 152 default: return "unknown"; 153 } 154 return "notreached"; 155} 156 157// 158// Convert a PROFILE_RESULT to a string 159// 160const char *profile_result_to_string(PROFILE_RESULT result) 161{ 162 switch(result) { 163 case OK_PROFILE_COLLECTION: 164 return "profile collection succeeded"; 165 case ERR_FORK_FAILED: 166 return "fork() system call failed"; 167 case ERR_PERF_RECORD_FAILED: 168 return "perf record returned bad exit status"; 169 case ERR_PERF_ENCODE_FAILED: 170 return "failure encoding perf.data to protobuf"; 171 case ERR_OPEN_ENCODED_FILE_FAILED: 172 return "failed to open encoded perf file"; 173 case ERR_WRITE_ENCODED_FILE_FAILED: 174 return "write to encoded perf file failed"; 175 default: return "unknown"; 176 } 177 return "notreached"; 178} 179 180// 181// Check to see whether we should perform a profile collection 182// 183static CKPROFILE_RESULT check_profiling_enabled(ConfigReader &config) 184{ 185 // 186 // Profile collection in the emulator doesn't make sense 187 // 188 assert(running_in_emulator != -1); 189 if (running_in_emulator) { 190 return DONT_PROFILE_RUNNING_IN_EMULATOR; 191 } 192 193 // 194 // Check for existence of semaphore file in config directory 195 // 196 if (access(config.getStringValue("config_directory").c_str(), F_OK) == -1) { 197 W_ALOGW("unable to open config directory %s: (%s)", 198 config.getStringValue("config_directory").c_str(), strerror(errno)); 199 return DONT_PROFILE_MISSING_CONFIG_DIR; 200 } 201 202 203 // Check for existence of semaphore file 204 std::string semaphore_filepath = config.getStringValue("config_directory") 205 + "/" + SEMAPHORE_FILENAME; 206 if (access(semaphore_filepath.c_str(), F_OK) == -1) { 207 return DONT_PROFILE_MISSING_SEMAPHORE; 208 } 209 210 // Check for existence of simpleperf/perf executable 211 std::string pp = config.getStringValue("perf_path"); 212 if (access(pp.c_str(), R_OK|X_OK) == -1) { 213 W_ALOGW("unable to access/execute %s", pp.c_str()); 214 return DONT_PROFILE_MISSING_PERF_EXECUTABLE; 215 } 216 217 // 218 // We are good to go 219 // 220 return DO_COLLECT_PROFILE; 221} 222 223bool get_booting() 224{ 225 char propBuf[PROPERTY_VALUE_MAX]; 226 propBuf[0] = '\0'; 227 property_get("sys.boot_completed", propBuf, ""); 228 return (propBuf[0] != '1'); 229} 230 231// 232// Constructor takes a timeout (in seconds) and a child pid; If an 233// alarm set for the specified number of seconds triggers, then a 234// SIGKILL is sent to the child. Destructor resets alarm. Example: 235// 236// pid_t child_pid = ...; 237// { AlarmHelper h(10, child_pid); 238// ... = read_from_child(child_pid, ...); 239// } 240// 241// NB: this helper is not re-entrant-- avoid nested use or 242// use by multiple threads 243// 244class AlarmHelper { 245 public: 246 AlarmHelper(unsigned num_seconds, pid_t child) 247 { 248 struct sigaction sigact; 249 assert(child); 250 assert(child_ == 0); 251 memset(&sigact, 0, sizeof(sigact)); 252 sigact.sa_sigaction = handler; 253 sigaction(SIGALRM, &sigact, &oldsigact_); 254 child_ = child; 255 alarm(num_seconds); 256 } 257 ~AlarmHelper() 258 { 259 alarm(0); 260 child_ = 0; 261 sigaction(SIGALRM, &oldsigact_, NULL); 262 } 263 static void handler(int, siginfo_t *, void *); 264 265 private: 266 struct sigaction oldsigact_; 267 static pid_t child_; 268}; 269 270pid_t AlarmHelper::child_; 271 272void AlarmHelper::handler(int, siginfo_t *, void *) 273{ 274 W_ALOGW("SIGALRM timeout"); 275 kill(child_, SIGKILL); 276} 277 278// 279// This implementation invokes "dumpsys media.camera" and inspects the 280// output to determine if any camera clients are active. NB: this is 281// currently disable (via config option) until the selinux issues can 282// be sorted out. Another possible implementation (not yet attempted) 283// would be to use the binder to call into the native camera service 284// via "ICameraService". 285// 286bool get_camera_active() 287{ 288 int pipefds[2]; 289 if (pipe2(pipefds, O_CLOEXEC) != 0) { 290 W_ALOGE("pipe2() failed (%s)", strerror(errno)); 291 return false; 292 } 293 pid_t pid = fork(); 294 if (pid == -1) { 295 W_ALOGE("fork() failed (%s)", strerror(errno)); 296 close(pipefds[0]); 297 close(pipefds[1]); 298 return false; 299 } else if (pid == 0) { 300 // child 301 close(pipefds[0]); 302 dup2(pipefds[1], fileno(stderr)); 303 dup2(pipefds[1], fileno(stdout)); 304 const char *argv[10]; 305 unsigned slot = 0; 306 argv[slot++] = "/system/bin/dumpsys"; 307 argv[slot++] = "media.camera"; 308 argv[slot++] = nullptr; 309 execvp(argv[0], (char * const *)argv); 310 W_ALOGE("execvp() failed (%s)", strerror(errno)); 311 return false; 312 } 313 // parent 314 AlarmHelper helper(10, pid); 315 close(pipefds[1]); 316 317 // read output 318 bool have_cam = false; 319 bool have_clients = true; 320 std::string dump_output; 321 bool result = android::base::ReadFdToString(pipefds[0], &dump_output); 322 close(pipefds[0]); 323 if (result) { 324 std::stringstream ss(dump_output); 325 std::string line; 326 while (std::getline(ss,line,'\n')) { 327 if (line.find("Camera module API version:") != 328 std::string::npos) { 329 have_cam = true; 330 } 331 if (line.find("No camera module available") != 332 std::string::npos || 333 line.find("No active camera clients yet") != 334 std::string::npos) { 335 have_clients = false; 336 } 337 } 338 } 339 340 // reap child (no zombies please) 341 int st = 0; 342 TEMP_FAILURE_RETRY(waitpid(pid, &st, 0)); 343 return have_cam && have_clients; 344} 345 346bool get_charging() 347{ 348 std::string psdir("/sys/class/power_supply"); 349 DIR* dir = opendir(psdir.c_str()); 350 if (dir == NULL) { 351 W_ALOGE("Failed to open dir %s (%s)", psdir.c_str(), strerror(errno)); 352 return false; 353 } 354 struct dirent* e; 355 bool result = false; 356 while ((e = readdir(dir)) != 0) { 357 if (e->d_name[0] != '.') { 358 std::string online_path = psdir + "/" + e->d_name + "/online"; 359 std::string contents; 360 int value = 0; 361 if (android::base::ReadFileToString(online_path.c_str(), &contents) && 362 sscanf(contents.c_str(), "%d", &value) == 1) { 363 if (value) { 364 result = true; 365 break; 366 } 367 } 368 } 369 } 370 closedir(dir); 371 return result; 372} 373 374bool postprocess_proc_stat_contents(const std::string &pscontents, 375 long unsigned *idleticks, 376 long unsigned *remainingticks) 377{ 378 long unsigned usertime, nicetime, systime, idletime, iowaittime; 379 long unsigned irqtime, softirqtime; 380 381 int rc = sscanf(pscontents.c_str(), "cpu %lu %lu %lu %lu %lu %lu %lu", 382 &usertime, &nicetime, &systime, &idletime, 383 &iowaittime, &irqtime, &softirqtime); 384 if (rc != 7) { 385 return false; 386 } 387 *idleticks = idletime; 388 *remainingticks = usertime + nicetime + systime + iowaittime + irqtime + softirqtime; 389 return true; 390} 391 392unsigned collect_cpu_utilization() 393{ 394 std::string contents; 395 long unsigned idle[2]; 396 long unsigned busy[2]; 397 for (unsigned iter = 0; iter < 2; ++iter) { 398 if (!android::base::ReadFileToString("/proc/stat", &contents)) { 399 return 0; 400 } 401 if (!postprocess_proc_stat_contents(contents, &idle[iter], &busy[iter])) { 402 return 0; 403 } 404 if (iter == 0) { 405 sleep(1); 406 } 407 } 408 long unsigned total_delta = (idle[1] + busy[1]) - (idle[0] + busy[0]); 409 long unsigned busy_delta = busy[1] - busy[0]; 410 return busy_delta * 100 / total_delta; 411} 412 413static void annotate_encoded_perf_profile(wireless_android_play_playlog::AndroidPerfProfile *profile, 414 const ConfigReader &config, 415 unsigned cpu_utilization) 416{ 417 // 418 // Incorporate cpu utilization (collected prior to perf run) 419 // 420 if (config.getUnsignedValue("collect_cpu_utilization")) { 421 profile->set_cpu_utilization(cpu_utilization); 422 } 423 424 // 425 // Load average as reported by the kernel 426 // 427 std::string load; 428 double fload = 0.0; 429 if (android::base::ReadFileToString("/proc/loadavg", &load) && 430 sscanf(load.c_str(), "%lf", &fload) == 1) { 431 int iload = static_cast<int>(fload * 100.0); 432 profile->set_sys_load_average(iload); 433 } else { 434 W_ALOGE("Failed to read or scan /proc/loadavg (%s)", strerror(errno)); 435 } 436 437 // 438 // Device still booting? Camera in use? Plugged into charger? 439 // 440 bool is_booting = get_booting(); 441 if (config.getUnsignedValue("collect_booting")) { 442 profile->set_booting(is_booting); 443 } 444 if (config.getUnsignedValue("collect_camera_active")) { 445 profile->set_camera_active(is_booting ? false : get_camera_active()); 446 } 447 if (config.getUnsignedValue("collect_charging_state")) { 448 profile->set_on_charger(get_charging()); 449 } 450 451 // 452 // Examine the contents of wake_unlock to determine whether the 453 // device display is on or off. NB: is this really the only way to 454 // determine this info? 455 // 456 std::string disp; 457 if (android::base::ReadFileToString("/sys/power/wake_unlock", &disp)) { 458 bool ison = (strstr(disp.c_str(), "PowerManagerService.Display") == 0); 459 profile->set_display_on(ison); 460 } else { 461 W_ALOGE("Failed to read /sys/power/wake_unlock (%s)", strerror(errno)); 462 } 463} 464 465inline char* string_as_array(std::string* str) { 466 return str->empty() ? NULL : &*str->begin(); 467} 468 469PROFILE_RESULT encode_to_proto(const std::string &data_file_path, 470 const char *encoded_file_path, 471 const ConfigReader &config, 472 unsigned cpu_utilization) 473{ 474 // 475 // Open and read perf.data file 476 // 477 const wireless_android_play_playlog::AndroidPerfProfile &encodedProfile = 478 wireless_android_logging_awp::RawPerfDataToAndroidPerfProfile(data_file_path); 479 480 // 481 // Issue error if no samples 482 // 483 if (encodedProfile.programs().size() == 0) { 484 return ERR_PERF_ENCODE_FAILED; 485 } 486 487 // All of the info in 'encodedProfile' is derived from the perf.data file; 488 // here we tack display status, cpu utilization, system load, etc. 489 wireless_android_play_playlog::AndroidPerfProfile &prof = 490 const_cast<wireless_android_play_playlog::AndroidPerfProfile&> 491 (encodedProfile); 492 annotate_encoded_perf_profile(&prof, config, cpu_utilization); 493 494 // 495 // Serialize protobuf to array 496 // 497 int size = encodedProfile.ByteSize(); 498 std::string data; 499 data.resize(size); 500 ::google::protobuf::uint8* dtarget = 501 reinterpret_cast<::google::protobuf::uint8*>(string_as_array(&data)); 502 encodedProfile.SerializeWithCachedSizesToArray(dtarget); 503 504 // 505 // Open file and write encoded data to it 506 // 507 FILE *fp = fopen(encoded_file_path, "w"); 508 if (!fp) { 509 return ERR_OPEN_ENCODED_FILE_FAILED; 510 } 511 size_t fsiz = size; 512 if (fwrite(dtarget, fsiz, 1, fp) != 1) { 513 fclose(fp); 514 return ERR_WRITE_ENCODED_FILE_FAILED; 515 } 516 fclose(fp); 517 chmod(encoded_file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); 518 519 return OK_PROFILE_COLLECTION; 520} 521 522// 523// Invoke "perf record". Return value is OK_PROFILE_COLLECTION for 524// success, or some other error code if something went wrong. 525// 526static PROFILE_RESULT invoke_perf(const std::string &perf_path, 527 unsigned sampling_period, 528 const char *stack_profile_opt, 529 unsigned duration, 530 const std::string &data_file_path, 531 const std::string &perf_stderr_path) 532{ 533 pid_t pid = fork(); 534 535 if (pid == -1) { 536 return ERR_FORK_FAILED; 537 } 538 539 if (pid == 0) { 540 // child 541 542 // Open file to receive stderr/stdout from perf 543 FILE *efp = fopen(perf_stderr_path.c_str(), "w"); 544 if (efp) { 545 dup2(fileno(efp), STDERR_FILENO); 546 dup2(fileno(efp), STDOUT_FILENO); 547 } else { 548 W_ALOGW("unable to open %s for writing", perf_stderr_path.c_str()); 549 } 550 551 // marshall arguments 552 constexpr unsigned max_args = 12; 553 const char *argv[max_args]; 554 unsigned slot = 0; 555 argv[slot++] = perf_path.c_str(); 556 argv[slot++] = "record"; 557 558 // -o perf.data 559 argv[slot++] = "-o"; 560 argv[slot++] = data_file_path.c_str(); 561 562 // -c N 563 argv[slot++] = "-c"; 564 std::string p_str = android::base::StringPrintf("%u", sampling_period); 565 argv[slot++] = p_str.c_str(); 566 567 // -g if desired 568 if (stack_profile_opt) 569 argv[slot++] = stack_profile_opt; 570 571 // system wide profiling 572 argv[slot++] = "-a"; 573 574 // sleep <duration> 575 argv[slot++] = "/system/bin/sleep"; 576 std::string d_str = android::base::StringPrintf("%u", duration); 577 argv[slot++] = d_str.c_str(); 578 579 // terminator 580 argv[slot++] = nullptr; 581 assert(slot < max_args); 582 583 // record the final command line in the error output file for 584 // posterity/debugging purposes 585 fprintf(stderr, "perf invocation (pid=%d):\n", getpid()); 586 for (unsigned i = 0; argv[i] != nullptr; ++i) { 587 fprintf(stderr, "%s%s", i ? " " : "", argv[i]); 588 } 589 fprintf(stderr, "\n"); 590 591 // exec 592 execvp(argv[0], (char * const *)argv); 593 fprintf(stderr, "exec failed: %s\n", strerror(errno)); 594 exit(1); 595 596 } else { 597 // parent 598 int st = 0; 599 pid_t reaped = TEMP_FAILURE_RETRY(waitpid(pid, &st, 0)); 600 601 if (reaped == -1) { 602 W_ALOGW("waitpid failed: %s", strerror(errno)); 603 } else if (WIFSIGNALED(st)) { 604 W_ALOGW("perf killed by signal %d", WTERMSIG(st)); 605 } else if (WEXITSTATUS(st) != 0) { 606 W_ALOGW("perf bad exit status %d", WEXITSTATUS(st)); 607 } else { 608 return OK_PROFILE_COLLECTION; 609 } 610 } 611 612 return ERR_PERF_RECORD_FAILED; 613} 614 615// 616// Remove all files in the destination directory during initialization 617// 618static void cleanup_destination_dir(const ConfigReader &config) 619{ 620 std::string dest_dir = config.getStringValue("destination_directory"); 621 DIR* dir = opendir(dest_dir.c_str()); 622 if (dir != NULL) { 623 struct dirent* e; 624 while ((e = readdir(dir)) != 0) { 625 if (e->d_name[0] != '.') { 626 std::string file_path = dest_dir + "/" + e->d_name; 627 remove(file_path.c_str()); 628 } 629 } 630 closedir(dir); 631 } else { 632 W_ALOGW("unable to open destination dir %s for cleanup", 633 dest_dir.c_str()); 634 } 635} 636 637// 638// Post-processes after profile is collected and converted to protobuf. 639// * GMS core stores processed file sequence numbers in 640// /data/data/com.google.android.gms/files/perfprofd_processed.txt 641// * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence 642// numbers that have been processed and append the current seq number 643// Returns true if the current_seq should increment. 644// 645static bool post_process(const ConfigReader &config, int current_seq) 646{ 647 std::string dest_dir = config.getStringValue("destination_directory"); 648 std::string processed_file_path = 649 config.getStringValue("config_directory") + "/" + PROCESSED_FILENAME; 650 std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME; 651 652 653 std::set<int> processed; 654 FILE *fp = fopen(processed_file_path.c_str(), "r"); 655 if (fp != NULL) { 656 int seq; 657 while(fscanf(fp, "%d\n", &seq) > 0) { 658 if (remove(android::base::StringPrintf( 659 "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) { 660 processed.insert(seq); 661 } 662 } 663 fclose(fp); 664 } 665 666 std::set<int> produced; 667 fp = fopen(produced_file_path.c_str(), "r"); 668 if (fp != NULL) { 669 int seq; 670 while(fscanf(fp, "%d\n", &seq) > 0) { 671 if (processed.find(seq) == processed.end()) { 672 produced.insert(seq); 673 } 674 } 675 fclose(fp); 676 } 677 678 unsigned maxLive = config.getUnsignedValue("max_unprocessed_profiles"); 679 if (produced.size() >= maxLive) { 680 return false; 681 } 682 683 produced.insert(current_seq); 684 fp = fopen(produced_file_path.c_str(), "w"); 685 if (fp == NULL) { 686 W_ALOGW("Cannot write %s", produced_file_path.c_str()); 687 return false; 688 } 689 for (std::set<int>::const_iterator iter = produced.begin(); 690 iter != produced.end(); ++iter) { 691 fprintf(fp, "%d\n", *iter); 692 } 693 fclose(fp); 694 chmod(produced_file_path.c_str(), 695 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); 696 return true; 697} 698 699// 700// Collect a perf profile. Steps for this operation are: 701// - kick off 'perf record' 702// - read perf.data, convert to protocol buf 703// 704static PROFILE_RESULT collect_profile(const ConfigReader &config, int seq) 705{ 706 // 707 // Collect cpu utilization if enabled 708 // 709 unsigned cpu_utilization = 0; 710 if (config.getUnsignedValue("collect_cpu_utilization")) { 711 cpu_utilization = collect_cpu_utilization(); 712 } 713 714 // 715 // Form perf.data file name, perf error output file name 716 // 717 std::string destdir = config.getStringValue("destination_directory"); 718 std::string data_file_path(destdir); 719 data_file_path += "/"; 720 data_file_path += PERF_OUTPUT; 721 std::string perf_stderr_path(destdir); 722 perf_stderr_path += "/perferr.txt"; 723 724 // 725 // Remove any existing perf.data file -- if we don't do this, perf 726 // will rename the old file and we'll have extra cruft lying around. 727 // 728 struct stat statb; 729 if (stat(data_file_path.c_str(), &statb) == 0) { // if file exists... 730 if (unlink(data_file_path.c_str())) { // then try to remove 731 W_ALOGW("unable to unlink previous perf.data file"); 732 } 733 } 734 735 // 736 // The "mpdecision" daemon can cause problems for profile 737 // collection: if it decides to online a CPU partway through the 738 // 'perf record' run, the activity on that CPU will be invisible to 739 // perf, and if it offlines a CPU during the recording this can 740 // sometimes leave the PMU in an unusable state (dmesg errors of the 741 // form "perfevents: unable to request IRQXXX for ..."). To avoid 742 // these issues, if "mpdecision" is running the helper below will 743 // stop the service and then online all available CPUs. The object 744 // destructor (invoked when this routine terminates) will then 745 // restart the service again when needed. 746 // 747 unsigned duration = config.getUnsignedValue("sample_duration"); 748 unsigned hardwire = config.getUnsignedValue("hardwire_cpus"); 749 unsigned max_duration = config.getUnsignedValue("hardwire_cpus_max_duration"); 750 bool take_action = (hardwire && duration <= max_duration); 751 HardwireCpuHelper helper(take_action); 752 753 // 754 // Invoke perf 755 // 756 const char *stack_profile_opt = 757 (config.getUnsignedValue("stack_profile") != 0 ? "-g" : nullptr); 758 std::string perf_path = config.getStringValue("perf_path"); 759 unsigned period = config.getUnsignedValue("sampling_period"); 760 761 PROFILE_RESULT ret = invoke_perf(perf_path.c_str(), 762 period, 763 stack_profile_opt, 764 duration, 765 data_file_path, 766 perf_stderr_path); 767 if (ret != OK_PROFILE_COLLECTION) { 768 return ret; 769 } 770 771 // 772 // Read the resulting perf.data file, encode into protocol buffer, then write 773 // the result to the file perf.data.encoded 774 // 775 std::string path = android::base::StringPrintf( 776 "%s.encoded.%d", data_file_path.c_str(), seq); 777 return encode_to_proto(data_file_path, path.c_str(), config, cpu_utilization); 778} 779 780// 781// Assuming that we want to collect a profile every N seconds, 782// randomly partition N into two sub-intervals. 783// 784static void determine_before_after(unsigned &sleep_before_collect, 785 unsigned &sleep_after_collect, 786 unsigned collection_interval) 787{ 788 double frac = erand48(random_seed); 789 sleep_before_collect = (unsigned) (((double)collection_interval) * frac); 790 assert(sleep_before_collect <= collection_interval); 791 sleep_after_collect = collection_interval - sleep_before_collect; 792} 793 794// 795// Set random number generator seed 796// 797static void set_seed(ConfigReader &config) 798{ 799 unsigned seed = 0; 800 unsigned use_fixed_seed = config.getUnsignedValue("use_fixed_seed"); 801 if (use_fixed_seed) { 802 // 803 // Use fixed user-specified seed 804 // 805 seed = use_fixed_seed; 806 } else { 807 // 808 // Randomized seed 809 // 810 seed = arc4random(); 811 } 812 W_ALOGI("random seed set to %u", seed); 813 // Distribute the 32-bit seed into the three 16-bit array 814 // elements. The specific values being written do not especially 815 // matter as long as we are setting them to something based on the seed. 816 random_seed[0] = seed & 0xffff; 817 random_seed[1] = (seed >> 16); 818 random_seed[2] = (random_seed[0] ^ random_seed[1]); 819} 820 821// 822// Initialization 823// 824static void init(ConfigReader &config) 825{ 826 if (!config.readFile()) { 827 W_ALOGE("unable to open configuration file %s", 828 config.getConfigFilePath()); 829 } 830 831 // Children of init inherit an artificially low OOM score -- this is not 832 // desirable for perfprofd (its OOM score should be on par with 833 // other user processes). 834 std::stringstream oomscore_path; 835 oomscore_path << "/proc/" << getpid() << "/oom_score_adj"; 836 if (!android::base::WriteStringToFile("0", oomscore_path.str())) { 837 W_ALOGE("unable to write to %s", oomscore_path.str().c_str()); 838 } 839 840 set_seed(config); 841 cleanup_destination_dir(config); 842 843 char propBuf[PROPERTY_VALUE_MAX]; 844 propBuf[0] = '\0'; 845 property_get("ro.kernel.qemu", propBuf, ""); 846 running_in_emulator = (propBuf[0] == '1'); 847 property_get("ro.debuggable", propBuf, ""); 848 is_debug_build = (propBuf[0] == '1'); 849 850 signal(SIGHUP, sig_hup); 851} 852 853// 854// Main routine: 855// 1. parse cmd line args 856// 2. read config file 857// 3. loop: { 858// sleep for a while 859// perform a profile collection 860// } 861// 862int perfprofd_main(int argc, char** argv) 863{ 864 ConfigReader config; 865 866 W_ALOGI("starting Android Wide Profiling daemon"); 867 868 parse_args(argc, argv); 869 init(config); 870 871 // Early exit if we're not supposed to run on this build flavor 872 if (is_debug_build != 1 && 873 config.getUnsignedValue("only_debug_build") == 1) { 874 W_ALOGI("early exit due to inappropriate build type"); 875 return 0; 876 } 877 878 unsigned iterations = 0; 879 int seq = 0; 880 while(config.getUnsignedValue("main_loop_iterations") == 0 || 881 iterations < config.getUnsignedValue("main_loop_iterations")) { 882 883 // Figure out where in the collection interval we're going to actually 884 // run perf 885 unsigned sleep_before_collect = 0; 886 unsigned sleep_after_collect = 0; 887 determine_before_after(sleep_before_collect, sleep_after_collect, 888 config.getUnsignedValue("collection_interval")); 889 perfprofd_sleep(sleep_before_collect); 890 891 // Reread config file -- the uploader may have rewritten it as a result 892 // of a gservices change 893 config.readFile(); 894 895 // Check for profiling enabled... 896 CKPROFILE_RESULT ckresult = check_profiling_enabled(config); 897 if (ckresult != DO_COLLECT_PROFILE) { 898 W_ALOGI("profile collection skipped (%s)", 899 ckprofile_result_to_string(ckresult)); 900 } else { 901 // Kick off the profiling run... 902 W_ALOGI("initiating profile collection"); 903 PROFILE_RESULT result = collect_profile(config, seq); 904 if (result != OK_PROFILE_COLLECTION) { 905 W_ALOGI("profile collection failed (%s)", 906 profile_result_to_string(result)); 907 } else { 908 if (post_process(config, seq)) { 909 seq++; 910 } 911 W_ALOGI("profile collection complete"); 912 } 913 } 914 perfprofd_sleep(sleep_after_collect); 915 iterations += 1; 916 } 917 918 W_ALOGI("finishing Android Wide Profiling daemon"); 919 return 0; 920} 921