• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "command.h"
18 
19 #include <string.h>
20 
21 #include <algorithm>
22 #include <map>
23 #include <string>
24 #include <vector>
25 
26 #include <android-base/logging.h>
27 #include <android-base/parsedouble.h>
28 #include <android-base/parseint.h>
29 
30 #include "utils.h"
31 
32 namespace simpleperf {
33 
NextArgumentOrError(const std::vector<std::string> & args,size_t * pi)34 bool Command::NextArgumentOrError(const std::vector<std::string>& args, size_t* pi) {
35   if (*pi + 1 == args.size()) {
36     LOG(ERROR) << "No argument following " << args[*pi] << " option. Try `simpleperf help " << name_
37                << "`";
38     return false;
39   }
40   ++*pi;
41   return true;
42 }
43 
ConvertArgsToOptions(const std::vector<std::string> & args,const OptionFormatMap & option_formats,const std::string & help_msg,OptionValueMap * options,std::vector<std::pair<OptionName,OptionValue>> * ordered_options,std::vector<std::string> * non_option_args)44 bool ConvertArgsToOptions(const std::vector<std::string>& args,
45                           const OptionFormatMap& option_formats, const std::string& help_msg,
46                           OptionValueMap* options,
47                           std::vector<std::pair<OptionName, OptionValue>>* ordered_options,
48                           std::vector<std::string>* non_option_args) {
49   options->values.clear();
50   ordered_options->clear();
51   size_t i;
52   for (i = 0; i < args.size() && !args[i].empty() && args[i][0] == '-'; i++) {
53     std::string value_after_equal;
54     auto it = option_formats.find(args[i]);
55     if (it == option_formats.end()) {
56       if (auto pos = args[i].find("="); pos != std::string::npos) {
57         value_after_equal = args[i].substr(pos + 1);
58         it = option_formats.find(args[i].substr(0, pos));
59       }
60       if (it == option_formats.end()) {
61         if (args[i] == "--") {
62           i++;
63           break;
64         }
65         LOG(ERROR) << "Unknown option " << args[i] << "." << help_msg;
66         return false;
67       }
68     }
69     const OptionName& name = it->first;
70     const OptionFormat& format = it->second;
71     OptionValue value;
72 
73     switch (format.value_type) {
74       case OptionValueType::NONE:
75         break;
76       case OptionValueType::STRING:
77         if (i + 1 == args.size()) {
78           LOG(ERROR) << "No argument following " << name << " option." << help_msg;
79           return false;
80         }
81         value.str_value = args[++i];
82         break;
83       case OptionValueType::OPT_STRING:
84         if (i + 1 < args.size() && !args[i + 1].empty() && args[i + 1][0] != '-') {
85           value.str_value = args[++i];
86         }
87         break;
88       case OptionValueType::OPT_STRING_AFTER_EQUAL:
89         value.str_value = value_after_equal;
90         break;
91       case OptionValueType::UINT:
92         if (i + 1 == args.size()) {
93           LOG(ERROR) << "No argument following " << name << " option." << help_msg;
94           return false;
95         }
96         if (!android::base::ParseUint(args[++i], &value.uint_value,
97                                       std::numeric_limits<uint64_t>::max(), true)) {
98           LOG(ERROR) << "Invalid argument for option " << name << ": " << args[i] << "."
99                      << help_msg;
100           return false;
101         }
102         break;
103       case OptionValueType::DOUBLE:
104         if (i + 1 == args.size()) {
105           LOG(ERROR) << "No argument following " << name << " option." << help_msg;
106           return false;
107         }
108         if (!android::base::ParseDouble(args[++i], &value.double_value)) {
109           LOG(ERROR) << "Invalid argument for option " << name << ": " << args[i] << "."
110                      << help_msg;
111           return false;
112         }
113         break;
114     }
115 
116     switch (format.type) {
117       case OptionType::SINGLE:
118         if (auto it = options->values.find(name); it != options->values.end()) {
119           it->second = value;
120         } else {
121           options->values.emplace(name, value);
122         }
123         break;
124       case OptionType::MULTIPLE:
125         options->values.emplace(name, value);
126         break;
127       case OptionType::ORDERED:
128         ordered_options->emplace_back(name, value);
129         break;
130     }
131   }
132   if (i < args.size()) {
133     if (non_option_args == nullptr) {
134       LOG(ERROR) << "Invalid option " << args[i] << "." << help_msg;
135       return false;
136     }
137     non_option_args->assign(args.begin() + i, args.end());
138   }
139   return true;
140 }
141 
PreprocessOptions(const std::vector<std::string> & args,const OptionFormatMap & option_formats,OptionValueMap * options,std::vector<std::pair<OptionName,OptionValue>> * ordered_options,std::vector<std::string> * non_option_args)142 bool Command::PreprocessOptions(const std::vector<std::string>& args,
143                                 const OptionFormatMap& option_formats, OptionValueMap* options,
144                                 std::vector<std::pair<OptionName, OptionValue>>* ordered_options,
145                                 std::vector<std::string>* non_option_args) {
146   const std::string help_msg = " Try `simpleperf help " + name_ + "`.";
147   return ConvertArgsToOptions(args, option_formats, help_msg, options, ordered_options,
148                               non_option_args);
149 }
150 
GetDoubleOption(const std::vector<std::string> & args,size_t * pi,double * value,double min,double max)151 bool Command::GetDoubleOption(const std::vector<std::string>& args, size_t* pi, double* value,
152                               double min, double max) {
153   if (!NextArgumentOrError(args, pi)) {
154     return false;
155   }
156   if (!android::base::ParseDouble(args[*pi].c_str(), value, min, max)) {
157     LOG(ERROR) << "Invalid argument for option " << args[*pi - 1] << ": " << args[*pi];
158     return false;
159   }
160   return true;
161 }
162 
ReportUnknownOption(const std::vector<std::string> & args,size_t i)163 void Command::ReportUnknownOption(const std::vector<std::string>& args, size_t i) {
164   LOG(ERROR) << "Unknown option for " << name_ << " command: '" << args[i]
165              << "'. Try `simpleperf help " << name_ << "`";
166 }
167 
168 typedef std::function<std::unique_ptr<Command>(void)> callback_t;
169 
CommandMap()170 static std::map<std::string, callback_t>& CommandMap() {
171   // commands is used in the constructor of Command. Defining it as a static
172   // variable in a function makes sure it is initialized before use.
173   static std::map<std::string, callback_t> command_map;
174   return command_map;
175 }
176 
RegisterCommand(const std::string & cmd_name,const std::function<std::unique_ptr<Command> (void)> & callback)177 void RegisterCommand(const std::string& cmd_name,
178                      const std::function<std::unique_ptr<Command>(void)>& callback) {
179   CommandMap().insert(std::make_pair(cmd_name, callback));
180 }
181 
UnRegisterCommand(const std::string & cmd_name)182 void UnRegisterCommand(const std::string& cmd_name) {
183   CommandMap().erase(cmd_name);
184 }
185 
CreateCommandInstance(const std::string & cmd_name)186 std::unique_ptr<Command> CreateCommandInstance(const std::string& cmd_name) {
187   auto it = CommandMap().find(cmd_name);
188   return (it == CommandMap().end()) ? nullptr : (it->second)();
189 }
190 
GetAllCommandNames()191 const std::vector<std::string> GetAllCommandNames() {
192   std::vector<std::string> names;
193   for (const auto& pair : CommandMap()) {
194     names.push_back(pair.first);
195   }
196   return names;
197 }
198 
RegisterAllCommands()199 void RegisterAllCommands() {
200   RegisterDumpRecordCommand();
201   RegisterHelpCommand();
202   RegisterInjectCommand();
203   RegisterKmemCommand();
204   RegisterMergeCommand();
205   RegisterReportCommand();
206   RegisterReportSampleCommand();
207 #if defined(__linux__)
208   RegisterListCommand();
209   RegisterRecordCommand();
210   RegisterStatCommand();
211   RegisterDebugUnwindCommand();
212   RegisterTraceSchedCommand();
213   RegisterMonitorCommand();
214 #if defined(__ANDROID__)
215   RegisterAPICommands();
216   RegisterBootRecordCommand();
217 #endif
218 #endif
219 }
220 
StderrLogger(android::base::LogId,android::base::LogSeverity severity,const char *,const char * file,unsigned int line,const char * message)221 static void StderrLogger(android::base::LogId, android::base::LogSeverity severity, const char*,
222                          const char* file, unsigned int line, const char* message) {
223   static const char log_characters[] = "VDIWEFF";
224   char severity_char = log_characters[severity];
225   fprintf(stderr, "simpleperf %c %s:%u] %s\n", severity_char, file, line, message);
226 }
227 
228 bool log_to_android_buffer = false;
229 
RunSimpleperfCmd(int argc,char ** argv)230 bool RunSimpleperfCmd(int argc, char** argv) {
231   android::base::InitLogging(argv, StderrLogger);
232   std::vector<std::string> args;
233   android::base::LogSeverity log_severity = android::base::INFO;
234   log_to_android_buffer = false;
235   const OptionFormatMap& common_option_formats = GetCommonOptionFormatMap();
236 
237   int i;
238   for (i = 1; i < argc && strcmp(argv[i], "--") != 0; ++i) {
239     std::string option_name = argv[i];
240     auto it = common_option_formats.find(option_name);
241     if (it == common_option_formats.end()) {
242       args.emplace_back(std::move(option_name));
243       continue;
244     }
245     if (it->second.value_type != OptionValueType::NONE && i + 1 == argc) {
246       LOG(ERROR) << "Missing argument for " << option_name;
247       return false;
248     }
249     if (option_name == "-h" || option_name == "--help") {
250       args.insert(args.begin(), "help");
251     } else if (option_name == "--log") {
252       if (!GetLogSeverity(argv[i + 1], &log_severity)) {
253         LOG(ERROR) << "Unknown log severity: " << argv[i + 1];
254       }
255       ++i;
256 #if defined(__ANDROID__)
257     } else if (option_name == "--log-to-android-buffer") {
258       android::base::SetLogger(android::base::LogdLogger());
259       log_to_android_buffer = true;
260 #endif
261     } else if (option_name == "--version") {
262       LOG(INFO) << "Simpleperf version " << GetSimpleperfVersion();
263       return true;
264     } else {
265       CHECK(false) << "Unreachable code";
266     }
267   }
268   while (i < argc) {
269     args.emplace_back(argv[i++]);
270   }
271 
272   android::base::ScopedLogSeverity severity(log_severity);
273   if (log_severity == android::base::VERBOSE) {
274     // If verbose, use android::base::StderrLogger to add time info.
275     android::base::SetLogger(android::base::StderrLogger);
276   }
277 
278   if (args.empty()) {
279     args.push_back("help");
280   }
281   std::unique_ptr<Command> command = CreateCommandInstance(args[0]);
282   if (command == nullptr) {
283     LOG(ERROR) << "malformed command line: unknown command " << args[0];
284     return false;
285   }
286   std::string command_name = args[0];
287   args.erase(args.begin());
288 
289   LOG(DEBUG) << "command '" << command_name << "' starts running";
290   int exit_code;
291   command->Run(args, &exit_code);
292   LOG(DEBUG) << "command '" << command_name << "' "
293              << (exit_code == 0 ? "finished successfully" : "failed");
294   // Quick exit to avoid the cost of freeing memory and closing files.
295   fflush(stdout);
296   fflush(stderr);
297   _Exit(exit_code);
298   return exit_code == 0;
299 }
300 
301 }  // namespace simpleperf
302