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       strncpy(buf, str, len);
155       buf[len] = '\0';
156       Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalArgumentException(),
157         "Boolean parsing error in command argument '%s'. Could not parse: %s.\n", _name, buf);
158     }
159   }
160 }
161 
162 template <> void DCmdArgument<bool>::init_value(TRAPS) {
163   if (has_default()) {
164     this->parse_value(_default_string, strlen(_default_string), THREAD);
165     if (HAS_PENDING_EXCEPTION) {
166       fatal("Default string must be parsable");
167     }
168   } else {
169     set_value(false);
170   }
171 }
172 
173 template <> void DCmdArgument<bool>::destroy_value() { }
174 
175 template <> void DCmdArgument<char*>::parse_value(const char* str,
176                                                   size_t len, TRAPS) {
177   if (str == NULL) {
178     _value = NULL;
179   } else {
180     _value = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
181     int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str);
182     assert((size_t)n <= len, "Unexpected number of characters in string");
183   }
184 }
185 
186 template <> void DCmdArgument<char*>::init_value(TRAPS) {
187   if (has_default() && _default_string != NULL) {
188     this->parse_value(_default_string, strlen(_default_string), THREAD);
189     if (HAS_PENDING_EXCEPTION) {
190      fatal("Default string must be parsable");
191     }
192   } else {
193     set_value(NULL);
194   }
195 }
196 
197 template <> void DCmdArgument<char*>::destroy_value() {
198   FREE_C_HEAP_ARRAY(char, _value);
199   set_value(NULL);
200 }
201 
202 template <> void DCmdArgument<NanoTimeArgument>::parse_value(const char* str,
203                                                  size_t len, TRAPS) {
204   if (str == NULL) {
205     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
206               "Integer parsing error nanotime value: syntax error, value is null\n");
207   }
208 
209   int argc = sscanf(str, JLONG_FORMAT, &_value._time);
210   if (argc != 1) {
211     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
212               "Integer parsing error nanotime value: syntax error\n");
213   }
214   size_t idx = 0;
215   while(idx < len && isdigit(str[idx])) {
216     idx++;
217   }
218   if (idx == len) {
219     // only accept missing unit if the value is 0
220     if (_value._time != 0) {
221       THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
222                 "Integer parsing error nanotime value: unit required\n");
223     } else {
224       _value._nanotime = 0;
225       strcpy(_value._unit, "ns");
226       return;
227     }
228   } else if(len - idx > 2) {
229     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
230               "Integer parsing error nanotime value: illegal unit\n");
231   } else {
232     strncpy(_value._unit, &str[idx], len - idx);
233     /*Write an extra null termination. This is safe because _value._unit
234      * is declared as char[3], and length is checked to be not larger than
235      * two above. Also, this is necessary, since length might be 1, and the
236      * default value already in the string is ns, which is two chars.
237      */
238     _value._unit[len-idx] = '\0';
239   }
240 
241   if (strcmp(_value._unit, "ns") == 0) {
242     _value._nanotime = _value._time;
243   } else if (strcmp(_value._unit, "us") == 0) {
244     _value._nanotime = _value._time * 1000;
245   } else if (strcmp(_value._unit, "ms") == 0) {
246     _value._nanotime = _value._time * 1000 * 1000;
247   } else if (strcmp(_value._unit, "s") == 0) {
248     _value._nanotime = _value._time * 1000 * 1000 * 1000;
249   } else if (strcmp(_value._unit, "m") == 0) {
250     _value._nanotime = _value._time * 60 * 1000 * 1000 * 1000;
251   } else if (strcmp(_value._unit, "h") == 0) {
252     _value._nanotime = _value._time * 60 * 60 * 1000 * 1000 * 1000;
253   } else if (strcmp(_value._unit, "d") == 0) {
254     _value._nanotime = _value._time * 24 * 60 * 60 * 1000 * 1000 * 1000;
255   } else {
256      THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
257                "Integer parsing error nanotime value: illegal unit\n");
258   }
259 }
260 
261 template <> void DCmdArgument<NanoTimeArgument>::init_value(TRAPS) {
262   if (has_default()) {
263     this->parse_value(_default_string, strlen(_default_string), THREAD);
264     if (HAS_PENDING_EXCEPTION) {
265       fatal("Default string must be parsable");
266     }
267   } else {
268     _value._time = 0;
269     _value._nanotime = 0;
270     strcpy(_value._unit, "ns");
271   }
272 }
273 
274 template <> void DCmdArgument<NanoTimeArgument>::destroy_value() { }
275 
276 // WARNING StringArrayArgument can only be used as an option, it cannot be
277 // used as an argument with the DCmdParser
278 
279 template <> void DCmdArgument<StringArrayArgument*>::parse_value(const char* str,
280                                                   size_t len, TRAPS) {
281   _value->add(str,len);
282 }
283 
284 template <> void DCmdArgument<StringArrayArgument*>::init_value(TRAPS) {
285   _value = new StringArrayArgument();
286   _allow_multiple = true;
287   if (has_default()) {
288     fatal("StringArrayArgument cannot have default value");
289   }
290 }
291 
292 template <> void DCmdArgument<StringArrayArgument*>::destroy_value() {
293   if (_value != NULL) {
294     delete _value;
295     set_value(NULL);
296   }
297 }
298 
299 template <> void DCmdArgument<MemorySizeArgument>::parse_value(const char* str,
300                                                   size_t len, TRAPS) {
301   if (str == NULL) {
302     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
303                "Parsing error memory size value: syntax error, value is null\n");
304   }
305   if (*str == '-') {
306     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
307                "Parsing error memory size value: negative values not allowed\n");
308   }
309   int res = sscanf(str, UINT64_FORMAT "%c", &_value._val, &_value._multiplier);
310   if (res == 2) {
311      switch (_value._multiplier) {
312       case 'k': case 'K':
313          _value._size = _value._val * 1024;
314          break;
315       case 'm': case 'M':
316          _value._size = _value._val * 1024 * 1024;
317          break;
318       case 'g': case 'G':
319          _value._size = _value._val * 1024 * 1024 * 1024;
320          break;
321        default:
322          _value._size = _value._val;
323          _value._multiplier = ' ';
324          //default case should be to break with no error, since user
325          //can write size in bytes, or might have a delimiter and next arg
326          break;
327      }
328    } else if (res == 1) {
329      _value._size = _value._val;
330    } else {
331      THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
332                "Parsing error memory size value: invalid value\n");
333    }
334 }
335 
336 template <> void DCmdArgument<MemorySizeArgument>::init_value(TRAPS) {
337   if (has_default()) {
338     this->parse_value(_default_string, strlen(_default_string), THREAD);
339     if (HAS_PENDING_EXCEPTION) {
340       fatal("Default string must be parsable");
341     }
342   } else {
343     _value._size = 0;
344     _value._val = 0;
345     _value._multiplier = ' ';
346   }
347 }
348 
349 template <> void DCmdArgument<MemorySizeArgument>::destroy_value() { }