50113
|
1 |
/*
|
|
2 |
* Copyright (c) 2016, 2018, 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. Oracle designates this
|
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
|
9 |
* by Oracle in the LICENSE file that accompanied this code.
|
|
10 |
*
|
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
15 |
* accompanied this code).
|
|
16 |
*
|
|
17 |
* You should have received a copy of the GNU General Public License version
|
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
20 |
*
|
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
22 |
* or visit www.oracle.com if you need additional information or have any
|
|
23 |
* questions.
|
|
24 |
*/
|
|
25 |
|
|
26 |
package jdk.jfr.internal;
|
|
27 |
|
|
28 |
import static java.util.concurrent.TimeUnit.MICROSECONDS;
|
|
29 |
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
30 |
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
|
31 |
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
32 |
|
|
33 |
import java.io.FileOutputStream;
|
|
34 |
import java.io.FileWriter;
|
|
35 |
import java.io.IOException;
|
|
36 |
import java.io.PrintWriter;
|
|
37 |
import java.io.RandomAccessFile;
|
|
38 |
import java.lang.annotation.Annotation;
|
|
39 |
import java.lang.annotation.Repeatable;
|
|
40 |
import java.lang.reflect.Field;
|
|
41 |
import java.lang.reflect.InvocationTargetException;
|
|
42 |
import java.lang.reflect.Method;
|
|
43 |
import java.lang.reflect.Modifier;
|
|
44 |
import java.nio.file.Path;
|
|
45 |
import java.time.Duration;
|
50745
|
46 |
import java.time.LocalDateTime;
|
50113
|
47 |
import java.util.ArrayList;
|
|
48 |
import java.util.Arrays;
|
|
49 |
import java.util.Collections;
|
|
50 |
import java.util.HashMap;
|
|
51 |
import java.util.List;
|
|
52 |
import java.util.Map;
|
|
53 |
import java.util.Objects;
|
|
54 |
|
|
55 |
import jdk.internal.org.objectweb.asm.ClassReader;
|
|
56 |
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
|
|
57 |
import jdk.jfr.Event;
|
|
58 |
import jdk.jfr.FlightRecorderPermission;
|
50745
|
59 |
import jdk.jfr.Recording;
|
50113
|
60 |
import jdk.jfr.RecordingState;
|
|
61 |
import jdk.jfr.internal.handlers.EventHandler;
|
|
62 |
import jdk.jfr.internal.settings.PeriodSetting;
|
|
63 |
import jdk.jfr.internal.settings.StackTraceSetting;
|
|
64 |
import jdk.jfr.internal.settings.ThresholdSetting;
|
|
65 |
|
|
66 |
public final class Utils {
|
|
67 |
|
|
68 |
private static Boolean SAVE_GENERATED;
|
|
69 |
|
|
70 |
public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events";
|
|
71 |
public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument";
|
|
72 |
public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers";
|
|
73 |
public static final String REGISTER_EVENT = "registerEvent";
|
|
74 |
public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder";
|
|
75 |
|
|
76 |
private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
|
|
77 |
|
|
78 |
public static void checkAccessFlightRecorder() throws SecurityException {
|
|
79 |
SecurityManager sm = System.getSecurityManager();
|
|
80 |
if (sm != null) {
|
|
81 |
sm.checkPermission(new FlightRecorderPermission(ACCESS_FLIGHT_RECORDER));
|
|
82 |
}
|
|
83 |
}
|
|
84 |
|
|
85 |
public static void checkRegisterPermission() throws SecurityException {
|
|
86 |
SecurityManager sm = System.getSecurityManager();
|
|
87 |
if (sm != null) {
|
|
88 |
sm.checkPermission(new FlightRecorderPermission(REGISTER_EVENT));
|
|
89 |
}
|
|
90 |
}
|
|
91 |
|
|
92 |
private static enum TimespanUnit {
|
|
93 |
NANOSECONDS("ns", 1000), MICROSECONDS("us", 1000), MILLISECONDS("ms", 1000), SECONDS("s", 60), MINUTES("m", 60), HOURS("h", 24), DAYS("d", 7);
|
|
94 |
|
|
95 |
final String text;
|
|
96 |
final long amount;
|
|
97 |
|
|
98 |
TimespanUnit(String unit, long amount) {
|
|
99 |
this.text = unit;
|
|
100 |
this.amount = amount;
|
|
101 |
}
|
|
102 |
}
|
|
103 |
|
|
104 |
public static String formatBytes(long bytes, String separation) {
|
|
105 |
if (bytes < 1024) {
|
|
106 |
return bytes + " bytes";
|
|
107 |
}
|
|
108 |
int exp = (int) (Math.log(bytes) / Math.log(1024));
|
|
109 |
char bytePrefix = "kMGTPE".charAt(exp - 1);
|
|
110 |
return String.format("%.1f%s%cB", bytes / Math.pow(1024, exp), separation, bytePrefix);
|
|
111 |
}
|
|
112 |
|
|
113 |
public static String formatTimespan(Duration dValue, String separation) {
|
|
114 |
if (dValue == null) {
|
|
115 |
return "0";
|
|
116 |
}
|
|
117 |
|
|
118 |
long value = dValue.toNanos();
|
|
119 |
TimespanUnit result = TimespanUnit.NANOSECONDS;
|
|
120 |
for (TimespanUnit unit : TimespanUnit.values()) {
|
|
121 |
result = unit;
|
|
122 |
long amount = unit.amount;
|
|
123 |
if (result == TimespanUnit.DAYS || value < amount || value % amount != 0) {
|
|
124 |
break;
|
|
125 |
}
|
|
126 |
value /= amount;
|
|
127 |
}
|
|
128 |
return String.format("%d%s%s", value, separation, result.text);
|
|
129 |
}
|
|
130 |
|
|
131 |
public static long parseTimespan(String s) {
|
|
132 |
if (s.endsWith("ns")) {
|
|
133 |
return Long.parseLong(s.substring(0, s.length() - 2).trim());
|
|
134 |
}
|
|
135 |
if (s.endsWith("us")) {
|
|
136 |
return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MICROSECONDS);
|
|
137 |
}
|
|
138 |
if (s.endsWith("ms")) {
|
|
139 |
return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MILLISECONDS);
|
|
140 |
}
|
|
141 |
if (s.endsWith("s")) {
|
|
142 |
return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
|
143 |
}
|
|
144 |
if (s.endsWith("m")) {
|
|
145 |
return 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
|
146 |
}
|
|
147 |
if (s.endsWith("h")) {
|
|
148 |
return 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
|
149 |
}
|
|
150 |
if (s.endsWith("d")) {
|
|
151 |
return 24 * 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
|
152 |
}
|
|
153 |
|
|
154 |
try {
|
|
155 |
Long.parseLong(s);
|
|
156 |
} catch (NumberFormatException nfe) {
|
|
157 |
throw new NumberFormatException("'" + s + "' is not a valid timespan. Shoule be numeric value followed by a unit, i.e. 20 ms. Valid units are ns, us, s, m, h and d.");
|
|
158 |
}
|
|
159 |
// Only accept values with units
|
|
160 |
throw new NumberFormatException("Timespan + '" + s + "' is missing unit. Valid units are ns, us, s, m, h and d.");
|
|
161 |
}
|
|
162 |
|
|
163 |
/**
|
|
164 |
* Return all annotations as they are visible in the source code
|
|
165 |
*
|
|
166 |
* @param clazz class to return annotations from
|
|
167 |
*
|
|
168 |
* @return list of annotation
|
|
169 |
*
|
|
170 |
*/
|
|
171 |
static List<Annotation> getAnnotations(Class<?> clazz) {
|
|
172 |
List<Annotation> annos = new ArrayList<>();
|
|
173 |
for (Annotation a : clazz.getAnnotations()) {
|
|
174 |
annos.addAll(getAnnotation(a));
|
|
175 |
}
|
|
176 |
return annos;
|
|
177 |
}
|
|
178 |
|
|
179 |
private static List<? extends Annotation> getAnnotation(Annotation a) {
|
|
180 |
Class<?> annotated = a.annotationType();
|
|
181 |
Method valueMethod = getValueMethod(annotated);
|
|
182 |
if (valueMethod != null) {
|
|
183 |
Class<?> returnType = valueMethod.getReturnType();
|
|
184 |
if (returnType.isArray()) {
|
|
185 |
Class<?> candidate = returnType.getComponentType();
|
|
186 |
Repeatable r = candidate.getAnnotation(Repeatable.class);
|
|
187 |
if (r != null) {
|
|
188 |
Class<?> repeatClass = r.value();
|
|
189 |
if (annotated == repeatClass) {
|
|
190 |
return getAnnotationValues(a, valueMethod);
|
|
191 |
}
|
|
192 |
}
|
|
193 |
}
|
|
194 |
}
|
|
195 |
List<Annotation> annos = new ArrayList<>();
|
|
196 |
annos.add(a);
|
|
197 |
return annos;
|
|
198 |
}
|
|
199 |
|
|
200 |
static boolean isAfter(RecordingState stateToTest, RecordingState b) {
|
|
201 |
return stateToTest.ordinal() > b.ordinal();
|
|
202 |
}
|
|
203 |
|
|
204 |
static boolean isBefore(RecordingState stateToTest, RecordingState b) {
|
|
205 |
return stateToTest.ordinal() < b.ordinal();
|
|
206 |
}
|
|
207 |
|
|
208 |
static boolean isState(RecordingState stateToTest, RecordingState... states) {
|
|
209 |
for (RecordingState s : states) {
|
|
210 |
if (s == stateToTest) {
|
|
211 |
return true;
|
|
212 |
}
|
|
213 |
}
|
|
214 |
return false;
|
|
215 |
}
|
|
216 |
|
|
217 |
private static List<Annotation> getAnnotationValues(Annotation a, Method valueMethod) {
|
|
218 |
try {
|
|
219 |
return Arrays.asList((Annotation[]) valueMethod.invoke(a, new Object[0]));
|
|
220 |
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
|
221 |
return new ArrayList<>();
|
|
222 |
}
|
|
223 |
}
|
|
224 |
|
|
225 |
private static Method getValueMethod(Class<?> annotated) {
|
|
226 |
try {
|
|
227 |
return annotated.getMethod("value", new Class<?>[0]);
|
|
228 |
} catch (NoSuchMethodException e) {
|
|
229 |
return null;
|
|
230 |
}
|
|
231 |
}
|
|
232 |
|
|
233 |
public static void touch(Path dumpFile) throws IOException {
|
|
234 |
RandomAccessFile raf = new RandomAccessFile(dumpFile.toFile(), "rw");
|
|
235 |
raf.close();
|
|
236 |
}
|
|
237 |
|
|
238 |
public static Class<?> unboxType(Class<?> t) {
|
|
239 |
if (t == Integer.class) {
|
|
240 |
return int.class;
|
|
241 |
}
|
|
242 |
if (t == Long.class) {
|
|
243 |
return long.class;
|
|
244 |
}
|
|
245 |
if (t == Float.class) {
|
|
246 |
return float.class;
|
|
247 |
}
|
|
248 |
if (t == Double.class) {
|
|
249 |
return double.class;
|
|
250 |
}
|
|
251 |
if (t == Byte.class) {
|
|
252 |
return byte.class;
|
|
253 |
}
|
|
254 |
if (t == Short.class) {
|
|
255 |
return short.class;
|
|
256 |
}
|
|
257 |
if (t == Boolean.class) {
|
|
258 |
return boolean.class;
|
|
259 |
}
|
|
260 |
if (t == Character.class) {
|
|
261 |
return char.class;
|
|
262 |
}
|
|
263 |
return t;
|
|
264 |
}
|
|
265 |
|
|
266 |
static long nanosToTicks(long nanos) {
|
|
267 |
return (long) (nanos * JVM.getJVM().getTimeConversionFactor());
|
|
268 |
}
|
|
269 |
|
|
270 |
static synchronized EventHandler getHandler(Class<? extends Event> eventClass) {
|
|
271 |
Utils.ensureValidEventSubclass(eventClass);
|
|
272 |
try {
|
|
273 |
Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
|
|
274 |
SecuritySupport.setAccessible(f);
|
|
275 |
return (EventHandler) f.get(null);
|
|
276 |
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
|
277 |
throw new InternalError("Could not access event handler");
|
|
278 |
}
|
|
279 |
}
|
|
280 |
|
|
281 |
static synchronized void setHandler(Class<? extends Event> eventClass, EventHandler handler) {
|
|
282 |
Utils.ensureValidEventSubclass(eventClass);
|
|
283 |
try {
|
|
284 |
Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
|
|
285 |
SecuritySupport.setAccessible(field);
|
|
286 |
field.set(null, handler);
|
|
287 |
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
|
288 |
throw new InternalError("Could not access event handler");
|
|
289 |
}
|
|
290 |
}
|
|
291 |
|
|
292 |
public static Map<String, String> sanitizeNullFreeStringMap(Map<String, String> settings) {
|
|
293 |
HashMap<String, String> map = new HashMap<>(settings.size());
|
|
294 |
for (Map.Entry<String, String> e : settings.entrySet()) {
|
|
295 |
String key = e.getKey();
|
|
296 |
if (key == null) {
|
|
297 |
throw new NullPointerException("Null key is not allowed in map");
|
|
298 |
}
|
|
299 |
String value = e.getValue();
|
|
300 |
if (value == null) {
|
|
301 |
throw new NullPointerException("Null value is not allowed in map");
|
|
302 |
}
|
|
303 |
map.put(key, value);
|
|
304 |
}
|
|
305 |
return map;
|
|
306 |
}
|
|
307 |
|
|
308 |
public static <T> List<T> sanitizeNullFreeList(List<T> elements, Class<T> clazz) {
|
|
309 |
List<T> sanitized = new ArrayList<>(elements.size());
|
|
310 |
for (T element : elements) {
|
|
311 |
if (element == null) {
|
|
312 |
throw new NullPointerException("Null is not an allowed element in list");
|
|
313 |
}
|
|
314 |
if (element.getClass() != clazz) {
|
|
315 |
throw new ClassCastException();
|
|
316 |
}
|
|
317 |
sanitized.add(element);
|
|
318 |
}
|
|
319 |
return sanitized;
|
|
320 |
}
|
|
321 |
|
|
322 |
static List<Field> getVisibleEventFields(Class<?> clazz) {
|
|
323 |
Utils.ensureValidEventSubclass(clazz);
|
|
324 |
List<Field> fields = new ArrayList<>();
|
|
325 |
for (Class<?> c = clazz; c != Event.class; c = c.getSuperclass()) {
|
|
326 |
for (Field field : c.getDeclaredFields()) {
|
|
327 |
// skip private field in base classes
|
|
328 |
if (c == clazz || !Modifier.isPrivate(field.getModifiers())) {
|
|
329 |
fields.add(field);
|
|
330 |
}
|
|
331 |
}
|
|
332 |
}
|
|
333 |
return fields;
|
|
334 |
}
|
|
335 |
|
|
336 |
public static void ensureValidEventSubclass(Class<?> eventClass) {
|
|
337 |
if (Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) {
|
|
338 |
throw new IllegalArgumentException("Abstract event classes are not allowed");
|
|
339 |
}
|
|
340 |
if (eventClass == Event.class || !Event.class.isAssignableFrom(eventClass)) {
|
|
341 |
throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName());
|
|
342 |
}
|
|
343 |
}
|
|
344 |
|
|
345 |
public static void writeGeneratedASM(String className, byte[] bytes) {
|
|
346 |
if (SAVE_GENERATED == null) {
|
|
347 |
// We can't calculate value statically because it will force
|
|
348 |
// initialization of SecuritySupport, which cause
|
|
349 |
// UnsatisfiedLinkedError on JDK 8 or non-Oracle JDKs
|
|
350 |
SAVE_GENERATED = SecuritySupport.getBooleanProperty("jfr.save.generated.asm");
|
|
351 |
}
|
|
352 |
if (SAVE_GENERATED) {
|
|
353 |
try {
|
|
354 |
try (FileOutputStream fos = new FileOutputStream(className + ".class")) {
|
|
355 |
fos.write(bytes);
|
|
356 |
}
|
|
357 |
|
|
358 |
try (FileWriter fw = new FileWriter(className + ".asm"); PrintWriter pw = new PrintWriter(fw)) {
|
|
359 |
ClassReader cr = new ClassReader(bytes);
|
|
360 |
CheckClassAdapter.verify(cr, true, pw);
|
|
361 |
}
|
|
362 |
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Instrumented code saved to " + className + ".class and .asm");
|
|
363 |
} catch (IOException e) {
|
|
364 |
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Could not save instrumented code, for " + className + ".class and .asm");
|
|
365 |
}
|
|
366 |
}
|
|
367 |
}
|
|
368 |
|
|
369 |
public static void ensureInitialized(Class<? extends Event> eventClass) {
|
|
370 |
SecuritySupport.ensureClassIsInitialized(eventClass);
|
|
371 |
}
|
|
372 |
|
|
373 |
public static Object makePrimitiveArray(String typeName, List<Object> values) {
|
|
374 |
int length = values.size();
|
|
375 |
switch (typeName) {
|
|
376 |
case "int":
|
|
377 |
int[] ints = new int[length];
|
|
378 |
for (int i = 0; i < length; i++) {
|
|
379 |
ints[i] = (int) values.get(i);
|
|
380 |
}
|
|
381 |
return ints;
|
|
382 |
case "long":
|
|
383 |
long[] longs = new long[length];
|
|
384 |
for (int i = 0; i < length; i++) {
|
|
385 |
longs[i] = (long) values.get(i);
|
|
386 |
}
|
|
387 |
return longs;
|
|
388 |
|
|
389 |
case "float":
|
|
390 |
float[] floats = new float[length];
|
|
391 |
for (int i = 0; i < length; i++) {
|
|
392 |
floats[i] = (float) values.get(i);
|
|
393 |
}
|
|
394 |
return floats;
|
|
395 |
|
|
396 |
case "double":
|
|
397 |
double[] doubles = new double[length];
|
|
398 |
for (int i = 0; i < length; i++) {
|
|
399 |
doubles[i] = (double) values.get(i);
|
|
400 |
}
|
|
401 |
return doubles;
|
|
402 |
|
|
403 |
case "short":
|
|
404 |
short[] shorts = new short[length];
|
|
405 |
for (int i = 0; i < length; i++) {
|
|
406 |
shorts[i] = (short) values.get(i);
|
|
407 |
}
|
|
408 |
return shorts;
|
|
409 |
case "char":
|
|
410 |
char[] chars = new char[length];
|
|
411 |
for (int i = 0; i < length; i++) {
|
|
412 |
chars[i] = (char) values.get(i);
|
|
413 |
}
|
|
414 |
return chars;
|
|
415 |
case "byte":
|
|
416 |
byte[] bytes = new byte[length];
|
|
417 |
for (int i = 0; i < length; i++) {
|
|
418 |
bytes[i] = (byte) values.get(i);
|
|
419 |
}
|
|
420 |
return bytes;
|
|
421 |
case "boolean":
|
|
422 |
boolean[] booleans = new boolean[length];
|
|
423 |
for (int i = 0; i < length; i++) {
|
|
424 |
booleans[i] = (boolean) values.get(i);
|
|
425 |
}
|
|
426 |
return booleans;
|
|
427 |
case "java.lang.String":
|
|
428 |
String[] strings = new String[length];
|
|
429 |
for (int i = 0; i < length; i++) {
|
|
430 |
strings[i] = (String) values.get(i);
|
|
431 |
}
|
|
432 |
return strings;
|
|
433 |
}
|
|
434 |
return null;
|
|
435 |
}
|
|
436 |
|
|
437 |
public static boolean isSettingVisible(Control c, boolean hasEventHook) {
|
|
438 |
if (c instanceof ThresholdSetting) {
|
|
439 |
return !hasEventHook;
|
|
440 |
}
|
|
441 |
if (c instanceof PeriodSetting) {
|
|
442 |
return hasEventHook;
|
|
443 |
}
|
|
444 |
if (c instanceof StackTraceSetting) {
|
|
445 |
return !hasEventHook;
|
|
446 |
}
|
|
447 |
return true;
|
|
448 |
}
|
|
449 |
|
|
450 |
public static boolean isSettingVisible(long typeId, boolean hasEventHook) {
|
|
451 |
if (ThresholdSetting.isType(typeId)) {
|
|
452 |
return !hasEventHook;
|
|
453 |
}
|
|
454 |
if (PeriodSetting.isType(typeId)) {
|
|
455 |
return hasEventHook;
|
|
456 |
}
|
|
457 |
if (StackTraceSetting.isType(typeId)) {
|
|
458 |
return !hasEventHook;
|
|
459 |
}
|
|
460 |
return true;
|
|
461 |
}
|
|
462 |
|
|
463 |
public static Type getValidType(Class<?> type, String name) {
|
|
464 |
Objects.requireNonNull(type, "Null is not a valid type for value descriptor " + name);
|
|
465 |
if (type.isArray()) {
|
|
466 |
type = type.getComponentType();
|
|
467 |
if (type != String.class && !type.isPrimitive()) {
|
|
468 |
throw new IllegalArgumentException("Only arrays of primitives and Strings are allowed");
|
|
469 |
}
|
|
470 |
}
|
|
471 |
|
|
472 |
Type knownType = Type.getKnownType(type);
|
|
473 |
if (knownType == null || knownType == Type.STACK_TRACE) {
|
|
474 |
throw new IllegalArgumentException("Only primitive types, java.lang.Thread, java.lang.String and java.lang.Class are allowed for value descriptors. " + type.getName());
|
|
475 |
}
|
|
476 |
return knownType;
|
|
477 |
}
|
|
478 |
|
|
479 |
public static <T> List<T> smallUnmodifiable(List<T> list) {
|
|
480 |
if (list.isEmpty()) {
|
|
481 |
return Collections.emptyList();
|
|
482 |
}
|
|
483 |
if (list.size() == 1) {
|
|
484 |
return Collections.singletonList(list.get(0));
|
|
485 |
}
|
|
486 |
return Collections.unmodifiableList(list);
|
|
487 |
}
|
|
488 |
|
|
489 |
public static String upgradeLegacyJDKEvent(String eventName) {
|
|
490 |
if (eventName.length() <= LEGACY_EVENT_NAME_PREFIX.length()) {
|
|
491 |
return eventName;
|
|
492 |
}
|
|
493 |
if (eventName.startsWith(LEGACY_EVENT_NAME_PREFIX)) {
|
|
494 |
int index = eventName.lastIndexOf(".");
|
|
495 |
if (index == LEGACY_EVENT_NAME_PREFIX.length() - 1) {
|
|
496 |
return Type.EVENT_NAME_PREFIX + eventName.substring(index + 1);
|
|
497 |
}
|
|
498 |
}
|
|
499 |
return eventName;
|
|
500 |
}
|
50745
|
501 |
|
|
502 |
public static String makeFilename(Recording recording) {
|
|
503 |
String pid = JVM.getJVM().getPid();
|
|
504 |
String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now());
|
|
505 |
String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId());
|
|
506 |
return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr";
|
|
507 |
}
|
50113
|
508 |
}
|