blob: 03165ed5a6aeed2398d2f0645637d386fe0a0c5b [file] [log] [blame]
Igor Murashkinaaebaa02015-01-26 10:55:53 -08001/*
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#ifndef ART_CMDLINE_CMDLINE_TYPES_H_
17#define ART_CMDLINE_CMDLINE_TYPES_H_
18
19#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
20
21#include "cmdline/memory_representation.h"
22#include "cmdline/detail/cmdline_debug_detail.h"
23#include "cmdline_type_parser.h"
24
25// Includes for the types that are being specialized
26#include <string>
27#include "unit.h"
28#include "jdwp/jdwp.h"
29#include "runtime/base/logging.h"
30#include "gc/collector_type.h"
31#include "gc/space/large_object_space.h"
32#include "profiler_options.h"
33
34namespace art {
35
36// The default specialization will always fail parsing the type from a string.
37// Provide your own specialization that inherits from CmdlineTypeParser<T>
38// and implements either Parse or ParseAndAppend
39// (only if the argument was defined with ::AppendValues()) but not both.
40template <typename T>
41struct CmdlineType : CmdlineTypeParser<T> {
42};
43
44// Specializations for CmdlineType<T> follow:
45
46// Parse argument definitions for Unit-typed arguments.
47template <>
48struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
49 Result Parse(const std::string& args) {
50 if (args == "") {
51 return Result::Success(Unit{}); // NOLINT [whitespace/braces] [5]
52 }
53 return Result::Failure("Unexpected extra characters " + args);
54 }
55};
56
57template <>
58struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +010059 /*
60 * Handle one of the JDWP name/value pairs.
61 *
62 * JDWP options are:
63 * help: if specified, show help message and bail
64 * transport: may be dt_socket or dt_shmem
65 * address: for dt_socket, "host:port", or just "port" when listening
66 * server: if "y", wait for debugger to attach; if "n", attach to debugger
67 * timeout: how long to wait for debugger to connect / listen
68 *
69 * Useful with server=n (these aren't supported yet):
70 * onthrow=<exception-name>: connect to debugger when exception thrown
71 * onuncaught=y|n: connect to debugger when uncaught exception thrown
72 * launch=<command-line>: launch the debugger itself
73 *
74 * The "transport" option is required, as is "address" if server=n.
75 */
Igor Murashkinaaebaa02015-01-26 10:55:53 -080076 Result Parse(const std::string& options) {
77 VLOG(jdwp) << "ParseJdwpOptions: " << options;
78
79 if (options == "help") {
80 return Result::Usage(
81 "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
82 "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
83 }
84
85 const std::string s;
86
87 std::vector<std::string> pairs;
88 Split(options, ',', &pairs);
89
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +010090 JDWP::JdwpOptions jdwp_options;
Igor Murashkinaaebaa02015-01-26 10:55:53 -080091
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +010092 for (const std::string& jdwp_option : pairs) {
93 std::string::size_type equals_pos = jdwp_option.find('=');
94 if (equals_pos == std::string::npos) {
Igor Murashkinaaebaa02015-01-26 10:55:53 -080095 return Result::Failure(s +
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +010096 "Can't parse JDWP option '" + jdwp_option + "' in '" + options + "'");
Igor Murashkinaaebaa02015-01-26 10:55:53 -080097 }
98
Sebastien Hertzb3b173b2015-02-06 09:16:32 +010099 Result parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos),
100 jdwp_option.substr(equals_pos + 1),
101 &jdwp_options);
102 if (parse_attempt.IsError()) {
103 // We fail to parse this JDWP option.
104 return parse_attempt;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800105 }
106 }
107
108 if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) {
109 return Result::Failure(s + "Must specify JDWP transport: " + options);
110 }
111 if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) {
112 return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options);
113 }
114
115 return Result::Success(std::move(jdwp_options));
116 }
117
Sebastien Hertzb3b173b2015-02-06 09:16:32 +0100118 Result ParseJdwpOption(const std::string& name, const std::string& value,
119 JDWP::JdwpOptions* jdwp_options) {
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800120 if (name == "transport") {
121 if (value == "dt_socket") {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100122 jdwp_options->transport = JDWP::kJdwpTransportSocket;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800123 } else if (value == "dt_android_adb") {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100124 jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800125 } else {
Sebastien Hertzb3b173b2015-02-06 09:16:32 +0100126 return Result::Failure("JDWP transport not supported: " + value);
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800127 }
128 } else if (name == "server") {
129 if (value == "n") {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100130 jdwp_options->server = false;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800131 } else if (value == "y") {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100132 jdwp_options->server = true;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800133 } else {
Sebastien Hertzb3b173b2015-02-06 09:16:32 +0100134 return Result::Failure("JDWP option 'server' must be 'y' or 'n'");
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800135 }
136 } else if (name == "suspend") {
137 if (value == "n") {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100138 jdwp_options->suspend = false;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800139 } else if (value == "y") {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100140 jdwp_options->suspend = true;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800141 } else {
Sebastien Hertzb3b173b2015-02-06 09:16:32 +0100142 return Result::Failure("JDWP option 'suspend' must be 'y' or 'n'");
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800143 }
144 } else if (name == "address") {
145 /* this is either <port> or <host>:<port> */
146 std::string port_string;
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100147 jdwp_options->host.clear();
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800148 std::string::size_type colon = value.find(':');
149 if (colon != std::string::npos) {
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100150 jdwp_options->host = value.substr(0, colon);
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800151 port_string = value.substr(colon + 1);
152 } else {
153 port_string = value;
154 }
155 if (port_string.empty()) {
Sebastien Hertzb3b173b2015-02-06 09:16:32 +0100156 return Result::Failure("JDWP address missing port: " + value);
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800157 }
158 char* end;
159 uint64_t port = strtoul(port_string.c_str(), &end, 10);
160 if (*end != '\0' || port > 0xffff) {
Sebastien Hertzb3b173b2015-02-06 09:16:32 +0100161 return Result::Failure("JDWP address has junk in port field: " + value);
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800162 }
Sebastien Hertz3be6e9d2015-02-05 16:30:58 +0100163 jdwp_options->port = port;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800164 } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
165 /* valid but unsupported */
166 LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
167 } else {
168 LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
169 }
170
Sebastien Hertzb3b173b2015-02-06 09:16:32 +0100171 return Result::SuccessNoValue();
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800172 }
173
174 static const char* Name() { return "JdwpOptions"; }
175};
176
177template <size_t Divisor>
178struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
179 using typename CmdlineTypeParser<Memory<Divisor>>::Result;
180
181 Result Parse(const std::string arg) {
182 CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
183 size_t val = ParseMemoryOption(arg.c_str(), Divisor);
184 CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
185
186 if (val == 0) {
187 return Result::Failure(std::string("not a valid memory value, or not divisible by ")
188 + std::to_string(Divisor));
189 }
190
191 return Result::Success(Memory<Divisor>(val));
192 }
193
194 // Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
195 // memory sizes. [kK] indicates kilobytes, [mM] megabytes, and
196 // [gG] gigabytes.
197 //
198 // "s" should point just past the "-Xm?" part of the string.
199 // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
200 // of 1024.
201 //
202 // The spec says the -Xmx and -Xms options must be multiples of 1024. It
203 // doesn't say anything about -Xss.
204 //
205 // Returns 0 (a useless size) if "s" is malformed or specifies a low or
206 // non-evenly-divisible value.
207 //
208 static size_t ParseMemoryOption(const char* s, size_t div) {
209 // strtoul accepts a leading [+-], which we don't want,
210 // so make sure our string starts with a decimal digit.
211 if (isdigit(*s)) {
212 char* s2;
213 size_t val = strtoul(s, &s2, 10);
214 if (s2 != s) {
215 // s2 should be pointing just after the number.
216 // If this is the end of the string, the user
217 // has specified a number of bytes. Otherwise,
218 // there should be exactly one more character
219 // that specifies a multiplier.
220 if (*s2 != '\0') {
221 // The remainder of the string is either a single multiplier
222 // character, or nothing to indicate that the value is in
223 // bytes.
224 char c = *s2++;
225 if (*s2 == '\0') {
226 size_t mul;
227 if (c == '\0') {
228 mul = 1;
229 } else if (c == 'k' || c == 'K') {
230 mul = KB;
231 } else if (c == 'm' || c == 'M') {
232 mul = MB;
233 } else if (c == 'g' || c == 'G') {
234 mul = GB;
235 } else {
236 // Unknown multiplier character.
237 return 0;
238 }
239
240 if (val <= std::numeric_limits<size_t>::max() / mul) {
241 val *= mul;
242 } else {
243 // Clamp to a multiple of 1024.
244 val = std::numeric_limits<size_t>::max() & ~(1024-1);
245 }
246 } else {
247 // There's more than one character after the numeric part.
248 return 0;
249 }
250 }
251 // The man page says that a -Xm value must be a multiple of 1024.
252 if (val % div == 0) {
253 return val;
254 }
255 }
256 }
257 return 0;
258 }
259
260 static const char* Name() { return Memory<Divisor>::Name(); }
261};
262
263template <>
264struct CmdlineType<double> : CmdlineTypeParser<double> {
265 Result Parse(const std::string& str) {
266 char* end = nullptr;
267 errno = 0;
268 double value = strtod(str.c_str(), &end);
269
270 if (*end != '\0') {
271 return Result::Failure("Failed to parse double from " + str);
272 }
273 if (errno == ERANGE) {
274 return Result::OutOfRange(
275 "Failed to parse double from " + str + "; overflow/underflow occurred");
276 }
277
278 return Result::Success(value);
279 }
280
281 static const char* Name() { return "double"; }
282};
283
284template <>
285struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
286 Result Parse(const std::string& str) {
287 const char* begin = str.c_str();
288 char* end;
289
290 // Parse into a larger type (long long) because we can't use strtoul
291 // since it silently converts negative values into unsigned long and doesn't set errno.
292 errno = 0;
293 long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
294 if (begin == end || *end != '\0' || errno == EINVAL) {
295 return Result::Failure("Failed to parse integer from " + str);
296 } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
297 result < std::numeric_limits<int>::min()
298 || result > std::numeric_limits<unsigned int>::max() || result < 0) {
299 return Result::OutOfRange(
300 "Failed to parse integer from " + str + "; out of unsigned int range");
301 }
302
303 return Result::Success(static_cast<unsigned int>(result));
304 }
305
306 static const char* Name() { return "unsigned integer"; }
307};
308
309// Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
310// to nanoseconds automatically after parsing.
311//
312// All implicit conversion from uint64_t uses nanoseconds.
313struct MillisecondsToNanoseconds {
314 // Create from nanoseconds.
315 MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) { // NOLINT [runtime/explicit] [5]
316 }
317
318 // Create from milliseconds.
319 static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
320 return MillisecondsToNanoseconds(MsToNs(milliseconds));
321 }
322
323 // Get the underlying nanoseconds value.
324 uint64_t GetNanoseconds() const {
325 return nanoseconds_;
326 }
327
328 // Get the milliseconds value [via a conversion]. Loss of precision will occur.
329 uint64_t GetMilliseconds() const {
330 return NsToMs(nanoseconds_);
331 }
332
333 // Get the underlying nanoseconds value.
334 operator uint64_t() const {
335 return GetNanoseconds();
336 }
337
338 // Default constructors/copy-constructors.
339 MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
Andreas Gampec801f0d2015-02-24 20:55:16 -0800340 MillisecondsToNanoseconds(const MillisecondsToNanoseconds&) = default;
341 MillisecondsToNanoseconds(MillisecondsToNanoseconds&&) = default;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800342
343 private:
344 uint64_t nanoseconds_;
345};
346
347template <>
348struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
349 Result Parse(const std::string& str) {
350 CmdlineType<unsigned int> uint_parser;
351 CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
352
353 if (res.IsSuccess()) {
354 return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
355 } else {
356 return Result::CastError(res);
357 }
358 }
359
360 static const char* Name() { return "MillisecondsToNanoseconds"; }
361};
362
363template <>
364struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
365 Result Parse(const std::string& args) {
366 return Result::Success(args);
367 }
368
369 Result ParseAndAppend(const std::string& args,
370 std::string& existing_value) {
371 if (existing_value.empty()) {
372 existing_value = args;
373 } else {
374 existing_value += ' ';
375 existing_value += args;
376 }
377 return Result::SuccessNoValue();
378 }
379};
380
381template <>
382struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
383 Result Parse(const std::string& args) {
384 assert(false && "Use AppendValues() for a string vector type");
385 return Result::Failure("Unconditional failure: string vector must be appended: " + args);
386 }
387
388 Result ParseAndAppend(const std::string& args,
389 std::vector<std::string>& existing_value) {
390 existing_value.push_back(args);
391 return Result::SuccessNoValue();
392 }
393
394 static const char* Name() { return "std::vector<std::string>"; }
395};
396
397template <char Separator>
398struct ParseStringList {
399 explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
400
401 operator std::vector<std::string>() const {
402 return list_;
403 }
404
405 operator std::vector<std::string>&&() && {
406 return std::move(list_);
407 }
408
409 size_t Size() const {
410 return list_.size();
411 }
412
413 std::string Join() const {
414 return art::Join(list_, Separator);
415 }
416
417 static ParseStringList<Separator> Split(const std::string& str) {
418 std::vector<std::string> list;
419 art::Split(str, Separator, &list);
420 return ParseStringList<Separator>(std::move(list));
421 }
422
423 ParseStringList() = default;
Andreas Gampec801f0d2015-02-24 20:55:16 -0800424 ParseStringList(const ParseStringList&) = default;
425 ParseStringList(ParseStringList&&) = default;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800426
427 private:
428 std::vector<std::string> list_;
429};
430
431template <char Separator>
432struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
433 using Result = CmdlineParseResult<ParseStringList<Separator>>;
434
435 Result Parse(const std::string& args) {
436 return Result::Success(ParseStringList<Separator>::Split(args));
437 }
438
439 static const char* Name() { return "ParseStringList<Separator>"; }
440};
441
442static gc::CollectorType ParseCollectorType(const std::string& option) {
443 if (option == "MS" || option == "nonconcurrent") {
444 return gc::kCollectorTypeMS;
445 } else if (option == "CMS" || option == "concurrent") {
446 return gc::kCollectorTypeCMS;
447 } else if (option == "SS") {
448 return gc::kCollectorTypeSS;
449 } else if (option == "GSS") {
450 return gc::kCollectorTypeGSS;
451 } else if (option == "CC") {
452 return gc::kCollectorTypeCC;
453 } else if (option == "MC") {
454 return gc::kCollectorTypeMC;
455 } else {
456 return gc::kCollectorTypeNone;
457 }
458}
459
460struct XGcOption {
461 // These defaults are used when the command line arguments for -Xgc:
462 // are either omitted completely or partially.
463 gc::CollectorType collector_type_ = kUseReadBarrier ?
464 // If RB is enabled (currently a build-time decision),
465 // use CC as the default GC.
466 gc::kCollectorTypeCC :
467 gc::kCollectorTypeDefault;
468 bool verify_pre_gc_heap_ = false;
469 bool verify_pre_sweeping_heap_ = kIsDebugBuild;
470 bool verify_post_gc_heap_ = false;
471 bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
472 bool verify_pre_sweeping_rosalloc_ = false;
473 bool verify_post_gc_rosalloc_ = false;
474};
475
476template <>
477struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
478 Result Parse(const std::string& option) { // -Xgc: already stripped
479 XGcOption xgc{}; // NOLINT [readability/braces] [4]
480
481 std::vector<std::string> gc_options;
482 Split(option, ',', &gc_options);
483 for (const std::string& gc_option : gc_options) {
484 gc::CollectorType collector_type = ParseCollectorType(gc_option);
485 if (collector_type != gc::kCollectorTypeNone) {
486 xgc.collector_type_ = collector_type;
487 } else if (gc_option == "preverify") {
488 xgc.verify_pre_gc_heap_ = true;
489 } else if (gc_option == "nopreverify") {
490 xgc.verify_pre_gc_heap_ = false;
491 } else if (gc_option == "presweepingverify") {
492 xgc.verify_pre_sweeping_heap_ = true;
493 } else if (gc_option == "nopresweepingverify") {
494 xgc.verify_pre_sweeping_heap_ = false;
495 } else if (gc_option == "postverify") {
496 xgc.verify_post_gc_heap_ = true;
497 } else if (gc_option == "nopostverify") {
498 xgc.verify_post_gc_heap_ = false;
499 } else if (gc_option == "preverify_rosalloc") {
500 xgc.verify_pre_gc_rosalloc_ = true;
501 } else if (gc_option == "nopreverify_rosalloc") {
502 xgc.verify_pre_gc_rosalloc_ = false;
503 } else if (gc_option == "presweepingverify_rosalloc") {
504 xgc.verify_pre_sweeping_rosalloc_ = true;
505 } else if (gc_option == "nopresweepingverify_rosalloc") {
506 xgc.verify_pre_sweeping_rosalloc_ = false;
507 } else if (gc_option == "postverify_rosalloc") {
508 xgc.verify_post_gc_rosalloc_ = true;
509 } else if (gc_option == "nopostverify_rosalloc") {
510 xgc.verify_post_gc_rosalloc_ = false;
511 } else if ((gc_option == "precise") ||
512 (gc_option == "noprecise") ||
513 (gc_option == "verifycardtable") ||
514 (gc_option == "noverifycardtable")) {
515 // Ignored for backwards compatibility.
516 } else {
517 return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
518 }
519 }
520
521 return Result::Success(std::move(xgc));
522 }
523
524 static const char* Name() { return "XgcOption"; }
525};
526
527struct BackgroundGcOption {
528 // If background_collector_type_ is kCollectorTypeNone, it defaults to the
529 // XGcOption::collector_type_ after parsing options. If you set this to
530 // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
531 // we transition to background instead of a normal collector transition.
532 gc::CollectorType background_collector_type_;
533
534 BackgroundGcOption(gc::CollectorType background_collector_type) // NOLINT [runtime/explicit] [5]
535 : background_collector_type_(background_collector_type) {}
536 BackgroundGcOption()
537 : background_collector_type_(gc::kCollectorTypeNone) {
538
539 if (kUseReadBarrier) {
540 background_collector_type_ = gc::kCollectorTypeCC; // Disable background compaction for CC.
541 }
542 }
543
544 operator gc::CollectorType() const { return background_collector_type_; }
545};
546
547template<>
548struct CmdlineType<BackgroundGcOption>
549 : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
550 Result Parse(const std::string& substring) {
551 // Special handling for HSpaceCompact since this is only valid as a background GC type.
552 if (substring == "HSpaceCompact") {
553 background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
554 } else {
555 gc::CollectorType collector_type = ParseCollectorType(substring);
556 if (collector_type != gc::kCollectorTypeNone) {
557 background_collector_type_ = collector_type;
558 } else {
559 return Result::Failure();
560 }
561 }
562
563 BackgroundGcOption res = *this;
564 return Result::Success(res);
565 }
566
567 static const char* Name() { return "BackgroundGcOption"; }
568};
569
570template <>
571struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
572 Result Parse(const std::string& options) {
573 LogVerbosity log_verbosity = LogVerbosity();
574
575 std::vector<std::string> verbose_options;
576 Split(options, ',', &verbose_options);
577 for (size_t j = 0; j < verbose_options.size(); ++j) {
578 if (verbose_options[j] == "class") {
579 log_verbosity.class_linker = true;
580 } else if (verbose_options[j] == "compiler") {
581 log_verbosity.compiler = true;
582 } else if (verbose_options[j] == "gc") {
583 log_verbosity.gc = true;
584 } else if (verbose_options[j] == "heap") {
585 log_verbosity.heap = true;
586 } else if (verbose_options[j] == "jdwp") {
587 log_verbosity.jdwp = true;
Mathieu Chartiere5f13e52015-02-24 09:37:21 -0800588 } else if (verbose_options[j] == "jit") {
589 log_verbosity.jit = true;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800590 } else if (verbose_options[j] == "jni") {
591 log_verbosity.jni = true;
592 } else if (verbose_options[j] == "monitor") {
593 log_verbosity.monitor = true;
Richard Uhler66d874d2015-01-15 09:37:19 -0800594 } else if (verbose_options[j] == "oat") {
595 log_verbosity.oat = true;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800596 } else if (verbose_options[j] == "profiler") {
597 log_verbosity.profiler = true;
598 } else if (verbose_options[j] == "signals") {
599 log_verbosity.signals = true;
600 } else if (verbose_options[j] == "startup") {
601 log_verbosity.startup = true;
602 } else if (verbose_options[j] == "third-party-jni") {
603 log_verbosity.third_party_jni = true;
604 } else if (verbose_options[j] == "threads") {
605 log_verbosity.threads = true;
606 } else if (verbose_options[j] == "verifier") {
607 log_verbosity.verifier = true;
608 } else {
609 return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
610 }
611 }
612
613 return Result::Success(log_verbosity);
614 }
615
616 static const char* Name() { return "LogVerbosity"; }
617};
618
619// TODO: Replace with art::ProfilerOptions for the real thing.
620struct TestProfilerOptions {
621 // Whether or not the applications should be profiled.
622 bool enabled_;
623 // Destination file name where the profiling data will be saved into.
624 std::string output_file_name_;
625 // Generate profile every n seconds.
626 uint32_t period_s_;
627 // Run profile for n seconds.
628 uint32_t duration_s_;
629 // Microseconds between samples.
630 uint32_t interval_us_;
631 // Coefficient to exponential backoff.
632 double backoff_coefficient_;
633 // Whether the profile should start upon app startup or be delayed by some random offset.
634 bool start_immediately_;
635 // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
636 double top_k_threshold_;
637 // How much the top K% samples needs to change in order for the app to be recompiled.
638 double top_k_change_threshold_;
639 // The type of profile data dumped to the disk.
640 ProfileDataType profile_type_;
641 // The max depth of the stack collected by the profiler
642 uint32_t max_stack_depth_;
643
644 TestProfilerOptions() :
645 enabled_(false),
646 output_file_name_(),
647 period_s_(0),
648 duration_s_(0),
649 interval_us_(0),
650 backoff_coefficient_(0),
651 start_immediately_(0),
652 top_k_threshold_(0),
653 top_k_change_threshold_(0),
654 profile_type_(ProfileDataType::kProfilerMethod),
655 max_stack_depth_(0) {
656 }
657
Andreas Gampec801f0d2015-02-24 20:55:16 -0800658 TestProfilerOptions(const TestProfilerOptions&) = default;
659 TestProfilerOptions(TestProfilerOptions&&) = default;
Igor Murashkinaaebaa02015-01-26 10:55:53 -0800660};
661
662static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
663 stream << "TestProfilerOptions {" << std::endl;
664
665#define PRINT_TO_STREAM(field) \
666 stream << #field << ": '" << options.field << "'" << std::endl;
667
668 PRINT_TO_STREAM(enabled_);
669 PRINT_TO_STREAM(output_file_name_);
670 PRINT_TO_STREAM(period_s_);
671 PRINT_TO_STREAM(duration_s_);
672 PRINT_TO_STREAM(interval_us_);
673 PRINT_TO_STREAM(backoff_coefficient_);
674 PRINT_TO_STREAM(start_immediately_);
675 PRINT_TO_STREAM(top_k_threshold_);
676 PRINT_TO_STREAM(top_k_change_threshold_);
677 PRINT_TO_STREAM(profile_type_);
678 PRINT_TO_STREAM(max_stack_depth_);
679
680 stream << "}";
681
682 return stream;
683#undef PRINT_TO_STREAM
684}
685
686template <>
687struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
688 using Result = CmdlineParseResult<TestProfilerOptions>;
689
690 private:
691 using StringResult = CmdlineParseResult<std::string>;
692 using DoubleResult = CmdlineParseResult<double>;
693
694 template <typename T>
695 static Result ParseInto(TestProfilerOptions& options,
696 T TestProfilerOptions::*pField,
697 CmdlineParseResult<T>&& result) {
698 assert(pField != nullptr);
699
700 if (result.IsSuccess()) {
701 options.*pField = result.ReleaseValue();
702 return Result::SuccessNoValue();
703 }
704
705 return Result::CastError(result);
706 }
707
708 template <typename T>
709 static Result ParseIntoRangeCheck(TestProfilerOptions& options,
710 T TestProfilerOptions::*pField,
711 CmdlineParseResult<T>&& result,
712 T min,
713 T max) {
714 if (result.IsSuccess()) {
715 const T& value = result.GetValue();
716
717 if (value < min || value > max) {
718 CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
719 return Result::CastError(out_of_range);
720 }
721 }
722
723 return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
724 }
725
726 static StringResult ParseStringAfterChar(const std::string& s, char c) {
727 std::string parsed_value;
728
729 std::string::size_type colon = s.find(c);
730 if (colon == std::string::npos) {
731 return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
732 }
733 // Add one to remove the char we were trimming until.
734 parsed_value = s.substr(colon + 1);
735 return StringResult::Success(parsed_value);
736 }
737
738 static std::string RemovePrefix(const std::string& source) {
739 size_t prefix_idx = source.find(":");
740
741 if (prefix_idx == std::string::npos) {
742 return "";
743 }
744
745 return source.substr(prefix_idx + 1);
746 }
747
748 public:
749 Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
750 // Special case which doesn't include a wildcard argument definition.
751 // We pass-it through as-is.
752 if (option == "-Xenable-profiler") {
753 existing.enabled_ = true;
754 return Result::SuccessNoValue();
755 }
756
757 // The rest of these options are always the wildcard from '-Xprofile-*'
758 std::string suffix = RemovePrefix(option);
759
760 if (StartsWith(option, "filename:")) {
761 CmdlineType<std::string> type_parser;
762
763 return ParseInto(existing,
764 &TestProfilerOptions::output_file_name_,
765 type_parser.Parse(suffix));
766 } else if (StartsWith(option, "period:")) {
767 CmdlineType<unsigned int> type_parser;
768
769 return ParseInto(existing,
770 &TestProfilerOptions::period_s_,
771 type_parser.Parse(suffix));
772 } else if (StartsWith(option, "duration:")) {
773 CmdlineType<unsigned int> type_parser;
774
775 return ParseInto(existing,
776 &TestProfilerOptions::duration_s_,
777 type_parser.Parse(suffix));
778 } else if (StartsWith(option, "interval:")) {
779 CmdlineType<unsigned int> type_parser;
780
781 return ParseInto(existing,
782 &TestProfilerOptions::interval_us_,
783 type_parser.Parse(suffix));
784 } else if (StartsWith(option, "backoff:")) {
785 CmdlineType<double> type_parser;
786
787 return ParseIntoRangeCheck(existing,
788 &TestProfilerOptions::backoff_coefficient_,
789 type_parser.Parse(suffix),
790 1.0,
791 10.0);
792
793 } else if (option == "start-immediately") {
794 existing.start_immediately_ = true;
795 return Result::SuccessNoValue();
796 } else if (StartsWith(option, "top-k-threshold:")) {
797 CmdlineType<double> type_parser;
798
799 return ParseIntoRangeCheck(existing,
800 &TestProfilerOptions::top_k_threshold_,
801 type_parser.Parse(suffix),
802 0.0,
803 100.0);
804 } else if (StartsWith(option, "top-k-change-threshold:")) {
805 CmdlineType<double> type_parser;
806
807 return ParseIntoRangeCheck(existing,
808 &TestProfilerOptions::top_k_change_threshold_,
809 type_parser.Parse(suffix),
810 0.0,
811 100.0);
812 } else if (option == "type:method") {
813 existing.profile_type_ = kProfilerMethod;
814 return Result::SuccessNoValue();
815 } else if (option == "type:stack") {
816 existing.profile_type_ = kProfilerBoundedStack;
817 return Result::SuccessNoValue();
818 } else if (StartsWith(option, "max-stack-depth:")) {
819 CmdlineType<unsigned int> type_parser;
820
821 return ParseInto(existing,
822 &TestProfilerOptions::max_stack_depth_,
823 type_parser.Parse(suffix));
824 } else {
825 return Result::Failure(std::string("Invalid suboption '") + option + "'");
826 }
827 }
828
829 static const char* Name() { return "TestProfilerOptions"; }
830 static constexpr bool kCanParseBlankless = true;
831};
832
833
834} // namespace art
835#endif // ART_CMDLINE_CMDLINE_TYPES_H_