OSDN Git Service

libperfmgr: change to use Value directly in config
[android-x86/system-extras.git] / perfprofd / perfprofdcore.cc
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
32 #include <memory>
33 #include <sstream>
34 #include <string>
35
36 #include <android-base/file.h>
37 #include <android-base/logging.h>
38 #include <android-base/macros.h>
39 #include <android-base/stringprintf.h>
40
41 #ifdef __BIONIC__
42 #include <android-base/properties.h>
43 #endif
44
45 #include "perfprofd_record.pb.h"
46
47 #include "config.h"
48 #include "cpuconfig.h"
49 #include "perf_data_converter.h"
50 #include "perfprofdcore.h"
51 #include "perfprofd_io.h"
52 #include "symbolizer.h"
53
54 //
55 // Perf profiling daemon -- collects system-wide profiles using
56 //
57 //       simpleperf record -a
58 //
59 // and encodes them so that they can be uploaded by a separate service.
60 //
61
62 //......................................................................
63
64 using ProtoUniquePtr = std::unique_ptr<android::perfprofd::PerfprofdRecord>;
65
66 //
67 // Output file from 'perf record'.
68 //
69 #define PERF_OUTPUT "perf.data"
70
71 //
72 // This enum holds the results of the "should we profile" configuration check.
73 //
74 typedef enum {
75
76   // All systems go for profile collection.
77   DO_COLLECT_PROFILE,
78
79   // The selected configuration directory doesn't exist.
80   DONT_PROFILE_MISSING_CONFIG_DIR,
81
82   // Destination directory does not contain the semaphore file that
83   // the perf profile uploading service creates when it determines
84   // that the user has opted "in" for usage data collection. No
85   // semaphore -> no user approval -> no profiling.
86   DONT_PROFILE_MISSING_SEMAPHORE,
87
88   // No perf executable present
89   DONT_PROFILE_MISSING_PERF_EXECUTABLE,
90
91   // We're running in the emulator, perf won't be able to do much
92   DONT_PROFILE_RUNNING_IN_EMULATOR
93
94 } CKPROFILE_RESULT;
95
96 static bool common_initialized = false;
97
98 //
99 // Are we running in the emulator? If so, stub out profile collection
100 // Starts as uninitialized (-1), then set to 1 or 0 at init time.
101 //
102 static int running_in_emulator = -1;
103
104 //
105 // Is this a debug build ('userdebug' or 'eng')?
106 //
107 static bool is_debug_build = false;
108
109 //
110 // Random number generator seed (set at startup time).
111 //
112 static unsigned short random_seed[3];
113
114 //
115 // Convert a CKPROFILE_RESULT to a string
116 //
117 static const char *ckprofile_result_to_string(CKPROFILE_RESULT result)
118 {
119   switch (result) {
120     case DO_COLLECT_PROFILE:
121       return "DO_COLLECT_PROFILE";
122     case DONT_PROFILE_MISSING_CONFIG_DIR:
123       return "missing config directory";
124     case DONT_PROFILE_MISSING_SEMAPHORE:
125       return "missing semaphore file";
126     case DONT_PROFILE_MISSING_PERF_EXECUTABLE:
127       return "missing 'perf' executable";
128     case DONT_PROFILE_RUNNING_IN_EMULATOR:
129       return "running in emulator";
130     default:
131       return "unknown";
132   }
133 }
134
135 //
136 // Check to see whether we should perform a profile collection
137 //
138 static CKPROFILE_RESULT check_profiling_enabled(const Config& config)
139 {
140   //
141   // Profile collection in the emulator doesn't make sense
142   //
143   assert(running_in_emulator != -1);
144   if (running_in_emulator) {
145     return DONT_PROFILE_RUNNING_IN_EMULATOR;
146   }
147
148   if (!config.IsProfilingEnabled()) {
149     return DONT_PROFILE_MISSING_CONFIG_DIR;
150   }
151
152   // Check for existence of simpleperf/perf executable
153   std::string pp = config.perf_path;
154   if (access(pp.c_str(), R_OK|X_OK) == -1) {
155     LOG(WARNING) << "unable to access/execute " << pp;
156     return DONT_PROFILE_MISSING_PERF_EXECUTABLE;
157   }
158
159   //
160   // We are good to go
161   //
162   return DO_COLLECT_PROFILE;
163 }
164
165 bool get_booting()
166 {
167 #ifdef __BIONIC__
168   return android::base::GetBoolProperty("sys.boot_completed", false) != true;
169 #else
170   return false;
171 #endif
172 }
173
174 //
175 // Constructor takes a timeout (in seconds) and a child pid; If an
176 // alarm set for the specified number of seconds triggers, then a
177 // SIGKILL is sent to the child. Destructor resets alarm. Example:
178 //
179 //       pid_t child_pid = ...;
180 //       { AlarmHelper h(10, child_pid);
181 //         ... = read_from_child(child_pid, ...);
182 //       }
183 //
184 // NB: this helper is not re-entrant-- avoid nested use or
185 // use by multiple threads
186 //
187 class AlarmHelper {
188  public:
189   AlarmHelper(unsigned num_seconds, pid_t child)
190   {
191     struct sigaction sigact;
192     assert(child);
193     assert(child_ == 0);
194     memset(&sigact, 0, sizeof(sigact));
195     sigact.sa_sigaction = handler;
196     sigaction(SIGALRM, &sigact, &oldsigact_);
197     child_ = child;
198     alarm(num_seconds);
199   }
200   ~AlarmHelper()
201   {
202     alarm(0);
203     child_ = 0;
204     sigaction(SIGALRM, &oldsigact_, NULL);
205   }
206   static void handler(int, siginfo_t *, void *);
207
208  private:
209   struct sigaction oldsigact_;
210   static pid_t child_;
211 };
212
213 pid_t AlarmHelper::child_;
214
215 void AlarmHelper::handler(int, siginfo_t *, void *)
216 {
217   LOG(WARNING) << "SIGALRM timeout";
218   kill(child_, SIGKILL);
219 }
220
221 //
222 // This implementation invokes "dumpsys media.camera" and inspects the
223 // output to determine if any camera clients are active. NB: this is
224 // currently disable (via config option) until the selinux issues can
225 // be sorted out. Another possible implementation (not yet attempted)
226 // would be to use the binder to call into the native camera service
227 // via "ICameraService".
228 //
229 bool get_camera_active()
230 {
231   int pipefds[2];
232   if (pipe2(pipefds, O_CLOEXEC) != 0) {
233     PLOG(ERROR) << "pipe2() failed";
234     return false;
235   }
236   pid_t pid = fork();
237   if (pid == -1) {
238     PLOG(ERROR) << "fork() failed";
239     close(pipefds[0]);
240     close(pipefds[1]);
241     return false;
242   } else if (pid == 0) {
243     // child
244     close(pipefds[0]);
245     dup2(pipefds[1], fileno(stderr));
246     dup2(pipefds[1], fileno(stdout));
247     const char *argv[10];
248     unsigned slot = 0;
249     argv[slot++] = "/system/bin/dumpsys";
250     argv[slot++] = "media.camera";
251     argv[slot++] = nullptr;
252     execvp(argv[0], (char * const *)argv);
253     PLOG(ERROR) << "execvp() failed";
254     return false;
255   }
256   // parent
257   AlarmHelper helper(10, pid);
258   close(pipefds[1]);
259
260   // read output
261   bool have_cam = false;
262   bool have_clients = true;
263   std::string dump_output;
264   bool result = android::base::ReadFdToString(pipefds[0], &dump_output);
265   close(pipefds[0]);
266   if (result) {
267     std::stringstream ss(dump_output);
268     std::string line;
269     while (std::getline(ss,line,'\n')) {
270       if (line.find("Camera module API version:") !=
271           std::string::npos) {
272         have_cam = true;
273       }
274       if (line.find("No camera module available") !=
275           std::string::npos ||
276           line.find("No active camera clients yet") !=
277           std::string::npos) {
278         have_clients = false;
279       }
280     }
281   }
282
283   // reap child (no zombies please)
284   int st = 0;
285   TEMP_FAILURE_RETRY(waitpid(pid, &st, 0));
286   return have_cam && have_clients;
287 }
288
289 bool get_charging()
290 {
291   std::string psdir("/sys/class/power_supply");
292   DIR* dir = opendir(psdir.c_str());
293   if (dir == NULL) {
294     PLOG(ERROR) << "Failed to open dir " << psdir;
295     return false;
296   }
297   struct dirent* e;
298   bool result = false;
299   while ((e = readdir(dir)) != 0) {
300     if (e->d_name[0] != '.') {
301       std::string online_path = psdir + "/" + e->d_name + "/online";
302       std::string contents;
303       int value = 0;
304       if (android::base::ReadFileToString(online_path.c_str(), &contents) &&
305           sscanf(contents.c_str(), "%d", &value) == 1) {
306         if (value) {
307           result = true;
308           break;
309         }
310       }
311     }
312   }
313   closedir(dir);
314   return result;
315 }
316
317 static bool postprocess_proc_stat_contents(const std::string &pscontents,
318                                            long unsigned *idleticks,
319                                            long unsigned *remainingticks)
320 {
321   long unsigned usertime, nicetime, systime, idletime, iowaittime;
322   long unsigned irqtime, softirqtime;
323
324   int rc = sscanf(pscontents.c_str(), "cpu  %lu %lu %lu %lu %lu %lu %lu",
325                   &usertime, &nicetime, &systime, &idletime,
326                   &iowaittime, &irqtime, &softirqtime);
327   if (rc != 7) {
328     return false;
329   }
330   *idleticks = idletime;
331   *remainingticks = usertime + nicetime + systime + iowaittime + irqtime + softirqtime;
332   return true;
333 }
334
335 unsigned collect_cpu_utilization()
336 {
337   std::string contents;
338   long unsigned idle[2];
339   long unsigned busy[2];
340   for (unsigned iter = 0; iter < 2; ++iter) {
341     if (!android::base::ReadFileToString("/proc/stat", &contents)) {
342       return 0;
343     }
344     if (!postprocess_proc_stat_contents(contents, &idle[iter], &busy[iter])) {
345       return 0;
346     }
347     if (iter == 0) {
348       sleep(1);
349     }
350   }
351   long unsigned total_delta = (idle[1] + busy[1]) - (idle[0] + busy[0]);
352   long unsigned busy_delta = busy[1] - busy[0];
353   return busy_delta * 100 / total_delta;
354 }
355
356 static void annotate_encoded_perf_profile(android::perfprofd::PerfprofdRecord* profile,
357                                           const Config& config,
358                                           unsigned cpu_utilization)
359 {
360   //
361   // Incorporate cpu utilization (collected prior to perf run)
362   //
363   if (config.collect_cpu_utilization) {
364     profile->set_cpu_utilization(cpu_utilization);
365   }
366
367   //
368   // Load average as reported by the kernel
369   //
370   std::string load;
371   double fload = 0.0;
372   if (android::base::ReadFileToString("/proc/loadavg", &load) &&
373       sscanf(load.c_str(), "%lf", &fload) == 1) {
374     int iload = static_cast<int>(fload * 100.0);
375     profile->set_sys_load_average(iload);
376   } else {
377     PLOG(ERROR) << "Failed to read or scan /proc/loadavg";
378   }
379
380   //
381   // Device still booting? Camera in use? Plugged into charger?
382   //
383   bool is_booting = get_booting();
384   if (config.collect_booting) {
385     profile->set_booting(is_booting);
386   }
387   if (config.collect_camera_active) {
388     profile->set_camera_active(is_booting ? false : get_camera_active());
389   }
390   if (config.collect_charging_state) {
391     profile->set_on_charger(get_charging());
392   }
393
394   //
395   // Examine the contents of wake_unlock to determine whether the
396   // device display is on or off. NB: is this really the only way to
397   // determine this info?
398   //
399   std::string disp;
400   if (android::base::ReadFileToString("/sys/power/wake_unlock", &disp)) {
401     bool ison = (strstr(disp.c_str(), "PowerManagerService.Display") == 0);
402     profile->set_display_on(ison);
403   } else {
404     PLOG(ERROR) << "Failed to read /sys/power/wake_unlock";
405   }
406 }
407
408 static ProtoUniquePtr encode_to_proto(const std::string &data_file_path,
409                                       const Config& config,
410                                       unsigned cpu_utilization,
411                                       perfprofd::Symbolizer* symbolizer) {
412   //
413   // Open and read perf.data file
414   //
415   ProtoUniquePtr encodedProfile(
416       android::perfprofd::RawPerfDataToAndroidPerfProfile(data_file_path, symbolizer));
417   if (encodedProfile == nullptr) {
418     return nullptr;
419   }
420
421   // All of the info in 'encodedProfile' is derived from the perf.data file;
422   // here we tack display status, cpu utilization, system load, etc.
423   annotate_encoded_perf_profile(encodedProfile.get(), config, cpu_utilization);
424
425   return encodedProfile;
426 }
427
428 PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
429                                const char *encoded_file_path,
430                                const Config& config,
431                                unsigned cpu_utilization,
432                                perfprofd::Symbolizer* symbolizer)
433 {
434   ProtoUniquePtr encodedProfile = encode_to_proto(data_file_path,
435                                                   config,
436                                                   cpu_utilization,
437                                                   symbolizer);
438
439   //
440   // Issue error if no samples
441   //
442   if (encodedProfile == nullptr || encodedProfile->perf_data().events_size() == 0) {
443     return ERR_PERF_ENCODE_FAILED;
444   }
445
446   return android::perfprofd::SerializeProtobuf(encodedProfile.get(),
447                                                encoded_file_path,
448                                                config.compress)
449       ? OK_PROFILE_COLLECTION
450       : ERR_WRITE_ENCODED_FILE_FAILED;
451 }
452
453 //
454 // Invoke "perf record". Return value is OK_PROFILE_COLLECTION for
455 // success, or some other error code if something went wrong.
456 //
457 static PROFILE_RESULT invoke_perf(Config& config,
458                                   const std::string &perf_path,
459                                   unsigned sampling_period,
460                                   const char *stack_profile_opt,
461                                   unsigned duration,
462                                   const std::string &data_file_path,
463                                   const std::string &perf_stderr_path)
464 {
465   pid_t pid = fork();
466
467   if (pid == -1) {
468     return ERR_FORK_FAILED;
469   }
470
471   if (pid == 0) {
472     // child
473
474     // Open file to receive stderr/stdout from perf
475     FILE *efp = fopen(perf_stderr_path.c_str(), "w");
476     if (efp) {
477       dup2(fileno(efp), STDERR_FILENO);
478       dup2(fileno(efp), STDOUT_FILENO);
479     } else {
480       PLOG(WARNING) << "unable to open " << perf_stderr_path << " for writing";
481     }
482
483     // marshall arguments
484     constexpr unsigned max_args = 15;
485     const char *argv[max_args];
486     unsigned slot = 0;
487     argv[slot++] = perf_path.c_str();
488     argv[slot++] = "record";
489
490     // -o perf.data
491     argv[slot++] = "-o";
492     argv[slot++] = data_file_path.c_str();
493
494     // -c/f N
495     std::string p_str;
496     if (config.sampling_frequency > 0) {
497       argv[slot++] = "-f";
498       p_str = android::base::StringPrintf("%u", sampling_period);
499       argv[slot++] = p_str.c_str();
500     } else if (config.sampling_period > 0) {
501       argv[slot++] = "-c";
502       p_str = android::base::StringPrintf("%u", sampling_period);
503       argv[slot++] = p_str.c_str();
504     }
505
506     // -g if desired
507     if (stack_profile_opt)
508       argv[slot++] = stack_profile_opt;
509
510     std::string pid_str;
511     if (config.process < 0) {
512       // system wide profiling
513       argv[slot++] = "-a";
514     } else {
515       argv[slot++] = "-p";
516       pid_str = std::to_string(config.process);
517       argv[slot++] = pid_str.c_str();
518     }
519
520     // no need for kernel or other symbols
521     argv[slot++] = "--no-dump-kernel-symbols";
522     argv[slot++] = "--no-dump-symbols";
523
524     // sleep <duration>
525     argv[slot++] = "--duration";
526     std::string d_str = android::base::StringPrintf("%u", duration);
527     argv[slot++] = d_str.c_str();
528
529     // terminator
530     argv[slot++] = nullptr;
531     assert(slot < max_args);
532
533     // record the final command line in the error output file for
534     // posterity/debugging purposes
535     fprintf(stderr, "perf invocation (pid=%d):\n", getpid());
536     for (unsigned i = 0; argv[i] != nullptr; ++i) {
537       fprintf(stderr, "%s%s", i ? " " : "", argv[i]);
538     }
539     fprintf(stderr, "\n");
540
541     // exec
542     execvp(argv[0], (char * const *)argv);
543     fprintf(stderr, "exec failed: %s\n", strerror(errno));
544     exit(1);
545
546   } else {
547     // parent
548
549     // Try to sleep.
550     config.Sleep(duration);
551
552     // We may have been woken up to stop profiling.
553     if (config.ShouldStopProfiling()) {
554       // Send SIGHUP to simpleperf to make it stop.
555       kill(pid, SIGHUP);
556     }
557
558     // Wait for the child, so it's reaped correctly.
559     int st = 0;
560     pid_t reaped = TEMP_FAILURE_RETRY(waitpid(pid, &st, 0));
561
562     if (reaped == -1) {
563       PLOG(WARNING) << "waitpid failed";
564     } else if (WIFSIGNALED(st)) {
565       if (WTERMSIG(st) == SIGHUP && config.ShouldStopProfiling()) {
566         // That was us...
567         return OK_PROFILE_COLLECTION;
568       }
569       LOG(WARNING) << "perf killed by signal " << WTERMSIG(st);
570     } else if (WEXITSTATUS(st) != 0) {
571       LOG(WARNING) << "perf bad exit status " << WEXITSTATUS(st);
572     } else {
573       return OK_PROFILE_COLLECTION;
574     }
575   }
576
577   return ERR_PERF_RECORD_FAILED;
578 }
579
580 //
581 // Remove all files in the destination directory during initialization
582 //
583 static void cleanup_destination_dir(const std::string& dest_dir)
584 {
585   DIR* dir = opendir(dest_dir.c_str());
586   if (dir != NULL) {
587     struct dirent* e;
588     while ((e = readdir(dir)) != 0) {
589       if (e->d_name[0] != '.') {
590         std::string file_path = dest_dir + "/" + e->d_name;
591         remove(file_path.c_str());
592       }
593     }
594     closedir(dir);
595   } else {
596     PLOG(WARNING) << "unable to open destination dir " << dest_dir << " for cleanup";
597   }
598 }
599
600 //
601 // Collect a perf profile. Steps for this operation are:
602 // - kick off 'perf record'
603 // - read perf.data, convert to protocol buf
604 //
605 static ProtoUniquePtr collect_profile(Config& config)
606 {
607   //
608   // Collect cpu utilization if enabled
609   //
610   unsigned cpu_utilization = 0;
611   if (config.collect_cpu_utilization) {
612     cpu_utilization = collect_cpu_utilization();
613   }
614
615   //
616   // Form perf.data file name, perf error output file name
617   //
618   const std::string& destdir = config.destination_directory;
619   std::string data_file_path(destdir);
620   data_file_path += "/";
621   data_file_path += PERF_OUTPUT;
622   std::string perf_stderr_path(destdir);
623   perf_stderr_path += "/perferr.txt";
624
625   //
626   // Remove any existing perf.data file -- if we don't do this, perf
627   // will rename the old file and we'll have extra cruft lying around.
628   //
629   struct stat statb;
630   if (stat(data_file_path.c_str(), &statb) == 0) { // if file exists...
631     if (unlink(data_file_path.c_str())) {          // then try to remove
632       PLOG(WARNING) << "unable to unlink previous perf.data file";
633     }
634   }
635
636   //
637   // The "mpdecision" daemon can cause problems for profile
638   // collection: if it decides to online a CPU partway through the
639   // 'perf record' run, the activity on that CPU will be invisible to
640   // perf, and if it offlines a CPU during the recording this can
641   // sometimes leave the PMU in an unusable state (dmesg errors of the
642   // form "perfevents: unable to request IRQXXX for ...").  To avoid
643   // these issues, if "mpdecision" is running the helper below will
644   // stop the service and then online all available CPUs. The object
645   // destructor (invoked when this routine terminates) will then
646   // restart the service again when needed.
647   //
648   uint32_t duration = config.sample_duration_in_s;
649   bool hardwire = config.hardwire_cpus;
650   uint32_t max_duration = config.hardwire_cpus_max_duration_in_s;
651   bool take_action = (hardwire && duration <= max_duration);
652   HardwireCpuHelper helper(take_action);
653
654   //
655   // Invoke perf
656   //
657   const char *stack_profile_opt =
658       (config.stack_profile ? "-g" : nullptr);
659   const std::string& perf_path = config.perf_path;
660   uint32_t period = config.sampling_period;
661
662   PROFILE_RESULT ret = invoke_perf(config,
663                                    perf_path.c_str(),
664                                    period,
665                                    stack_profile_opt,
666                                    duration,
667                                    data_file_path,
668                                    perf_stderr_path);
669   if (ret != OK_PROFILE_COLLECTION) {
670     return nullptr;
671   }
672
673   //
674   // Read the resulting perf.data file, encode into protocol buffer, then write
675   // the result to the file perf.data.encoded
676   //
677   std::unique_ptr<perfprofd::Symbolizer> symbolizer;
678   if (config.use_elf_symbolizer) {
679     symbolizer = perfprofd::CreateELFSymbolizer();
680   }
681   return encode_to_proto(data_file_path, config, cpu_utilization, symbolizer.get());
682 }
683
684 //
685 // Assuming that we want to collect a profile every N seconds,
686 // randomly partition N into two sub-intervals.
687 //
688 static void determine_before_after(unsigned &sleep_before_collect,
689                                    unsigned &sleep_after_collect,
690                                    unsigned collection_interval)
691 {
692   double frac = erand48(random_seed);
693   sleep_before_collect = (unsigned) (((double)collection_interval) * frac);
694   assert(sleep_before_collect <= collection_interval);
695   sleep_after_collect = collection_interval - sleep_before_collect;
696 }
697
698 //
699 // Set random number generator seed
700 //
701 static void set_seed(uint32_t use_fixed_seed)
702 {
703   unsigned seed = 0;
704   if (use_fixed_seed) {
705     //
706     // Use fixed user-specified seed
707     //
708     seed = use_fixed_seed;
709   } else {
710     //
711     // Randomized seed
712     //
713 #ifdef __BIONIC__
714     seed = arc4random();
715 #else
716     seed = 12345678u;
717 #endif
718   }
719   LOG(INFO) << "random seed set to " << seed;
720   // Distribute the 32-bit seed into the three 16-bit array
721   // elements. The specific values being written do not especially
722   // matter as long as we are setting them to something based on the seed.
723   random_seed[0] = seed & 0xffff;
724   random_seed[1] = (seed >> 16);
725   random_seed[2] = (random_seed[0] ^ random_seed[1]);
726 }
727
728 void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) {
729   // Children of init inherit an artificially low OOM score -- this is not
730   // desirable for perfprofd (its OOM score should be on par with
731   // other user processes).
732   std::stringstream oomscore_path;
733   oomscore_path << "/proc/" << getpid() << "/oom_score_adj";
734   if (!android::base::WriteStringToFile("0", oomscore_path.str())) {
735     LOG(ERROR) << "unable to write to " << oomscore_path.str();
736   }
737
738   set_seed(use_fixed_seed);
739   if (dest_dir != nullptr) {
740     cleanup_destination_dir(dest_dir);
741   }
742
743 #ifdef __BIONIC__
744   running_in_emulator = android::base::GetBoolProperty("ro.kernel.qemu", false);
745   is_debug_build = android::base::GetBoolProperty("ro.debuggable", false);
746 #else
747   running_in_emulator = false;
748   is_debug_build = true;
749 #endif
750
751   common_initialized = true;
752 }
753
754 bool IsDebugBuild() {
755   CHECK(common_initialized);
756   return is_debug_build;
757 }
758
759 template <typename ConfigFn, typename UpdateFn>
760 static void ProfilingLoopImpl(ConfigFn config, UpdateFn update, HandlerFn handler) {
761   unsigned iterations = 0;
762   while(config()->main_loop_iterations == 0 ||
763       iterations < config()->main_loop_iterations) {
764     if (config()->ShouldStopProfiling()) {
765       return;
766     }
767
768     // Figure out where in the collection interval we're going to actually
769     // run perf
770     unsigned sleep_before_collect = 0;
771     unsigned sleep_after_collect = 0;
772     determine_before_after(sleep_before_collect,
773                            sleep_after_collect,
774                            config()->collection_interval_in_s);
775     if (sleep_before_collect > 0) {
776       config()->Sleep(sleep_before_collect);
777     }
778
779     if (config()->ShouldStopProfiling()) {
780       return;
781     }
782
783     // Run any necessary updates.
784     update();
785
786     // Check for profiling enabled...
787     CKPROFILE_RESULT ckresult = check_profiling_enabled(*config());
788     if (ckresult != DO_COLLECT_PROFILE) {
789       LOG(INFO) << "profile collection skipped (" << ckprofile_result_to_string(ckresult) << ")";
790     } else {
791       // Kick off the profiling run...
792       LOG(INFO) << "initiating profile collection";
793       ProtoUniquePtr proto = collect_profile(*config());
794       if (proto == nullptr) {
795         LOG(WARNING) << "profile collection failed";
796       }
797
798       // Always report, even a null result.
799       bool handle_result = handler(proto.get(), config());
800       if (handle_result) {
801         LOG(INFO) << "profile collection complete";
802       } else if (proto != nullptr) {
803         LOG(WARNING) << "profile handling failed";
804       }
805     }
806
807     if (config()->ShouldStopProfiling()) {
808       return;
809     }
810
811     if (sleep_after_collect > 0) {
812       config()->Sleep(sleep_after_collect);
813     }
814     iterations += 1;
815   }
816 }
817
818 void ProfilingLoop(Config& config, HandlerFn handler) {
819   CommonInit(config.use_fixed_seed, nullptr);
820
821   auto config_fn = [&config]() {
822     return &config;;
823   };
824   auto do_nothing = []() {
825   };
826   ProfilingLoopImpl(config_fn, do_nothing, handler);
827 }
828
829 void ProfilingLoop(std::function<Config*()> config_fn,
830                    std::function<void()> update_fn,
831                    HandlerFn handler) {
832   ProfilingLoopImpl(config_fn, update_fn, handler);
833 }