1 /* 2 * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "jvm.h" 27 #include "memory/allocation.inline.hpp" 28 #include "memory/resourceArea.hpp" 29 #include "runtime/thread.hpp" 30 #include "services/diagnosticArgument.hpp" 31 32 StringArrayArgument::StringArrayArgument() { 33 _array = new(ResourceObj::C_HEAP, mtInternal)GrowableArray<char *>(32, true); 34 assert(_array != NULL, "Sanity check"); 35 } 36 37 StringArrayArgument::~StringArrayArgument() { 38 for (int i=0; i<_array->length(); i++) { 39 FREE_C_HEAP_ARRAY(char, _array->at(i)); 40 } 41 delete _array; 42 } 43 44 void StringArrayArgument::add(const char* str, size_t len) { 45 if (str != NULL) { 46 char* ptr = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); 47 strncpy(ptr, str, len); 48 ptr[len] = 0; 49 _array->append(ptr); 50 } 51 } 52 53 void GenDCmdArgument::read_value(const char* str, size_t len, TRAPS) { 54 /* NOTE:Some argument types doesn't require a value, 55 * for instance boolean arguments: "enableFeatureX". is 56 * equivalent to "enableFeatureX=true". In these cases, 57 * str will be null. This is perfectly valid. 58 * All argument types must perform null checks on str. 59 */ 60 61 if (is_set() && !allow_multiple()) { 62 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 63 "Duplicates in diagnostic command arguments\n"); 64 } 65 parse_value(str, len, CHECK); 66 set_is_set(true); 67 } 68 69 void GenDCmdArgument::to_string(jlong l, char* buf, size_t len) const { 70 jio_snprintf(buf, len, INT64_FORMAT, l); 71 } 72 73 void GenDCmdArgument::to_string(bool b, char* buf, size_t len) const { 74 jio_snprintf(buf, len, b ? "true" : "false"); 75 } 76 77 void GenDCmdArgument::to_string(NanoTimeArgument n, char* buf, size_t len) const { 78 jio_snprintf(buf, len, INT64_FORMAT, n._nanotime); 79 } 80 81 void GenDCmdArgument::to_string(MemorySizeArgument m, char* buf, size_t len) const { 82 jio_snprintf(buf, len, INT64_FORMAT, m._size); 83 } 84 85 void GenDCmdArgument::to_string(char* c, char* buf, size_t len) const { 86 jio_snprintf(buf, len, "%s", (c != NULL) ? c : ""); 87 } 88 89 void GenDCmdArgument::to_string(StringArrayArgument* f, char* buf, size_t len) const { 90 int length = f->array()->length(); 91 size_t written = 0; 92 buf[0] = 0; 93 for (int i = 0; i < length; i++) { 94 char* next_str = f->array()->at(i); 95 size_t next_size = strlen(next_str); 96 //Check if there's room left to write next element 97 if (written + next_size > len) { 98 return; 99 } 100 //Actually write element 101 strcat(buf, next_str); 102 written += next_size; 103 //Check if there's room left for the comma 104 if (i < length-1 && len - written > 0) { 105 strcat(buf, ","); 106 } 107 } 108 } 109 110 template <> void DCmdArgument<jlong>::parse_value(const char* str, 111 size_t len, TRAPS) { 112 int scanned = -1; 113 if (str == NULL 114 || sscanf(str, JLONG_FORMAT "%n", &_value, &scanned) != 1 115 || (size_t)scanned != len) 116 { 117 ResourceMark rm; 118 119 char* buf = NEW_RESOURCE_ARRAY(char, len + 1); 120 strncpy(buf, str, len); 121 buf[len] = '\0'; 122 Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalArgumentException(), 123 "Integer parsing error in command argument '%s'. Could not parse: %s.\n", _name, buf); 124 } 125 } 126 127 template <> void DCmdArgument<jlong>::init_value(TRAPS) { 128 if (has_default()) { 129 this->parse_value(_default_string, strlen(_default_string), THREAD); 130 if (HAS_PENDING_EXCEPTION) { 131 fatal("Default string must be parseable"); 132 } 133 } else { 134 set_value(0); 135 } 136 } 137 138 template <> void DCmdArgument<jlong>::destroy_value() { } 139 140 template <> void DCmdArgument<bool>::parse_value(const char* str, 141 size_t len, TRAPS) { 142 // len is the length of the current token starting at str 143 if (len == 0) { 144 set_value(true); 145 } else { 146 if (len == strlen("true") && strncasecmp(str, "true", len) == 0) { 147 set_value(true); 148 } else if (len == strlen("false") && strncasecmp(str, "false", len) == 0) { 149 set_value(false); 150 } else { 151 ResourceMark rm; 152 153 char* buf = NEW_RESOURCE_ARRAY(char, len + 1); 154 155 // This code would be warned as "stringop-truncation" by GCC 8 or later. 156 // So we avoid it via PRAGMA_STRINGOP_TRUNCATION_IGNORED macro. 157 PRAGMA_DIAG_PUSH 158 PRAGMA_STRINGOP_TRUNCATION_IGNORED 159 strncpy(buf, str, len); 160 PRAGMA_DIAG_POP 161 162 buf[len] = '\0'; 163 Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalArgumentException(), 164 "Boolean parsing error in command argument '%s'. Could not parse: %s.\n", _name, buf); 165 } 166 } 167 } 168 169 template <> void DCmdArgument<bool>::init_value(TRAPS) { 170 if (has_default()) { 171 this->parse_value(_default_string, strlen(_default_string), THREAD); 172 if (HAS_PENDING_EXCEPTION) { 173 fatal("Default string must be parsable"); 174 } 175 } else { 176 set_value(false); 177 } 178 } 179 180 template <> void DCmdArgument<bool>::destroy_value() { } 181 182 template <> void DCmdArgument<char*>::parse_value(const char* str, 183 size_t len, TRAPS) { 184 if (str == NULL) { 185 _value = NULL; 186 } else { 187 _value = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal); 188 int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str); 189 assert((size_t)n <= len, "Unexpected number of characters in string"); 190 } 191 } 192 193 template <> void DCmdArgument<char*>::init_value(TRAPS) { 194 if (has_default() && _default_string != NULL) { 195 this->parse_value(_default_string, strlen(_default_string), THREAD); 196 if (HAS_PENDING_EXCEPTION) { 197 fatal("Default string must be parsable"); 198 } 199 } else { 200 set_value(NULL); 201 } 202 } 203 204 template <> void DCmdArgument<char*>::destroy_value() { 205 FREE_C_HEAP_ARRAY(char, _value); 206 set_value(NULL); 207 } 208 209 template <> void DCmdArgument<NanoTimeArgument>::parse_value(const char* str, 210 size_t len, TRAPS) { 211 if (str == NULL) { 212 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 213 "Integer parsing error nanotime value: syntax error, value is null\n"); 214 } 215 216 int argc = sscanf(str, JLONG_FORMAT, &_value._time); 217 if (argc != 1) { 218 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 219 "Integer parsing error nanotime value: syntax error\n"); 220 } 221 size_t idx = 0; 222 while(idx < len && isdigit(str[idx])) { 223 idx++; 224 } 225 if (idx == len) { 226 // only accept missing unit if the value is 0 227 if (_value._time != 0) { 228 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 229 "Integer parsing error nanotime value: unit required\n"); 230 } else { 231 _value._nanotime = 0; 232 strcpy(_value._unit, "ns"); 233 return; 234 } 235 } else if(len - idx > 2) { 236 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 237 "Integer parsing error nanotime value: illegal unit\n"); 238 } else { 239 strncpy(_value._unit, &str[idx], len - idx); 240 /*Write an extra null termination. This is safe because _value._unit 241 * is declared as char[3], and length is checked to be not larger than 242 * two above. Also, this is necessary, since length might be 1, and the 243 * default value already in the string is ns, which is two chars. 244 */ 245 _value._unit[len-idx] = '\0'; 246 } 247 248 if (strcmp(_value._unit, "ns") == 0) { 249 _value._nanotime = _value._time; 250 } else if (strcmp(_value._unit, "us") == 0) { 251 _value._nanotime = _value._time * 1000; 252 } else if (strcmp(_value._unit, "ms") == 0) { 253 _value._nanotime = _value._time * 1000 * 1000; 254 } else if (strcmp(_value._unit, "s") == 0) { 255 _value._nanotime = _value._time * 1000 * 1000 * 1000; 256 } else if (strcmp(_value._unit, "m") == 0) { 257 _value._nanotime = _value._time * 60 * 1000 * 1000 * 1000; 258 } else if (strcmp(_value._unit, "h") == 0) { 259 _value._nanotime = _value._time * 60 * 60 * 1000 * 1000 * 1000; 260 } else if (strcmp(_value._unit, "d") == 0) { 261 _value._nanotime = _value._time * 24 * 60 * 60 * 1000 * 1000 * 1000; 262 } else { 263 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 264 "Integer parsing error nanotime value: illegal unit\n"); 265 } 266 } 267 268 template <> void DCmdArgument<NanoTimeArgument>::init_value(TRAPS) { 269 if (has_default()) { 270 this->parse_value(_default_string, strlen(_default_string), THREAD); 271 if (HAS_PENDING_EXCEPTION) { 272 fatal("Default string must be parsable"); 273 } 274 } else { 275 _value._time = 0; 276 _value._nanotime = 0; 277 strcpy(_value._unit, "ns"); 278 } 279 } 280 281 template <> void DCmdArgument<NanoTimeArgument>::destroy_value() { } 282 283 // WARNING StringArrayArgument can only be used as an option, it cannot be 284 // used as an argument with the DCmdParser 285 286 template <> void DCmdArgument<StringArrayArgument*>::parse_value(const char* str, 287 size_t len, TRAPS) { 288 _value->add(str,len); 289 } 290 291 template <> void DCmdArgument<StringArrayArgument*>::init_value(TRAPS) { 292 _value = new StringArrayArgument(); 293 _allow_multiple = true; 294 if (has_default()) { 295 fatal("StringArrayArgument cannot have default value"); 296 } 297 } 298 299 template <> void DCmdArgument<StringArrayArgument*>::destroy_value() { 300 if (_value != NULL) { 301 delete _value; 302 set_value(NULL); 303 } 304 } 305 306 template <> void DCmdArgument<MemorySizeArgument>::parse_value(const char* str, 307 size_t len, TRAPS) { 308 if (str == NULL) { 309 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 310 "Parsing error memory size value: syntax error, value is null\n"); 311 } 312 if (*str == '-') { 313 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 314 "Parsing error memory size value: negative values not allowed\n"); 315 } 316 int res = sscanf(str, UINT64_FORMAT "%c", &_value._val, &_value._multiplier); 317 if (res == 2) { 318 switch (_value._multiplier) { 319 case 'k': case 'K': 320 _value._size = _value._val * 1024; 321 break; 322 case 'm': case 'M': 323 _value._size = _value._val * 1024 * 1024; 324 break; 325 case 'g': case 'G': 326 _value._size = _value._val * 1024 * 1024 * 1024; 327 break; 328 default: 329 _value._size = _value._val; 330 _value._multiplier = ' '; 331 //default case should be to break with no error, since user 332 //can write size in bytes, or might have a delimiter and next arg 333 break; 334 } 335 } else if (res == 1) { 336 _value._size = _value._val; 337 } else { 338 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 339 "Parsing error memory size value: invalid value\n"); 340 } 341 } 342 343 template <> void DCmdArgument<MemorySizeArgument>::init_value(TRAPS) { 344 if (has_default()) { 345 this->parse_value(_default_string, strlen(_default_string), THREAD); 346 if (HAS_PENDING_EXCEPTION) { 347 fatal("Default string must be parsable"); 348 } 349 } else { 350 _value._size = 0; 351 _value._val = 0; 352 _value._multiplier = ' '; 353 } 354 } 355 356 template <> void DCmdArgument<MemorySizeArgument>::destroy_value() { }