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