1 /* 2 * Copyright (c) 2011, 2017, 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 if(_array->at(i) != NULL) { // Safety check 40 FREE_C_HEAP_ARRAY(char, _array->at(i)); 41 } 42 } 43 delete _array; 44 } 45 46 void StringArrayArgument::add(const char* str, size_t len) { 47 if (str != NULL) { 48 char* ptr = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); 49 strncpy(ptr, str, len); 50 ptr[len] = 0; 51 _array->append(ptr); 52 } 53 } 54 55 void GenDCmdArgument::read_value(const char* str, size_t len, TRAPS) { 56 /* NOTE:Some argument types doesn't require a value, 57 * for instance boolean arguments: "enableFeatureX". is 58 * equivalent to "enableFeatureX=true". In these cases, 59 * str will be null. This is perfectly valid. 60 * All argument types must perform null checks on str. 61 */ 62 63 if (is_set() && !allow_multiple()) { 64 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 65 "Duplicates in diagnostic command arguments\n"); 66 } 67 parse_value(str, len, CHECK); 68 set_is_set(true); 69 } 70 71 void GenDCmdArgument::to_string(jlong l, char* buf, size_t len) const { 72 jio_snprintf(buf, len, INT64_FORMAT, l); 73 } 74 75 void GenDCmdArgument::to_string(bool b, char* buf, size_t len) const { 76 jio_snprintf(buf, len, b ? "true" : "false"); 77 } 78 79 void GenDCmdArgument::to_string(NanoTimeArgument n, char* buf, size_t len) const { 80 jio_snprintf(buf, len, INT64_FORMAT, n._nanotime); 81 } 82 83 void GenDCmdArgument::to_string(MemorySizeArgument m, char* buf, size_t len) const { 84 jio_snprintf(buf, len, INT64_FORMAT, m._size); 85 } 86 87 void GenDCmdArgument::to_string(char* c, char* buf, size_t len) const { 88 jio_snprintf(buf, len, "%s", (c != NULL) ? c : ""); 89 } 90 91 void GenDCmdArgument::to_string(StringArrayArgument* f, char* buf, size_t len) const { 92 int length = f->array()->length(); 93 size_t written = 0; 94 buf[0] = 0; 95 for (int i = 0; i < length; i++) { 96 char* next_str = f->array()->at(i); 97 size_t next_size = strlen(next_str); 98 //Check if there's room left to write next element 99 if (written + next_size > len) { 100 return; 101 } 102 //Actually write element 103 strcat(buf, next_str); 104 written += next_size; 105 //Check if there's room left for the comma 106 if (i < length-1 && len - written > 0) { 107 strcat(buf, ","); 108 } 109 } 110 } 111 112 template <> void DCmdArgument<jlong>::parse_value(const char* str, 113 size_t len, TRAPS) { 114 int scanned = -1; 115 if (str == NULL 116 || sscanf(str, JLONG_FORMAT "%n", &_value, &scanned) != 1 117 || (size_t)scanned != len) 118 { 119 ResourceMark rm; 120 121 char* buf = NEW_RESOURCE_ARRAY(char, len + 1); 122 strncpy(buf, str, len); 123 buf[len] = '\0'; 124 Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalArgumentException(), 125 "Integer parsing error in command argument '%s'. Could not parse: %s.\n", _name, buf); 126 } 127 } 128 129 template <> void DCmdArgument<jlong>::init_value(TRAPS) { 130 if (has_default()) { 131 this->parse_value(_default_string, strlen(_default_string), THREAD); 132 if (HAS_PENDING_EXCEPTION) { 133 fatal("Default string must be parseable"); 134 } 135 } else { 136 set_value(0); 137 } 138 } 139 140 template <> void DCmdArgument<jlong>::destroy_value() { } 141 142 template <> void DCmdArgument<bool>::parse_value(const char* str, 143 size_t len, TRAPS) { 144 // len is the length of the current token starting at str 145 if (len == 0) { 146 set_value(true); 147 } else { 148 if (len == strlen("true") && strncasecmp(str, "true", len) == 0) { 149 set_value(true); 150 } else if (len == strlen("false") && strncasecmp(str, "false", len) == 0) { 151 set_value(false); 152 } else { 153 ResourceMark rm; 154 155 char* buf = NEW_RESOURCE_ARRAY(char, len + 1); 156 strncpy(buf, str, len); 157 buf[len] = '\0'; 158 Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalArgumentException(), 159 "Boolean parsing error in command argument '%s'. Could not parse: %s.\n", _name, buf); 160 } 161 } 162 } 163 164 template <> void DCmdArgument<bool>::init_value(TRAPS) { 165 if (has_default()) { 166 this->parse_value(_default_string, strlen(_default_string), THREAD); 167 if (HAS_PENDING_EXCEPTION) { 168 fatal("Default string must be parsable"); 169 } 170 } else { 171 set_value(false); 172 } 173 } 174 175 template <> void DCmdArgument<bool>::destroy_value() { } 176 177 template <> void DCmdArgument<char*>::parse_value(const char* str, 178 size_t len, TRAPS) { 179 if (str == NULL) { 180 _value = NULL; 181 } else { 182 _value = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); 183 strncpy(_value, str, len); 184 _value[len] = 0; 185 } 186 } 187 188 template <> void DCmdArgument<char*>::init_value(TRAPS) { 189 if (has_default() && _default_string != NULL) { 190 this->parse_value(_default_string, strlen(_default_string), THREAD); 191 if (HAS_PENDING_EXCEPTION) { 192 fatal("Default string must be parsable"); 193 } 194 } else { 195 set_value(NULL); 196 } 197 } 198 199 template <> void DCmdArgument<char*>::destroy_value() { 200 if (_value != NULL) { 201 FREE_C_HEAP_ARRAY(char, _value); 202 set_value(NULL); 203 } 204 } 205 206 template <> void DCmdArgument<NanoTimeArgument>::parse_value(const char* str, 207 size_t len, TRAPS) { 208 if (str == NULL) { 209 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 210 "Integer parsing error nanotime value: syntax error, value is null\n"); 211 } 212 213 int argc = sscanf(str, JLONG_FORMAT, &_value._time); 214 if (argc != 1) { 215 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 216 "Integer parsing error nanotime value: syntax error\n"); 217 } 218 size_t idx = 0; 219 while(idx < len && isdigit(str[idx])) { 220 idx++; 221 } 222 if (idx == len) { 223 // only accept missing unit if the value is 0 224 if (_value._time != 0) { 225 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 226 "Integer parsing error nanotime value: unit required\n"); 227 } else { 228 _value._nanotime = 0; 229 strcpy(_value._unit, "ns"); 230 return; 231 } 232 } else if(len - idx > 2) { 233 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 234 "Integer parsing error nanotime value: illegal unit\n"); 235 } else { 236 strncpy(_value._unit, &str[idx], len - idx); 237 /*Write an extra null termination. This is safe because _value._unit 238 * is declared as char[3], and length is checked to be not larger than 239 * two above. Also, this is necessary, since length might be 1, and the 240 * default value already in the string is ns, which is two chars. 241 */ 242 _value._unit[len-idx] = '\0'; 243 } 244 245 if (strcmp(_value._unit, "ns") == 0) { 246 _value._nanotime = _value._time; 247 } else if (strcmp(_value._unit, "us") == 0) { 248 _value._nanotime = _value._time * 1000; 249 } else if (strcmp(_value._unit, "ms") == 0) { 250 _value._nanotime = _value._time * 1000 * 1000; 251 } else if (strcmp(_value._unit, "s") == 0) { 252 _value._nanotime = _value._time * 1000 * 1000 * 1000; 253 } else if (strcmp(_value._unit, "m") == 0) { 254 _value._nanotime = _value._time * 60 * 1000 * 1000 * 1000; 255 } else if (strcmp(_value._unit, "h") == 0) { 256 _value._nanotime = _value._time * 60 * 60 * 1000 * 1000 * 1000; 257 } else if (strcmp(_value._unit, "d") == 0) { 258 _value._nanotime = _value._time * 24 * 60 * 60 * 1000 * 1000 * 1000; 259 } else { 260 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 261 "Integer parsing error nanotime value: illegal unit\n"); 262 } 263 } 264 265 template <> void DCmdArgument<NanoTimeArgument>::init_value(TRAPS) { 266 if (has_default()) { 267 this->parse_value(_default_string, strlen(_default_string), THREAD); 268 if (HAS_PENDING_EXCEPTION) { 269 fatal("Default string must be parsable"); 270 } 271 } else { 272 _value._time = 0; 273 _value._nanotime = 0; 274 strcpy(_value._unit, "ns"); 275 } 276 } 277 278 template <> void DCmdArgument<NanoTimeArgument>::destroy_value() { } 279 280 // WARNING StringArrayArgument can only be used as an option, it cannot be 281 // used as an argument with the DCmdParser 282 283 template <> void DCmdArgument<StringArrayArgument*>::parse_value(const char* str, 284 size_t len, TRAPS) { 285 _value->add(str,len); 286 } 287 288 template <> void DCmdArgument<StringArrayArgument*>::init_value(TRAPS) { 289 _value = new StringArrayArgument(); 290 _allow_multiple = true; 291 if (has_default()) { 292 fatal("StringArrayArgument cannot have default value"); 293 } 294 } 295 296 template <> void DCmdArgument<StringArrayArgument*>::destroy_value() { 297 if (_value != NULL) { 298 delete _value; 299 set_value(NULL); 300 } 301 } 302 303 template <> void DCmdArgument<MemorySizeArgument>::parse_value(const char* str, 304 size_t len, TRAPS) { 305 if (str == NULL) { 306 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 307 "Parsing error memory size value: syntax error, value is null\n"); 308 } 309 if (*str == '-') { 310 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 311 "Parsing error memory size value: negative values not allowed\n"); 312 } 313 int res = sscanf(str, UINT64_FORMAT "%c", &_value._val, &_value._multiplier); 314 if (res == 2) { 315 switch (_value._multiplier) { 316 case 'k': case 'K': 317 _value._size = _value._val * 1024; 318 break; 319 case 'm': case 'M': 320 _value._size = _value._val * 1024 * 1024; 321 break; 322 case 'g': case 'G': 323 _value._size = _value._val * 1024 * 1024 * 1024; 324 break; 325 default: 326 _value._size = _value._val; 327 _value._multiplier = ' '; 328 //default case should be to break with no error, since user 329 //can write size in bytes, or might have a delimiter and next arg 330 break; 331 } 332 } else if (res == 1) { 333 _value._size = _value._val; 334 } else { 335 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 336 "Parsing error memory size value: invalid value\n"); 337 } 338 } 339 340 template <> void DCmdArgument<MemorySizeArgument>::init_value(TRAPS) { 341 if (has_default()) { 342 this->parse_value(_default_string, strlen(_default_string), THREAD); 343 if (HAS_PENDING_EXCEPTION) { 344 fatal("Default string must be parsable"); 345 } 346 } else { 347 _value._size = 0; 348 _value._val = 0; 349 _value._multiplier = ' '; 350 } 351 } 352 353 template <> void DCmdArgument<MemorySizeArgument>::destroy_value() { }