/* * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #ifndef SHARE_RUNTIME_FLAGS_JVMFLAG_HPP #define SHARE_RUNTIME_FLAGS_JVMFLAG_HPP #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" class outputStream; // function type that will construct default range string typedef const char* (*RangeStrFunc)(void); struct JVMFlag { friend class VMStructs; friend class JVMCIVMStructs; enum Attr { DEFAULT = 0, COMMAND_LINE = 1, ENVIRON_VAR = 2, CONFIG_FILE = 3, MANAGEMENT = 4, ERGONOMIC = 5, ATTACH_ON_DEMAND = 6, INTERNAL = 7, JIMAGE_RESOURCE = 8, LAST_VALUE_ORIGIN = JIMAGE_RESOURCE, VALUE_ORIGIN_BITS = 4, VALUE_ORIGIN_MASK = right_n_bits(VALUE_ORIGIN_BITS), IS_PRODUCT = 1 << 4, MANAGEABLE = 1 << 5, DIAGNOSTIC = 1 << 6, EXPERIMENTAL = 1 << 7, NOT_PRODUCT = 1 << 8, DEVELOP = 1 << 9, PLATFORM_DEPENDENT = 1 << 10, READ_WRITE = 1 << 11, C1 = 1 << 12, C2 = 1 << 13, ARCH = 1 << 14, LP64 = 1 << 15, JVMCI = 1 << 16, STRINGLIST = 1 << 17, // replaces the old ccstrlist -- TODO document RANGE = 1 << 18, // -- TODO document CONSTRAINT = 1 << 19, // -- TODO document // set this bit if the flag was set on the command line ORIG_COMMAND_LINE = 1 << 21, // huh COMMAND_LINE and ORIG_COMMAND_LINE KIND_MASK = ~(VALUE_ORIGIN_MASK | ORIG_COMMAND_LINE | STRINGLIST | RANGE | CONSTRAINT), ALL_NOT_PRODUCT_FLAGS = DEVELOP | NOT_PRODUCT | MANAGEABLE | EXPERIMENTAL | DIAGNOSTIC | LP64 | JVMCI }; typedef Attr Flags; // compatibility for usage of JVMFlag::Flags -- TODO fix in separate RFE enum Error { // no error SUCCESS = 0, // flag name is missing MISSING_NAME, // flag value is missing MISSING_VALUE, // error parsing the textual form of the value WRONG_FORMAT, // flag is not writable NON_WRITABLE, // flag value is outside of its bounds OUT_OF_BOUNDS, // flag value violates its constraint VIOLATES_CONSTRAINT, // there is no flag with the given name INVALID_FLAG, // the flag can only be set only on command line during invocation of the VM COMMAND_LINE_ONLY, // the flag may only be set once SET_ONLY_ONCE, // the flag is not writable in this combination of product/debug build CONSTANT, // other, unspecified error related to setting the flag ERR_OTHER }; enum MsgType { NONE = 0, DIAGNOSTIC_FLAG_BUT_LOCKED, EXPERIMENTAL_FLAG_BUT_LOCKED, DEVELOPER_FLAG_BUT_PRODUCT_BUILD, NOTPRODUCT_FLAG_BUT_PRODUCT_BUILD }; // During VM initialization, constraint validation will be done order of ConstraintPhase. enum ConstraintPhase { // Will be validated during argument processing (Arguments::parse_argument). AtParse = 0, // Will be validated inside Threads::create_vm(), right after Arguments::apply_ergo(). AfterErgo = 1, // Will be validated inside universe_init(), right after Metaspace::global_initialize(). AfterMemoryInit = 2 }; #define JVM_FLAG_ALL_TYPES_DO(f) \ f(bool) \ f(int) \ f(uint) \ f(intx) \ f(uintx) \ f(uint64_t) \ f(size_t) \ f(double) \ f(ccstr) #define JVM_FLAG_TYPE_DECLARE(t) \ TYPE_ ## t, // Define TYPE_bool, TYPE_int, .... etc enum FlagType { JVM_FLAG_ALL_TYPES_DO(JVM_FLAG_TYPE_DECLARE) NUM_TYPES }; #define FLAG_ACCESSOR(t) \ t get_##t() const { return *((t*)value_addr()); } \ bool is_##t() const { return _type == JVMFlag::TYPE_ ## t; } JVM_FLAG_ALL_TYPES_DO(FLAG_ACCESSOR) private: const char* _name; int _attr; short _name_len; short _type; protected: void* _range; void* _constraint; void* _value_addr; private: NOT_PRODUCT(const char* _docs;) JVMFlag* _next; static JVMFlag* _head; static int _num_flags; static ConstraintPhase _constraint_validating_phase; static const JVMFlag* _current_checking; protected: static void set_current_checking(const JVMFlag* f) { assert(_current_checking == NULL || f == NULL, "no recursion"); _current_checking = f; } public: JVMFlag(FlagType type, const char* name, int attr, void* value_addr, const char* docs) : _name(name), // So we don't need to explicitly say IS_PRODUCT all over the place! _attr(attr | ((attr & ALL_NOT_PRODUCT_FLAGS) ? 0 : IS_PRODUCT)), _name_len(0), _type(short(type)), _range(NULL), _constraint(NULL), _value_addr(value_addr), NOT_PRODUCT_ARG(_docs(docs)) _next(_head) { assert(is_default(), "original must be initialized to DEFAULT"); _head = this; _num_flags ++; } const char* name() const { return _name; } const char* docs() const { return NOT_PRODUCT(_docs) PRODUCT_ONLY(""); } JVMFlag* next() const { return _next; } int attr() const { return _attr; } void* value_addr() const { return _value_addr; } FlagType type() const { assert(0 <= _type && _type < JVMFlag::NUM_TYPES, "must be"); return (FlagType)_type; } static JVMFlag* head() { return _head; } static int num_flags() { return _num_flags; } static const JVMFlag* current_checking() { return _current_checking; } static bool verbose_checking() { return _constraint_validating_phase < AfterErgo; }; void set_origin(Flags origin); int get_origin() const { return _attr & VALUE_ORIGIN_MASK; } bool is_default() const { return (get_origin() == DEFAULT); } bool is_ergonomic() const { return (get_origin() == ERGONOMIC); } bool is_command_line() const { return (_attr & ORIG_COMMAND_LINE) != 0; } void set_command_line() { _attr |= ORIG_COMMAND_LINE; } bool is_product() const { return (_attr & IS_PRODUCT) != 0; } bool is_manageable() const { return (_attr & MANAGEABLE) != 0; } bool is_diagnostic() const { return (_attr & DIAGNOSTIC) != 0; } bool is_experimental() const { return (_attr & EXPERIMENTAL) != 0; } bool is_notproduct() const { return (_attr & NOT_PRODUCT) != 0; } bool is_develop() const { return (_attr & DEVELOP) != 0; } bool is_read_write() const { return (_attr & READ_WRITE) != 0; } bool ccstr_accumulates() const { return is_ccstr() && (_attr & STRINGLIST) != 0; } // is this flag a constant in the JVM binary. bool is_constant_in_binary() const { return PRODUCT_ONLY(is_notproduct() || is_develop()) NOT_PRODUCT(false); } bool is_writeable() const { return is_manageable() || (is_product() && is_read_write()); } // All flags except "manageable" are assumed to be internal flags. // Long term, we need to define a mechanism to specify which flags // are external/stable and change this function accordingly. bool is_external() const { return is_manageable(); } static void printAllFlags(); const char* type_string() const; void set_range(void* range) { assert(_range == NULL, "set only once"); assert((_attr & RANGE) != 0, "must be"); _range = range; } void set_constraint(void* constraint) { assert(_constraint == NULL, "set only once"); assert((_attr & CONSTRAINT) != 0, "must be"); _constraint = constraint; } bool has_range() const { return _range != NULL; } void print_value(outputStream*) const; void print_range(outputStream*) const; JVMFlag::Error check_range(void* new_value, bool verbose) const; JVMFlag::Error check_constraint(void* new_value, bool verbose) const; static void validate_flags() PRODUCT_RETURN; // Range and constraint checking struct PrintFormat { NOT_PRODUCT(JVMFlag::FlagType type;) const char* print_value_format; const char* range_error_format; const char* print_range_format; }; static PrintFormat print_formats[]; // This can be changed to a const table with c++11 bool is_jimage_resource(); template inline void print_range_helper(outputStream* st, T min, T max) const; void print_range_for_constraint(outputStream* st, void* constraint_func) const; void print_attr(outputStream* st, int width) const; protected: template inline void print_range_error(T new_value, T min, T max, bool verbose) const; template Error check_range_helper(T new_value, T min, T max, bool verbose) const { if ((new_value < min) || (new_value > max)) { print_range_error(new_value, min, max, verbose); return JVMFlag::OUT_OF_BOUNDS; } else { return JVMFlag::SUCCESS; } } template Error check_constraint_helper(T new_value, void* func, ConstraintPhase phase, bool verbose) const { typedef Error ConstraintFunc(T value, bool verbose); if (phase > _constraint_validating_phase) { // This constraint is for a later phase. Don't call it now. return JVMFlag::SUCCESS; } return ((ConstraintFunc*)func)(new_value, verbose); } private: static JVMFlag* find_flag(const char* name, size_t length, bool allow_locked, bool return_flag); public: static JVMFlag* find_flag(const char* name) { return find_flag(name, strlen(name), false, false); } static const JVMFlag* find_declared_flag(const char* name, size_t length) { return find_flag(name, length, true, true); } static const JVMFlag* find_declared_flag(const char* name) { return find_declared_flag(name, strlen(name)); } static JVMFlag* fuzzy_match(const char* name, size_t length, bool allow_locked = false); size_t get_name_length(); bool is_unlocker() const; bool is_unlocked() const; void clear_diagnostic(); void clear_experimental(); void set_product(); JVMFlag::MsgType get_locked_message(char*, int) const; JVMFlag::MsgType get_locked_message_ext(char*, int) const; // printRanges will print out flags type, name and range values as expected by -XX:+PrintFlagsRanges void print_on(outputStream* st, bool withComments = false, bool printRanges = false) const; void print_kind(outputStream* st, unsigned int width) const; void print_origin(outputStream* st, unsigned int width) const; void print_as_flag(outputStream* st) const; static const char* flag_error_str(JVMFlag::Error error); public: static JVMFlag::Error boolAt(const JVMFlag* flag, bool* value); static JVMFlag::Error boolAtPut(JVMFlag* flag, bool value, Attr origin); static JVMFlag::Error intAt(const JVMFlag* flag, int* value); static JVMFlag::Error intAtPut(JVMFlag* flag, int value, Attr origin); static JVMFlag::Error uintAt(const JVMFlag* flag, uint* value); static JVMFlag::Error uintAtPut(JVMFlag* flag, uint value, Attr origin); static JVMFlag::Error intxAt(const JVMFlag* flag, intx* value); static JVMFlag::Error intxAtPut(JVMFlag* flag, intx value, Attr origin); static JVMFlag::Error uintxAt(const JVMFlag* flag, uintx* value); static JVMFlag::Error uintxAtPut(JVMFlag* flag, uintx value, Attr origin); static JVMFlag::Error size_tAt(const JVMFlag* flag, size_t* value); static JVMFlag::Error size_tAtPut(JVMFlag* flag, size_t value, Attr origin); static JVMFlag::Error uint64_tAt(const JVMFlag* flag, uint64_t* value); static JVMFlag::Error uint64_tAtPut(JVMFlag* flag, uint64_t value, Attr origin); static JVMFlag::Error doubleAt(const JVMFlag* flag, double* value); static JVMFlag::Error doubleAtPut(JVMFlag* flag, double value, Attr origin); static JVMFlag::Error ccstrAt(const JVMFlag* flag, ccstr* value); // Contract: JVMFlag will make private copy of the incoming value. // Outgoing value is always malloc-ed, and caller MUST call free. static JVMFlag::Error ccstrAtPut(JVMFlag* flag, ccstr* value, Attr origin); static JVMFlag::Error ccstrAtPut(JVMFlag* flag, ccstr value, Attr origin) { return ccstrAtPut(flag, &value, origin); } static void printSetFlags(outputStream* out); // printRanges will print out flags type, name and range values as expected by -XX:+PrintFlagsRanges static void printFlags(outputStream* out, bool withComments, bool printRanges = false, bool skipDefaults = false); static void printError(bool verbose, const char* msg, ...) ATTRIBUTE_PRINTF(2, 3); static bool check_all_ranges(); static bool check_all_constraints(ConstraintPhase phase); static void verify() PRODUCT_RETURN; }; template class TypedJVMFlag; template class JVMFlagRange { public: T _min; T _max; inline JVMFlagRange(TypedJVMFlag* f, T min, T max); void init(T min, T max) { _min = min; _max = max; } }; template class JVMFlagConstraint { public: void* _func; JVMFlag::ConstraintPhase _phase; inline JVMFlagConstraint(TypedJVMFlag* f, void* func, JVMFlag::ConstraintPhase phase); }; template class TypedJVMFlag : public JVMFlag { protected: T* value_addr() const { return (T*)_value_addr; } const JVMFlagRange* range() const { return (const JVMFlagRange*)_range; } const JVMFlagConstraint* constraint() const { return (const JVMFlagConstraint*)_constraint; } public: TypedJVMFlag(JVMFlag::FlagType type, const char* name, int attr, T* value_addr, const char* docs) : JVMFlag(type, name, attr, (void*)value_addr, docs) {} static const TypedJVMFlag* cast(const JVMFlag* flag) { return (const TypedJVMFlag*)flag; } T get_value() const { return *value_addr(); } void print_typed_range(outputStream* st) const { if (range() != NULL) { JVMFlag::print_range_helper(st, range()->_min, range()->_max); } else { assert(constraint() != NULL, "must be"); JVMFlag::print_range_for_constraint(st, constraint()->_func); } } JVMFlag::Error check_typed_range(void* new_value, bool verbose) const { assert(range() != NULL, "must be"); T n = *((T*)new_value); return JVMFlag::check_range_helper(n, range()->_min, range()->_max, verbose); } JVMFlag::Error check_typed_constraint(void* new_value, bool verbose) const { assert(constraint() != NULL, "must be"); T n = *((T*)new_value); return check_constraint_helper(n, constraint()->_func, constraint()->_phase, verbose); } }; template inline JVMFlagRange::JVMFlagRange(TypedJVMFlag* f, T min, T max) { _min = min; _max = max; f->set_range(this); } template inline JVMFlagConstraint::JVMFlagConstraint(TypedJVMFlag* f, void* func, JVMFlag::ConstraintPhase phase) { _func = func; _phase = phase; f->set_constraint(this); } template class ProductFlag : public TypedJVMFlag { public: ProductFlag(JVMFlag::FlagType type, const char* name, int attr, T* value_addr, const char* docs) : TypedJVMFlag(type, name, attr, value_addr, docs) {} JVMFlag::Error check_new_value(T new_value, JVMFlag::Attr origin); static ProductFlag* cast(JVMFlag* flag) { return (ProductFlag*)flag; } void write_value(T value) { *TypedJVMFlag::value_addr() = value; } }; #ifdef PRODUCT template class DevelopFlag : public TypedJVMFlag { public: DevelopFlag(JVMFlag::FlagType type, const char* name, int attr, const T* value_addr) : TypedJVMFlag(type, name, attr, (T*)value_addr, "") {} }; #endif // PRODUCT (class DevelopFlag) // The FLAG_COMMON_ATTRS and FLAG_ATTR macros allow you to define a group of flags all with the same // common attribute. See globals.hpp. #ifndef FLAG_COMMON_ATTRS #define FLAG_COMMON_ATTRS 0 #endif #define FLAG_ATTR(attr) (attr | FLAG_COMMON_ATTRS) // ----- Macros for setting the values of the JVM flags ----- #define FLAG_STRUCT(name) (FLAG_##name) #define FLAG_MEMBER_SET(name) Flag_##name##_set #define FLAG_IS_DEFAULT(name) (FLAG_STRUCT(name).is_default()) #define FLAG_IS_ERGO(name) (FLAG_STRUCT(name).is_ergonomic()) #define FLAG_IS_CMDLINE(name) (FLAG_STRUCT(name).is_command_line()) #define FLAG_IS_JIMAGE_RESOURCE(name) (FLAG_STRUCT(name).is_jimage_resource()) #define FLAG_SET_DEFAULT(name, value) ((name) = (value)) #define FLAG_SET_CMDLINE(name, value) (FLAG_STRUCT(name).set_command_line(), \ FLAG_MEMBER_SET(name)(value, JVMFlag::COMMAND_LINE)) #define FLAG_SET_ERGO(name, value) (FLAG_MEMBER_SET(name)(value, JVMFlag::ERGONOMIC)) #define FLAG_SET_MGMT(name, value) (FLAG_MEMBER_SET(name)(value, JVMFlag::MANAGEMENT)) #define FLAG_SET_ERGO_IF_DEFAULT(name, value) \ do { \ if (FLAG_IS_DEFAULT(name)) { \ FLAG_SET_ERGO(name, value); \ } \ } while (0) #define JVMFLAG_FOR_EACH(f) \ for (f = JVMFlag::head(); f != NULL; f = f->next()) // ---- Macros for declaring VM flags in *globals.hpp files ----- #define ALL_FLAG_COMMON(type, name, attr, docs) \ typedef type FLAG_TYPE_##name; \ inline JVMFlag::FlagType FLAG_TYPE_NAME_##name() { return JVMFlag::TYPE_##type; } \ inline int FLAG_ATTR_##name() { return attr; } \ inline const char* FLAG_DOCS_##name() { return docs; } #define PRODUCT_FLAG_COMMON(type, name, attr, docs) \ ALL_FLAG_COMMON(type, name, attr, docs); \ extern ProductFlag FLAG_##name; \ inline JVMFlag::Error FLAG_MEMBER_SET(name)(type value, JVMFlag::Flags origin) { return JVMFlag::type##AtPut(&FLAG_##name, value, origin); } \ extern "C" type name #define PRODUCT_FLAG(type, name, default_value, attr, docs) \ inline type FLAG_DEFVAL_##name() { return default_value; } \ PRODUCT_FLAG_COMMON(type, name, attr, docs) #define PRODUCT_FLAG_PD(type, name, attr, docs) \ PRODUCT_FLAG_COMMON(type, name, ((attr) | JVMFlag::PLATFORM_DEPENDENT), docs) #ifdef PRODUCT // In product builds, meta information for DEVELOP_FLAG is stored in DevelopFlag #define DEVELOP_FLAG_COMMON(type, name, attr, docs) \ ALL_FLAG_COMMON(type, name, attr, docs); \ extern DevelopFlag FLAG_##name #define DEVELOP_FLAG(type, name, default_value, attr, docs) \ DEVELOP_FLAG_COMMON(type, name, ((attr) | JVMFlag::DEVELOP), docs); \ const type name = default_value #define DEVELOP_FLAG_PD(type, name, attr, docs) \ DEVELOP_FLAG_COMMON(type, name, ((attr) | JVMFlag::DEVELOP | JVMFlag::PLATFORM_DEPENDENT), docs); \ const type name = pd_##name // In product builds, NOTPROD_FLAGs are not visible at all, but we // emit a typedef to make the FLAG_RANGE macro happy. #define NOTPROD_FLAG(type, name, default_value, attr, docs) \ typedef type FLAG_TYPE_##name #else // !defined(PRODUCT) // In develop builds, DEVELOP_FLAG and NOTPROD_FLAG are equivalent to PRODUCT_FLAG #define NOTPROD_FLAG(type, name, default_value, attr, docs) \ PRODUCT_FLAG(type, name, default_value, ((attr) | JVMFlag::NOT_PRODUCT) , docs) #define DEVELOP_FLAG(type, name, default_value, attr, docs) \ PRODUCT_FLAG(type, name, default_value, ((attr) | JVMFlag::DEVELOP), docs) #define DEVELOP_FLAG_PD(type, name, attr, docs) \ PRODUCT_FLAG_PD(type, name, ((attr) | JVMFlag::DEVELOP), docs) #endif // PRODUCT // ---- Macros for specifying VM flags constraints in *globals.hpp files ----- #define FLAG_RANGE(name, min, max) \ inline FLAG_TYPE_##name FLAG_MIN_##name() { return min;} \ inline FLAG_TYPE_##name FLAG_MAX_##name() { return max;} #define FLAG_CONSTRAINT(name, func, phase) \ inline void* FLAG_CONSTRAINT_FUNC_##name() { return (void*)func;} \ inline JVMFlag::ConstraintPhase FLAG_CONSTRAINT_PHASE_##name() { return phase; } class JVMFlagCustomRange { JVMFlagCustomRange* _next; static JVMFlagCustomRange* _head; protected: virtual void init() = 0; public: JVMFlagCustomRange() { _next = _head; _head = this; } static void init_all() { for (JVMFlagCustomRange* r = _head; r; r = r->_next) { r->init(); } } }; class JVMFlagRange_VMPageSize : public JVMFlagCustomRange { JVMFlagRange _r; protected: virtual void init(); public: JVMFlagRange_VMPageSize(TypedJVMFlag* f) : JVMFlagCustomRange(), _r(f, 0, 0) {} }; class JVMFlagRange_VMAllocationGranularity : public JVMFlagCustomRange { JVMFlagRange _r; protected: virtual void init(); public: JVMFlagRange_VMAllocationGranularity(TypedJVMFlag* f) : JVMFlagCustomRange(), _r(f, 0, 0) {} }; #define FLAG_CUSTOM_RANGE(name, which) \ typedef JVMFlagRange_##which FLAG_RANGE_TYPE_##name #endif // SHARE_RUNTIME_FLAGS_JVMFLAG_HPP