23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package jdk.jfr.internal.consumer; |
26 package jdk.jfr.internal.consumer; |
27 |
27 |
28 import java.io.DataInput; |
|
29 import java.io.IOException; |
28 import java.io.IOException; |
30 |
29 |
31 import jdk.jfr.internal.LogLevel; |
30 import jdk.jfr.internal.LogLevel; |
32 import jdk.jfr.internal.LogTag; |
31 import jdk.jfr.internal.LogTag; |
33 import jdk.jfr.internal.Logger; |
32 import jdk.jfr.internal.Logger; |
34 import jdk.jfr.internal.MetadataDescriptor; |
33 import jdk.jfr.internal.MetadataDescriptor; |
|
34 import jdk.jfr.internal.Utils; |
35 |
35 |
36 public final class ChunkHeader { |
36 public final class ChunkHeader { |
|
37 private static final long HEADER_SIZE = 68; |
|
38 private static final byte UPDATING_CHUNK_HEADER = (byte) 255; |
|
39 private static final long CHUNK_SIZE_POSITION = 8; |
|
40 private static final long DURATION_NANOS_POSITION = 40; |
|
41 private static final long FILE_STATE_POSITION = 64; |
37 private static final long METADATA_TYPE_ID = 0; |
42 private static final long METADATA_TYPE_ID = 0; |
38 private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' }; |
43 private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' }; |
39 |
44 |
40 private final short major; |
45 private final short major; |
41 private final short minor; |
46 private final short minor; |
42 private final long chunkSize; |
|
43 private final long chunkStartTicks; |
47 private final long chunkStartTicks; |
44 private final long ticksPerSecond; |
48 private final long ticksPerSecond; |
45 private final long chunkStartNanos; |
49 private final long chunkStartNanos; |
46 private final long metadataPosition; |
|
47 // private final long absoluteInitialConstantPoolPosition; |
|
48 private final long absoluteChunkEnd; |
|
49 private final long absoluteEventStart; |
|
50 private final long absoluteChunkStart; |
50 private final long absoluteChunkStart; |
51 private final boolean lastChunk; |
|
52 private final RecordingInput input; |
51 private final RecordingInput input; |
53 private final long durationNanos; |
|
54 private final long id; |
52 private final long id; |
55 private long constantPoolPosition; |
53 private long absoluteEventStart; |
|
54 private long chunkSize = 0; |
|
55 private long constantPoolPosition = 0; |
|
56 private long metadataPosition = 0; |
|
57 private long durationNanos; |
|
58 private long absoluteChunkEnd; |
|
59 private boolean isFinished; |
|
60 private boolean finished; |
56 |
61 |
57 public ChunkHeader(RecordingInput input) throws IOException { |
62 public ChunkHeader(RecordingInput input) throws IOException { |
58 this(input, 0, 0); |
63 this(input, 0, 0); |
59 } |
64 } |
60 |
65 |
61 private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException { |
66 private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException { |
|
67 this.absoluteChunkStart = absoluteChunkStart; |
|
68 this.absoluteEventStart = absoluteChunkStart + HEADER_SIZE; |
|
69 if (input.getFileSize() < HEADER_SIZE) { |
|
70 throw new IOException("Not a complete Chunk header"); |
|
71 } |
|
72 input.setValidSize(absoluteChunkStart + HEADER_SIZE); |
62 input.position(absoluteChunkStart); |
73 input.position(absoluteChunkStart); |
63 if (input.position() >= input.size()) { |
74 if (input.position() >= input.size()) { |
64 throw new IOException("Chunk contains no data"); |
75 throw new IOException("Chunk contains no data"); |
65 } |
76 } |
66 verifyMagic(input); |
77 verifyMagic(input); |
67 this.input = input; |
78 this.input = input; |
68 this.id = id; |
79 this.id = id; |
69 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id); |
80 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: " + id); |
|
81 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: file=" + input.getFilename()); |
70 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart); |
82 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart); |
71 major = input.readRawShort(); |
83 major = input.readRawShort(); |
72 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major); |
84 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major); |
73 minor = input.readRawShort(); |
85 minor = input.readRawShort(); |
74 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor); |
86 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor); |
75 if (major != 1 && major != 2) { |
87 if (major != 1 && major != 2) { |
76 throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK."); |
88 throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK."); |
77 } |
89 } |
78 chunkSize = input.readRawLong(); |
90 input.readRawLong(); // chunk size |
79 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize); |
91 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize); |
80 this.constantPoolPosition = input.readRawLong(); |
92 input.readRawLong(); // constant pool position |
81 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition); |
93 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition); |
82 metadataPosition = input.readRawLong(); |
94 input.readRawLong(); // metadata position |
83 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition); |
95 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition); |
84 chunkStartNanos = input.readRawLong(); // nanos since epoch |
96 chunkStartNanos = input.readRawLong(); // nanos since epoch |
85 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos); |
97 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos); |
86 durationNanos = input.readRawLong(); // duration nanos, not used |
98 durationNanos = input.readRawLong(); // duration nanos, not used |
87 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos); |
99 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos); |
89 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks); |
101 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks); |
90 ticksPerSecond = input.readRawLong(); |
102 ticksPerSecond = input.readRawLong(); |
91 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond); |
103 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond); |
92 input.readRawInt(); // features, not used |
104 input.readRawInt(); // features, not used |
93 |
105 |
94 // set up boundaries |
106 refresh(); |
95 this.absoluteChunkStart = absoluteChunkStart; |
|
96 absoluteChunkEnd = absoluteChunkStart + chunkSize; |
|
97 lastChunk = input.size() == absoluteChunkEnd; |
|
98 absoluteEventStart = input.position(); |
|
99 |
|
100 // read metadata |
|
101 input.position(absoluteEventStart); |
107 input.position(absoluteEventStart); |
|
108 } |
|
109 |
|
110 void refresh() throws IOException { |
|
111 while (true) { |
|
112 byte fileState1; |
|
113 input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION); |
|
114 while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) { |
|
115 Utils.takeNap(1); |
|
116 input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION); |
|
117 } |
|
118 input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION); |
|
119 long chunkSize = input.readPhysicalLong(); |
|
120 long constantPoolPosition = input.readPhysicalLong(); |
|
121 long metadataPosition = input.readPhysicalLong(); |
|
122 input.positionPhysical(absoluteChunkStart + DURATION_NANOS_POSITION); |
|
123 long durationNanos = input.readPhysicalLong(); |
|
124 input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION); |
|
125 byte fileState2 = input.readPhysicalByte(); |
|
126 if (fileState1 == fileState2) { // valid header |
|
127 finished = fileState1 == 0; |
|
128 if (metadataPosition != 0) { |
|
129 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Setting input size to " + (absoluteChunkStart + chunkSize)); |
|
130 if (finished) { |
|
131 // This assumes that the whole recording |
|
132 // is finished if the first chunk is. |
|
133 // This is a limitation we may want to |
|
134 // remove, but greatly improves performance as |
|
135 // data can be read across chunk boundaries |
|
136 // of multi-chunk files and only once. |
|
137 input.setValidSize(input.getFileSize()); |
|
138 } else { |
|
139 input.setValidSize(absoluteChunkStart + chunkSize); |
|
140 } |
|
141 this.chunkSize = chunkSize; |
|
142 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize); |
|
143 this.constantPoolPosition = constantPoolPosition; |
|
144 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition); |
|
145 this.metadataPosition = metadataPosition; |
|
146 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition); |
|
147 this.durationNanos = durationNanos; |
|
148 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos =" + durationNanos); |
|
149 isFinished = fileState2 == 0; |
|
150 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: generation=" + fileState2); |
|
151 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: finished=" + isFinished); |
|
152 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: fileSize=" + input.size()); |
|
153 absoluteChunkEnd = absoluteChunkStart + chunkSize; |
|
154 return; |
|
155 } |
|
156 } |
|
157 } |
|
158 } |
|
159 |
|
160 public void awaitFinished() throws IOException { |
|
161 if (finished) { |
|
162 return; |
|
163 } |
|
164 long pos = input.position(); |
|
165 try { |
|
166 input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION); |
|
167 while (true) { |
|
168 byte filestate = input.readPhysicalByte(); |
|
169 if (filestate == 0) { |
|
170 finished = true; |
|
171 return; |
|
172 } |
|
173 Utils.takeNap(1); |
|
174 } |
|
175 } finally { |
|
176 input.position(pos); |
|
177 } |
|
178 } |
|
179 |
|
180 public boolean isLastChunk() throws IOException { |
|
181 awaitFinished(); |
|
182 // streaming files only have one chunk |
|
183 return input.getFileSize() == absoluteChunkEnd; |
|
184 } |
|
185 |
|
186 public boolean isFinished() throws IOException { |
|
187 return isFinished; |
102 } |
188 } |
103 |
189 |
104 public ChunkHeader nextHeader() throws IOException { |
190 public ChunkHeader nextHeader() throws IOException { |
105 return new ChunkHeader(input, absoluteChunkEnd, id + 1); |
191 return new ChunkHeader(input, absoluteChunkEnd, id + 1); |
106 } |
192 } |
107 |
|
108 public MetadataDescriptor readMetadata() throws IOException { |
193 public MetadataDescriptor readMetadata() throws IOException { |
|
194 return readMetadata(null); |
|
195 } |
|
196 |
|
197 public MetadataDescriptor readMetadata(MetadataDescriptor previous) throws IOException { |
109 input.position(absoluteChunkStart + metadataPosition); |
198 input.position(absoluteChunkStart + metadataPosition); |
110 input.readInt(); // size |
199 input.readInt(); // size |
111 long id = input.readLong(); // event type id |
200 long id = input.readLong(); // event type id |
112 if (id != METADATA_TYPE_ID) { |
201 if (id != METADATA_TYPE_ID) { |
113 throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID); |
202 throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID); |
114 } |
203 } |
115 input.readLong(); // start time |
204 input.readLong(); // start time |
116 input.readLong(); // duration |
205 input.readLong(); // duration |
117 long metadataId = input.readLong(); |
206 long metadataId = input.readLong(); |
118 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId); |
207 if (previous != null && metadataId == previous.metadataId) { |
119 // No need to read if metadataId == lastMetadataId, but we |
208 return previous; |
120 // do it for verification purposes. |
209 } |
121 return MetadataDescriptor.read(input); |
210 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "New metadata id = " + metadataId); |
122 } |
211 MetadataDescriptor m = MetadataDescriptor.read(input); |
123 |
212 m.metadataId = metadataId; |
124 public boolean isLastChunk() { |
213 return m; |
125 return lastChunk; |
214 } |
126 } |
215 |
127 |
216 |
128 public short getMajor() { |
217 public short getMajor() { |
129 return major; |
218 return major; |
130 } |
219 } |
131 |
220 |