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