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
32StringArrayArgument::StringArrayArgument() {
33 _array = new(ResourceObj::C_HEAP, mtInternal)GrowableArray<char *>(32, true);
34 assert(_array != NULL, "Sanity check");
35}
36
37StringArrayArgument::~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
46void 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
55void 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
71void GenDCmdArgument::to_string(jlong l, char* buf, size_t len) const {
72 jio_snprintf(buf, len, INT64_FORMAT, l);
73}
74
75void GenDCmdArgument::to_string(bool b, char* buf, size_t len) const {
76 jio_snprintf(buf, len, b ? "true" : "false");
77}
78
79void GenDCmdArgument::to_string(NanoTimeArgument n, char* buf, size_t len) const {
80 jio_snprintf(buf, len, INT64_FORMAT, n._nanotime);
81}
82
83void GenDCmdArgument::to_string(MemorySizeArgument m, char* buf, size_t len) const {
84 jio_snprintf(buf, len, INT64_FORMAT, m._size);
85}
86
87void GenDCmdArgument::to_string(char* c, char* buf, size_t len) const {
88 jio_snprintf(buf, len, "%s", (c != NULL) ? c : "");
89}
90
91void 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
112template <> 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
129template <> 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
140template <> void DCmdArgument<jlong>::destroy_value() { }
141
142template <> 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
164template <> 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
175template <> void DCmdArgument<bool>::destroy_value() { }
176
177template <> 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
188template <> 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
199template <> void DCmdArgument<char*>::destroy_value() {
200 if (_value != NULL) {
201 FREE_C_HEAP_ARRAY(char, _value);
202 set_value(NULL);
203 }
204}
205
206template <> 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
265template <> 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
278template <> 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
283template <> void DCmdArgument<StringArrayArgument*>::parse_value(const char* str,
284 size_t len, TRAPS) {
285 _value->add(str,len);
286}
287
288template <> 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
296template <> void DCmdArgument<StringArrayArgument*>::destroy_value() {
297 if (_value != NULL) {
298 delete _value;
299 set_value(NULL);
300 }
301}
302
303template <> 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
340template <> 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
353template <> void DCmdArgument<MemorySizeArgument>::destroy_value() { }
354