• 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 "perfprofd_cmdline.h"
19 
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 
29 #include <set>
30 #include <string>
31 
32 #include <android-base/logging.h>
33 #include <android-base/macros.h>
34 #include <android-base/stringprintf.h>
35 
36 #include "perfprofd_record.pb.h"
37 
38 #include "configreader.h"
39 #include "dropbox.h"
40 #include "perfprofdcore.h"
41 #include "perfprofd_io.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 // Path to the perf file to convert and exit? Empty value is the default, daemon mode.
60 //
61 static std::string perf_file_to_convert = "";
62 
63 //
64 // SIGHUP handler. Sending SIGHUP to the daemon can be used to break it
65 // out of a sleep() call so as to trigger a new collection (debugging)
66 //
sig_hup(int)67 static void sig_hup(int /* signum */)
68 {
69   LOG(WARNING) << "SIGHUP received";
70 }
71 
72 //
73 // Parse command line args. Currently supported flags:
74 // *  "-c PATH" sets the path of the config file to PATH.
75 // *  "-x PATH" reads PATH as a perf data file and saves it as a file in
76 //    perf_profile.proto format. ".encoded" suffix is appended to PATH to form
77 //    the output file path.
78 //
parse_args(int argc,char ** argv)79 static void parse_args(int argc, char** argv)
80 {
81   int ac;
82 
83   for (ac = 1; ac < argc; ++ac) {
84     if (!strcmp(argv[ac], "-c")) {
85       if (ac >= argc-1) {
86         LOG(ERROR) << "malformed command line: -c option requires argument)";
87         continue;
88       }
89       ConfigReader::setConfigFilePath(argv[ac+1]);
90       ++ac;
91     } else if (!strcmp(argv[ac], "-x")) {
92       if (ac >= argc-1) {
93         LOG(ERROR) << "malformed command line: -x option requires argument)";
94         continue;
95       }
96       perf_file_to_convert = argv[ac+1];
97       ++ac;
98     } else {
99       LOG(ERROR) << "malformed command line: unknown option or arg " <<  argv[ac] << ")";
100       continue;
101     }
102   }
103 }
104 
105 //
106 // Post-processes after profile is collected and converted to protobuf.
107 // * GMS core stores processed file sequence numbers in
108 //   /data/data/com.google.android.gms/files/perfprofd_processed.txt
109 // * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence
110 //   numbers that have been processed and append the current seq number
111 // Returns true if the current_seq should increment.
112 //
post_process(const Config & config,int current_seq)113 static bool post_process(const Config& config, int current_seq)
114 {
115   const std::string& dest_dir = config.destination_directory;
116   std::string processed_file_path =
117       config.config_directory + "/" + PROCESSED_FILENAME;
118   std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME;
119 
120 
121   std::set<int> processed;
122   FILE *fp = fopen(processed_file_path.c_str(), "r");
123   if (fp != NULL) {
124     int seq;
125     while(fscanf(fp, "%d\n", &seq) > 0) {
126       if (remove(android::base::StringPrintf(
127           "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) {
128         processed.insert(seq);
129       }
130     }
131     fclose(fp);
132   }
133 
134   std::set<int> produced;
135   fp = fopen(produced_file_path.c_str(), "r");
136   if (fp != NULL) {
137     int seq;
138     while(fscanf(fp, "%d\n", &seq) > 0) {
139       if (processed.find(seq) == processed.end()) {
140         produced.insert(seq);
141       }
142     }
143     fclose(fp);
144   }
145 
146   uint32_t maxLive = config.max_unprocessed_profiles;
147   if (produced.size() >= maxLive) {
148     return false;
149   }
150 
151   produced.insert(current_seq);
152   fp = fopen(produced_file_path.c_str(), "w");
153   if (fp == NULL) {
154     PLOG(WARNING) << "Cannot write " <<  produced_file_path;
155     return false;
156   }
157   for (std::set<int>::const_iterator iter = produced.begin();
158        iter != produced.end(); ++iter) {
159     fprintf(fp, "%d\n", *iter);
160   }
161   fclose(fp);
162   chmod(produced_file_path.c_str(),
163         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
164   return true;
165 }
166 
167 //
168 // Initialization
169 //
170 
init(ConfigReader & config)171 static void init(ConfigReader &config)
172 {
173   if (!config.readFile()) {
174     LOG(ERROR) << "unable to open configuration file " << config.getConfigFilePath();
175   }
176 
177   CommonInit(static_cast<uint32_t>(config.getUnsignedValue("use_fixed_seed")),
178              config.getStringValue("destination_directory").c_str());
179 
180   signal(SIGHUP, sig_hup);
181 }
182 
183 //
184 // Main routine:
185 // 1. parse cmd line args
186 // 2. read config file
187 // 3. loop: {
188 //       sleep for a while
189 //       perform a profile collection
190 //    }
191 //
perfprofd_main(int argc,char ** argv,Config * config)192 int perfprofd_main(int argc, char** argv, Config* config)
193 {
194   LOG(INFO) << "starting Android Wide Profiling daemon";
195 
196   parse_args(argc, argv);
197   {
198     ConfigReader config_reader;
199     init(config_reader);
200     config_reader.FillConfig(config);
201   }
202   GlobalInit(config->perf_path);
203 
204   if (!perf_file_to_convert.empty()) {
205     std::string encoded_path = perf_file_to_convert + ".encoded";
206     encode_to_proto(perf_file_to_convert, encoded_path.c_str(), *config, 0, nullptr);
207     return 0;
208   }
209 
210   // Early exit if we're not supposed to run on this build flavor
211   if (!IsDebugBuild() && config->only_debug_build) {
212     LOG(INFO) << "early exit due to inappropriate build type";
213     return 0;
214   }
215 
216   auto config_fn = [config]() {
217     return config;
218   };
219   auto reread_config = [config]() {
220     // Reread config file -- the uploader may have rewritten it.
221     ConfigReader config_reader;
222     if (config_reader.readFile()) {
223       config_reader.FillConfig(config);
224     }
225   };
226   int seq = 0;
227   auto handler = [&seq](android::perfprofd::PerfprofdRecord* proto, Config* handler_config) {
228     if (proto == nullptr) {
229       return false;
230     }
231     if (handler_config->send_to_dropbox) {
232       std::string error_msg;
233       if (!android::perfprofd::dropbox::SendToDropbox(proto,
234                                                       handler_config->destination_directory,
235                                                       &error_msg)) {
236         LOG(ERROR) << "Failed dropbox submission: " << error_msg;
237         return false;
238       }
239     } else {
240       std::string data_file_path(handler_config->destination_directory);
241       data_file_path += "/";
242       data_file_path += PERF_OUTPUT;
243       std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq);
244       if (!android::perfprofd::SerializeProtobuf(proto, path.c_str(), handler_config->compress)) {
245         return false;
246       }
247       if (!post_process(*handler_config, seq)) {
248         return false;
249       }
250     }
251     seq++;
252     return true;
253   };
254   ProfilingLoop(config_fn, reread_config, handler);
255 
256   LOG(INFO) << "finishing Android Wide Profiling daemon";
257   return 0;
258 }
259