1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/flags.h" |
6 | |
7 | #include "platform/assert.h" |
8 | #include "vm/json_stream.h" |
9 | #include "vm/os.h" |
10 | |
11 | namespace dart { |
12 | |
13 | DEFINE_FLAG(bool, print_flags, false, "Print flags as they are being parsed." ); |
14 | DEFINE_FLAG(bool, |
15 | ignore_unrecognized_flags, |
16 | false, |
17 | "Ignore unrecognized flags." ); |
18 | |
19 | #define PRODUCT_FLAG_MACRO(name, type, default_value, comment) \ |
20 | type FLAG_##name = \ |
21 | Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
22 | |
23 | #if defined(DEBUG) |
24 | #define DEBUG_FLAG_MACRO(name, type, default_value, comment) \ |
25 | type FLAG_##name = \ |
26 | Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
27 | #else // defined(DEBUG) |
28 | #define DEBUG_FLAG_MACRO(name, type, default_value, comment) |
29 | #endif // defined(DEBUG) |
30 | |
31 | #if defined(PRODUCT) && defined(DART_PRECOMPILED_RUNTIME) |
32 | // Nothing to be done for the product flag definitions. |
33 | #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) |
34 | // Nothing to be done for the precompilation flag definitions. |
35 | #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
36 | default_value, comment) |
37 | |
38 | #elif defined(PRODUCT) // !PRECOMPILED |
39 | // Nothing to be done for the product flag definitions. |
40 | #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) |
41 | // Nothing to be done for the precompilation flag definitions. |
42 | #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
43 | default_value, comment) |
44 | |
45 | #elif defined(DART_PRECOMPILED_RUNTIME) // !PRODUCT |
46 | #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \ |
47 | type FLAG_##name = \ |
48 | Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
49 | // Nothing to be done for the precompilation flag definitions. |
50 | #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
51 | default_value, comment) |
52 | |
53 | #else // !PRODUCT && !PRECOMPILED |
54 | #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \ |
55 | type FLAG_##name = \ |
56 | Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
57 | #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
58 | default_value, comment) \ |
59 | type FLAG_##name = \ |
60 | Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
61 | #endif |
62 | |
63 | // Define all of the non-product flags here. |
64 | FLAG_LIST(PRODUCT_FLAG_MACRO, |
65 | RELEASE_FLAG_MACRO, |
66 | PRECOMPILE_FLAG_MACRO, |
67 | DEBUG_FLAG_MACRO) |
68 | |
69 | #undef PRODUCT_FLAG_MACRO |
70 | #undef RELEASE_FLAG_MACRO |
71 | #undef PRECOMPILE_FLAG_MACRO |
72 | #undef DEBUG_FLAG_MACRO |
73 | |
74 | bool Flags::initialized_ = false; |
75 | |
76 | // List of registered flags. |
77 | Flag** Flags::flags_ = NULL; |
78 | intptr_t Flags::capacity_ = 0; |
79 | intptr_t Flags::num_flags_ = 0; |
80 | |
81 | class Flag { |
82 | public: |
83 | enum FlagType { |
84 | kBoolean, |
85 | kInteger, |
86 | kUint64, |
87 | kString, |
88 | kFlagHandler, |
89 | kOptionHandler, |
90 | kNumFlagTypes |
91 | }; |
92 | |
93 | Flag(const char* name, const char* , void* addr, FlagType type) |
94 | : name_(name), comment_(comment), addr_(addr), type_(type) {} |
95 | Flag(const char* name, const char* , FlagHandler handler) |
96 | : name_(name), |
97 | comment_(comment), |
98 | flag_handler_(handler), |
99 | type_(kFlagHandler) {} |
100 | Flag(const char* name, const char* , OptionHandler handler) |
101 | : name_(name), |
102 | comment_(comment), |
103 | option_handler_(handler), |
104 | type_(kOptionHandler) {} |
105 | |
106 | void Print() { |
107 | if (IsUnrecognized()) { |
108 | OS::PrintErr("%s: unrecognized\n" , name_); |
109 | return; |
110 | } |
111 | switch (type_) { |
112 | case kBoolean: { |
113 | OS::PrintErr("%s: %s (%s)\n" , name_, |
114 | *this->bool_ptr_ ? "true" : "false" , comment_); |
115 | break; |
116 | } |
117 | case kInteger: { |
118 | OS::PrintErr("%s: %d (%s)\n" , name_, *this->int_ptr_, comment_); |
119 | break; |
120 | } |
121 | case kUint64: { |
122 | OS::PrintErr("%s: %" Pu64 " (%s)\n" , name_, *this->uint64_ptr_, |
123 | comment_); |
124 | break; |
125 | } |
126 | case kString: { |
127 | if (*this->charp_ptr_ != NULL) { |
128 | OS::PrintErr("%s: '%s' (%s)\n" , name_, *this->charp_ptr_, comment_); |
129 | } else { |
130 | OS::PrintErr("%s: (null) (%s)\n" , name_, comment_); |
131 | } |
132 | break; |
133 | } |
134 | case kOptionHandler: |
135 | case kFlagHandler: { |
136 | OS::PrintErr("%s: (%s)\n" , name_, comment_); |
137 | break; |
138 | } |
139 | default: |
140 | UNREACHABLE(); |
141 | break; |
142 | } |
143 | } |
144 | |
145 | bool IsUnrecognized() const { |
146 | return (type_ == kBoolean) && (bool_ptr_ == NULL); |
147 | } |
148 | |
149 | const char* name_; |
150 | const char* ; |
151 | union { |
152 | void* addr_; |
153 | bool* bool_ptr_; |
154 | int* int_ptr_; |
155 | uint64_t* uint64_ptr_; |
156 | charp* charp_ptr_; |
157 | FlagHandler flag_handler_; |
158 | OptionHandler option_handler_; |
159 | }; |
160 | FlagType type_; |
161 | bool changed_ = false; |
162 | }; |
163 | |
164 | Flag* Flags::Lookup(const char* name) { |
165 | for (intptr_t i = 0; i < num_flags_; i++) { |
166 | Flag* flag = flags_[i]; |
167 | if (strcmp(flag->name_, name) == 0) { |
168 | return flag; |
169 | } |
170 | } |
171 | return NULL; |
172 | } |
173 | |
174 | bool Flags::IsSet(const char* name) { |
175 | Flag* flag = Lookup(name); |
176 | return (flag != NULL) && (flag->type_ == Flag::kBoolean) && |
177 | (flag->bool_ptr_ != NULL) && (*flag->bool_ptr_ == true); |
178 | } |
179 | |
180 | void Flags::Cleanup() { |
181 | ASSERT(initialized_); |
182 | initialized_ = false; |
183 | } |
184 | |
185 | void Flags::AddFlag(Flag* flag) { |
186 | ASSERT(!initialized_); |
187 | if (num_flags_ == capacity_) { |
188 | if (flags_ == NULL) { |
189 | capacity_ = 256; |
190 | flags_ = new Flag*[capacity_]; |
191 | } else { |
192 | intptr_t new_capacity = capacity_ * 2; |
193 | Flag** new_flags = new Flag*[new_capacity]; |
194 | for (intptr_t i = 0; i < num_flags_; i++) { |
195 | new_flags[i] = flags_[i]; |
196 | } |
197 | delete[] flags_; |
198 | flags_ = new_flags; |
199 | capacity_ = new_capacity; |
200 | } |
201 | } |
202 | flags_[num_flags_++] = flag; |
203 | } |
204 | |
205 | bool Flags::Register_bool(bool* addr, |
206 | const char* name, |
207 | bool default_value, |
208 | const char* ) { |
209 | Flag* flag = Lookup(name); |
210 | if (flag != NULL) { |
211 | ASSERT(flag->IsUnrecognized()); |
212 | return default_value; |
213 | } |
214 | flag = new Flag(name, comment, addr, Flag::kBoolean); |
215 | AddFlag(flag); |
216 | return default_value; |
217 | } |
218 | |
219 | int Flags::Register_int(int* addr, |
220 | const char* name, |
221 | int default_value, |
222 | const char* ) { |
223 | ASSERT(Lookup(name) == NULL); |
224 | |
225 | Flag* flag = new Flag(name, comment, addr, Flag::kInteger); |
226 | AddFlag(flag); |
227 | |
228 | return default_value; |
229 | } |
230 | |
231 | uint64_t Flags::Register_uint64_t(uint64_t* addr, |
232 | const char* name, |
233 | uint64_t default_value, |
234 | const char* ) { |
235 | ASSERT(Lookup(name) == NULL); |
236 | |
237 | Flag* flag = new Flag(name, comment, addr, Flag::kUint64); |
238 | AddFlag(flag); |
239 | |
240 | return default_value; |
241 | } |
242 | |
243 | const char* Flags::Register_charp(charp* addr, |
244 | const char* name, |
245 | const char* default_value, |
246 | const char* ) { |
247 | ASSERT(Lookup(name) == NULL); |
248 | Flag* flag = new Flag(name, comment, addr, Flag::kString); |
249 | AddFlag(flag); |
250 | return default_value; |
251 | } |
252 | |
253 | bool Flags::RegisterFlagHandler(FlagHandler handler, |
254 | const char* name, |
255 | const char* ) { |
256 | ASSERT(Lookup(name) == NULL); |
257 | Flag* flag = new Flag(name, comment, handler); |
258 | AddFlag(flag); |
259 | return false; |
260 | } |
261 | |
262 | bool Flags::RegisterOptionHandler(OptionHandler handler, |
263 | const char* name, |
264 | const char* ) { |
265 | ASSERT(Lookup(name) == NULL); |
266 | Flag* flag = new Flag(name, comment, handler); |
267 | AddFlag(flag); |
268 | return false; |
269 | } |
270 | |
271 | static void Normalize(char* s) { |
272 | intptr_t len = strlen(s); |
273 | for (intptr_t i = 0; i < len; i++) { |
274 | if (s[i] == '-') { |
275 | s[i] = '_'; |
276 | } |
277 | } |
278 | } |
279 | |
280 | bool Flags::SetFlagFromString(Flag* flag, const char* argument) { |
281 | ASSERT(!flag->IsUnrecognized()); |
282 | switch (flag->type_) { |
283 | case Flag::kBoolean: { |
284 | if (strcmp(argument, "true" ) == 0) { |
285 | *flag->bool_ptr_ = true; |
286 | } else if (strcmp(argument, "false" ) == 0) { |
287 | *flag->bool_ptr_ = false; |
288 | } else { |
289 | return false; |
290 | } |
291 | break; |
292 | } |
293 | case Flag::kString: { |
294 | *flag->charp_ptr_ = argument == NULL ? NULL : Utils::StrDup(argument); |
295 | break; |
296 | } |
297 | case Flag::kInteger: { |
298 | char* endptr = NULL; |
299 | const intptr_t len = strlen(argument); |
300 | int base = 10; |
301 | if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) { |
302 | base = 16; |
303 | } |
304 | int val = strtol(argument, &endptr, base); |
305 | if (endptr == argument + len) { |
306 | *flag->int_ptr_ = val; |
307 | } else { |
308 | return false; |
309 | } |
310 | break; |
311 | } |
312 | case Flag::kUint64: { |
313 | char* endptr = NULL; |
314 | const intptr_t len = strlen(argument); |
315 | int base = 10; |
316 | if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) { |
317 | base = 16; |
318 | } |
319 | int64_t val = strtoll(argument, &endptr, base); |
320 | if (endptr == argument + len) { |
321 | *flag->uint64_ptr_ = static_cast<uint64_t>(val); |
322 | } else { |
323 | return false; |
324 | } |
325 | break; |
326 | } |
327 | case Flag::kFlagHandler: { |
328 | if (strcmp(argument, "true" ) == 0) { |
329 | (flag->flag_handler_)(true); |
330 | } else if (strcmp(argument, "false" ) == 0) { |
331 | (flag->flag_handler_)(false); |
332 | } else { |
333 | return false; |
334 | } |
335 | break; |
336 | } |
337 | case Flag::kOptionHandler: { |
338 | (flag->option_handler_)(argument); |
339 | break; |
340 | } |
341 | default: { |
342 | UNREACHABLE(); |
343 | return false; |
344 | } |
345 | } |
346 | flag->changed_ = true; |
347 | return true; |
348 | } |
349 | |
350 | void Flags::Parse(const char* option) { |
351 | // Find the beginning of the option argument, if it exists. |
352 | const char* equals = option; |
353 | while ((*equals != '\0') && (*equals != '=')) { |
354 | equals++; |
355 | } |
356 | |
357 | const char* argument = NULL; |
358 | |
359 | // Determine if this is an option argument. |
360 | if (*equals != '=') { |
361 | // No explicit option argument. Determine if there is a "no_" prefix |
362 | // preceding the name. |
363 | const char* const kNo1Prefix = "no_" ; |
364 | const char* const kNo2Prefix = "no-" ; |
365 | const intptr_t kNo1PrefixLen = strlen(kNo1Prefix); |
366 | const intptr_t kNo2PrefixLen = strlen(kNo2Prefix); |
367 | if (strncmp(option, kNo1Prefix, kNo1PrefixLen) == 0) { |
368 | option += kNo1PrefixLen; // Skip the "no_" when looking up the name. |
369 | argument = "false" ; |
370 | } else if (strncmp(option, kNo2Prefix, kNo2PrefixLen) == 0) { |
371 | option += kNo2PrefixLen; // Skip the "no-" when looking up the name. |
372 | argument = "false" ; |
373 | } else { |
374 | argument = "true" ; |
375 | } |
376 | } else { |
377 | // The argument for the option starts right after the equals sign. |
378 | argument = equals + 1; |
379 | } |
380 | |
381 | // Initialize the flag name. |
382 | intptr_t name_len = equals - option; |
383 | char* name = new char[name_len + 1]; |
384 | strncpy(name, option, name_len); |
385 | name[name_len] = '\0'; |
386 | Normalize(name); |
387 | |
388 | Flag* flag = Flags::Lookup(name); |
389 | if (flag == NULL) { |
390 | // Collect unrecognized flags. |
391 | char* new_flag = new char[name_len + 1]; |
392 | strncpy(new_flag, option, name_len); |
393 | new_flag[name_len] = '\0'; |
394 | Flags::Register_bool(NULL, new_flag, true, NULL); |
395 | } else { |
396 | // Only set values for recognized flags, skip collected |
397 | // unrecognized flags. |
398 | if (!flag->IsUnrecognized()) { |
399 | if (!SetFlagFromString(flag, argument)) { |
400 | OS::PrintErr("Ignoring flag: %s is an invalid value for flag %s\n" , |
401 | argument, name); |
402 | } |
403 | } |
404 | } |
405 | |
406 | delete[] name; |
407 | } |
408 | |
409 | static bool IsValidFlag(const char* name, |
410 | const char* prefix, |
411 | intptr_t prefix_length) { |
412 | intptr_t name_length = strlen(name); |
413 | return ((name_length > prefix_length) && |
414 | (strncmp(name, prefix, prefix_length) == 0)); |
415 | } |
416 | |
417 | int Flags::CompareFlagNames(const void* left, const void* right) { |
418 | const Flag* left_flag = *reinterpret_cast<const Flag* const*>(left); |
419 | const Flag* right_flag = *reinterpret_cast<const Flag* const*>(right); |
420 | return strcmp(left_flag->name_, right_flag->name_); |
421 | } |
422 | |
423 | char* Flags::ProcessCommandLineFlags(int number_of_vm_flags, |
424 | const char** vm_flags) { |
425 | if (initialized_) { |
426 | return Utils::StrDup("Flags already set" ); |
427 | } |
428 | |
429 | qsort(flags_, num_flags_, sizeof flags_[0], CompareFlagNames); |
430 | |
431 | const char* const kPrefix = "--" ; |
432 | const intptr_t kPrefixLen = strlen(kPrefix); |
433 | |
434 | int i = 0; |
435 | while ((i < number_of_vm_flags) && |
436 | IsValidFlag(vm_flags[i], kPrefix, kPrefixLen)) { |
437 | const char* option = vm_flags[i] + kPrefixLen; |
438 | Parse(option); |
439 | i++; |
440 | } |
441 | |
442 | if (!FLAG_ignore_unrecognized_flags) { |
443 | int unrecognized_count = 0; |
444 | TextBuffer error(64); |
445 | for (intptr_t j = 0; j < num_flags_; j++) { |
446 | Flag* flag = flags_[j]; |
447 | if (flag->IsUnrecognized()) { |
448 | if (unrecognized_count == 0) { |
449 | error.Printf("Unrecognized flags: %s" , flag->name_); |
450 | } else { |
451 | error.Printf(", %s" , flag->name_); |
452 | } |
453 | unrecognized_count++; |
454 | } |
455 | } |
456 | if (unrecognized_count > 0) { |
457 | return error.Steal(); |
458 | } |
459 | } |
460 | if (FLAG_print_flags) { |
461 | PrintFlags(); |
462 | } |
463 | |
464 | initialized_ = true; |
465 | return NULL; |
466 | } |
467 | |
468 | bool Flags::SetFlag(const char* name, const char* value, const char** error) { |
469 | Flag* flag = Lookup(name); |
470 | if (flag == NULL) { |
471 | *error = "Cannot set flag: flag not found" ; |
472 | return false; |
473 | } |
474 | if (!SetFlagFromString(flag, value)) { |
475 | *error = "Cannot set flag: invalid value" ; |
476 | return false; |
477 | } |
478 | return true; |
479 | } |
480 | |
481 | void Flags::PrintFlags() { |
482 | OS::PrintErr("Flag settings:\n" ); |
483 | for (intptr_t i = 0; i < num_flags_; ++i) { |
484 | flags_[i]->Print(); |
485 | } |
486 | } |
487 | |
488 | #ifndef PRODUCT |
489 | void Flags::PrintFlagToJSONArray(JSONArray* jsarr, const Flag* flag) { |
490 | if (flag->IsUnrecognized() || flag->type_ == Flag::kFlagHandler || |
491 | flag->type_ == Flag::kOptionHandler) { |
492 | return; |
493 | } |
494 | JSONObject jsflag(jsarr); |
495 | jsflag.AddProperty("name" , flag->name_); |
496 | jsflag.AddProperty("comment" , flag->comment_); |
497 | jsflag.AddProperty("modified" , flag->changed_); |
498 | switch (flag->type_) { |
499 | case Flag::kBoolean: { |
500 | jsflag.AddProperty("_flagType" , "Bool" ); |
501 | jsflag.AddProperty("valueAsString" , |
502 | (*flag->bool_ptr_ ? "true" : "false" )); |
503 | break; |
504 | } |
505 | case Flag::kInteger: { |
506 | jsflag.AddProperty("_flagType" , "Int" ); |
507 | jsflag.AddPropertyF("valueAsString" , "%d" , *flag->int_ptr_); |
508 | break; |
509 | } |
510 | case Flag::kUint64: { |
511 | jsflag.AddProperty("_flagType" , "UInt64" ); |
512 | jsflag.AddPropertyF("valueAsString" , "%" Pu64, *flag->uint64_ptr_); |
513 | break; |
514 | } |
515 | case Flag::kString: { |
516 | jsflag.AddProperty("_flagType" , "String" ); |
517 | if (flag->charp_ptr_ != NULL) { |
518 | jsflag.AddPropertyF("valueAsString" , "%s" , *flag->charp_ptr_); |
519 | } else { |
520 | // valueAsString missing means NULL. |
521 | } |
522 | break; |
523 | } |
524 | default: |
525 | UNREACHABLE(); |
526 | break; |
527 | } |
528 | } |
529 | |
530 | void Flags::PrintJSON(JSONStream* js) { |
531 | JSONObject jsobj(js); |
532 | jsobj.AddProperty("type" , "FlagList" ); |
533 | JSONArray jsarr(&jsobj, "flags" ); |
534 | for (intptr_t i = 0; i < num_flags_; ++i) { |
535 | PrintFlagToJSONArray(&jsarr, flags_[i]); |
536 | } |
537 | } |
538 | #endif // !PRODUCT |
539 | |
540 | } // namespace dart |
541 | |