OSDN Git Service

Dump kernel/modules/thread mmap information in `simpleperf record`.
[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 #include <string>
32 #include <sstream>
33 #include <map>
34 #include <cctype>
35
36 #include <cutils/properties.h>
37
38 #include "perfprofdcore.h"
39 #include "perfprofdutils.h"
40 #include "perf_data_converter.h"
41 #include "cpuconfig.h"
42
43 //
44 // Perf profiling daemon -- collects system-wide profiles using
45 //
46 //       simpleperf record -a
47 //
48 // and encodes them so that they can be uploaded by a separate service.
49 //
50
51 //......................................................................
52
53 //
54 // Output file from 'perf record'.
55 //
56 #define PERF_OUTPUT "perf.data"
57
58 //
59 // This enum holds the results of the "should we profile" configuration check.
60 //
61 typedef enum {
62
63   // All systems go for profile collection.
64   DO_COLLECT_PROFILE,
65
66   // The selected configuration directory doesn't exist.
67   DONT_PROFILE_MISSING_CONFIG_DIR,
68
69   // Destination directory does not contain the semaphore file that
70   // the perf profile uploading service creates when it determines
71   // that the user has opted "in" for usage data collection. No
72   // semaphore -> no user approval -> no profiling.
73   DONT_PROFILE_MISSING_SEMAPHORE,
74
75   // No perf executable present
76   DONT_PROFILE_MISSING_PERF_EXECUTABLE,
77
78   // We're running in the emulator, perf won't be able to do much
79   DONT_PROFILE_RUNNING_IN_EMULATOR
80
81 } CKPROFILE_RESULT;
82
83 //
84 // Are we running in the emulator? If so, stub out profile collection
85 // Starts as uninitialized (-1), then set to 1 or 0 at init time.
86 //
87 static int running_in_emulator = -1;
88
89 //
90 // Is this a debug build ('userdebug' or 'eng')?
91 // Starts as uninitialized (-1), then set to 1 or 0 at init time.
92 //
93 static int is_debug_build = -1;
94
95 //
96 // Random number generator seed (set at startup time).
97 //
98 static unsigned short random_seed[3];
99
100 //
101 // Config file path. May be overridden with -c command line option
102 //
103 static const char *config_file_path =
104     "/data/data/com.google.android.gms/files/perfprofd.conf";
105
106 //
107 // Set by SIGHUP signal handler
108 //
109 volatile unsigned please_reread_config_file = 0;
110
111 //
112 // This table describes the config file syntax in terms of key/value pairs.
113 // Values come in two flavors: strings, or unsigned integers. In the latter
114 // case the reader sets allowable minimum/maximum for the setting.
115 //
116 class ConfigReader {
117
118  public:
119   ConfigReader();
120   ~ConfigReader();
121
122   // Ask for the current setting of a config item
123   unsigned getUnsignedValue(const char *key) const;
124   std::string getStringValue(const char *key) const;
125
126   // read the specified config file, applying any settings it contains
127   void readFile();
128
129  private:
130   void addUnsignedEntry(const char *key,
131                         unsigned default_value,
132                         unsigned min_value,
133                         unsigned max_value);
134   void addStringEntry(const char *key, const char *default_value);
135   void addDefaultEntries();
136   void parseLine(const char *key, const char *value, unsigned linecount);
137
138   typedef struct { unsigned minv, maxv; } values;
139   std::map<std::string, values> u_info;
140   std::map<std::string, unsigned> u_entries;
141   std::map<std::string, std::string> s_entries;
142   bool trace_config_read;
143 };
144
145 ConfigReader::ConfigReader()
146     : trace_config_read(false)
147 {
148   addDefaultEntries();
149 }
150
151 ConfigReader::~ConfigReader()
152 {
153 }
154
155 //
156 // Populate the reader with the set of allowable entries
157 //
158 void ConfigReader::addDefaultEntries()
159 {
160   // Average number of seconds between perf profile collections (if
161   // set to 100, then over time we want to see a perf profile
162   // collected every 100 seconds). The actual time within the interval
163   // for the collection is chosen randomly.
164   addUnsignedEntry("collection_interval", 901, 100, UINT32_MAX);
165
166   // Use the specified fixed seed for random number generation (unit
167   // testing)
168   addUnsignedEntry("use_fixed_seed", 0, 0, UINT32_MAX);
169
170   // For testing purposes, number of times to iterate through main
171   // loop.  Value of zero indicates that we should loop forever.
172   addUnsignedEntry("main_loop_iterations", 0, 0, UINT32_MAX);
173
174   // Destination directory (where to write profiles). This location
175   // chosen since it is accessible to the uploader service.
176   addStringEntry("destination_directory", "/data/misc/perfprofd");
177
178   // Config directory (where to read configs).
179   addStringEntry("config_directory", "/data/data/com.google.android.gms/files");
180
181   // Full path to 'perf' executable.
182   addStringEntry("perf_path", "/system/xbin/simpleperf");
183
184   // Desired sampling period (passed to perf -c option). Small
185   // sampling periods can perturb the collected profiles, so enforce
186   // min/max.
187   addUnsignedEntry("sampling_period", 500000, 5000, UINT32_MAX);
188
189   // Length of time to collect samples (number of seconds for 'perf
190   // record -a' run).
191   addUnsignedEntry("sample_duration", 3, 2, 600);
192
193   // If this parameter is non-zero it will cause perfprofd to
194   // exit immediately if the build type is not userdebug or eng.
195   // Currently defaults to 1 (true).
196   addUnsignedEntry("only_debug_build", 1, 0, 1);
197
198   // If the "mpdecision" service is running at the point we are ready
199   // to kick off a profiling run, then temporarily disable the service
200   // and hard-wire all cores on prior to the collection run, provided
201   // that the duration of the recording is less than or equal to the value of
202   // 'hardwire_cpus_max_duration'.
203   addUnsignedEntry("hardwire_cpus", 1, 0, 1);
204   addUnsignedEntry("hardwire_cpus_max_duration", 5, 1, UINT32_MAX);
205
206   // If set to 1, pass the -g option when invoking 'perf' (requests
207   // stack traces as opposed to flat profile).
208   addUnsignedEntry("stack_profile", 0, 0, 1);
209
210   // For unit testing only: if set to 1, emit info messages on config
211   // file parsing.
212   addUnsignedEntry("trace_config_read", 0, 0, 1);
213 }
214
215 void ConfigReader::addUnsignedEntry(const char *key,
216                                     unsigned default_value,
217                                     unsigned min_value,
218                                     unsigned max_value)
219 {
220   std::string ks(key);
221   if (u_entries.find(ks) != u_entries.end() ||
222       s_entries.find(ks) != s_entries.end()) {
223     W_ALOGE("internal error -- duplicate entry for key %s", key);
224     exit(9);
225   }
226   values vals;
227   vals.minv = min_value;
228   vals.maxv = max_value;
229   u_info[ks] = vals;
230   u_entries[ks] = default_value;
231 }
232
233 void ConfigReader::addStringEntry(const char *key, const char *default_value)
234 {
235   std::string ks(key);
236   if (u_entries.find(ks) != u_entries.end() ||
237       s_entries.find(ks) != s_entries.end()) {
238     W_ALOGE("internal error -- duplicate entry for key %s", key);
239     exit(9);
240   }
241   if (default_value == nullptr) {
242     W_ALOGE("internal error -- bad default value for key %s", key);
243     exit(9);
244   }
245   s_entries[ks] = std::string(default_value);
246 }
247
248 unsigned ConfigReader::getUnsignedValue(const char *key) const
249 {
250   std::string ks(key);
251   auto it = u_entries.find(ks);
252   assert(it != u_entries.end());
253   return it->second;
254 }
255
256 std::string ConfigReader::getStringValue(const char *key) const
257 {
258   std::string ks(key);
259   auto it = s_entries.find(ks);
260   assert(it != s_entries.end());
261   return it->second;
262 }
263
264 //
265 // Parse a key=value pair read from the config file. This will issue
266 // warnings or errors to the system logs if the line can't be
267 // interpreted properly.
268 //
269 void ConfigReader::parseLine(const char *key,
270                              const char *value,
271                              unsigned linecount)
272 {
273   assert(key);
274   assert(value);
275
276   auto uit = u_entries.find(key);
277   if (uit != u_entries.end()) {
278     unsigned uvalue = 0;
279     if (isdigit(value[0]) == 0 || sscanf(value, "%u", &uvalue) != 1) {
280       W_ALOGW("line %d: malformed unsigned value (ignored)", linecount);
281     } else {
282       values vals;
283       auto iit = u_info.find(key);
284       assert(iit != u_info.end());
285       vals = iit->second;
286       if (uvalue < vals.minv || uvalue > vals.maxv) {
287         W_ALOGW("line %d: specified value %u for '%s' "
288                 "outside permitted range [%u %u] (ignored)",
289                 linecount, uvalue, key, vals.minv, vals.maxv);
290       } else {
291         if (trace_config_read) {
292           W_ALOGI("option %s set to %u", key, uvalue);
293         }
294         uit->second = uvalue;
295       }
296     }
297     trace_config_read = (getUnsignedValue("trace_config_read") != 0);
298     return;
299   }
300
301   auto sit = s_entries.find(key);
302   if (sit != s_entries.end()) {
303     if (trace_config_read) {
304       W_ALOGI("option %s set to %s", key, value);
305     }
306     sit->second = std::string(value);
307     return;
308   }
309
310   W_ALOGW("line %d: unknown option '%s' ignored", linecount, key);
311 }
312
313 static bool isblank(const std::string &line)
314 {
315   for (std::string::const_iterator it = line.begin(); it != line.end(); ++it)
316   {
317     if (isspace(*it) == 0) {
318       return false;
319     }
320   }
321   return true;
322 }
323
324 void ConfigReader::readFile()
325 {
326   FILE *fp = fopen(config_file_path, "r");
327   if (!fp) {
328     W_ALOGE("unable to open configuration file %s", config_file_path);
329     return;
330   }
331
332   char *linebuf = NULL;
333   size_t line_length = 0;
334   for (unsigned linecount = 1;
335        getline(&linebuf, &line_length, fp) != -1;
336        ++linecount) {
337     char *eq = 0;
338     char *key, *value;
339
340     // comment line?
341     if (linebuf[0] == '#') {
342       continue;
343     }
344
345     // blank line?
346     if (isblank(linebuf)) {
347       continue;
348     }
349
350     // look for X=Y assignment
351     eq = strchr(linebuf, '=');
352     if (!eq) {
353       W_ALOGW("line %d: line malformed (no '=' found)", linecount);
354       continue;
355     }
356
357     *eq = '\0';
358     key = linebuf;
359     value = eq+1;
360     char *ln = strrchr(value, '\n');
361     if (ln) { *ln = '\0'; }
362
363     parseLine(key, value, linecount);
364   }
365   free(linebuf);
366   fclose(fp);
367 }
368
369 //
370 // Parse command line args. Currently you can supply "-c P" to set
371 // the path of the config file to P.
372 //
373 static void parse_args(int argc, char** argv)
374 {
375   int ac;
376
377   for (ac = 1; ac < argc; ++ac) {
378     if (!strcmp(argv[ac], "-c")) {
379       if (ac >= argc-1) {
380         W_ALOGE("malformed command line: -c option requires argument)");
381         continue;
382       }
383       config_file_path = strdup(argv[ac+1]);
384       W_ALOGI("config file path set to %s", config_file_path);
385       ++ac;
386     } else {
387       W_ALOGE("malformed command line: unknown option or arg %s)", argv[ac]);
388       continue;
389     }
390   }
391 }
392
393 //
394 // Convert a CKPROFILE_RESULT to a string
395 //
396 const char *ckprofile_result_to_string(CKPROFILE_RESULT result)
397 {
398   switch (result) {
399     case DO_COLLECT_PROFILE:
400       return "DO_COLLECT_PROFILE";
401     case DONT_PROFILE_MISSING_CONFIG_DIR:
402       return "missing config directory";
403     case DONT_PROFILE_MISSING_SEMAPHORE:
404       return "missing semaphore file";
405     case DONT_PROFILE_MISSING_PERF_EXECUTABLE:
406       return "missing 'perf' executable";
407     case DONT_PROFILE_RUNNING_IN_EMULATOR:
408       return "running in emulator";
409     default: return "unknown";
410   }
411   return "notreached";
412 }
413
414 //
415 // Convert a PROFILE_RESULT to a string
416 //
417 const char *profile_result_to_string(PROFILE_RESULT result)
418 {
419   switch(result) {
420     case OK_PROFILE_COLLECTION:
421       return "profile collection succeeded";
422     case ERR_FORK_FAILED:
423       return "fork() system call failed";
424     case ERR_PERF_RECORD_FAILED:
425       return "perf record returned bad exit status";
426     case ERR_PERF_ENCODE_FAILED:
427       return "failure encoding perf.data to protobuf";
428     case ERR_OPEN_ENCODED_FILE_FAILED:
429       return "failed to open encoded perf file";
430     case ERR_WRITE_ENCODED_FILE_FAILED:
431       return "write to encoded perf file failed";
432     default: return "unknown";
433   }
434   return "notreached";
435 }
436
437 //
438 // Check to see whether we should perform a profile collection
439 //
440 static CKPROFILE_RESULT check_profiling_enabled(ConfigReader &config)
441 {
442   //
443   // Profile collection in the emulator doesn't make sense
444   //
445   assert(running_in_emulator != -1);
446   if (running_in_emulator) {
447     return DONT_PROFILE_RUNNING_IN_EMULATOR;
448   }
449
450   //
451   // Check for existence of semaphore file in config directory
452   //
453   if (access(config.getStringValue("config_directory").c_str(), F_OK) == -1) {
454     W_ALOGW("unable to open config directory %s: (%s)",
455             config.getStringValue("config_directory").c_str(), strerror(errno));
456     return DONT_PROFILE_MISSING_CONFIG_DIR;
457   }
458
459
460   // Check for existence of semaphore file
461   std::string semaphore_filepath = config.getStringValue("config_directory")
462                                    + "/" + SEMAPHORE_FILENAME;
463   if (access(semaphore_filepath.c_str(), F_OK) == -1) {
464     return DONT_PROFILE_MISSING_SEMAPHORE;
465   }
466
467   // Check for existence of simpleperf/perf executable
468   std::string pp = config.getStringValue("perf_path");
469   if (access(pp.c_str(), R_OK|X_OK) == -1) {
470     W_ALOGW("unable to access/execute %s", pp.c_str());
471     return DONT_PROFILE_MISSING_PERF_EXECUTABLE;
472   }
473
474   //
475   // We are good to go
476   //
477   return DO_COLLECT_PROFILE;
478 }
479
480 inline char* string_as_array(std::string* str) {
481   return str->empty() ? NULL : &*str->begin();
482 }
483
484 PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
485                                const std::string &encoded_file_path)
486 {
487   //
488   // Open and read perf.data file
489   //
490   const wireless_android_play_playlog::AndroidPerfProfile &encodedProfile =
491       wireless_android_logging_awp::RawPerfDataToAndroidPerfProfile(data_file_path);
492
493   //
494   // Issue error if no samples
495   //
496   if (encodedProfile.programs().size() == 0) {
497     return ERR_PERF_ENCODE_FAILED;
498   }
499
500   //
501   // Serialize protobuf to array
502   //
503   int size = encodedProfile.ByteSize();
504   std::string data;
505   data.resize(size);
506   ::google::protobuf::uint8* dtarget =
507         reinterpret_cast<::google::protobuf::uint8*>(string_as_array(&data));
508   encodedProfile.SerializeWithCachedSizesToArray(dtarget);
509
510   //
511   // Open file and write encoded data to it
512   //
513   FILE *fp = fopen(encoded_file_path.c_str(), "w");
514   if (!fp) {
515     return ERR_OPEN_ENCODED_FILE_FAILED;
516   }
517   size_t fsiz = size;
518   if (fwrite(dtarget, fsiz, 1, fp) != 1) {
519     fclose(fp);
520     return ERR_WRITE_ENCODED_FILE_FAILED;
521   }
522   fclose(fp);
523   chmod(encoded_file_path.c_str(),
524         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
525
526   return OK_PROFILE_COLLECTION;
527 }
528
529 //
530 // Invoke "perf record". Return value is OK_PROFILE_COLLECTION for
531 // success, or some other error code if something went wrong.
532 //
533 static PROFILE_RESULT invoke_perf(const std::string &perf_path,
534                                   unsigned sampling_period,
535                                   const char *stack_profile_opt,
536                                   unsigned duration,
537                                   const std::string &data_file_path,
538                                   const std::string &perf_stderr_path)
539 {
540   pid_t pid = fork();
541
542   if (pid == -1) {
543     return ERR_FORK_FAILED;
544   }
545
546   if (pid == 0) {
547     // child
548
549     // Open file to receive stderr/stdout from perf
550     FILE *efp = fopen(perf_stderr_path.c_str(), "w");
551     if (efp) {
552       dup2(fileno(efp), STDERR_FILENO);
553       dup2(fileno(efp), STDOUT_FILENO);
554     } else {
555       W_ALOGW("unable to open %s for writing", perf_stderr_path.c_str());
556     }
557
558     // marshall arguments
559     constexpr unsigned max_args = 12;
560     const char *argv[max_args];
561     unsigned slot = 0;
562     argv[slot++] = perf_path.c_str();
563     argv[slot++] = "record";
564
565     // -o perf.data
566     argv[slot++] = "-o";
567     argv[slot++] = data_file_path.c_str();
568
569     // -c N
570     argv[slot++] = "-c";
571     char pbuf[64]; snprintf(pbuf, 64, "%u", sampling_period);
572     argv[slot++] = pbuf;
573
574     // -g if desired
575     if (stack_profile_opt)
576       argv[slot++] = stack_profile_opt;
577
578     // system wide profiling
579     argv[slot++] = "-a";
580
581     // sleep <duration>
582     argv[slot++] = "/system/bin/sleep";
583     char dbuf[64]; snprintf(dbuf, 64, "%u", duration);
584     argv[slot++] = dbuf;
585
586     // terminator
587     argv[slot++] = nullptr;
588     assert(slot < max_args);
589
590     // record the final command line in the error output file for
591     // posterity/debugging purposes
592     fprintf(stderr, "perf invocation (pid=%d):\n", getpid());
593     for (unsigned i = 0; argv[i] != nullptr; ++i) {
594       fprintf(stderr, "%s%s", i ? " " : "", argv[i]);
595     }
596     fprintf(stderr, "\n");
597
598     // exec
599     execvp(argv[0], (char * const *)argv);
600     fprintf(stderr, "exec failed: %s\n", strerror(errno));
601     exit(1);
602
603   } else {
604     // parent
605     int st = 0;
606     pid_t reaped = TEMP_FAILURE_RETRY(waitpid(pid, &st, 0));
607
608     if (reaped == -1) {
609       W_ALOGW("waitpid failed: %s", strerror(errno));
610     } else if (WIFSIGNALED(st)) {
611       W_ALOGW("perf killed by signal %d", WTERMSIG(st));
612     } else if (WEXITSTATUS(st) != 0) {
613       W_ALOGW("perf bad exit status %d", WEXITSTATUS(st));
614     } else {
615       return OK_PROFILE_COLLECTION;
616     }
617   }
618
619   return ERR_PERF_RECORD_FAILED;
620 }
621
622 //
623 // Collect a perf profile. Steps for this operation are:
624 // - kick off 'perf record'
625 // - read perf.data, convert to protocol buf
626 //
627 static PROFILE_RESULT collect_profile(ConfigReader &config)
628 {
629   //
630   // Form perf.data file name, perf error output file name
631   //
632   std::string destdir = config.getStringValue("destination_directory");
633   std::string data_file_path(destdir);
634   data_file_path += "/";
635   data_file_path += PERF_OUTPUT;
636   std::string perf_stderr_path(destdir);
637   perf_stderr_path += "/perferr.txt";
638
639   //
640   // Remove any existing perf.data file -- if we don't do this, perf
641   // will rename the old file and we'll have extra cruft lying around.
642   //
643   struct stat statb;
644   if (stat(data_file_path.c_str(), &statb) == 0) { // if file exists...
645     if (unlink(data_file_path.c_str())) {          // then try to remove
646       W_ALOGW("unable to unlink previous perf.data file");
647     }
648   }
649
650   //
651   // The "mpdecision" daemon can cause problems for profile
652   // collection: if it decides to online a CPU partway through the
653   // 'perf record' run, the activity on that CPU will be invisible to
654   // perf, and if it offlines a CPU during the recording this can
655   // sometimes leave the PMU in an unusable state (dmesg errors of the
656   // form "perfevents: unable to request IRQXXX for ...").  To avoid
657   // these issues, if "mpdecision" is running the helper below will
658   // stop the service and then online all available CPUs. The object
659   // destructor (invoked when this routine terminates) will then
660   // restart the service again when needed.
661   //
662   unsigned duration = config.getUnsignedValue("sample_duration");
663   unsigned hardwire = config.getUnsignedValue("hardwire_cpus");
664   unsigned max_duration = config.getUnsignedValue("hardwire_cpus_max_duration");
665   bool take_action = (hardwire && duration <= max_duration);
666   HardwireCpuHelper helper(take_action);
667
668   //
669   // Invoke perf
670   //
671   const char *stack_profile_opt =
672       (config.getUnsignedValue("stack_profile") != 0 ? "-g" : nullptr);
673   std::string perf_path = config.getStringValue("perf_path");
674   unsigned period = config.getUnsignedValue("sampling_period");
675
676   PROFILE_RESULT ret = invoke_perf(perf_path.c_str(),
677                                   period,
678                                   stack_profile_opt,
679                                   duration,
680                                   data_file_path,
681                                   perf_stderr_path);
682   if (ret != OK_PROFILE_COLLECTION) {
683     return ret;
684   }
685
686   //
687   // Read the resulting perf.data file, encode into protocol buffer, then write
688   // the result to the file perf.data.encoded
689   //
690   std::string encoded_file_path(data_file_path);
691   encoded_file_path += ".encoded";
692   return encode_to_proto(data_file_path, encoded_file_path);
693 }
694
695 //
696 // SIGHUP handler. Sets a flag to indicate that we should reread the
697 // config file
698 //
699 static void sig_hup(int /* signum */)
700 {
701   please_reread_config_file = 1;
702 }
703
704 //
705 // Assuming that we want to collect a profile every N seconds,
706 // randomly partition N into two sub-intervals.
707 //
708 static void determine_before_after(unsigned &sleep_before_collect,
709                                    unsigned &sleep_after_collect,
710                                    unsigned collection_interval)
711 {
712   double frac = erand48(random_seed);
713   sleep_before_collect = (unsigned) (((double)collection_interval) * frac);
714   assert(sleep_before_collect <= collection_interval);
715   sleep_after_collect = collection_interval - sleep_before_collect;
716 }
717
718 //
719 // Set random number generator seed
720 //
721 static void set_seed(ConfigReader &config)
722 {
723   unsigned seed = 0;
724   unsigned use_fixed_seed = config.getUnsignedValue("use_fixed_seed");
725   if (use_fixed_seed) {
726     //
727     // Use fixed user-specified seed
728     //
729     seed = use_fixed_seed;
730   } else {
731     //
732     // Randomized seed
733     //
734     seed = arc4random();
735   }
736   W_ALOGI("random seed set to %u", seed);
737   // Distribute the 32-bit seed into the three 16-bit array
738   // elements. The specific values being written do not especially
739   // matter as long as we are setting them to something based on the seed.
740   random_seed[0] = seed & 0xffff;
741   random_seed[1] = (seed >> 16);
742   random_seed[2] = (random_seed[0] ^ random_seed[1]);
743 }
744
745 //
746 // Initialization
747 //
748 static void init(ConfigReader &config)
749 {
750   config.readFile();
751   set_seed(config);
752
753   char propBuf[PROPERTY_VALUE_MAX];
754   propBuf[0] = '\0';
755   property_get("ro.kernel.qemu", propBuf, "");
756   running_in_emulator = (propBuf[0] == '1');
757   property_get("ro.debuggable", propBuf, "");
758   is_debug_build = (propBuf[0] == '1');
759
760   signal(SIGHUP, sig_hup);
761 }
762
763 //
764 // Main routine:
765 // 1. parse cmd line args
766 // 2. read config file
767 // 3. loop: {
768 //       sleep for a while
769 //       perform a profile collection
770 //    }
771 //
772 int perfprofd_main(int argc, char** argv)
773 {
774   ConfigReader config;
775
776   W_ALOGI("starting Android Wide Profiling daemon");
777
778   parse_args(argc, argv);
779   init(config);
780
781   // Early exit if we're not supposed to run on this build flavor
782   if (is_debug_build != 1 &&
783       config.getUnsignedValue("only_debug_build") == 1) {
784     W_ALOGI("early exit due to inappropriate build type");
785     return 0;
786   }
787
788   unsigned iterations = 0;
789   while(config.getUnsignedValue("main_loop_iterations") == 0 ||
790         iterations < config.getUnsignedValue("main_loop_iterations")) {
791
792     // Figure out where in the collection interval we're going to actually
793     // run perf
794     unsigned sleep_before_collect = 0;
795     unsigned sleep_after_collect = 0;
796     determine_before_after(sleep_before_collect, sleep_after_collect,
797                            config.getUnsignedValue("collection_interval"));
798     perfprofd_sleep(sleep_before_collect);
799
800     // Reread config file if someone sent a SIGHUP
801     if (please_reread_config_file) {
802       config.readFile();
803       please_reread_config_file = 0;
804     }
805
806     // Check for profiling enabled...
807     CKPROFILE_RESULT ckresult = check_profiling_enabled(config);
808     if (ckresult != DO_COLLECT_PROFILE) {
809       W_ALOGI("profile collection skipped (%s)",
810               ckprofile_result_to_string(ckresult));
811     } else {
812       // Kick off the profiling run...
813       W_ALOGI("initiating profile collection");
814       PROFILE_RESULT result = collect_profile(config);
815       if (result != OK_PROFILE_COLLECTION) {
816         W_ALOGI("profile collection failed (%s)",
817                 profile_result_to_string(result));
818       } else {
819         W_ALOGI("profile collection complete");
820       }
821     }
822     perfprofd_sleep(sleep_after_collect);
823     iterations += 1;
824   }
825
826   W_ALOGI("finishing Android Wide Profiling daemon");
827   return 0;
828 }