8282773: Refactor parsing of integer VM options

Reviewed-by: dholmes, kbarrett
This commit is contained in:
Ioi Lam 2022-03-17 17:50:20 +00:00
parent 5ef1990d6c
commit b004fb0550
4 changed files with 529 additions and 205 deletions

View File

@ -40,6 +40,7 @@
#include "logging/logStream.hpp"
#include "logging/logTag.hpp"
#include "memory/allocation.inline.hpp"
#include "metaprogramming/enableIf.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
@ -56,6 +57,7 @@
#include "services/management.hpp"
#include "services/nmtCommon.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
#include "utilities/defaultStream.hpp"
#include "utilities/macros.hpp"
#include "utilities/powerOfTwo.hpp"
@ -63,6 +65,7 @@
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
#include <limits>
#define DEFAULT_JAVA_LAUNCHER "generic"
@ -744,20 +747,84 @@ bool Arguments::verify_special_jvm_flags(bool check_globals) {
}
#endif
// Parses a size specification string.
bool Arguments::atojulong(const char *s, julong* result) {
julong n = 0;
template <typename T, ENABLE_IF(std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 4)> // signed 32-bit
static bool parse_integer_impl(const char *s, char **endptr, int base, T* result) {
// Don't use strtol -- on 64-bit builds, "long" could be either 32- or 64-bits
// so the range tests could be tautological and might cause compiler warnings.
STATIC_ASSERT(sizeof(long long) >= 8); // C++ specification
errno = 0; // errno is thread safe
long long v = strtoll(s, endptr, base);
if (errno != 0 || v < min_jint || v > max_jint) {
return false;
}
*result = static_cast<T>(v);
return true;
}
// First char must be a digit. Don't allow negative numbers or leading spaces.
if (!isdigit(*s)) {
template <typename T, ENABLE_IF(!std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 4)> // unsigned 32-bit
static bool parse_integer_impl(const char *s, char **endptr, int base, T* result) {
if (s[0] == '-') {
return false;
}
// Don't use strtoul -- same reason as above.
STATIC_ASSERT(sizeof(unsigned long long) >= 8); // C++ specification
errno = 0; // errno is thread safe
unsigned long long v = strtoull(s, endptr, base);
if (errno != 0 || v > max_juint) {
return false;
}
*result = static_cast<T>(v);
return true;
}
template <typename T, ENABLE_IF(std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 8)> // signed 64-bit
static bool parse_integer_impl(const char *s, char **endptr, int base, T* result) {
errno = 0; // errno is thread safe
*result = strtoll(s, endptr, base);
return errno == 0;
}
template <typename T, ENABLE_IF(!std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 8)> // unsigned 64-bit
static bool parse_integer_impl(const char *s, char **endptr, int base, T* result) {
if (s[0] == '-') {
return false;
}
errno = 0; // errno is thread safe
*result = strtoull(s, endptr, base);
return errno == 0;
}
template<typename T>
static bool multiply_by_1k(T& n) {
if (n >= std::numeric_limits<T>::min() / 1024 &&
n <= std::numeric_limits<T>::max() / 1024) {
n *= 1024;
return true;
} else {
return false;
}
}
// All of the integral types that can be used for command line options:
// int, uint, intx, uintx, uint64_t, size_t
//
// In all supported platforms, these types can be mapped to only 4 native types:
// {signed, unsigned} x {32-bit, 64-bit}
//
// We use SFINAE to pick the correct parse_integer_impl() function
template<typename T>
static bool parse_integer(const char *s, T* result) {
if (!isdigit(s[0]) && s[0] != '-') {
// strtoll/strtoull may allow leading spaces. Forbid it.
return false;
}
bool is_hex = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'));
T n = 0;
bool is_hex = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ||
(s[0] == '-' && s[1] == '0' && (s[2] == 'x' || s[3] == 'X'));
char* remainder;
errno = 0;
n = strtoull(s, &remainder, (is_hex ? 16 : 10));
if (errno != 0) {
if (!parse_integer_impl(s, &remainder, (is_hex ? 16 : 10), &n)) {
return false;
}
@ -768,28 +835,29 @@ bool Arguments::atojulong(const char *s, julong* result) {
switch (*remainder) {
case 'T': case 't':
*result = n * G * K;
// Check for overflow.
if (*result/((julong)G * K) != n) return false;
return true;
if (!multiply_by_1k(n)) return false;
// fall-through
case 'G': case 'g':
*result = n * G;
if (*result/G != n) return false;
return true;
if (!multiply_by_1k(n)) return false;
// fall-through
case 'M': case 'm':
*result = n * M;
if (*result/M != n) return false;
return true;
if (!multiply_by_1k(n)) return false;
// fall-through
case 'K': case 'k':
*result = n * K;
if (*result/K != n) return false;
return true;
if (!multiply_by_1k(n)) return false;
break;
case '\0':
*result = n;
return true;
break;
default:
return false;
}
*result = n;
return true;
}
bool Arguments::atojulong(const char *s, julong* result) {
return parse_integer(s, result);
}
Arguments::ArgsRange Arguments::check_memory_size(julong size, julong min_size, julong max_size) {
@ -838,72 +906,57 @@ static bool set_fp_numeric_flag(JVMFlag* flag, char* value, JVMFlagOrigin origin
return false;
}
static bool set_numeric_flag(JVMFlag* flag, char* value, JVMFlagOrigin origin) {
julong v;
int int_v;
intx intx_v;
bool is_neg = false;
static JVMFlag::Error set_numeric_flag(JVMFlag* flag, char* value, JVMFlagOrigin origin) {
if (flag == NULL) {
return false;
return JVMFlag::INVALID_FLAG;
}
// Check the sign first since atojulong() parses only unsigned values.
if (*value == '-') {
if (!flag->is_intx() && !flag->is_int()) {
return false;
}
value++;
is_neg = true;
}
if (!Arguments::atojulong(value, &v)) {
return false;
}
if (flag->is_int()) {
int_v = (int) v;
if (is_neg) {
int_v = -int_v;
int v;
if (parse_integer(value, &v)) {
return JVMFlagAccess::set_int(flag, &v, origin);
}
if ((!is_neg && v > max_jint) || (is_neg && -(intx)v < min_jint)) {
return false;
}
return JVMFlagAccess::set_int(flag, &int_v, origin) == JVMFlag::SUCCESS;
} else if (flag->is_uint()) {
if (v > max_juint) {
return false;
uint v;
if (parse_integer(value, &v)) {
return JVMFlagAccess::set_uint(flag, &v, origin);
}
uint uint_v = (uint) v;
return JVMFlagAccess::set_uint(flag, &uint_v, origin) == JVMFlag::SUCCESS;
} else if (flag->is_intx()) {
intx_v = (intx) v;
if (is_neg) {
if (intx_v != min_intx) {
intx_v = - intx_v;
if (intx_v > 0) {
return false; // underflow
}
}
} else {
if (intx_v < 0) {
return false; // overflow
}
intx v;
if (parse_integer(value, &v)) {
return JVMFlagAccess::set_intx(flag, &v, origin);
}
return JVMFlagAccess::set_intx(flag, &intx_v, origin) == JVMFlag::SUCCESS;
} else if (flag->is_uintx()) {
uintx uintx_v = (uintx) v;
return JVMFlagAccess::set_uintx(flag, &uintx_v, origin) == JVMFlag::SUCCESS;
uintx v;
if (parse_integer(value, &v)) {
return JVMFlagAccess::set_uintx(flag, &v, origin);
}
} else if (flag->is_uint64_t()) {
uint64_t uint64_t_v = (uint64_t) v;
return JVMFlagAccess::set_uint64_t(flag, &uint64_t_v, origin) == JVMFlag::SUCCESS;
uint64_t v;
if (parse_integer(value, &v)) {
return JVMFlagAccess::set_uint64_t(flag, &v, origin);
}
} else if (flag->is_size_t()) {
size_t size_t_v = (size_t) v;
return JVMFlagAccess::set_size_t(flag, &size_t_v, origin) == JVMFlag::SUCCESS;
size_t v;
if (parse_integer(value, &v)) {
return JVMFlagAccess::set_size_t(flag, &v, origin);
}
} else if (flag->is_double()) {
double double_v = (double) v;
return JVMFlagAccess::set_double(flag, &double_v, origin) == JVMFlag::SUCCESS;
} else {
return false;
// This function parses only input strings without a decimal
// point character (.)
// If a string looks like a FP number, it would be parsed by
// set_fp_numeric_flag(). See Arguments::parse_argument().
jlong v;
if (parse_integer(value, &v)) {
double double_v = (double) v;
if (value[0] == '-' && v == 0) { // special case: 0.0 is different than -0.0.
double_v = -0.0;
}
return JVMFlagAccess::set_double(flag, &double_v, origin);
}
}
return JVMFlag::WRONG_FORMAT;
}
static bool set_string_flag(JVMFlag* flag, const char* value, JVMFlagOrigin origin) {
@ -1065,7 +1118,7 @@ bool Arguments::parse_argument(const char* arg, JVMFlagOrigin origin) {
return false;
}
JVMFlag* flag = JVMFlag::find_flag(real_name);
return set_numeric_flag(flag, value, origin);
return set_numeric_flag(flag, value, origin) == JVMFlag::SUCCESS;
}
return false;
@ -2082,24 +2135,16 @@ static const char* system_assertion_options[] = {
bool Arguments::parse_uintx(const char* value,
uintx* uintx_arg,
uintx min_size) {
// Check the sign first since atojulong() parses only unsigned values.
bool value_is_positive = !(*value == '-');
if (value_is_positive) {
julong n;
bool good_return = atojulong(value, &n);
if (good_return) {
bool above_minimum = n >= min_size;
bool value_is_too_large = n > max_uintx;
if (above_minimum && !value_is_too_large) {
*uintx_arg = n;
return true;
}
}
uintx n;
if (!parse_integer(value, &n)) {
return false;
}
if (n >= min_size) {
*uintx_arg = n;
return true;
} else {
return false;
}
return false;
}
bool Arguments::create_module_property(const char* prop_name, const char* prop_value, PropertyInternal internal) {
@ -2150,7 +2195,7 @@ Arguments::ArgsRange Arguments::parse_memory_size(const char* s,
julong* long_arg,
julong min_size,
julong max_size) {
if (!atojulong(s, long_arg)) return arg_unreadable;
if (!parse_integer(s, long_arg)) return arg_unreadable;
return check_memory_size(*long_arg, min_size, max_size);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, 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
@ -62,6 +62,29 @@
product(ccstr, DummyManageableStringFlag, NULL, MANAGEABLE, \
"Dummy flag for testing string handling in WriteableFlags") \
\
product(bool, TestFlagFor_bool, false, \
"Used by VM internal regression tests only") \
\
product(int, TestFlagFor_int, 0, \
"Used by VM internal regression tests only") \
\
product(uint, TestFlagFor_uint, 0, \
"Used by VM internal regression tests only") \
\
product(intx, TestFlagFor_intx, 0, \
"Used by VM internal regression tests only") \
\
product(uintx, TestFlagFor_uintx, 0, \
"Used by VM internal regression tests only") \
\
product(uint64_t, TestFlagFor_uint64_t, 0, \
"Used by VM internal regression tests only") \
\
product(size_t, TestFlagFor_size_t, 0, \
"Used by VM internal regression tests only") \
\
product(double, TestFlagFor_double, 0.0, \
"Used by VM internal regression tests only") \
// end of DEBUG_RUNTIME_FLAGS

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2022, 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
@ -25,6 +25,7 @@
#include "jvm.h"
#include "unittest.hpp"
#include "runtime/arguments.hpp"
#include "runtime/flags/jvmFlag.hpp"
#include "utilities/align.hpp"
#include "utilities/globalDefinitions.hpp"
@ -41,6 +42,16 @@ public:
static jint parse_xss(const JavaVMOption* option, const char* tail, intx* out_ThreadStackSize) {
return Arguments::parse_xss(option, tail, out_ThreadStackSize);
}
static bool parse_argument(const char* name, const char* value) {
char buf[1024];
int ret = jio_snprintf(buf, sizeof(buf), "%s=%s", name, value);
if (ret > 0) {
return Arguments::parse_argument(buf, JVMFlagOrigin::COMMAND_LINE);
} else {
return false;
}
}
};
TEST_F(ArgumentsTest, atojulong) {
@ -201,3 +212,356 @@ TEST_VM_F(ArgumentsTest, parse_xss) {
EXPECT_EQ(parse_xss_inner(to_string(K + 1), JNI_OK), calc_expected(K + 1));
}
}
struct Dummy {};
static Dummy BAD;
template <typename T>
struct NumericArgument {
bool bad;
const char* str;
T expected_value;
NumericArgument(const char* s, T v) : bad(false), str(s), expected_value(v) {}
NumericArgument(const char* s, Dummy & dummy) : bad(true), str(s), expected_value(0) {}
};
static void check_invalid_numeric_string(JVMFlag* flag, const char** invalid_strings) {
for (uint i = 0; ; i++) {
const char* str = invalid_strings[i];
if (str == NULL) {
return;
}
ASSERT_FALSE(ArgumentsTest::parse_argument(flag->name(), str))
<< "Invalid string '" << str
<< "' parsed without error for type " << flag->type_string() << ".";
}
}
template <typename T>
void check_numeric_flag(JVMFlag* flag, T getvalue(JVMFlag* flag),
NumericArgument<T>* valid_args, size_t n,
bool is_double = false) {
for (size_t i = 0; i < n; i++) {
NumericArgument<T>* info = &valid_args[i];
const char* str = info->str;
if (info->bad) {
ASSERT_FALSE(ArgumentsTest::parse_argument(flag->name(), str))
<< "Invalid string '" << str
<< "' parsed without error for type " << flag->type_string() << ".";
} else {
ASSERT_TRUE(ArgumentsTest::parse_argument(flag->name(), str))
<< "Valid string '" <<
str << "' did not parse for type " << flag->type_string() << ".";
ASSERT_EQ(getvalue(flag), info->expected_value)
<< "Valid string '" << str
<< "' did not parse to the correct value for type "
<< flag->type_string() << ".";
}
}
{
// Invalid strings for *any* type of integer VM arguments
const char* invalid_strings[] = {
"", " 1", "2 ", "3 2",
"0x", "0x0x1" "e"
"K", "M", "G", "1MB", "1KM", "AA", "0B",
"18446744073709551615K", "17179869184G",
"999999999999999999999999999999",
"0x10000000000000000", "18446744073709551616",
"-0x10000000000000000", "-18446744073709551616",
"-0x8000000000000001", "-9223372036854775809",
"0x8000000t", "0x800000000g",
"0x800000000000m", "0x800000000000000k",
"-0x8000000t", "-0x800000000g",
"-0x800000000000m", "-0x800000000000000k",
NULL,
};
check_invalid_numeric_string(flag, invalid_strings);
}
if (!is_double) {
const char* invalid_strings_for_integers[] = {
"1.0", "0x4.5", "0.001", "4e10",
NULL,
};
check_invalid_numeric_string(flag, invalid_strings_for_integers);
}
}
#define INTEGER_TEST_TABLE(f) \
/*input i32 u32 i64 u64 */ \
f("0", 0, 0, 0, 0 ) \
f("-0", 0, BAD, 0, BAD ) \
f("-1", -1, BAD, -1, BAD ) \
f("0x1", 1, 1, 1, 1 ) \
f("-0x1", -1, BAD, -1, BAD ) \
f("4711", 4711, 4711, 4711, 4711 ) \
f("1K", 1024, 1024, 1024, 1024 ) \
f("1k", 1024, 1024, 1024, 1024 ) \
f("2M", 2097152, 2097152, 2097152, 2097152 ) \
f("2m", 2097152, 2097152, 2097152, 2097152 ) \
f("1G", 1073741824, 1073741824, 1073741824, 1073741824 ) \
f("2G", BAD, 0x80000000, 2147483648LL, 2147483648ULL ) \
f("1T", BAD, BAD, 1099511627776LL, 1099511627776ULL ) \
f("1t", BAD, BAD, 1099511627776LL, 1099511627776ULL ) \
f("-1K", -1024, BAD, -1024, BAD ) \
f("0x1K", 1024, 1024, 1024, 1024 ) \
f("-0x1K", -1024, BAD, -1024, BAD ) \
f("0K", 0, 0, 0, 0 ) \
f("0x1000000k", BAD, BAD, 17179869184LL, 17179869184ULL ) \
f("0x800000m", BAD, BAD, 0x80000000000LL, 0x80000000000ULL ) \
f("0x8000g", BAD, BAD, 0x200000000000LL, 0x200000000000ULL ) \
f("0x8000t", BAD, BAD, 0x80000000000000LL, 0x80000000000000ULL ) \
f("-0x1000000k", BAD, BAD, -17179869184LL, BAD ) \
f("-0x800000m", BAD, BAD, -0x80000000000LL, BAD ) \
f("-0x8000g", BAD, BAD, -0x200000000000LL, BAD ) \
f("-0x8000t", BAD, BAD, -0x80000000000000LL, BAD ) \
f("0x7fffffff", 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff ) \
f("0xffffffff", BAD, 0xffffffff, 0xffffffff, 0xffffffff ) \
f("0x80000000", BAD, 0x80000000, 0x80000000, 0x80000000 ) \
f("-0x7fffffff", -2147483647, BAD, -2147483647LL, BAD ) \
f("-0x80000000", -2147483648, BAD, -2147483648LL, BAD ) \
f("-0x80000001", BAD, BAD, -2147483649LL, BAD ) \
f("0x100000000", BAD, BAD, 0x100000000LL, 0x100000000ULL ) \
f("0xcafebabe", BAD, 0xcafebabe, 0xcafebabe, 0xcafebabe ) \
f("0XCAFEBABE", BAD, 0xcafebabe, 0xcafebabe, 0xcafebabe ) \
f("0XCAFEbabe", BAD, 0xcafebabe, 0xcafebabe, 0xcafebabe ) \
f("0xcafebabe1", BAD, BAD, 0xcafebabe1, 0xcafebabe1 ) \
f("0x7fffffffffffffff", BAD, BAD, max_jlong, 9223372036854775807ULL ) \
f("0x8000000000000000", BAD, BAD, BAD, 9223372036854775808ULL ) \
f("0xffffffffffffffff", BAD, BAD, BAD, max_julong ) \
f("9223372036854775807", BAD, BAD, 9223372036854775807LL, 9223372036854775807ULL ) \
f("9223372036854775808", BAD, BAD, BAD, 9223372036854775808ULL ) \
f("-9223372036854775808", BAD, BAD, min_jlong, BAD ) \
f("18446744073709551615", BAD, BAD, BAD, max_julong ) \
\
/* All edge cases without a k/m/g/t suffix */ \
f("0x7ffffffe", max_jint-1, 0x7ffffffe, 0x7ffffffeLL, 0x7ffffffeULL ) \
f("0x7fffffff", max_jint, 0x7fffffff, 0x7fffffffLL, 0x7fffffffULL ) \
f("0x80000000", BAD, 0x80000000, 0x80000000LL, 0x80000000ULL ) \
f("0xfffffffe", BAD, max_juint-1, 0xfffffffeLL, 0xfffffffeULL ) \
f("0xffffffff", BAD, max_juint, 0xffffffffLL, 0xffffffffULL ) \
f("0x100000000", BAD, BAD, 0x100000000LL, 0x100000000ULL ) \
f("-0x7fffffff", min_jint+1, BAD, -0x7fffffffLL, BAD ) \
f("-0x80000000", min_jint, BAD, -0x80000000LL, BAD ) \
f("-0x80000001", BAD, BAD, -0x80000001LL, BAD ) \
\
f("0x7ffffffffffffffe", BAD, BAD, max_jlong-1, 0x7ffffffffffffffeULL ) \
f("0x7fffffffffffffff", BAD, BAD, max_jlong, 0x7fffffffffffffffULL ) \
f("0x8000000000000000", BAD, BAD, BAD, 0x8000000000000000ULL ) \
f("0xfffffffffffffffe", BAD, BAD, BAD, max_julong-1 ) \
f("0xffffffffffffffff", BAD, BAD, BAD, max_julong ) \
f("0x10000000000000000", BAD, BAD, BAD, BAD ) \
f("-0x7fffffffffffffff", BAD, BAD, min_jlong+1, BAD ) \
f("-0x8000000000000000", BAD, BAD, min_jlong, BAD ) \
f("-0x8000000000000001", BAD, BAD, BAD, BAD ) \
\
/* edge cases for suffix: K */ \
f("0x1ffffek", 0x1ffffe * k, 0x1ffffeU * k,0x1ffffeLL * k, 0x1ffffeULL * k ) \
f("0x1fffffk", 0x1fffff * k, 0x1fffffU * k,0x1fffffLL * k, 0x1fffffULL * k ) \
f("0x200000k", BAD, 0x200000U * k,0x200000LL * k, 0x200000ULL * k ) \
f("0x3ffffek", BAD, 0x3ffffeU * k,0x3ffffeLL * k, 0x3ffffeULL * k ) \
f("0x3fffffk", BAD, 0x3fffffU * k,0x3fffffLL * k, 0x3fffffULL * k ) \
f("0x400000k", BAD, BAD, 0x400000LL * k, 0x400000ULL * k ) \
f("-0x1fffffk", -0x1fffff * k, BAD, -0x1fffffLL * k, BAD ) \
f("-0x200000k", -0x200000 * k, BAD, -0x200000LL * k, BAD ) \
f("-0x200001k", BAD, BAD, -0x200001LL * k, BAD ) \
\
f("0x1ffffffffffffek", BAD, BAD, 0x1ffffffffffffeLL * k, 0x1ffffffffffffeULL * k ) \
f("0x1fffffffffffffk", BAD, BAD, 0x1fffffffffffffLL * k, 0x1fffffffffffffULL * k ) \
f("0x20000000000000k", BAD, BAD, BAD, 0x20000000000000ULL * k ) \
f("0x3ffffffffffffek", BAD, BAD, BAD, 0x3ffffffffffffeULL * k ) \
f("0x3fffffffffffffk", BAD, BAD, BAD, 0x3fffffffffffffULL * k ) \
f("0x40000000000000k", BAD, BAD, BAD, BAD ) \
f("-0x1fffffffffffffk", BAD, BAD, -0x1fffffffffffffLL * k, BAD ) \
f("-0x20000000000000k", BAD, BAD, -0x20000000000000LL * k, BAD ) \
f("-0x20000000000001k", BAD, BAD, BAD, BAD ) \
\
/* edge cases for suffix: M */ \
f("0x7fem", 0x7fe * m, 0x7feU * m, 0x7feLL * m, 0x7feULL * m ) \
f("0x7ffm", 0x7ff * m, 0x7ffU * m, 0x7ffLL * m, 0x7ffULL * m ) \
f("0x800m", BAD, 0x800U * m, 0x800LL * m, 0x800ULL * m ) \
f("0xffem", BAD, 0xffeU * m, 0xffeLL * m, 0xffeULL * m ) \
f("0xfffm", BAD, 0xfffU * m, 0xfffLL * m, 0xfffULL * m ) \
f("0x1000m", BAD, BAD, 0x1000LL * m, 0x1000ULL * m ) \
f("-0x7ffm", -0x7ff * m, BAD, -0x7ffLL * m, BAD ) \
f("-0x800m", -0x800 * m, BAD, -0x800LL * m, BAD ) \
f("-0x801m", BAD, BAD, -0x801LL * m, BAD ) \
\
f("0x7fffffffffem", BAD, BAD, 0x7fffffffffeLL * m, 0x7fffffffffeULL * m ) \
f("0x7ffffffffffm", BAD, BAD, 0x7ffffffffffLL * m, 0x7ffffffffffULL * m ) \
f("0x80000000000m", BAD, BAD, BAD, 0x80000000000ULL * m ) \
f("0xffffffffffem", BAD, BAD, BAD, 0xffffffffffeULL * m ) \
f("0xfffffffffffm", BAD, BAD, BAD, 0xfffffffffffULL * m ) \
f("0x100000000000m", BAD, BAD, BAD, BAD ) \
f("-0x7ffffffffffm", BAD, BAD, -0x7ffffffffffLL * m, BAD ) \
f("-0x80000000000m", BAD, BAD, -0x80000000000LL * m, BAD ) \
f("-0x80000000001m", BAD, BAD, BAD, BAD ) \
\
/* edge cases for suffix: G */ \
f("0x0g", 0x0 * g, 0x0U * g, 0x0LL * g, 0x0ULL * g ) \
f("0x1g", 0x1 * g, 0x1U * g, 0x1LL * g, 0x1ULL * g ) \
f("0x2g", BAD, 0x2U * g, 0x2LL * g, 0x2ULL * g ) \
f("0x3g", BAD, 0x3U * g, 0x3LL * g, 0x3ULL * g ) \
f("0x4g", BAD, BAD, 0x4LL * g, 0x4ULL * g ) \
f("-0x1g", -0x1 * g, BAD, -0x1LL * g, BAD ) \
f("-0x2g", -0x2 * g, BAD, -0x2LL * g, BAD ) \
f("-0x3g", BAD, BAD, -0x3LL * g, BAD ) \
\
f("0x1fffffffeg", BAD, BAD, 0x1fffffffeLL * g, 0x1fffffffeULL * g ) \
f("0x1ffffffffg", BAD, BAD, 0x1ffffffffLL * g, 0x1ffffffffULL * g ) \
f("0x200000000g", BAD, BAD, BAD, 0x200000000ULL * g ) \
f("0x3fffffffeg", BAD, BAD, BAD, 0x3fffffffeULL * g ) \
f("0x3ffffffffg", BAD, BAD, BAD, 0x3ffffffffULL * g ) \
f("0x400000000g", BAD, BAD, BAD, BAD ) \
f("-0x1ffffffffg", BAD, BAD, -0x1ffffffffLL * g, BAD ) \
f("-0x200000000g", BAD, BAD, -0x200000000LL * g, BAD ) \
f("-0x200000001g", BAD, BAD, BAD, BAD ) \
\
/* edge cases for suffix: T */ \
f("0x7ffffet", BAD, BAD, 0x7ffffeLL * t, 0x7ffffeULL * t ) \
f("0x7ffffft", BAD, BAD, 0x7fffffLL * t, 0x7fffffULL * t ) \
f("0x800000t", BAD, BAD, BAD, 0x800000ULL * t ) \
f("0xfffffet", BAD, BAD, BAD, 0xfffffeULL * t ) \
f("0xfffffft", BAD, BAD, BAD, 0xffffffULL * t ) \
f("0x1000000t", BAD, BAD, BAD, BAD ) \
f("-0x7ffffft", BAD, BAD, -0x7fffffLL * t, BAD ) \
f("-0x800000t", BAD, BAD, -0x800000LL * t, BAD ) \
f("-0x800001t", BAD, BAD, BAD, BAD )
#define INTEGER_TEST_i32(s, i32, u32, i64, u64) NumericArgument<T>(s, i32),
#define INTEGER_TEST_u32(s, i32, u32, i64, u64) NumericArgument<T>(s, u32),
#define INTEGER_TEST_i64(s, i32, u32, i64, u64) NumericArgument<T>(s, i64),
#define INTEGER_TEST_u64(s, i32, u32, i64, u64) NumericArgument<T>(s, u64),
// signed 32-bit
template <typename T, ENABLE_IF(std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 4)>
void check_flag(const char* f, T getvalue(JVMFlag* flag)) {
JVMFlag* flag = JVMFlag::find_flag(f);
if (flag == NULL) { // not available in product builds
return;
}
T k = static_cast<T>(K);
T m = static_cast<T>(M);
T g = static_cast<T>(G);
NumericArgument<T> valid_strings[] = { INTEGER_TEST_TABLE(INTEGER_TEST_i32) };
check_numeric_flag(flag, getvalue, valid_strings, ARRAY_SIZE(valid_strings));
}
// unsigned 32-bit
template <typename T, ENABLE_IF(!std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 4)>
void check_flag(const char* f, T getvalue(JVMFlag* flag)) {
JVMFlag* flag = JVMFlag::find_flag(f);
if (flag == NULL) { // not available in product builds
return;
}
T k = static_cast<T>(K);
T m = static_cast<T>(M);
T g = static_cast<T>(G);
NumericArgument<T> valid_strings[] = { INTEGER_TEST_TABLE(INTEGER_TEST_u32) };
check_numeric_flag(flag, getvalue, valid_strings, ARRAY_SIZE(valid_strings));
}
// signed 64-bit
template <typename T, ENABLE_IF(std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 8)>
void check_flag(const char* f, T getvalue(JVMFlag* flag)) {
JVMFlag* flag = JVMFlag::find_flag(f);
if (flag == NULL) { // not available in product builds
return;
}
T k = static_cast<T>(K);
T m = static_cast<T>(M);
T g = static_cast<T>(G);
T t = static_cast<T>(G) * k;
NumericArgument<T> valid_strings[] = { INTEGER_TEST_TABLE(INTEGER_TEST_i64) };
check_numeric_flag(flag, getvalue, valid_strings, ARRAY_SIZE(valid_strings));
}
// unsigned 64-bit
template <typename T, ENABLE_IF(!std::is_signed<T>::value), ENABLE_IF(sizeof(T) == 8)>
void check_flag(const char* f, T getvalue(JVMFlag* flag)) {
JVMFlag* flag = JVMFlag::find_flag(f);
if (flag == NULL) { // not available in product builds
return;
}
T k = static_cast<T>(K);
T m = static_cast<T>(M);
T g = static_cast<T>(G);
T t = static_cast<T>(G) * k;
NumericArgument<T> valid_strings[] = { INTEGER_TEST_TABLE(INTEGER_TEST_u64) };
check_numeric_flag(flag, getvalue, valid_strings, ARRAY_SIZE(valid_strings));
}
// Testing the parsing of -XX:<SomeFlag>=<an integer value>
//
// All of the integral types that can be used for command line options:
// int, uint, intx, uintx, uint64_t, size_t
//
// In all supported platforms, these types can be mapped to only 4 native types:
// {signed, unsigned} x {32-bit, 64-bit}
//
// We use SFINAE to pick the correct column in the INTEGER_TEST_TABLE for each type.
TEST_VM_F(ArgumentsTest, set_numeric_flag_int) {
check_flag<int>("TestFlagFor_int", [] (JVMFlag* flag) {
return flag->get_int();
});
}
TEST_VM_F(ArgumentsTest, set_numeric_flag_uint) {
check_flag<uint>("TestFlagFor_uint", [] (JVMFlag* flag) {
return flag->get_uint();
});
}
TEST_VM_F(ArgumentsTest, set_numeric_flag_intx) {
check_flag<intx>("TestFlagFor_intx", [] (JVMFlag* flag) {
return flag->get_intx();
});
}
TEST_VM_F(ArgumentsTest, set_numeric_flag_uintx) {
check_flag<uintx>("TestFlagFor_uintx", [] (JVMFlag* flag) {
return flag->get_uintx();
});
}
TEST_VM_F(ArgumentsTest, set_numeric_flag_uint64_t) {
check_flag<uint64_t>("TestFlagFor_uint64_t", [] (JVMFlag* flag) {
return flag->get_uint64_t();
});
}
TEST_VM_F(ArgumentsTest, set_numeric_flag_size_t) {
check_flag<size_t>("TestFlagFor_size_t", [] (JVMFlag* flag) {
return flag->get_size_t();
});
}
TEST_VM_F(ArgumentsTest, set_numeric_flag_double) {
JVMFlag* flag = JVMFlag::find_flag("TestFlagFor_double");
if (flag == NULL) { // not available in product builds
return;
}
// TODO -- JDK-8282774
// Need to add more test input that have a fractional part like "4.2".
NumericArgument<double> valid_strings[] = {
NumericArgument<double>("0", 0.0),
NumericArgument<double>("1", 1.0),
NumericArgument<double>("-0", -0.0),
NumericArgument<double>("-1", -1.0),
};
auto getvalue = [] (JVMFlag* flag) {
return flag->get_double();
};
check_numeric_flag<double>(flag, getvalue, valid_strings,
ARRAY_SIZE(valid_strings), /*is_double=*/true);
}

View File

@ -1,108 +0,0 @@
/*
* Copyright (c) 2022, 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.
*/
#include "precompiled.hpp"
#include "compiler/compiler_globals.hpp"
#include "runtime/arguments.hpp"
#include "runtime/flags/jvmFlag.hpp"
#include "runtime/globals.hpp"
#include "unittest.hpp"
class LargeOptionsTest : public ::testing::Test {
public:
static bool test_option_value(const char* option, intx value) {
char buffer[100];
UnlockDiagnosticVMOptions = true;
os::snprintf(buffer, 100, "%s=" INTX_FORMAT, option, value);
return Arguments::parse_argument(buffer, JVMFlagOrigin::COMMAND_LINE);
}
static bool test_option_value(const char* option) {
UnlockDiagnosticVMOptions = true;
return Arguments::parse_argument(option, JVMFlagOrigin::COMMAND_LINE);
}
};
#ifdef _LP64
// CompilerDirectivesLimit is a diagnostic int option.
TEST_VM(LARGE_OPTION, large_ints) {
for (intx x = max_jint - 1; x <= (intx)max_jint + 1; x++) {
bool result = LargeOptionsTest::test_option_value("CompilerDirectivesLimit", x);
if (x > max_jint) {
ASSERT_FALSE(result);
} else {
ASSERT_TRUE(result);
ASSERT_EQ(CompilerDirectivesLimit, x);
}
}
}
TEST_VM(LARGE_OPTION, small_ints) {
for (intx x = min_jint + 1; x >= (intx)min_jint - 1; x--) {
bool result = LargeOptionsTest::test_option_value("CompilerDirectivesLimit", x);
if (x < min_jint) {
ASSERT_FALSE(result);
} else {
ASSERT_TRUE(result);
ASSERT_EQ(CompilerDirectivesLimit, x);
}
}
}
TEST_VM(LARGE_OPTION, large_int_overflow) { // Test 0x100000000
ASSERT_FALSE(LargeOptionsTest::test_option_value("CompilerDirectivesLimit", 4294967296));
}
#endif
// HandshakeTimeout is a diagnostic uint option.
TEST_VM(LARGE_OPTION, large_uints) {
for (uintx x = max_juint - 1; x <= (uintx)max_juint + 1; x++) {
bool result = LargeOptionsTest::test_option_value("HandshakeTimeout", x);
if (x <= max_juint) {
ASSERT_TRUE(result);
ASSERT_EQ(HandshakeTimeout, x);
} else {
ASSERT_FALSE(result);
}
}
}
#ifdef _LP64
// MaxJNILocalCapacity is an intx option.
TEST_VM(LARGE_OPTION, large_intxs) {
// max_intx + 1 equals min_intx!
for (julong x = max_intx - 1; x <= (julong)max_intx + 1; x++) {
ASSERT_TRUE(LargeOptionsTest::test_option_value("MaxJNILocalCapacity", x));
ASSERT_EQ((julong)MaxJNILocalCapacity, x);
}
}
TEST_VM(LARGE_OPTION, small_intxs) {
ASSERT_TRUE(LargeOptionsTest::test_option_value("MaxJNILocalCapacity", min_intx + 1));
ASSERT_EQ(MaxJNILocalCapacity, -9223372036854775807);
ASSERT_TRUE(LargeOptionsTest::test_option_value("MaxJNILocalCapacity", min_intx));
ASSERT_EQ(MaxJNILocalCapacity, min_intx);
// Test value that's less than min_intx (-0x8000000000000001).
ASSERT_FALSE(LargeOptionsTest::test_option_value("MaxJNILocalCapacity=-9223372036854775809"));
}
#endif