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() { } |
354 | |