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     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     int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str);
184     assert((size_t)n <= len, "Unexpected number of characters in string");
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() { }