1 /* |
1 /* |
2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package jdk.jfr.consumer; |
26 package jdk.jfr.consumer; |
27 |
27 |
|
28 import java.io.IOException; |
28 import java.io.PrintWriter; |
29 import java.io.PrintWriter; |
29 import java.io.StringWriter; |
30 import java.io.StringWriter; |
30 import java.time.Duration; |
31 import java.time.Duration; |
31 import java.time.Instant; |
32 import java.time.Instant; |
32 import java.time.OffsetDateTime; |
33 import java.time.OffsetDateTime; |
|
34 import java.util.Comparator; |
33 import java.util.List; |
35 import java.util.List; |
34 import java.util.Objects; |
36 import java.util.Objects; |
35 |
37 |
36 import jdk.jfr.Timespan; |
38 import jdk.jfr.Timespan; |
37 import jdk.jfr.Timestamp; |
39 import jdk.jfr.Timestamp; |
38 import jdk.jfr.ValueDescriptor; |
40 import jdk.jfr.ValueDescriptor; |
|
41 import jdk.jfr.internal.consumer.JdkJfrConsumer; |
|
42 import jdk.jfr.internal.consumer.ObjectFactory; |
39 import jdk.jfr.internal.PrivateAccess; |
43 import jdk.jfr.internal.PrivateAccess; |
|
44 import jdk.jfr.internal.Type; |
|
45 import jdk.jfr.internal.consumer.ObjectContext; |
40 import jdk.jfr.internal.tool.PrettyWriter; |
46 import jdk.jfr.internal.tool.PrettyWriter; |
41 |
47 |
42 /** |
48 /** |
43 * A complex data type that consists of one or more fields. |
49 * A complex data type that consists of one or more fields. |
44 * <p> |
50 * <p> |
49 * |
55 * |
50 * @since 9 |
56 * @since 9 |
51 */ |
57 */ |
52 public class RecordedObject { |
58 public class RecordedObject { |
53 |
59 |
|
60 static{ |
|
61 JdkJfrConsumer access = new JdkJfrConsumer() { |
|
62 public List<Type> readTypes(RecordingFile file) throws IOException { |
|
63 return file.readTypes(); |
|
64 } |
|
65 |
|
66 public boolean isLastEventInChunk(RecordingFile file) { |
|
67 return file.isLastEventInChunk(); |
|
68 } |
|
69 |
|
70 @Override |
|
71 public Object getOffsetDataTime(RecordedObject event, String name) { |
|
72 return event.getOffsetDateTime(name); |
|
73 } |
|
74 |
|
75 @Override |
|
76 public RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values) { |
|
77 return new RecordedClass(objectContext, id, values); |
|
78 } |
|
79 |
|
80 @Override |
|
81 public RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values) { |
|
82 return new RecordedClassLoader(objectContext, id, values); |
|
83 } |
|
84 |
|
85 @Override |
|
86 public Comparator<? super RecordedEvent> eventComparator() { |
|
87 return new Comparator<RecordedEvent>() { |
|
88 @Override |
|
89 public int compare(RecordedEvent e1, RecordedEvent e2) { |
|
90 return Long.compare(e1.endTimeTicks, e2.endTimeTicks); |
|
91 } |
|
92 }; |
|
93 } |
|
94 |
|
95 @Override |
|
96 public RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values) { |
|
97 return new RecordedStackTrace(objectContext, values); |
|
98 } |
|
99 |
|
100 @Override |
|
101 public RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values) { |
|
102 return new RecordedThreadGroup(objectContext, values); |
|
103 } |
|
104 |
|
105 @Override |
|
106 public RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values) { |
|
107 return new RecordedFrame(objectContext, values); |
|
108 } |
|
109 |
|
110 @Override |
|
111 public RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values) { |
|
112 return new RecordedThread(objectContext, id, values); |
|
113 } |
|
114 |
|
115 @Override |
|
116 public RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values) { |
|
117 return new RecordedMethod(objectContext, values); |
|
118 } |
|
119 |
|
120 @Override |
|
121 public RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) { |
|
122 return new RecordedEvent(objectContext, values, startTimeTicks, endTimeTicks); |
|
123 } |
|
124 |
|
125 @Override |
|
126 public void setStartTicks(RecordedEvent event, long startTicks) { |
|
127 event.startTimeTicks = startTicks; |
|
128 } |
|
129 |
|
130 @Override |
|
131 public void setEndTicks(RecordedEvent event, long endTicks) { |
|
132 event.endTimeTicks = endTicks; |
|
133 } |
|
134 |
|
135 @Override |
|
136 public Object[] eventValues(RecordedEvent event) { |
|
137 return event.objects; |
|
138 } |
|
139 }; |
|
140 JdkJfrConsumer.setAccess(access); |
|
141 } |
|
142 |
54 private final static class UnsignedValue { |
143 private final static class UnsignedValue { |
55 private final Object o; |
144 private final Object o; |
56 |
145 |
57 UnsignedValue(Object o) { |
146 UnsignedValue(Object o) { |
58 this.o = o; |
147 this.o = o; |
61 Object value() { |
150 Object value() { |
62 return o; |
151 return o; |
63 } |
152 } |
64 } |
153 } |
65 |
154 |
66 private final Object[] objects; |
155 final Object[] objects; |
67 private final List<ValueDescriptor> descriptors; |
156 final ObjectContext objectContext; |
68 private final TimeConverter timeConverter; |
|
69 |
157 |
70 // package private, not to be subclassed outside this package |
158 // package private, not to be subclassed outside this package |
71 RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) { |
159 RecordedObject(ObjectContext objectContext, Object[] objects) { |
72 this.descriptors = descriptors; |
160 this.objectContext = objectContext; |
73 this.objects = objects; |
161 this.objects = objects; |
74 this.timeConverter = timeConverter; |
|
75 } |
162 } |
76 |
163 |
77 // package private |
164 // package private |
78 final <T> T getTyped(String name, Class<T> clazz, T defaultValue) { |
165 final <T> T getTyped(String name, Class<T> clazz, T defaultValue) { |
79 // Unnecessary to check field presence twice, but this |
166 // Unnecessary to check field presence twice, but this |
99 * |
186 * |
100 * @see #getFields() |
187 * @see #getFields() |
101 */ |
188 */ |
102 public boolean hasField(String name) { |
189 public boolean hasField(String name) { |
103 Objects.requireNonNull(name); |
190 Objects.requireNonNull(name); |
104 for (ValueDescriptor v : descriptors) { |
191 for (ValueDescriptor v : objectContext.fields) { |
105 if (v.getName().equals(name)) { |
192 if (v.getName().equals(name)) { |
106 return true; |
193 return true; |
107 } |
194 } |
108 } |
195 } |
109 int dotIndex = name.indexOf("."); |
196 int dotIndex = name.indexOf("."); |
110 if (dotIndex > 0) { |
197 if (dotIndex > 0) { |
111 String structName = name.substring(0, dotIndex); |
198 String structName = name.substring(0, dotIndex); |
112 for (ValueDescriptor v : descriptors) { |
199 for (ValueDescriptor v : objectContext.fields) { |
113 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
200 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
114 RecordedObject child = getValue(structName); |
201 RecordedObject child = getValue(structName); |
115 if (child != null) { |
202 if (child != null) { |
116 return child.hasField(name.substring(dotIndex + 1)); |
203 return child.hasField(name.substring(dotIndex + 1)); |
117 } |
204 } |
167 @SuppressWarnings("unchecked") |
254 @SuppressWarnings("unchecked") |
168 T t = (T) getValue(name, false); |
255 T t = (T) getValue(name, false); |
169 return t; |
256 return t; |
170 } |
257 } |
171 |
258 |
|
259 protected Object objectAt(int index) { |
|
260 return objects[index]; |
|
261 } |
|
262 |
172 private Object getValue(String name, boolean allowUnsigned) { |
263 private Object getValue(String name, boolean allowUnsigned) { |
173 Objects.requireNonNull(name); |
264 Objects.requireNonNull(name); |
174 int index = 0; |
265 int index = 0; |
175 for (ValueDescriptor v : descriptors) { |
266 for (ValueDescriptor v : objectContext.fields) { |
176 if (name.equals(v.getName())) { |
267 if (name.equals(v.getName())) { |
177 Object object = objects[index]; |
268 Object object = objectAt(index); |
178 if (object == null) { |
269 if (object == null) { |
179 // error or missing |
270 // error or missing |
180 return null; |
271 return null; |
181 } |
272 } |
182 if (v.getFields().isEmpty()) { |
273 if (v.getFields().isEmpty()) { |
198 if (v.isArray()) { |
289 if (v.isArray()) { |
199 // struct array |
290 // struct array |
200 return structifyArray(v, array, 0); |
291 return structifyArray(v, array, 0); |
201 } |
292 } |
202 // struct |
293 // struct |
203 return new RecordedObject(v.getFields(), (Object[]) object, timeConverter); |
294 return new RecordedObject(objectContext.getInstance(v), (Object[]) object); |
204 } |
295 } |
205 } |
296 } |
206 index++; |
297 index++; |
207 } |
298 } |
208 |
299 |
209 int dotIndex = name.indexOf("."); |
300 int dotIndex = name.indexOf("."); |
210 if (dotIndex > 0) { |
301 if (dotIndex > 0) { |
211 String structName = name.substring(0, dotIndex); |
302 String structName = name.substring(0, dotIndex); |
212 for (ValueDescriptor v : descriptors) { |
303 for (ValueDescriptor v : objectContext.fields) { |
213 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
304 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
214 RecordedObject child = getValue(structName); |
305 RecordedObject child = getValue(structName); |
215 String subName = name.substring(dotIndex + 1); |
306 String subName = name.substring(dotIndex + 1); |
216 if (child != null) { |
307 if (child != null) { |
217 return child.getValue(subName, allowUnsigned); |
308 return child.getValue(subName, allowUnsigned); |
259 // This is to prevent a call to getString on a thread field, that is |
350 // This is to prevent a call to getString on a thread field, that is |
260 // null to succeed. |
351 // null to succeed. |
261 private <T> T getTypedValue(String name, String typeName) { |
352 private <T> T getTypedValue(String name, String typeName) { |
262 Objects.requireNonNull(name); |
353 Objects.requireNonNull(name); |
263 // Validate name and type first |
354 // Validate name and type first |
264 getValueDescriptor(descriptors, name, typeName); |
355 getValueDescriptor(objectContext.fields, name, typeName); |
265 return getValue(name); |
356 return getValue(name); |
266 } |
357 } |
267 |
358 |
268 private Object[] structifyArray(ValueDescriptor v, Object[] array, int dimension) { |
359 private Object[] structifyArray(ValueDescriptor v, Object[] array, int dimension) { |
269 if (array == null) { |
360 if (array == null) { |
270 return null; |
361 return null; |
271 } |
362 } |
272 Object[] structArray = new Object[array.length]; |
363 Object[] structArray = new Object[array.length]; |
|
364 ObjectContext objContext = objectContext.getInstance(v); |
273 for (int i = 0; i < structArray.length; i++) { |
365 for (int i = 0; i < structArray.length; i++) { |
274 Object arrayElement = array[i]; |
366 Object arrayElement = array[i]; |
275 if (dimension == 0) { |
367 if (dimension == 0) { |
276 // No general way to handle structarrays |
368 // No general way to handle structarrays |
277 // without invoking ObjectFactory for every instance (which may require id) |
369 // without invoking ObjectFactory for every instance (which may require id) |
278 if (isStackFrameType(v.getTypeName())) { |
370 if (isStackFrameType(v.getTypeName())) { |
279 structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter); |
371 structArray[i] = new RecordedFrame(objContext, (Object[]) arrayElement); |
280 } else { |
372 } else { |
281 structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter); |
373 structArray[i] = new RecordedObject(objContext, (Object[]) arrayElement); |
282 } |
374 } |
283 } else { |
375 } else { |
284 structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1); |
376 structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1); |
285 } |
377 } |
286 } |
378 } |
723 } |
815 } |
724 throw newIllegalArgumentException(name, "java,time.Duration"); |
816 throw newIllegalArgumentException(name, "java,time.Duration"); |
725 } |
817 } |
726 |
818 |
727 private Duration getDuration(long timespan, String name) throws InternalError { |
819 private Duration getDuration(long timespan, String name) throws InternalError { |
728 ValueDescriptor v = getValueDescriptor(descriptors, name, null); |
820 ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null); |
729 if (timespan == Long.MIN_VALUE) { |
821 if (timespan == Long.MIN_VALUE) { |
730 return Duration.ofSeconds(Long.MIN_VALUE, 0); |
822 return Duration.ofSeconds(Long.MIN_VALUE, 0); |
731 } |
823 } |
732 Timespan ts = v.getAnnotation(Timespan.class); |
824 Timespan ts = v.getAnnotation(Timespan.class); |
733 if (ts != null) { |
825 if (ts != null) { |
739 case Timespan.MILLISECONDS: |
831 case Timespan.MILLISECONDS: |
740 return Duration.ofMillis(timespan); |
832 return Duration.ofMillis(timespan); |
741 case Timespan.NANOSECONDS: |
833 case Timespan.NANOSECONDS: |
742 return Duration.ofNanos(timespan); |
834 return Duration.ofNanos(timespan); |
743 case Timespan.TICKS: |
835 case Timespan.TICKS: |
744 return Duration.ofNanos(timeConverter.convertTimespan(timespan)); |
836 return Duration.ofNanos(objectContext.convertTimespan(timespan)); |
745 } |
837 } |
746 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value()); |
838 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value()); |
747 } |
839 } |
748 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timespan"); |
840 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timespan"); |
749 } |
841 } |
802 } |
894 } |
803 throw newIllegalArgumentException(name, "java.time.Instant"); |
895 throw newIllegalArgumentException(name, "java.time.Instant"); |
804 } |
896 } |
805 |
897 |
806 private Instant getInstant(long timestamp, String name) { |
898 private Instant getInstant(long timestamp, String name) { |
807 ValueDescriptor v = getValueDescriptor(descriptors, name, null); |
899 ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null); |
808 Timestamp ts = v.getAnnotation(Timestamp.class); |
900 Timestamp ts = v.getAnnotation(Timestamp.class); |
809 if (ts != null) { |
901 if (ts != null) { |
810 if (timestamp == Long.MIN_VALUE) { |
902 if (timestamp == Long.MIN_VALUE) { |
811 return Instant.MIN; |
903 return Instant.MIN; |
812 } |
904 } |
813 switch (ts.value()) { |
905 switch (ts.value()) { |
814 case Timestamp.MILLISECONDS_SINCE_EPOCH: |
906 case Timestamp.MILLISECONDS_SINCE_EPOCH: |
815 return Instant.ofEpochMilli(timestamp); |
907 return Instant.ofEpochMilli(timestamp); |
816 case Timestamp.TICKS: |
908 case Timestamp.TICKS: |
817 return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp)); |
909 return Instant.ofEpochSecond(0, objectContext.convertTimestamp(timestamp)); |
818 } |
910 } |
819 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value()); |
911 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value()); |
820 } |
912 } |
821 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timestamp"); |
913 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timestamp"); |
822 } |
914 } |
887 p.flush(true); |
979 p.flush(true); |
888 return s.toString(); |
980 return s.toString(); |
889 } |
981 } |
890 |
982 |
891 // package private for now. Used by EventWriter |
983 // package private for now. Used by EventWriter |
892 OffsetDateTime getOffsetDateTime(String name) { |
984 private OffsetDateTime getOffsetDateTime(String name) { |
893 Instant instant = getInstant(name); |
985 Instant instant = getInstant(name); |
894 if (instant.equals(Instant.MIN)) { |
986 if (instant.equals(Instant.MIN)) { |
895 return OffsetDateTime.MIN; |
987 return OffsetDateTime.MIN; |
896 } |
988 } |
897 return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset()); |
989 return OffsetDateTime.ofInstant(getInstant(name), objectContext.getZoneOffset()); |
898 } |
990 } |
899 |
991 |
900 private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) { |
992 private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) { |
901 return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName); |
993 return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName); |
902 } |
994 } |