Fri, 28 Jun 2013 20:18:04 -0700
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
1.1 --- a/src/share/vm/runtime/arguments.cpp Mon Jul 01 09:30:23 2013 -0700 1.2 +++ b/src/share/vm/runtime/arguments.cpp Fri Jun 28 20:18:04 2013 -0700 1.3 @@ -849,7 +849,7 @@ 1.4 arg_len = equal_sign - argname; 1.5 } 1.6 1.7 - Flag* found_flag = Flag::find_flag((char*)argname, arg_len, true); 1.8 + Flag* found_flag = Flag::find_flag((const char*)argname, arg_len, true); 1.9 if (found_flag != NULL) { 1.10 char locked_message_buf[BUFLEN]; 1.11 found_flag->get_locked_message(locked_message_buf, BUFLEN); 1.12 @@ -870,6 +870,14 @@ 1.13 } else { 1.14 jio_fprintf(defaultStream::error_stream(), 1.15 "Unrecognized VM option '%s'\n", argname); 1.16 + Flag* fuzzy_matched = Flag::fuzzy_match((const char*)argname, arg_len, true); 1.17 + if (fuzzy_matched != NULL) { 1.18 + jio_fprintf(defaultStream::error_stream(), 1.19 + "Did you mean '%s%s%s'?\n", 1.20 + (fuzzy_matched->is_bool()) ? "(+/-)" : "", 1.21 + fuzzy_matched->name, 1.22 + (fuzzy_matched->is_bool()) ? "" : "=<value>"); 1.23 + } 1.24 } 1.25 1.26 // allow for commandline "commenting out" options like -XX:#+Verbose
2.1 --- a/src/share/vm/runtime/globals.cpp Mon Jul 01 09:30:23 2013 -0700 2.2 +++ b/src/share/vm/runtime/globals.cpp Fri Jun 28 20:18:04 2013 -0700 2.3 @@ -276,14 +276,14 @@ 2.4 Flag* Flag::flags = flagTable; 2.5 size_t Flag::numFlags = (sizeof(flagTable) / sizeof(Flag)); 2.6 2.7 -inline bool str_equal(const char* s, char* q, size_t len) { 2.8 +inline bool str_equal(const char* s, const char* q, size_t len) { 2.9 // s is null terminated, q is not! 2.10 if (strlen(s) != (unsigned int) len) return false; 2.11 return strncmp(s, q, len) == 0; 2.12 } 2.13 2.14 // Search the flag table for a named flag 2.15 -Flag* Flag::find_flag(char* name, size_t length, bool allow_locked) { 2.16 +Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked) { 2.17 for (Flag* current = &flagTable[0]; current->name != NULL; current++) { 2.18 if (str_equal(current->name, name, length)) { 2.19 // Found a matching entry. Report locked flags only if allowed. 2.20 @@ -301,6 +301,52 @@ 2.21 return NULL; 2.22 } 2.23 2.24 +// Compute string similarity based on Dice's coefficient 2.25 +static float str_similar(const char* str1, const char* str2, size_t len2) { 2.26 + int len1 = (int) strlen(str1); 2.27 + int total = len1 + (int) len2; 2.28 + 2.29 + int hit = 0; 2.30 + 2.31 + for (int i = 0; i < len1 -1; ++i) { 2.32 + for (int j = 0; j < (int) len2 -1; ++j) { 2.33 + if ((str1[i] == str2[j]) && (str1[i+1] == str2[j+1])) { 2.34 + ++hit; 2.35 + break; 2.36 + } 2.37 + } 2.38 + } 2.39 + 2.40 + return 2.0f * (float) hit / (float) total; 2.41 +} 2.42 + 2.43 +Flag* Flag::fuzzy_match(const char* name, size_t length, bool allow_locked) { 2.44 + float VMOptionsFuzzyMatchSimilarity = 0.7f; 2.45 + Flag* match = NULL; 2.46 + float score; 2.47 + float max_score = -1; 2.48 + 2.49 + for (Flag* current = &flagTable[0]; current->name != NULL; current++) { 2.50 + score = str_similar(current->name, name, length); 2.51 + if (score > max_score) { 2.52 + max_score = score; 2.53 + match = current; 2.54 + } 2.55 + } 2.56 + 2.57 + if (!(match->is_unlocked() || match->is_unlocker())) { 2.58 + if (!allow_locked) { 2.59 + return NULL; 2.60 + } 2.61 + } 2.62 + 2.63 + if (max_score < VMOptionsFuzzyMatchSimilarity) { 2.64 + return NULL; 2.65 + } 2.66 + 2.67 + return match; 2.68 +} 2.69 + 2.70 // Returns the address of the index'th element 2.71 static Flag* address_of_flag(CommandLineFlagWithType flag) { 2.72 assert((size_t)flag < Flag::numFlags, "bad command line flag index");
3.1 --- a/src/share/vm/runtime/globals.hpp Mon Jul 01 09:30:23 2013 -0700 3.2 +++ b/src/share/vm/runtime/globals.hpp Fri Jun 28 20:18:04 2013 -0700 3.3 @@ -220,7 +220,8 @@ 3.4 // number of flags 3.5 static size_t numFlags; 3.6 3.7 - static Flag* find_flag(char* name, size_t length, bool allow_locked = false); 3.8 + static Flag* find_flag(const char* name, size_t length, bool allow_locked = false); 3.9 + static Flag* fuzzy_match(const char* name, size_t length, bool allow_locked = false); 3.10 3.11 bool is_bool() const { return strcmp(type, "bool") == 0; } 3.12 bool get_bool() const { return *((bool*) addr); }
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/gc/arguments/TestUnrecognizedVMOptionsHandling.java Fri Jun 28 20:18:04 2013 -0700 4.3 @@ -0,0 +1,69 @@ 4.4 +/* 4.5 +* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 4.6 +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 +* 4.8 +* This code is free software; you can redistribute it and/or modify it 4.9 +* under the terms of the GNU General Public License version 2 only, as 4.10 +* published by the Free Software Foundation. 4.11 +* 4.12 +* This code is distributed in the hope that it will be useful, but WITHOUT 4.13 +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 +* version 2 for more details (a copy is included in the LICENSE file that 4.16 +* accompanied this code). 4.17 +* 4.18 +* You should have received a copy of the GNU General Public License version 4.19 +* 2 along with this work; if not, write to the Free Software Foundation, 4.20 +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 +* 4.22 +* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 +* or visit www.oracle.com if you need additional information or have any 4.24 +* questions. 4.25 +*/ 4.26 + 4.27 +/* 4.28 + * @test TestUnrecognizedVMOptionsHandling 4.29 + * @key gc 4.30 + * @bug 8017611 4.31 + * @summary Tests handling unrecognized VM options 4.32 + * @library /testlibrary 4.33 + * @run main/othervm TestUnrecognizedVMOptionsHandling 4.34 + */ 4.35 + 4.36 +import com.oracle.java.testlibrary.*; 4.37 + 4.38 +public class TestUnrecognizedVMOptionsHandling { 4.39 + 4.40 + public static void main(String args[]) throws Exception { 4.41 + // The first two JAVA processes are expected to fail, but with a correct VM option suggestion 4.42 + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( 4.43 + "-XX:+PrintGc", 4.44 + "-version" 4.45 + ); 4.46 + OutputAnalyzer outputWithError = new OutputAnalyzer(pb.start()); 4.47 + outputWithError.shouldContain("Did you mean '(+/-)PrintGC'?"); 4.48 + if (outputWithError.getExitValue() == 0) { 4.49 + throw new RuntimeException("Not expected to get exit value 0"); 4.50 + } 4.51 + 4.52 + pb = ProcessTools.createJavaProcessBuilder( 4.53 + "-XX:MaxiumHeapSize=500m", 4.54 + "-version" 4.55 + ); 4.56 + outputWithError = new OutputAnalyzer(pb.start()); 4.57 + outputWithError.shouldContain("Did you mean 'MaxHeapSize=<value>'?"); 4.58 + if (outputWithError.getExitValue() == 0) { 4.59 + throw new RuntimeException("Not expected to get exit value 0"); 4.60 + } 4.61 + 4.62 + // The last JAVA process should run successfully for the purpose of sanity check 4.63 + pb = ProcessTools.createJavaProcessBuilder( 4.64 + "-XX:+PrintGC", 4.65 + "-version" 4.66 + ); 4.67 + OutputAnalyzer outputWithNoError = new OutputAnalyzer(pb.start()); 4.68 + outputWithNoError.shouldNotContain("Did you mean '(+/-)PrintGC'?"); 4.69 + outputWithNoError.shouldHaveExitValue(0); 4.70 + } 4.71 +} 4.72 +