mirror of
https://github.com/dart-lang/sdk
synced 2024-10-15 00:04:55 +00:00
698bf0846b
Hoist `CStringUniquePtr` out of the `Utils` class as there is no reason it has to be nested inside a class - it just makes code more verbose. This simplifies code of the form std::unique_ptr<T, decltype(std::free)> a = { nullptr, std::free }; to CAllocUniquePtr<T> a; TEST=ci Change-Id: Ice42c1b16dfa5b20b321c13fbe5b28b3918581cb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368425 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com>
585 lines
17 KiB
C++
585 lines
17 KiB
C++
// 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)
|
|
|
|
#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)
|
|
|
|
#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)
|
|
|
|
#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);
|
|
#endif
|
|
|
|
// Define all of the non-product flags here.
|
|
FLAG_LIST(PRODUCT_FLAG_MACRO,
|
|
RELEASE_FLAG_MACRO,
|
|
PRECOMPILE_FLAG_MACRO,
|
|
DEBUG_FLAG_MACRO)
|
|
|
|
#undef PRODUCT_FLAG_MACRO
|
|
#undef RELEASE_FLAG_MACRO
|
|
#undef PRECOMPILE_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
|