From 72e1760d7ffd25cb9876f95e8a30815582b31780 Mon Sep 17 00:00:00 2001 From: nloodin Date: Thu, 15 Mar 2012 13:37:13 +0100 Subject: [PATCH] 7148488: Whitebox tests for the Diagnostic Framework Parser Reviewed-by: brutisso, sla, mgerdin --- make/bsd/makefiles/wb.make | 2 +- make/linux/makefiles/wb.make | 2 +- make/solaris/makefiles/wb.make | 2 +- make/windows/create_obj_files.sh | 2 + make/windows/makefiles/projectcreator.make | 1 + make/windows/makefiles/vm.make | 7 + make/windows/makefiles/wb.make | 2 +- .../tools/whitebox/sun/hotspot/WhiteBox.java | 2 + .../sun/hotspot/parser/DiagnosticCommand.java | 43 +++++ .../vm/prims/wbtestmethods/parserTests.cpp | 148 +++++++++++++++++ .../vm/prims/wbtestmethods/parserTests.hpp | 32 ++++ src/share/vm/prims/whitebox.cpp | 66 ++++++-- src/share/vm/prims/whitebox.hpp | 17 ++ src/share/vm/services/diagnosticArgument.cpp | 41 +++++ src/share/vm/services/diagnosticArgument.hpp | 9 ++ test/serviceability/ParserTest.java | 152 ++++++++++++++++++ 16 files changed, 515 insertions(+), 13 deletions(-) create mode 100644 src/share/tools/whitebox/sun/hotspot/parser/DiagnosticCommand.java create mode 100644 src/share/vm/prims/wbtestmethods/parserTests.cpp create mode 100644 src/share/vm/prims/wbtestmethods/parserTests.hpp create mode 100644 test/serviceability/ParserTest.java diff --git a/make/bsd/makefiles/wb.make b/make/bsd/makefiles/wb.make index 2ec216e6f..8298d871c 100644 --- a/make/bsd/makefiles/wb.make +++ b/make/bsd/makefiles/wb.make @@ -36,7 +36,7 @@ WB_JAVA_CLASSES = $(patsubst $(WBSRCDIR)/%,$(WB_JAVA_CLASSDIR)/%, \ $(patsubst %.java,%.class,$(WB_JAVA_SRCS))) $(WB_JAVA_CLASSDIR)/%.class: $(WBSRCDIR)/%.java $(WB_JAVA_CLASSDIR) - $(REMOTE) $(COMPILE.JAVAC) -nowarn -d $(WB_JAVA_CLASSDIR) $< + $(REMOTE) $(COMPILE.JAVAC) -sourcepath $(WBSRCDIR) -nowarn -d $(WB_JAVA_CLASSDIR) $< $(WB_JAR): $(WB_JAVA_CLASSES) $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(WB_JAVA_CLASSDIR)/ . diff --git a/make/linux/makefiles/wb.make b/make/linux/makefiles/wb.make index 2ec216e6f..8298d871c 100644 --- a/make/linux/makefiles/wb.make +++ b/make/linux/makefiles/wb.make @@ -36,7 +36,7 @@ WB_JAVA_CLASSES = $(patsubst $(WBSRCDIR)/%,$(WB_JAVA_CLASSDIR)/%, \ $(patsubst %.java,%.class,$(WB_JAVA_SRCS))) $(WB_JAVA_CLASSDIR)/%.class: $(WBSRCDIR)/%.java $(WB_JAVA_CLASSDIR) - $(REMOTE) $(COMPILE.JAVAC) -nowarn -d $(WB_JAVA_CLASSDIR) $< + $(REMOTE) $(COMPILE.JAVAC) -sourcepath $(WBSRCDIR) -nowarn -d $(WB_JAVA_CLASSDIR) $< $(WB_JAR): $(WB_JAVA_CLASSES) $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(WB_JAVA_CLASSDIR)/ . diff --git a/make/solaris/makefiles/wb.make b/make/solaris/makefiles/wb.make index 225978df4..a4a24ddc7 100644 --- a/make/solaris/makefiles/wb.make +++ b/make/solaris/makefiles/wb.make @@ -36,7 +36,7 @@ WB_JAVA_CLASSES = $(patsubst $(WBSRCDIR)/%,$(WB_JAVA_CLASSDIR)/%, \ $(patsubst %.java,%.class,$(WB_JAVA_SRCS))) $(WB_JAVA_CLASSDIR)/%.class: $(WBSRCDIR)/%.java $(WB_JAVA_CLASSDIR) - $(REMOTE) $(COMPILE.JAVAC) -nowarn -d $(WB_JAVA_CLASSDIR) $< + $(REMOTE) $(COMPILE.JAVAC) -sourcepath $(WBSRCDIR) -nowarn -d $(WB_JAVA_CLASSDIR) $< $(WB_JAR): $(WB_JAVA_CLASSES) $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(WB_JAVA_CLASSDIR)/ . diff --git a/make/windows/create_obj_files.sh b/make/windows/create_obj_files.sh index 61903387d..78333bf79 100644 --- a/make/windows/create_obj_files.sh +++ b/make/windows/create_obj_files.sh @@ -80,6 +80,8 @@ if [ -d "${ALTSRC}/share/vm/jfr" ]; then BASE_PATHS="${BASE_PATHS} ${ALTSRC}/share/vm/jfr" fi +BASE_PATHS="${BASE_PATHS} ${COMMONSRC}/share/vm/prims/wbtestmethods" + CORE_PATHS="${BASE_PATHS}" # shared is already in BASE_PATHS. Should add vm/memory but that one is also in BASE_PATHS. if [ -d "${ALTSRC}/share/vm/gc_implementation" ]; then diff --git a/make/windows/makefiles/projectcreator.make b/make/windows/makefiles/projectcreator.make index 0c110f928..ebc5a0a84 100644 --- a/make/windows/makefiles/projectcreator.make +++ b/make/windows/makefiles/projectcreator.make @@ -51,6 +51,7 @@ ProjectCreatorIncludesPRIVATE=\ -relativeInclude src\closed\cpu\$(Platform_arch)\vm \ -relativeInclude src\share\vm \ -relativeInclude src\share\vm\precompiled \ + -relativeInclude src\share\vm\prims\wbtestmethods \ -relativeInclude src\share\vm\prims \ -relativeInclude src\os\windows\vm \ -relativeInclude src\os_cpu\windows_$(Platform_arch)\vm \ diff --git a/make/windows/makefiles/vm.make b/make/windows/makefiles/vm.make index d3e226c88..ee878fe73 100644 --- a/make/windows/makefiles/vm.make +++ b/make/windows/makefiles/vm.make @@ -172,6 +172,7 @@ VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/asm VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/memory VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/oops VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/prims +VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/prims/wbtestmethods VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/runtime VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/services VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/trace @@ -269,6 +270,9 @@ bytecodeInterpreterWithChecks.obj: ..\generated\jvmtifiles\bytecodeInterpreterWi {$(COMMONSRC)\share\vm\prims}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< +{$(COMMONSRC)\share\vm\prims\wbtestmethods}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + {$(COMMONSRC)\share\vm\runtime}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< @@ -349,6 +353,9 @@ bytecodeInterpreterWithChecks.obj: ..\generated\jvmtifiles\bytecodeInterpreterWi {$(ALTSRC)\share\vm\prims}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< +{$(ALTSRC)\share\vm\prims\wbtestmethods}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + {$(ALTSRC)\share\vm\runtime}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< diff --git a/make/windows/makefiles/wb.make b/make/windows/makefiles/wb.make index b0add7227..76b4318bd 100644 --- a/make/windows/makefiles/wb.make +++ b/make/windows/makefiles/wb.make @@ -40,7 +40,7 @@ wb_java_srcs: $(WorkSpace)\src\share\tools\whitebox\sun\hotspot\*.java $(WB_CLAS {$(WorkSpace)\src\share\tools\whitebox\sun\hotspot}.java.class:: - $(COMPILE_JAVAC) -d $(WB_CLASSES) $< + $(COMPILE_JAVAC) -sourcepath $(WBSRCDIR) -d $(WB_CLASSES) $< $(WB_JAR): wb_java_srcs $(RUN_JAR) cf $@ -C $(WB_CLASSES) . diff --git a/src/share/tools/whitebox/sun/hotspot/WhiteBox.java b/src/share/tools/whitebox/sun/hotspot/WhiteBox.java index ee6a18d50..1495b73a2 100644 --- a/src/share/tools/whitebox/sun/hotspot/WhiteBox.java +++ b/src/share/tools/whitebox/sun/hotspot/WhiteBox.java @@ -24,6 +24,7 @@ package sun.hotspot; import java.security.BasicPermission; +import sun.hotspot.parser.DiagnosticCommand; public class WhiteBox { @@ -67,4 +68,5 @@ public class WhiteBox { public native boolean g1IsHumongous(Object o); public native long g1NumFreeRegions(); public native int g1RegionSize(); + public native Object[] parseCommandLine(String commandline, DiagnosticCommand[] args); } diff --git a/src/share/tools/whitebox/sun/hotspot/parser/DiagnosticCommand.java b/src/share/tools/whitebox/sun/hotspot/parser/DiagnosticCommand.java new file mode 100644 index 000000000..2099901a4 --- /dev/null +++ b/src/share/tools/whitebox/sun/hotspot/parser/DiagnosticCommand.java @@ -0,0 +1,43 @@ +package sun.hotspot.parser; + +public class DiagnosticCommand { + + public enum DiagnosticArgumentType { + JLONG, BOOLEAN, STRING, NANOTIME, STRINGARRAY, MEMORYSIZE + } + + private String name; + private String desc; + private DiagnosticArgumentType type; + private boolean mandatory; + private String defaultValue; + + public DiagnosticCommand(String name, String desc, DiagnosticArgumentType type, + boolean mandatory, String defaultValue) { + this.name = name; + this.desc = desc; + this.type = type; + this.mandatory = mandatory; + this.defaultValue = defaultValue; + } + + public String getName() { + return name; + } + + public String getDesc() { + return desc; + } + + public DiagnosticArgumentType getType() { + return type; + } + + public boolean isMandatory() { + return mandatory; + } + + public String getDefaultValue() { + return defaultValue; + } +} diff --git a/src/share/vm/prims/wbtestmethods/parserTests.cpp b/src/share/vm/prims/wbtestmethods/parserTests.cpp new file mode 100644 index 000000000..54c8a3173 --- /dev/null +++ b/src/share/vm/prims/wbtestmethods/parserTests.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2011, 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 "classfile/symbolTable.hpp" + +#include "prims/jni.h" +#include "prims/whitebox.hpp" +#include "prims/wbtestmethods/parserTests.hpp" +#include "runtime/interfaceSupport.hpp" + +#include "memory/oopFactory.hpp" + +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticFramework.hpp" + +//There's no way of beforeahnd knowing an upper size +//Of the length of a string representation of +//the value of an argument. +#define VALUE_MAXLEN 256 + +// DiagnosticFramework test utility methods + +/* + * The DiagnosticArgumentType class contains an enum that says which type + * this argument represents. (JLONG, BOOLEAN etc). + * This method Returns a char* representation of that enum value. + */ +static const char* lookup_diagnosticArgumentEnum(const char* field_name, oop object) { + Thread* THREAD = Thread::current(); + const char* enum_sig = "Lsun/hotspot/parser/DiagnosticCommand$DiagnosticArgumentType;"; + TempNewSymbol enumSigSymbol = SymbolTable::lookup(enum_sig, (int) strlen(enum_sig), THREAD); + int offset = WhiteBox::offset_for_field(field_name, object, enumSigSymbol); + oop enumOop = object->obj_field(offset); + + const char* ret = WhiteBox::lookup_jstring("name", enumOop); + return ret; +} + +/* + * Takes an oop to a DiagnosticArgumentType-instance and + * reads the fields from it. Fills an native DCmdParser with + * this info. + */ +static void fill_in_parser(DCmdParser* parser, oop argument) +{ + const char* name = WhiteBox::lookup_jstring("name", argument); + const char* desc = WhiteBox::lookup_jstring("desc", argument); + const char* default_value = WhiteBox::lookup_jstring("defaultValue", argument); + bool mandatory = WhiteBox::lookup_bool("mandatory", argument); + const char* type = lookup_diagnosticArgumentEnum("type", argument); + + if (strcmp(type, "STRING") == 0) { + DCmdArgument* argument = new DCmdArgument( + name, desc, + "STRING", mandatory, default_value); + parser->add_dcmd_option(argument); + } else if (strcmp(type, "NANOTIME") == 0) { + DCmdArgument* argument = new DCmdArgument( + name, desc, + "NANOTIME", mandatory, default_value); + parser->add_dcmd_option(argument); + } else if (strcmp(type, "JLONG") == 0) { + DCmdArgument* argument = new DCmdArgument( + name, desc, + "JLONG", mandatory, default_value); + parser->add_dcmd_option(argument); + } else if (strcmp(type, "BOOLEAN") == 0) { + DCmdArgument* argument = new DCmdArgument( + name, desc, + "BOOLEAN", mandatory, default_value); + parser->add_dcmd_option(argument); + } else if (strcmp(type, "MEMORYSIZE") == 0) { + DCmdArgument* argument = new DCmdArgument( + name, desc, + "MEMORY SIZE", mandatory, default_value); + parser->add_dcmd_option(argument); + } else if (strcmp(type, "STRINGARRAY") == 0) { + DCmdArgument* argument = new DCmdArgument( + name, desc, + "STRING SET", mandatory); + parser->add_dcmd_option(argument); + } +} + +/* + * Will Fill in a java object array with alternating names of parsed command line options and + * the value that has been parsed for it: + * { name, value, name, value ... } + * This can then be checked from java. + */ +WB_ENTRY(jobjectArray, WB_ParseCommandLine(JNIEnv* env, jobject o, jstring j_cmdline, jobjectArray arguments)) + ResourceMark rm; + DCmdParser parser; + + const char* c_cmdline = java_lang_String::as_utf8_string(JNIHandles::resolve(j_cmdline)); + objArrayOop argumentArray = objArrayOop(JNIHandles::resolve_non_null(arguments)); + + int length = argumentArray->length(); + + for (int i = 0; i < length; i++) { + oop argument_oop = argumentArray->obj_at(i); + fill_in_parser(&parser, argument_oop); + } + + CmdLine cmdline(c_cmdline, strlen(c_cmdline), true); + parser.parse(&cmdline,',',CHECK_NULL); + + klassOop k = SystemDictionary::Object_klass(); + objArrayOop returnvalue_array = oopFactory::new_objArray(k, parser.num_arguments() * 2, CHECK_NULL); + + GrowableArray*parsedArgNames = parser.argument_name_array(); + + for (int i = 0; i < parser.num_arguments(); i++) { + oop parsedName = java_lang_String::create_oop_from_str(parsedArgNames->at(i), CHECK_NULL); + returnvalue_array->obj_at_put(i*2, parsedName); + GenDCmdArgument* arg = parser.lookup_dcmd_option(parsedArgNames->at(i), strlen(parsedArgNames->at(i))); + char buf[VALUE_MAXLEN]; + arg->value_as_str(buf, sizeof(buf)); + oop parsedValue = java_lang_String::create_oop_from_str(buf, CHECK_NULL); + returnvalue_array->obj_at_put(i*2+1, parsedValue); + } + + return (jobjectArray) JNIHandles::make_local(returnvalue_array); + +WB_END diff --git a/src/share/vm/prims/wbtestmethods/parserTests.hpp b/src/share/vm/prims/wbtestmethods/parserTests.hpp new file mode 100644 index 000000000..8efdd9db1 --- /dev/null +++ b/src/share/vm/prims/wbtestmethods/parserTests.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012, 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_VM_PRIMS_WBTESTMETHODS_PARSERTESTS_H +#define SHARE_VM_PRIMS_WBTESTMETHODS_PARSERTESTS_H + +#include "prims/jni.h" +#include "prims/whitebox.hpp" + +WB_METHOD_DECLARE WB_ParseCommandLine(JNIEnv* env, jobject o, jstring args, jobjectArray arguments); + +#endif //SHARE_VM_PRIMS_WBTESTMETHODS_PARSERTESTS_H diff --git a/src/share/vm/prims/whitebox.cpp b/src/share/vm/prims/whitebox.cpp index 86dd7c506..eadc7df52 100644 --- a/src/share/vm/prims/whitebox.cpp +++ b/src/share/vm/prims/whitebox.cpp @@ -24,11 +24,14 @@ #include "precompiled.hpp" -#include "jni.h" - #include "memory/universe.hpp" #include "oops/oop.inline.hpp" + +#include "classfile/symbolTable.hpp" + #include "prims/whitebox.hpp" +#include "prims/wbtestmethods/parserTests.hpp" + #include "runtime/interfaceSupport.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" @@ -41,13 +44,6 @@ bool WhiteBox::_used = false; -// Entry macro to transition from JNI to VM state. - -#define WB_ENTRY(result_type, header) JNI_ENTRY(result_type, header) -#define WB_END JNI_END - -// Definitions of functions exposed via Whitebox API - WB_ENTRY(jlong, WB_GetObjectAddress(JNIEnv* env, jobject o, jobject obj)) return (jlong)(void*)JNIHandles::resolve(obj); WB_END @@ -81,11 +77,63 @@ WB_ENTRY(jint, WB_G1RegionSize(JNIEnv* env, jobject o)) WB_END #endif // !SERIALGC +//Some convenience methods to deal with objects from java +int WhiteBox::offset_for_field(const char* field_name, oop object, + Symbol* signature_symbol) { + assert(field_name != NULL && strlen(field_name) > 0, "Field name not valid"); + Thread* THREAD = Thread::current(); + + //Get the class of our object + klassOop arg_klass = object->klass(); + //Turn it into an instance-klass + instanceKlass* ik = instanceKlass::cast(arg_klass); + + //Create symbols to look for in the class + TempNewSymbol name_symbol = SymbolTable::lookup(field_name, (int) strlen(field_name), + THREAD); + + //To be filled in with an offset of the field we're looking for + fieldDescriptor fd; + + klassOop res = ik->find_field(name_symbol, signature_symbol, &fd); + if (res == NULL) { + tty->print_cr("Invalid layout of %s at %s", ik->external_name(), + name_symbol->as_C_string()); + fatal("Invalid layout of preloaded class"); + } + + //fetch the field at the offset we've found + int dest_offset = fd.offset(); + + return dest_offset; +} + + +const char* WhiteBox::lookup_jstring(const char* field_name, oop object) { + int offset = offset_for_field(field_name, object, + vmSymbols::string_signature()); + oop string = object->obj_field(offset); + const char* ret = java_lang_String::as_utf8_string(string); + return ret; +} + +bool WhiteBox::lookup_bool(const char* field_name, oop object) { + int offset = + offset_for_field(field_name, object, vmSymbols::bool_signature()); + bool ret = (object->bool_field(offset) == JNI_TRUE); + return ret; +} + + #define CC (char*) static JNINativeMethod methods[] = { {CC"getObjectAddress", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectAddress }, {CC"getHeapOopSize", CC"()I", (void*)&WB_GetHeapOopSize }, + {CC "parseCommandLine", + CC "(Ljava/lang/String;[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;", + (void*) &WB_ParseCommandLine + }, #ifndef SERIALGC {CC"g1InConcurrentMark", CC"()Z", (void*)&WB_G1InConcurrentMark}, {CC"g1IsHumongous", CC"(Ljava/lang/Object;)Z", (void*)&WB_G1IsHumongous }, diff --git a/src/share/vm/prims/whitebox.hpp b/src/share/vm/prims/whitebox.hpp index 53adc6874..a5dda9b46 100644 --- a/src/share/vm/prims/whitebox.hpp +++ b/src/share/vm/prims/whitebox.hpp @@ -25,12 +25,29 @@ #ifndef SHARE_VM_PRIMS_WHITEBOX_HPP #define SHARE_VM_PRIMS_WHITEBOX_HPP +#include "prims/jni.h" + +#include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" + +// Entry macro to transition from JNI to VM state. + +#define WB_ENTRY(result_type, header) JNI_ENTRY(result_type, header) +#define WB_END JNI_END +#define WB_METHOD_DECLARE extern "C" jobjectArray JNICALL + class WhiteBox : public AllStatic { private: static bool _used; public: static bool used() { return _used; } static void set_used() { _used = true; } + static int offset_for_field(const char* field_name, oop object, + Symbol* signature_symbol); + static const char* lookup_jstring(const char* field_name, oop object); + static bool lookup_bool(const char* field_name, oop object); }; + + #endif // SHARE_VM_PRIMS_WHITEBOX_HPP diff --git a/src/share/vm/services/diagnosticArgument.cpp b/src/share/vm/services/diagnosticArgument.cpp index a3a698271..e2ced3891 100644 --- a/src/share/vm/services/diagnosticArgument.cpp +++ b/src/share/vm/services/diagnosticArgument.cpp @@ -43,6 +43,47 @@ void GenDCmdArgument::read_value(const char* str, size_t len, TRAPS) { set_is_set(true); } +void GenDCmdArgument::to_string(jlong l, char* buf, size_t len) { + jio_snprintf(buf, len, INT64_FORMAT, l); +} + +void GenDCmdArgument::to_string(bool b, char* buf, size_t len) { + jio_snprintf(buf, len, b ? "true" : "false"); +} + +void GenDCmdArgument::to_string(NanoTimeArgument n, char* buf, size_t len) { + jio_snprintf(buf, len, INT64_FORMAT, n._nanotime); +} + +void GenDCmdArgument::to_string(MemorySizeArgument m, char* buf, size_t len) { + jio_snprintf(buf, len, INT64_FORMAT, m._size); +} + +void GenDCmdArgument::to_string(char* c, char* buf, size_t len) { + jio_snprintf(buf, len, "%s", c); +} + +void GenDCmdArgument::to_string(StringArrayArgument* f, char* buf, size_t len) { + int length = f->array()->length(); + size_t written = 0; + buf[0] = 0; + for (int i = 0; i < length; i++) { + char* next_str = f->array()->at(i); + size_t next_size = strlen(next_str); + //Check if there's room left to write next element + if (written + next_size > len) { + return; + } + //Actually write element + strcat(buf, next_str); + written += next_size; + //Check if there's room left for the comma + if (i < length-1 && len - written > 0) { + strcat(buf, ","); + } + } +} + template <> void DCmdArgument::parse_value(const char* str, size_t len, TRAPS) { if (str == NULL || sscanf(str, INT64_FORMAT, &_value) != 1) { diff --git a/src/share/vm/services/diagnosticArgument.hpp b/src/share/vm/services/diagnosticArgument.hpp index 380f7ebe1..8881e9955 100644 --- a/src/share/vm/services/diagnosticArgument.hpp +++ b/src/share/vm/services/diagnosticArgument.hpp @@ -110,12 +110,20 @@ public: virtual void init_value(TRAPS) = 0; virtual void reset(TRAPS) = 0; virtual void cleanup() = 0; + virtual void value_as_str(char* buf, size_t len) = 0; void set_next(GenDCmdArgument* arg) { _next = arg; } GenDCmdArgument* next() { return _next; } + + void to_string(jlong l, char* buf, size_t len); + void to_string(bool b, char* buf, size_t len); + void to_string(char* c, char* buf, size_t len); + void to_string(NanoTimeArgument n, char* buf, size_t len); + void to_string(MemorySizeArgument f, char* buf, size_t len); + void to_string(StringArrayArgument* s, char* buf, size_t len); }; template class DCmdArgument: public GenDCmdArgument { @@ -143,6 +151,7 @@ public: void parse_value(const char* str, size_t len, TRAPS); void init_value(TRAPS); void destroy_value(); + void value_as_str(char *buf, size_t len) { return to_string(_value, buf, len);} }; #endif /* SHARE_VM_SERVICES_DIAGNOSTICARGUMENT_HPP */ diff --git a/test/serviceability/ParserTest.java b/test/serviceability/ParserTest.java new file mode 100644 index 000000000..b031b4de0 --- /dev/null +++ b/test/serviceability/ParserTest.java @@ -0,0 +1,152 @@ +/* + * @test ParserTest + * @summary verify that whitebox functions can be linked and executed + * @run compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI ParserTest.java + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ParserTest + */ + +import java.math.BigInteger; + +import sun.hotspot.parser.DiagnosticCommand; +import sun.hotspot.parser.DiagnosticCommand.DiagnosticArgumentType; +import sun.hotspot.WhiteBox; + +public class ParserTest { + WhiteBox wb; + + public ParserTest() throws Exception { + wb = WhiteBox.getWhiteBox(); + + testNanoTime(); + testJLong(); + testBool(); + testMemorySize(); + } + + public static void main(String... args) throws Exception { + new ParserTest(); + } + + public void testNanoTime() throws Exception { + String name = "name"; + DiagnosticCommand arg = new DiagnosticCommand(name, + "desc", DiagnosticArgumentType.NANOTIME, + false, "0"); + DiagnosticCommand[] args = {arg}; + + BigInteger bi = new BigInteger("7"); + //These should work + parse(name, bi.toString(), name + "=7ns", args); + + bi = bi.multiply(BigInteger.valueOf(1000)); + parse(name, bi.toString(), name + "=7us", args); + + bi = bi.multiply(BigInteger.valueOf(1000)); + parse(name, bi.toString(), name + "=7ms", args); + + bi = bi.multiply(BigInteger.valueOf(1000)); + parse(name, bi.toString(), name + "=7s", args); + + bi = bi.multiply(BigInteger.valueOf(60)); + parse(name, bi.toString() , name + "=7m", args); + + bi = bi.multiply(BigInteger.valueOf(60)); + parse(name, bi.toString() , name + "=7h", args); + + bi = bi.multiply(BigInteger.valueOf(24)); + parse(name, bi.toString() , name + "=7d", args); + + parse(name, "0", name + "=0", args); + + shouldFail(name + "=7xs", args); + shouldFail(name + "=7mms", args); + shouldFail(name + "=7f", args); + //Currently, only value 0 is allowed without unit + shouldFail(name + "=7", args); + } + + public void testJLong() throws Exception { + String name = "name"; + DiagnosticCommand arg = new DiagnosticCommand(name, + "desc", DiagnosticArgumentType.JLONG, + false, "0"); + DiagnosticCommand[] args = {arg}; + + wb.parseCommandLine(name + "=10", args); + parse(name, "10", name + "=10", args); + parse(name, "-5", name + "=-5", args); + + //shouldFail(name + "=12m", args); <-- should fail, doesn't + } + + public void testBool() throws Exception { + String name = "name"; + DiagnosticCommand arg = new DiagnosticCommand(name, + "desc", DiagnosticArgumentType.BOOLEAN, + false, "false"); + DiagnosticCommand[] args = {arg}; + + parse(name, "true", name + "=true", args); + parse(name, "false", name + "=false", args); + parse(name, "true", name, args); + + //Empty commandline to parse, tests default value + //of the parameter "name" + parse(name, "false", "", args); + } + + public void testMemorySize() throws Exception { + String name = "name"; + String defaultValue = "1024"; + DiagnosticCommand arg = new DiagnosticCommand(name, + "desc", DiagnosticArgumentType.MEMORYSIZE, + false, defaultValue); + DiagnosticCommand[] args = {arg}; + + BigInteger bi = new BigInteger("7"); + parse(name, bi.toString(), name + "=7b", args); + + bi = bi.multiply(BigInteger.valueOf(1024)); + parse(name, bi.toString(), name + "=7k", args); + + bi = bi.multiply(BigInteger.valueOf(1024)); + parse(name, bi.toString(), name + "=7m", args); + + bi = bi.multiply(BigInteger.valueOf(1024)); + parse(name, bi.toString(), name + "=7g", args); + parse(name, defaultValue, "", args); + + //shouldFail(name + "=7gg", args); <---- should fail, doesn't + //shouldFail(name + "=7t", args); <----- should fail, doesn't + } + + public void parse(String searchName, String expectedValue, + String cmdLine, DiagnosticCommand[] argumentTypes) throws Exception { + //parseCommandLine will return an object array that looks like + //{, ... } + Object[] res = wb.parseCommandLine(cmdLine, argumentTypes); + for (int i = 0; i < res.length-1; i+=2) { + String parsedName = (String) res[i]; + if (searchName.equals(parsedName)) { + String parsedValue = (String) res[i+1]; + if (expectedValue.equals(parsedValue)) { + return; + } else { + throw new Exception("Parsing of cmdline '" + cmdLine + "' failed!\n" + + searchName + " parsed as " + parsedValue + + "! Expected: " + expectedValue); + } + } + } + throw new Exception(searchName + " not found as a parsed Argument!"); + } + + private void shouldFail(String argument, DiagnosticCommand[] argumentTypes) throws Exception { + try { + wb.parseCommandLine(argument, argumentTypes); + throw new Exception("Parser accepted argument: " + argument); + } catch (IllegalArgumentException e) { + //expected + } + } +} -- GitLab