# HG changeset patch # User tamao # Date 1372475884 25200 # Node ID 6e3634222155fdb3cd2a5231d0aaba82e43d0280 # Parent 5ea20b3bd249573288f67c5bbbbba570146cb8b2 8017611: Auto corrector for mistyped vm options Summary: The auto corrector for mistyped vm options fuzzy-matches existing flags based on string similarity (Dice's coefficient). Reviewed-by: kvn, dsamersoff, hseigel, johnc diff -r 5ea20b3bd249 -r 6e3634222155 src/share/vm/runtime/arguments.cpp --- a/src/share/vm/runtime/arguments.cpp Mon Jul 01 09:30:23 2013 -0700 +++ b/src/share/vm/runtime/arguments.cpp Fri Jun 28 20:18:04 2013 -0700 @@ -849,7 +849,7 @@ arg_len = equal_sign - argname; } - Flag* found_flag = Flag::find_flag((char*)argname, arg_len, true); + Flag* found_flag = Flag::find_flag((const char*)argname, arg_len, true); if (found_flag != NULL) { char locked_message_buf[BUFLEN]; found_flag->get_locked_message(locked_message_buf, BUFLEN); @@ -870,6 +870,14 @@ } else { jio_fprintf(defaultStream::error_stream(), "Unrecognized VM option '%s'\n", argname); + Flag* fuzzy_matched = Flag::fuzzy_match((const char*)argname, arg_len, true); + if (fuzzy_matched != NULL) { + jio_fprintf(defaultStream::error_stream(), + "Did you mean '%s%s%s'?\n", + (fuzzy_matched->is_bool()) ? "(+/-)" : "", + fuzzy_matched->name, + (fuzzy_matched->is_bool()) ? "" : "="); + } } // allow for commandline "commenting out" options like -XX:#+Verbose diff -r 5ea20b3bd249 -r 6e3634222155 src/share/vm/runtime/globals.cpp --- a/src/share/vm/runtime/globals.cpp Mon Jul 01 09:30:23 2013 -0700 +++ b/src/share/vm/runtime/globals.cpp Fri Jun 28 20:18:04 2013 -0700 @@ -276,14 +276,14 @@ Flag* Flag::flags = flagTable; size_t Flag::numFlags = (sizeof(flagTable) / sizeof(Flag)); -inline bool str_equal(const char* s, char* q, size_t len) { +inline bool str_equal(const char* s, const char* q, size_t len) { // s is null terminated, q is not! if (strlen(s) != (unsigned int) len) return false; return strncmp(s, q, len) == 0; } // Search the flag table for a named flag -Flag* Flag::find_flag(char* name, size_t length, bool allow_locked) { +Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked) { for (Flag* current = &flagTable[0]; current->name != NULL; current++) { if (str_equal(current->name, name, length)) { // Found a matching entry. Report locked flags only if allowed. @@ -301,6 +301,52 @@ return NULL; } +// Compute string similarity based on Dice's coefficient +static float str_similar(const char* str1, const char* str2, size_t len2) { + int len1 = (int) strlen(str1); + int total = len1 + (int) len2; + + int hit = 0; + + for (int i = 0; i < len1 -1; ++i) { + for (int j = 0; j < (int) len2 -1; ++j) { + if ((str1[i] == str2[j]) && (str1[i+1] == str2[j+1])) { + ++hit; + break; + } + } + } + + return 2.0f * (float) hit / (float) total; +} + +Flag* Flag::fuzzy_match(const char* name, size_t length, bool allow_locked) { + float VMOptionsFuzzyMatchSimilarity = 0.7f; + Flag* match = NULL; + float score; + float max_score = -1; + + for (Flag* current = &flagTable[0]; current->name != NULL; current++) { + score = str_similar(current->name, name, length); + if (score > max_score) { + max_score = score; + match = current; + } + } + + if (!(match->is_unlocked() || match->is_unlocker())) { + if (!allow_locked) { + return NULL; + } + } + + if (max_score < VMOptionsFuzzyMatchSimilarity) { + return NULL; + } + + return match; +} + // Returns the address of the index'th element static Flag* address_of_flag(CommandLineFlagWithType flag) { assert((size_t)flag < Flag::numFlags, "bad command line flag index"); diff -r 5ea20b3bd249 -r 6e3634222155 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Mon Jul 01 09:30:23 2013 -0700 +++ b/src/share/vm/runtime/globals.hpp Fri Jun 28 20:18:04 2013 -0700 @@ -220,7 +220,8 @@ // number of flags static size_t numFlags; - static Flag* find_flag(char* name, size_t length, bool allow_locked = false); + static Flag* find_flag(const char* name, size_t length, bool allow_locked = false); + static Flag* fuzzy_match(const char* name, size_t length, bool allow_locked = false); bool is_bool() const { return strcmp(type, "bool") == 0; } bool get_bool() const { return *((bool*) addr); } diff -r 5ea20b3bd249 -r 6e3634222155 test/gc/arguments/TestUnrecognizedVMOptionsHandling.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/arguments/TestUnrecognizedVMOptionsHandling.java Fri Jun 28 20:18:04 2013 -0700 @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2013, 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. +*/ + +/* + * @test TestUnrecognizedVMOptionsHandling + * @key gc + * @bug 8017611 + * @summary Tests handling unrecognized VM options + * @library /testlibrary + * @run main/othervm TestUnrecognizedVMOptionsHandling + */ + +import com.oracle.java.testlibrary.*; + +public class TestUnrecognizedVMOptionsHandling { + + public static void main(String args[]) throws Exception { + // The first two JAVA processes are expected to fail, but with a correct VM option suggestion + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+PrintGc", + "-version" + ); + OutputAnalyzer outputWithError = new OutputAnalyzer(pb.start()); + outputWithError.shouldContain("Did you mean '(+/-)PrintGC'?"); + if (outputWithError.getExitValue() == 0) { + throw new RuntimeException("Not expected to get exit value 0"); + } + + pb = ProcessTools.createJavaProcessBuilder( + "-XX:MaxiumHeapSize=500m", + "-version" + ); + outputWithError = new OutputAnalyzer(pb.start()); + outputWithError.shouldContain("Did you mean 'MaxHeapSize='?"); + if (outputWithError.getExitValue() == 0) { + throw new RuntimeException("Not expected to get exit value 0"); + } + + // The last JAVA process should run successfully for the purpose of sanity check + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+PrintGC", + "-version" + ); + OutputAnalyzer outputWithNoError = new OutputAnalyzer(pb.start()); + outputWithNoError.shouldNotContain("Did you mean '(+/-)PrintGC'?"); + outputWithNoError.shouldHaveExitValue(0); + } +} +