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