| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/flags.h" |
| |
| #include "platform/assert.h" |
| #include "vm/isolate.h" |
| #include "vm/json_stream.h" |
| #include "vm/os.h" |
| |
| namespace dart { |
| |
| DEFINE_FLAG(bool, print_flags, false, "Print flags as they are being parsed."); |
| DEFINE_FLAG(bool, |
| ignore_unrecognized_flags, |
| false, |
| "Ignore unrecognized flags."); |
| |
| #define PRODUCT_FLAG_MACRO(name, type, default_value, comment) \ |
| type FLAG_##name = \ |
| Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
| |
| #if defined(DEBUG) |
| #define DEBUG_FLAG_MACRO(name, type, default_value, comment) \ |
| type FLAG_##name = \ |
| Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
| #else // defined(DEBUG) |
| #define DEBUG_FLAG_MACRO(name, type, default_value, comment) |
| #endif // defined(DEBUG) |
| |
| #if defined(PRODUCT) && defined(DART_PRECOMPILED_RUNTIME) |
| // Nothing to be done for the product flag definitions. |
| #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) |
| // Nothing to be done for the precompilation flag definitions. |
| #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
| default_value, comment) |
| // Nothing to be done for the AOT flag definitions. |
| #define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment) |
| |
| #elif defined(PRODUCT) // !PRECOMPILED |
| // Nothing to be done for the product flag definitions. |
| #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) |
| // Nothing to be done for the precompilation flag definitions. |
| #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
| default_value, comment) |
| #define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment) \ |
| type FLAG_##name = \ |
| Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
| |
| #elif defined(DART_PRECOMPILED_RUNTIME) // !PRODUCT |
| #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \ |
| type FLAG_##name = \ |
| Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
| // Nothing to be done for the precompilation flag definitions. |
| #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
| default_value, comment) |
| // Nothing to be done for the AOT flag definitions. |
| #define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment) |
| |
| #else // !PRODUCT && !PRECOMPILED |
| #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \ |
| type FLAG_##name = \ |
| Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
| #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ |
| default_value, comment) \ |
| type FLAG_##name = \ |
| Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
| #define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment) \ |
| type FLAG_##name = \ |
| Flags::Register_##type(&FLAG_##name, #name, default_value, comment); |
| #endif |
| |
| // Define all of the non-product flags here. |
| FLAG_LIST(PRODUCT_FLAG_MACRO, |
| RELEASE_FLAG_MACRO, |
| PRECOMPILE_FLAG_MACRO, |
| AOT_FLAG_MACRO, |
| DEBUG_FLAG_MACRO) |
| |
| #undef PRODUCT_FLAG_MACRO |
| #undef RELEASE_FLAG_MACRO |
| #undef PRECOMPILE_FLAG_MACRO |
| #undef AOT_FLAG_MACRO |
| #undef DEBUG_FLAG_MACRO |
| |
| #if defined(DART_PRECOMPILER) |
| #if defined(TARGET_USES_THREAD_SANITIZER) |
| constexpr bool kDefaultTargetThreadSanitizer = true; |
| #else |
| constexpr bool kDefaultTargetThreadSanitizer = false; |
| #endif |
| DEFINE_FLAG(bool, |
| target_thread_sanitizer, |
| kDefaultTargetThreadSanitizer, |
| "Generate Dart code compatible with Thread Sanitizer"); |
| #if defined(TARGET_USES_MEMORY_SANITIZER) |
| constexpr bool kDefaultTargetMemorySanitizer = true; |
| #else |
| constexpr bool kDefaultTargetMemorySanitizer = false; |
| #endif |
| DEFINE_FLAG(bool, |
| target_memory_sanitizer, |
| kDefaultTargetMemorySanitizer, |
| "Generate Dart code compatible with Memory Sanitizer"); |
| #endif |
| |
| bool Flags::initialized_ = false; |
| |
| // List of registered flags. |
| Flag** Flags::flags_ = nullptr; |
| intptr_t Flags::capacity_ = 0; |
| intptr_t Flags::num_flags_ = 0; |
| |
| class Flag { |
| public: |
| enum FlagType { |
| kBoolean, |
| kInteger, |
| kUint64, |
| kString, |
| kFlagHandler, |
| kOptionHandler, |
| kNumFlagTypes |
| }; |
| |
| Flag(const char* name, const char* comment, void* addr, FlagType type) |
| : name_(name), comment_(comment), addr_(addr), type_(type) {} |
| Flag(const char* name, const char* comment, FlagHandler handler) |
| : name_(name), |
| comment_(comment), |
| flag_handler_(handler), |
| type_(kFlagHandler) {} |
| Flag(const char* name, const char* comment, OptionHandler handler) |
| : name_(name), |
| comment_(comment), |
| option_handler_(handler), |
| type_(kOptionHandler) {} |
| |
| void Print() { |
| if (IsUnrecognized()) { |
| OS::PrintErr("%s: unrecognized\n", name_); |
| return; |
| } |
| switch (type_) { |
| case kBoolean: { |
| OS::Print("%s: %s (%s)\n", name_, *this->bool_ptr_ ? "true" : "false", |
| comment_); |
| break; |
| } |
| case kInteger: { |
| OS::Print("%s: %d (%s)\n", name_, *this->int_ptr_, comment_); |
| break; |
| } |
| case kUint64: { |
| OS::Print("%s: %" Pu64 " (%s)\n", name_, *this->uint64_ptr_, comment_); |
| break; |
| } |
| case kString: { |
| if (*this->charp_ptr_ != nullptr) { |
| OS::Print("%s: '%s' (%s)\n", name_, *this->charp_ptr_, comment_); |
| } else { |
| OS::Print("%s: (null) (%s)\n", name_, comment_); |
| } |
| break; |
| } |
| case kOptionHandler: |
| case kFlagHandler: { |
| OS::Print("%s: (%s)\n", name_, comment_); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| bool IsUnrecognized() const { |
| return (type_ == kBoolean) && (bool_ptr_ == nullptr); |
| } |
| |
| const char* name_; |
| const char* comment_; |
| |
| // For kString, kOptionHandler, kFlagHandler flags this stores the copy |
| // of the original flag value passed to SetFlagFromString |
| CStringUniquePtr string_value_; |
| union { |
| void* addr_; |
| bool* bool_ptr_; |
| int* int_ptr_; |
| uint64_t* uint64_ptr_; |
| charp* charp_ptr_; |
| FlagHandler flag_handler_; |
| OptionHandler option_handler_; |
| }; |
| const FlagType type_; |
| bool changed_ = false; |
| }; |
| |
| Flag* Flags::Lookup(const char* name) { |
| for (intptr_t i = 0; i < num_flags_; i++) { |
| Flag* flag = flags_[i]; |
| if (strcmp(flag->name_, name) == 0) { |
| return flag; |
| } |
| } |
| return nullptr; |
| } |
| |
| bool Flags::IsSet(const char* name) { |
| Flag* flag = Lookup(name); |
| return (flag != nullptr) && (flag->type_ == Flag::kBoolean) && |
| (flag->bool_ptr_ != nullptr) && (*flag->bool_ptr_ == true); |
| } |
| |
| void Flags::Cleanup() { |
| ASSERT(initialized_); |
| initialized_ = false; |
| } |
| |
| void Flags::AddFlag(Flag* flag) { |
| ASSERT(!initialized_); |
| if (num_flags_ == capacity_) { |
| if (flags_ == nullptr) { |
| capacity_ = 256; |
| flags_ = new Flag*[capacity_]; |
| } else { |
| intptr_t new_capacity = capacity_ * 2; |
| Flag** new_flags = new Flag*[new_capacity]; |
| for (intptr_t i = 0; i < num_flags_; i++) { |
| new_flags[i] = flags_[i]; |
| } |
| delete[] flags_; |
| flags_ = new_flags; |
| capacity_ = new_capacity; |
| } |
| } |
| flags_[num_flags_++] = flag; |
| } |
| |
| bool Flags::Register_bool(bool* addr, |
| const char* name, |
| bool default_value, |
| const char* comment) { |
| Flag* flag = Lookup(name); |
| if (flag != nullptr) { |
| ASSERT(flag->IsUnrecognized()); |
| return default_value; |
| } |
| flag = new Flag(name, comment, addr, Flag::kBoolean); |
| AddFlag(flag); |
| return default_value; |
| } |
| |
| int Flags::Register_int(int* addr, |
| const char* name, |
| int default_value, |
| const char* comment) { |
| ASSERT(Lookup(name) == nullptr); |
| |
| Flag* flag = new Flag(name, comment, addr, Flag::kInteger); |
| AddFlag(flag); |
| |
| return default_value; |
| } |
| |
| uint64_t Flags::Register_uint64_t(uint64_t* addr, |
| const char* name, |
| uint64_t default_value, |
| const char* comment) { |
| ASSERT(Lookup(name) == nullptr); |
| |
| Flag* flag = new Flag(name, comment, addr, Flag::kUint64); |
| AddFlag(flag); |
| |
| return default_value; |
| } |
| |
| const char* Flags::Register_charp(charp* addr, |
| const char* name, |
| const char* default_value, |
| const char* comment) { |
| ASSERT(Lookup(name) == nullptr); |
| Flag* flag = new Flag(name, comment, addr, Flag::kString); |
| AddFlag(flag); |
| return default_value; |
| } |
| |
| bool Flags::RegisterFlagHandler(FlagHandler handler, |
| const char* name, |
| const char* comment) { |
| ASSERT(Lookup(name) == nullptr); |
| Flag* flag = new Flag(name, comment, handler); |
| AddFlag(flag); |
| return false; |
| } |
| |
| bool Flags::RegisterOptionHandler(OptionHandler handler, |
| const char* name, |
| const char* comment) { |
| ASSERT(Lookup(name) == nullptr); |
| Flag* flag = new Flag(name, comment, handler); |
| AddFlag(flag); |
| return false; |
| } |
| |
| static void Normalize(char* s) { |
| intptr_t len = strlen(s); |
| for (intptr_t i = 0; i < len; i++) { |
| if (s[i] == '-') { |
| s[i] = '_'; |
| } |
| } |
| } |
| |
| bool Flags::SetFlagFromString(Flag* flag, const char* argument) { |
| ASSERT(!flag->IsUnrecognized()); |
| switch (flag->type_) { |
| case Flag::kBoolean: { |
| if (strcmp(argument, "true") == 0) { |
| *flag->bool_ptr_ = true; |
| } else if (strcmp(argument, "false") == 0) { |
| *flag->bool_ptr_ = false; |
| } else { |
| return false; |
| } |
| break; |
| } |
| case Flag::kString: { |
| flag->string_value_.reset(argument == nullptr ? nullptr |
| : Utils::StrDup(argument)); |
| *flag->charp_ptr_ = flag->string_value_.get(); |
| break; |
| } |
| case Flag::kInteger: { |
| char* endptr = nullptr; |
| const intptr_t len = strlen(argument); |
| int base = 10; |
| if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) { |
| base = 16; |
| } |
| int val = strtol(argument, &endptr, base); |
| if (endptr == argument + len) { |
| *flag->int_ptr_ = val; |
| } else { |
| return false; |
| } |
| break; |
| } |
| case Flag::kUint64: { |
| char* endptr = nullptr; |
| const intptr_t len = strlen(argument); |
| int base = 10; |
| if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) { |
| base = 16; |
| } |
| int64_t val = strtoll(argument, &endptr, base); |
| if (endptr == argument + len) { |
| *flag->uint64_ptr_ = static_cast<uint64_t>(val); |
| } else { |
| return false; |
| } |
| break; |
| } |
| case Flag::kFlagHandler: { |
| if (strcmp(argument, "true") == 0) { |
| (flag->flag_handler_)(true); |
| } else if (strcmp(argument, "false") == 0) { |
| (flag->flag_handler_)(false); |
| } else { |
| return false; |
| } |
| flag->string_value_.reset(Utils::StrDup(argument)); |
| break; |
| } |
| case Flag::kOptionHandler: { |
| flag->string_value_.reset(Utils::StrDup(argument)); |
| (flag->option_handler_)(argument); |
| break; |
| } |
| default: { |
| UNREACHABLE(); |
| return false; |
| } |
| } |
| flag->changed_ = true; |
| return true; |
| } |
| |
| void Flags::Parse(const char* option) { |
| // Find the beginning of the option argument, if it exists. |
| const char* equals = option; |
| while ((*equals != '\0') && (*equals != '=')) { |
| equals++; |
| } |
| |
| const char* argument = nullptr; |
| |
| // Determine if this is an option argument. |
| if (*equals != '=') { |
| // No explicit option argument. Determine if there is a "no_" prefix |
| // preceding the name. |
| const char* const kNo1Prefix = "no_"; |
| const char* const kNo2Prefix = "no-"; |
| const intptr_t kNo1PrefixLen = strlen(kNo1Prefix); |
| const intptr_t kNo2PrefixLen = strlen(kNo2Prefix); |
| if (strncmp(option, kNo1Prefix, kNo1PrefixLen) == 0) { |
| option += kNo1PrefixLen; // Skip the "no_" when looking up the name. |
| argument = "false"; |
| } else if (strncmp(option, kNo2Prefix, kNo2PrefixLen) == 0) { |
| option += kNo2PrefixLen; // Skip the "no-" when looking up the name. |
| argument = "false"; |
| } else { |
| argument = "true"; |
| } |
| } else { |
| // The argument for the option starts right after the equals sign. |
| argument = equals + 1; |
| } |
| |
| // Initialize the flag name. |
| intptr_t name_len = equals - option; |
| char* name = new char[name_len + 1]; |
| strncpy(name, option, name_len); |
| name[name_len] = '\0'; |
| Normalize(name); |
| |
| Flag* flag = Flags::Lookup(name); |
| if (flag == nullptr) { |
| // Collect unrecognized flags. |
| char* new_flag = new char[name_len + 1]; |
| strncpy(new_flag, option, name_len); |
| new_flag[name_len] = '\0'; |
| Normalize(new_flag); // Or a later lookup may fail. |
| Flags::Register_bool(nullptr, new_flag, true, nullptr); |
| } else { |
| // Only set values for recognized flags, skip collected |
| // unrecognized flags. |
| if (!flag->IsUnrecognized()) { |
| if (!SetFlagFromString(flag, argument)) { |
| OS::PrintErr("Ignoring flag: %s is an invalid value for flag %s\n", |
| argument, name); |
| } |
| } |
| } |
| |
| delete[] name; |
| } |
| |
| static bool IsValidFlag(const char* name, |
| const char* prefix, |
| intptr_t prefix_length) { |
| intptr_t name_length = strlen(name); |
| return ((name_length > prefix_length) && |
| (strncmp(name, prefix, prefix_length) == 0)); |
| } |
| |
| int Flags::CompareFlagNames(const void* left, const void* right) { |
| const Flag* left_flag = *reinterpret_cast<const Flag* const*>(left); |
| const Flag* right_flag = *reinterpret_cast<const Flag* const*>(right); |
| return strcmp(left_flag->name_, right_flag->name_); |
| } |
| |
| char* Flags::ProcessCommandLineFlags(int number_of_vm_flags, |
| const char** vm_flags) { |
| if (initialized_) { |
| return Utils::StrDup("Flags already set"); |
| } |
| |
| qsort(flags_, num_flags_, sizeof flags_[0], CompareFlagNames); |
| |
| const char* const kPrefix = "--"; |
| const intptr_t kPrefixLen = strlen(kPrefix); |
| |
| int i = 0; |
| while ((i < number_of_vm_flags) && |
| IsValidFlag(vm_flags[i], kPrefix, kPrefixLen)) { |
| const char* option = vm_flags[i] + kPrefixLen; |
| Parse(option); |
| i++; |
| } |
| |
| if (!FLAG_ignore_unrecognized_flags) { |
| int unrecognized_count = 0; |
| TextBuffer error(64); |
| for (intptr_t j = 0; j < num_flags_; j++) { |
| Flag* flag = flags_[j]; |
| if (flag->IsUnrecognized()) { |
| if (unrecognized_count == 0) { |
| error.Printf("Unrecognized flags: %s", flag->name_); |
| } else { |
| error.Printf(", %s", flag->name_); |
| } |
| unrecognized_count++; |
| } |
| } |
| if (unrecognized_count > 0) { |
| return error.Steal(); |
| } |
| } |
| if (FLAG_print_flags) { |
| PrintFlags(); |
| } |
| |
| initialized_ = true; |
| return nullptr; |
| } |
| |
| bool Flags::SetFlag(const char* name, const char* value, const char** error) { |
| Flag* flag = Lookup(name); |
| if (flag == nullptr) { |
| *error = "Cannot set flag: flag not found"; |
| return false; |
| } |
| if (!SetFlagFromString(flag, value)) { |
| *error = "Cannot set flag: invalid value"; |
| return false; |
| } |
| return true; |
| } |
| |
| void Flags::PrintFlags() { |
| OS::Print("Flag settings:\n"); |
| for (intptr_t i = 0; i < num_flags_; ++i) { |
| flags_[i]->Print(); |
| } |
| } |
| |
| #ifndef PRODUCT |
| void Flags::PrintFlagToJSONArray(JSONArray* jsarr, const Flag* flag) { |
| if (flag->IsUnrecognized()) { |
| return; |
| } |
| JSONObject jsflag(jsarr); |
| jsflag.AddProperty("name", flag->name_); |
| jsflag.AddProperty("comment", flag->comment_); |
| jsflag.AddProperty("modified", flag->changed_); |
| switch (flag->type_) { |
| case Flag::kBoolean: { |
| jsflag.AddProperty("_flagType", "Bool"); |
| jsflag.AddProperty("valueAsString", |
| (*flag->bool_ptr_ ? "true" : "false")); |
| break; |
| } |
| case Flag::kInteger: { |
| jsflag.AddProperty("_flagType", "Int"); |
| jsflag.AddPropertyF("valueAsString", "%d", *flag->int_ptr_); |
| break; |
| } |
| case Flag::kUint64: { |
| jsflag.AddProperty("_flagType", "UInt64"); |
| jsflag.AddPropertyF("valueAsString", "%" Pu64, *flag->uint64_ptr_); |
| break; |
| } |
| case Flag::kString: { |
| jsflag.AddProperty("_flagType", "String"); |
| if (flag->charp_ptr_ != nullptr) { |
| jsflag.AddPropertyF("valueAsString", "%s", *flag->charp_ptr_); |
| } else { |
| // valueAsString missing means nullptr. |
| } |
| break; |
| } |
| case Flag::kFlagHandler: { |
| jsflag.AddProperty("_flagType", "Bool"); |
| const char* value = flag->string_value_.get(); |
| jsflag.AddProperty("valueAsString", value == nullptr ? "false" : value); |
| break; |
| } |
| case Flag::kOptionHandler: { |
| jsflag.AddProperty("_flagType", "String"); |
| if (flag->string_value_ != nullptr) { |
| jsflag.AddProperty("valueAsString", flag->string_value_.get()); |
| } else { |
| // valueAsString missing means nullptr. |
| } |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| void Flags::PrintJSON(JSONStream* js) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "FlagList"); |
| JSONArray jsarr(&jsobj, "flags"); |
| for (intptr_t i = 0; i < num_flags_; ++i) { |
| PrintFlagToJSONArray(&jsarr, flags_[i]); |
| } |
| } |
| #endif // !PRODUCT |
| |
| } // namespace dart |