21 * questions. |
21 * questions. |
22 * |
22 * |
23 */ |
23 */ |
24 |
24 |
25 #include "precompiled.hpp" |
25 #include "precompiled.hpp" |
26 #include "jfr/recorder/repository/jfrChunkState.hpp" |
26 #include "jfr/recorder/repository/jfrChunk.hpp" |
27 #include "jfr/recorder/repository/jfrChunkWriter.hpp" |
27 #include "jfr/recorder/repository/jfrChunkWriter.hpp" |
28 #include "jfr/recorder/service/jfrOptionSet.hpp" |
28 #include "jfr/recorder/service/jfrOptionSet.hpp" |
29 #include "jfr/utilities/jfrTime.hpp" |
29 #include "jfr/utilities/jfrTime.hpp" |
|
30 #include "jfr/utilities/jfrTimeConverter.hpp" |
30 #include "jfr/utilities/jfrTypes.hpp" |
31 #include "jfr/utilities/jfrTypes.hpp" |
31 #include "runtime/mutexLocker.hpp" |
32 #include "runtime/mutexLocker.hpp" |
32 #include "runtime/os.hpp" |
33 #include "runtime/os.hpp" |
33 #include "runtime/os.inline.hpp" |
34 #include "runtime/os.inline.hpp" |
34 |
35 |
35 static const u2 JFR_VERSION_MAJOR = 2; |
36 static const u2 JFR_VERSION_MAJOR = 2; |
36 static const u2 JFR_VERSION_MINOR = 0; |
37 static const u2 JFR_VERSION_MINOR = 0; |
37 static const size_t MAGIC_LEN = 4; |
38 |
38 static const size_t FILEHEADER_SLOT_SIZE = 8; |
39 static const int64_t MAGIC_OFFSET = 0; |
39 static const size_t CHUNK_SIZE_OFFSET = 8; |
40 static const int64_t MAGIC_LEN = 4; |
40 |
41 static const int64_t VERSION_OFFSET = MAGIC_LEN; |
41 JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunkstate(NULL) {} |
42 static const int64_t SIZE_OFFSET = 8; |
42 |
43 static const int64_t SLOT_SIZE = 8; |
43 bool JfrChunkWriter::initialize() { |
44 static const int64_t CHECKPOINT_OFFSET = SIZE_OFFSET + SLOT_SIZE; |
44 assert(_chunkstate == NULL, "invariant"); |
45 static const int64_t METADATA_OFFSET = CHECKPOINT_OFFSET + SLOT_SIZE; |
45 _chunkstate = new JfrChunkState(); |
46 static const int64_t START_NANOS_OFFSET = METADATA_OFFSET + SLOT_SIZE; |
46 return _chunkstate != NULL; |
47 static const int64_t DURATION_NANOS_OFFSET = START_NANOS_OFFSET + SLOT_SIZE; |
47 } |
48 static const int64_t START_TICKS_OFFSET = DURATION_NANOS_OFFSET + SLOT_SIZE; |
|
49 static const int64_t CPU_FREQUENCY_OFFSET = START_TICKS_OFFSET + SLOT_SIZE; |
|
50 static const int64_t GENERATION_OFFSET = CPU_FREQUENCY_OFFSET + SLOT_SIZE; |
|
51 static const int64_t CAPABILITY_OFFSET = GENERATION_OFFSET + 2; |
|
52 static const int64_t HEADER_SIZE = CAPABILITY_OFFSET + 2; |
|
53 static const int64_t RESERVE_SIZE = GENERATION_OFFSET - (4 * SIZE_OFFSET); |
|
54 static const int64_t VOLATILE_FIELD_SIZE = SLOT_SIZE * 2; |
|
55 |
|
56 static const u1 COMPLETE = 0; |
|
57 static const u1 GUARD = 0xff; |
|
58 static const u1 PAD = 0; |
|
59 static const size_t GENERATION_SIZE = sizeof(u2); |
|
60 static const size_t HEAD_BUFFER_SIZE = HEADER_SIZE + SLOT_SIZE; |
|
61 |
|
62 typedef NoOwnershipAdapter JfrHeadBuffer; // stack local array as buffer |
|
63 typedef StreamWriterHost<JfrHeadBuffer, StackObj> JfrBufferedHeadWriter; |
|
64 typedef WriterHost<BigEndianEncoder, BigEndianEncoder, JfrBufferedHeadWriter> JfrHeadWriterBase; |
|
65 |
|
66 static uint8_t head_buffer[HEAD_BUFFER_SIZE] = {0}; |
48 |
67 |
49 static fio_fd open_chunk(const char* path) { |
68 static fio_fd open_chunk(const char* path) { |
50 assert(JfrStream_lock->owned_by_self(), "invariant"); |
|
51 return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd; |
69 return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd; |
52 } |
70 } |
53 |
71 |
|
72 class JfrChunkHeadWriter : public StackObj { |
|
73 friend class JfrChunkWriter; |
|
74 private: |
|
75 JfrChunkWriter* _writer; |
|
76 JfrChunk* _chunk; |
|
77 |
|
78 void write_magic() { |
|
79 assert(MAGIC_OFFSET == _writer->current_offset(), "invariant"); |
|
80 _writer->bytes("FLR", MAGIC_LEN); |
|
81 } |
|
82 |
|
83 void write_version() { |
|
84 assert(VERSION_OFFSET == _writer->current_offset(), "invariant"); |
|
85 _writer->be_write((u2)JFR_VERSION_MAJOR); |
|
86 _writer->be_write((u2)JFR_VERSION_MINOR); |
|
87 } |
|
88 |
|
89 void write_size(int64_t size) { |
|
90 assert(SIZE_OFFSET == _writer->current_offset(), "invariant"); |
|
91 _writer->be_write(size); |
|
92 } |
|
93 |
|
94 void write_checkpoint() { |
|
95 assert(CHECKPOINT_OFFSET == _writer->current_offset(), "invariant"); |
|
96 _writer->be_write(_chunk->last_checkpoint_offset()); |
|
97 } |
|
98 |
|
99 void write_metadata() { |
|
100 assert(METADATA_OFFSET == _writer->current_offset(), "invariant"); |
|
101 _writer->be_write(_chunk->last_metadata_offset()); |
|
102 } |
|
103 |
|
104 void write_time(bool finalize) { |
|
105 assert(_writer->is_valid(), "invariant"); |
|
106 assert(_chunk != NULL, "invariant"); |
|
107 assert(START_NANOS_OFFSET == _writer->current_offset(), "invariant"); |
|
108 if (finalize) { |
|
109 _writer->be_write(_chunk->previous_start_nanos()); |
|
110 _writer->be_write(_chunk->last_chunk_duration()); |
|
111 _writer->be_write(_chunk->previous_start_ticks()); |
|
112 return; |
|
113 } |
|
114 _writer->be_write(_chunk->start_nanos()); |
|
115 _writer->be_write(_chunk->duration()); |
|
116 _writer->be_write(_chunk->start_ticks()); |
|
117 } |
|
118 |
|
119 void write_cpu_frequency() { |
|
120 assert(CPU_FREQUENCY_OFFSET == _writer->current_offset(), "invariant"); |
|
121 static const jlong frequency = JfrTime::frequency(); |
|
122 _writer->be_write(frequency); |
|
123 } |
|
124 |
|
125 void write_capabilities() { |
|
126 assert(CAPABILITY_OFFSET == _writer->current_offset(), "invariant"); |
|
127 // chunk capabilities, CompressedIntegers etc |
|
128 static bool compressed_integers = JfrOptionSet::compressed_integers(); |
|
129 _writer->be_write(compressed_integers ? (u2)1 : (u2)0); |
|
130 } |
|
131 |
|
132 void write_generation(bool finalize) { |
|
133 assert(GENERATION_OFFSET == _writer->current_offset(), "invariant"); |
|
134 _writer->be_write(finalize ? COMPLETE : _chunk->generation()); |
|
135 _writer->be_write(PAD); |
|
136 } |
|
137 |
|
138 void write_guard() { |
|
139 assert(GENERATION_OFFSET == _writer->current_offset(), "invariant"); |
|
140 _writer->be_write(GUARD); |
|
141 _writer->be_write(PAD); |
|
142 } |
|
143 |
|
144 void write_guard_flush() { |
|
145 assert(GENERATION_OFFSET == _writer->current_offset(), "invariant"); |
|
146 write_guard(); |
|
147 _writer->flush(); |
|
148 } |
|
149 |
|
150 void initialize() { |
|
151 assert(_writer->is_valid(), "invariant"); |
|
152 assert(_chunk != NULL, "invariant"); |
|
153 assert(0 == _writer->current_offset(), "invariant"); |
|
154 write_magic(); |
|
155 write_version(); |
|
156 write_size(HEADER_SIZE); |
|
157 write_checkpoint(); |
|
158 write_metadata(); |
|
159 write_time(false); |
|
160 write_cpu_frequency(); |
|
161 write_generation(false); |
|
162 write_capabilities(); |
|
163 assert(HEADER_SIZE == _writer->current_offset(), "invariant"); |
|
164 _writer->flush(); |
|
165 } |
|
166 |
|
167 void flush(int64_t size, bool finalize) { |
|
168 assert(_writer->is_valid(), "invariant"); |
|
169 assert(_chunk != NULL, "invariant"); |
|
170 assert(SIZE_OFFSET == _writer->current_offset(), "invariant"); |
|
171 write_size(size); |
|
172 write_checkpoint(); |
|
173 write_metadata(); |
|
174 write_time(finalize); |
|
175 write_cpu_frequency(); |
|
176 write_generation(finalize); |
|
177 // no need to write capabilities |
|
178 _writer->seek(size); // implicit flush |
|
179 } |
|
180 |
|
181 JfrChunkHeadWriter(JfrChunkWriter* writer, int64_t offset) : _writer(writer), _chunk(writer->_chunk) { |
|
182 assert(_writer != NULL, "invariant"); |
|
183 assert(_writer->is_valid(), "invariant"); |
|
184 assert(_chunk != NULL, "invariant"); |
|
185 if (0 == _writer->current_offset()) { |
|
186 assert(HEADER_SIZE == offset, "invariant"); |
|
187 initialize(); |
|
188 } else { |
|
189 _writer->seek(GENERATION_OFFSET); |
|
190 write_guard(); |
|
191 _writer->seek(offset); |
|
192 } |
|
193 assert(offset == _writer->current_offset(), "invariant"); |
|
194 } |
|
195 }; |
|
196 |
|
197 JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunk(new JfrChunk()) {} |
|
198 |
|
199 JfrChunkWriter::~JfrChunkWriter() { |
|
200 assert(_chunk != NULL, "invariant"); |
|
201 delete _chunk; |
|
202 } |
|
203 |
|
204 void JfrChunkWriter::set_path(const char* path) { |
|
205 assert(_chunk != NULL, "invariant"); |
|
206 _chunk->set_path(path); |
|
207 } |
|
208 |
|
209 void JfrChunkWriter::time_stamp_chunk_now() { |
|
210 assert(_chunk != NULL, "invariant"); |
|
211 _chunk->update_time_to_now(); |
|
212 } |
|
213 |
|
214 int64_t JfrChunkWriter::flushpoint(bool finalize) { |
|
215 assert(_chunk != NULL, "invariant"); |
|
216 const int64_t sz_written = size_written(); |
|
217 if (!finalize) { |
|
218 _chunk->update(); |
|
219 } |
|
220 JfrChunkHeadWriter head(this, SIZE_OFFSET); |
|
221 head.flush(sz_written, finalize); |
|
222 return sz_written; |
|
223 } |
|
224 |
|
225 int64_t JfrChunkWriter::size_written() const { |
|
226 return this->is_valid() ? this->current_offset() : 0; |
|
227 } |
|
228 |
|
229 int64_t JfrChunkWriter::last_checkpoint_offset() const { |
|
230 assert(_chunk != NULL, "invariant"); |
|
231 return _chunk->last_checkpoint_offset(); |
|
232 } |
|
233 |
|
234 int64_t JfrChunkWriter::current_chunk_start_nanos() const { |
|
235 assert(_chunk != NULL, "invariant"); |
|
236 return this->is_valid() ? _chunk->start_nanos() : invalid_time; |
|
237 } |
|
238 |
|
239 void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) { |
|
240 assert(_chunk != NULL, "invariant"); |
|
241 _chunk->set_last_checkpoint_offset(offset); |
|
242 } |
|
243 |
|
244 bool JfrChunkWriter::is_initial_flushpoint_for_chunk() const { |
|
245 assert(_chunk != NULL, "invariant"); |
|
246 assert(_chunk->is_started(), "invariant"); |
|
247 assert(!_chunk->is_finished(), "invariant"); |
|
248 return _chunk->is_initial_flush(); |
|
249 } |
|
250 |
|
251 void JfrChunkWriter::set_last_metadata_offset(int64_t offset) { |
|
252 assert(_chunk != NULL, "invariant"); |
|
253 _chunk->set_last_metadata_offset(offset); |
|
254 } |
|
255 |
|
256 bool JfrChunkWriter::has_metadata() const { |
|
257 assert(_chunk != NULL, "invariant"); |
|
258 return _chunk->has_metadata(); |
|
259 } |
|
260 |
54 bool JfrChunkWriter::open() { |
261 bool JfrChunkWriter::open() { |
55 assert(_chunkstate != NULL, "invariant"); |
262 assert(_chunk != NULL, "invariant"); |
56 JfrChunkWriterBase::reset(open_chunk(_chunkstate->path())); |
263 JfrChunkWriterBase::reset(open_chunk(_chunk->path())); |
57 const bool is_open = this->has_valid_fd(); |
264 const bool is_open = this->has_valid_fd(); |
58 if (is_open) { |
265 if (is_open) { |
59 this->bytes("FLR", MAGIC_LEN); |
266 assert(0 == this->current_offset(), "invariant"); |
60 this->be_write((u2)JFR_VERSION_MAJOR); |
267 _chunk->reset(); |
61 this->be_write((u2)JFR_VERSION_MINOR); |
268 JfrChunkHeadWriter head(this, HEADER_SIZE); |
62 this->reserve(6 * FILEHEADER_SLOT_SIZE); |
|
63 // u8 chunk_size |
|
64 // u8 initial checkpoint offset |
|
65 // u8 metadata section offset |
|
66 // u8 chunk start nanos |
|
67 // u8 chunk duration nanos |
|
68 // u8 chunk start ticks |
|
69 this->be_write(JfrTime::frequency()); |
|
70 // chunk capabilities, CompressedIntegers etc |
|
71 this->be_write((u4)JfrOptionSet::compressed_integers() ? 1 : 0); |
|
72 _chunkstate->reset(); |
|
73 } |
269 } |
74 return is_open; |
270 return is_open; |
75 } |
271 } |
76 |
272 |
77 size_t JfrChunkWriter::close(int64_t metadata_offset) { |
273 int64_t JfrChunkWriter::close() { |
78 write_header(metadata_offset); |
274 assert(this->has_valid_fd(), "invariant"); |
79 this->flush(); |
275 const int64_t size_written = flushpoint(true); |
80 this->close_fd(); |
276 this->close_fd(); |
81 return (size_t)size_written(); |
277 assert(!this->is_valid(), "invariant"); |
82 } |
278 return size_written; |
83 |
279 } |
84 void JfrChunkWriter::write_header(int64_t metadata_offset) { |
|
85 assert(this->is_valid(), "invariant"); |
|
86 // Chunk size |
|
87 this->write_be_at_offset(size_written(), CHUNK_SIZE_OFFSET); |
|
88 // initial checkpoint event offset |
|
89 this->write_be_at_offset(_chunkstate->previous_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE)); |
|
90 // metadata event offset |
|
91 this->write_be_at_offset(metadata_offset, CHUNK_SIZE_OFFSET + (2 * FILEHEADER_SLOT_SIZE)); |
|
92 // start of chunk in nanos since epoch |
|
93 this->write_be_at_offset(_chunkstate->previous_start_nanos(), CHUNK_SIZE_OFFSET + (3 * FILEHEADER_SLOT_SIZE)); |
|
94 // duration of chunk in nanos |
|
95 this->write_be_at_offset(_chunkstate->last_chunk_duration(), CHUNK_SIZE_OFFSET + (4 * FILEHEADER_SLOT_SIZE)); |
|
96 // start of chunk in ticks |
|
97 this->write_be_at_offset(_chunkstate->previous_start_ticks(), CHUNK_SIZE_OFFSET + (5 * FILEHEADER_SLOT_SIZE)); |
|
98 } |
|
99 |
|
100 void JfrChunkWriter::set_chunk_path(const char* chunk_path) { |
|
101 _chunkstate->set_path(chunk_path); |
|
102 } |
|
103 |
|
104 int64_t JfrChunkWriter::size_written() const { |
|
105 return this->is_valid() ? this->current_offset() : 0; |
|
106 } |
|
107 |
|
108 int64_t JfrChunkWriter::previous_checkpoint_offset() const { |
|
109 return _chunkstate->previous_checkpoint_offset(); |
|
110 } |
|
111 |
|
112 void JfrChunkWriter::set_previous_checkpoint_offset(int64_t offset) { |
|
113 _chunkstate->set_previous_checkpoint_offset(offset); |
|
114 } |
|
115 |
|
116 void JfrChunkWriter::time_stamp_chunk_now() { |
|
117 _chunkstate->update_time_to_now(); |
|
118 } |
|