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" |
|
29 #include "jfr/utilities/jfrTime.hpp" |
28 #include "jfr/utilities/jfrTime.hpp" |
30 #include "jfr/utilities/jfrTypes.hpp" |
|
31 #include "runtime/mutexLocker.hpp" |
29 #include "runtime/mutexLocker.hpp" |
32 #include "runtime/os.hpp" |
|
33 #include "runtime/os.inline.hpp" |
30 #include "runtime/os.inline.hpp" |
34 |
31 |
35 static const u2 JFR_VERSION_MAJOR = 2; |
32 static const int64_t MAGIC_OFFSET = 0; |
36 static const u2 JFR_VERSION_MINOR = 0; |
33 static const int64_t MAGIC_LEN = 4; |
37 static const size_t MAGIC_LEN = 4; |
34 static const int64_t VERSION_OFFSET = MAGIC_LEN; |
38 static const size_t FILEHEADER_SLOT_SIZE = 8; |
35 static const int64_t SIZE_OFFSET = 8; |
39 static const size_t CHUNK_SIZE_OFFSET = 8; |
36 static const int64_t SLOT_SIZE = 8; |
40 |
37 static const int64_t CHECKPOINT_OFFSET = SIZE_OFFSET + SLOT_SIZE; |
41 JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunkstate(NULL) {} |
38 static const int64_t METADATA_OFFSET = CHECKPOINT_OFFSET + SLOT_SIZE; |
42 |
39 static const int64_t START_NANOS_OFFSET = METADATA_OFFSET + SLOT_SIZE; |
43 bool JfrChunkWriter::initialize() { |
40 static const int64_t DURATION_NANOS_OFFSET = START_NANOS_OFFSET + SLOT_SIZE; |
44 assert(_chunkstate == NULL, "invariant"); |
41 static const int64_t START_TICKS_OFFSET = DURATION_NANOS_OFFSET + SLOT_SIZE; |
45 _chunkstate = new JfrChunkState(); |
42 static const int64_t CPU_FREQUENCY_OFFSET = START_TICKS_OFFSET + SLOT_SIZE; |
46 return _chunkstate != NULL; |
43 static const int64_t GENERATION_OFFSET = CPU_FREQUENCY_OFFSET + SLOT_SIZE; |
47 } |
44 static const int64_t CAPABILITY_OFFSET = GENERATION_OFFSET + 2; |
|
45 static const int64_t HEADER_SIZE = CAPABILITY_OFFSET + 2; |
48 |
46 |
49 static fio_fd open_chunk(const char* path) { |
47 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; |
48 return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd; |
52 } |
49 } |
53 |
50 |
|
51 #ifdef ASSERT |
|
52 static void assert_writer_position(JfrChunkWriter* writer, int64_t offset) { |
|
53 assert(writer != NULL, "invariant"); |
|
54 assert(offset == writer->current_offset(), "invariant"); |
|
55 } |
|
56 #endif |
|
57 |
|
58 class JfrChunkHeadWriter : public StackObj { |
|
59 private: |
|
60 JfrChunkWriter* _writer; |
|
61 JfrChunk* _chunk; |
|
62 public: |
|
63 void write_magic() { |
|
64 _writer->bytes(_chunk->magic(), MAGIC_LEN); |
|
65 } |
|
66 |
|
67 void write_version() { |
|
68 _writer->be_write(_chunk->major_version()); |
|
69 _writer->be_write(_chunk->minor_version()); |
|
70 } |
|
71 |
|
72 void write_size(int64_t size) { |
|
73 _writer->be_write(size); |
|
74 } |
|
75 |
|
76 void write_checkpoint() { |
|
77 _writer->be_write(_chunk->last_checkpoint_offset()); |
|
78 } |
|
79 |
|
80 void write_metadata() { |
|
81 _writer->be_write(_chunk->last_metadata_offset()); |
|
82 } |
|
83 |
|
84 void write_time(bool finalize) { |
|
85 if (finalize) { |
|
86 _writer->be_write(_chunk->previous_start_nanos()); |
|
87 _writer->be_write(_chunk->last_chunk_duration()); |
|
88 _writer->be_write(_chunk->previous_start_ticks()); |
|
89 return; |
|
90 } |
|
91 _writer->be_write(_chunk->start_nanos()); |
|
92 _writer->be_write(_chunk->duration()); |
|
93 _writer->be_write(_chunk->start_ticks()); |
|
94 } |
|
95 |
|
96 void write_cpu_frequency() { |
|
97 _writer->be_write(_chunk->cpu_frequency()); |
|
98 } |
|
99 |
|
100 void write_generation(bool finalize) { |
|
101 _writer->be_write(finalize ? COMPLETE : _chunk->generation()); |
|
102 _writer->be_write(PAD); |
|
103 } |
|
104 |
|
105 void write_next_generation() { |
|
106 _writer->be_write(_chunk->next_generation()); |
|
107 _writer->be_write(PAD); |
|
108 } |
|
109 |
|
110 void write_guard() { |
|
111 _writer->be_write(GUARD); |
|
112 _writer->be_write(PAD); |
|
113 } |
|
114 |
|
115 void write_guard_flush() { |
|
116 write_guard(); |
|
117 _writer->flush(); |
|
118 } |
|
119 |
|
120 void write_capabilities() { |
|
121 _writer->be_write(_chunk->capabilities()); |
|
122 } |
|
123 |
|
124 void write_size_to_generation(int64_t size, bool finalize) { |
|
125 write_size(size); |
|
126 write_checkpoint(); |
|
127 write_metadata(); |
|
128 write_time(finalize); |
|
129 write_cpu_frequency(); |
|
130 write_generation(finalize); |
|
131 } |
|
132 |
|
133 void flush(int64_t size, bool finalize) { |
|
134 assert(_writer->is_valid(), "invariant"); |
|
135 assert(_chunk != NULL, "invariant"); |
|
136 DEBUG_ONLY(assert_writer_position(_writer, SIZE_OFFSET);) |
|
137 write_size_to_generation(size, finalize); |
|
138 // no need to write capabilities |
|
139 _writer->seek(size); // implicit flush |
|
140 } |
|
141 |
|
142 void initialize() { |
|
143 assert(_writer->is_valid(), "invariant"); |
|
144 assert(_chunk != NULL, "invariant"); |
|
145 DEBUG_ONLY(assert_writer_position(_writer, 0);) |
|
146 write_magic(); |
|
147 write_version(); |
|
148 write_size_to_generation(HEADER_SIZE, false); |
|
149 write_capabilities(); |
|
150 DEBUG_ONLY(assert_writer_position(_writer, HEADER_SIZE);) |
|
151 _writer->flush(); |
|
152 } |
|
153 |
|
154 JfrChunkHeadWriter(JfrChunkWriter* writer, int64_t offset, bool guard = true) : _writer(writer), _chunk(writer->_chunk) { |
|
155 assert(_writer != NULL, "invariant"); |
|
156 assert(_writer->is_valid(), "invariant"); |
|
157 assert(_chunk != NULL, "invariant"); |
|
158 if (0 == _writer->current_offset()) { |
|
159 assert(HEADER_SIZE == offset, "invariant"); |
|
160 initialize(); |
|
161 } else { |
|
162 if (guard) { |
|
163 _writer->seek(GENERATION_OFFSET); |
|
164 write_guard(); |
|
165 _writer->seek(offset); |
|
166 } else { |
|
167 _chunk->update_current_nanos(); |
|
168 } |
|
169 } |
|
170 DEBUG_ONLY(assert_writer_position(_writer, offset);) |
|
171 } |
|
172 }; |
|
173 |
|
174 static int64_t prepare_chunk_header_constant_pool(JfrChunkWriter& cw, int64_t event_offset, bool flushpoint) { |
|
175 const int64_t delta = cw.last_checkpoint_offset() == 0 ? 0 : cw.last_checkpoint_offset() - event_offset; |
|
176 const u4 checkpoint_type = flushpoint ? (u4)(FLUSH | HEADER) : (u4)HEADER; |
|
177 cw.reserve(sizeof(u4)); |
|
178 cw.write<u8>(EVENT_CHECKPOINT); |
|
179 cw.write<u8>(JfrTicks::now().value()); |
|
180 cw.write<u8>(0); // duration |
|
181 cw.write<u8>(delta); // to previous checkpoint |
|
182 cw.write<u4>(checkpoint_type); |
|
183 cw.write<u4>(1); // pool count |
|
184 cw.write<u8>(TYPE_CHUNKHEADER); |
|
185 cw.write<u4>(1); // count |
|
186 cw.write<u8>(1); // key |
|
187 cw.write<u4>(HEADER_SIZE); // length of byte array |
|
188 return cw.current_offset(); |
|
189 } |
|
190 |
|
191 int64_t JfrChunkWriter::write_chunk_header_checkpoint(bool flushpoint) { |
|
192 assert(this->has_valid_fd(), "invariant"); |
|
193 const int64_t event_size_offset = current_offset(); |
|
194 const int64_t header_content_pos = prepare_chunk_header_constant_pool(*this, event_size_offset, flushpoint); |
|
195 JfrChunkHeadWriter head(this, header_content_pos, false); |
|
196 head.write_magic(); |
|
197 head.write_version(); |
|
198 const int64_t chunk_size_offset = reserve(sizeof(int64_t)); // size to be decided when we are done |
|
199 be_write(event_size_offset); // last checkpoint offset will be this checkpoint |
|
200 head.write_metadata(); |
|
201 head.write_time(false); |
|
202 head.write_cpu_frequency(); |
|
203 head.write_next_generation(); |
|
204 head.write_capabilities(); |
|
205 assert(current_offset() - header_content_pos == HEADER_SIZE, "invariant"); |
|
206 const u4 checkpoint_size = current_offset() - event_size_offset; |
|
207 write_padded_at_offset<u4>(checkpoint_size, event_size_offset); |
|
208 set_last_checkpoint_offset(event_size_offset); |
|
209 const size_t sz_written = size_written(); |
|
210 write_be_at_offset(sz_written, chunk_size_offset); |
|
211 return sz_written; |
|
212 } |
|
213 |
|
214 int64_t JfrChunkWriter::flush_chunk(bool flushpoint) { |
|
215 assert(_chunk != NULL, "invariant"); |
|
216 const int64_t sz_written = write_chunk_header_checkpoint(flushpoint); |
|
217 assert(size_written() == sz_written, "invariant"); |
|
218 JfrChunkHeadWriter head(this, SIZE_OFFSET); |
|
219 head.flush(sz_written, !flushpoint); |
|
220 return sz_written; |
|
221 } |
|
222 |
|
223 JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunk(new JfrChunk()) {} |
|
224 |
|
225 JfrChunkWriter::~JfrChunkWriter() { |
|
226 assert(_chunk != NULL, "invariant"); |
|
227 delete _chunk; |
|
228 } |
|
229 |
|
230 void JfrChunkWriter::set_path(const char* path) { |
|
231 assert(_chunk != NULL, "invariant"); |
|
232 _chunk->set_path(path); |
|
233 } |
|
234 |
|
235 void JfrChunkWriter::set_time_stamp() { |
|
236 assert(_chunk != NULL, "invariant"); |
|
237 _chunk->set_time_stamp(); |
|
238 } |
|
239 |
|
240 int64_t JfrChunkWriter::size_written() const { |
|
241 return this->is_valid() ? this->current_offset() : 0; |
|
242 } |
|
243 |
|
244 int64_t JfrChunkWriter::last_checkpoint_offset() const { |
|
245 assert(_chunk != NULL, "invariant"); |
|
246 return _chunk->last_checkpoint_offset(); |
|
247 } |
|
248 |
|
249 int64_t JfrChunkWriter::current_chunk_start_nanos() const { |
|
250 assert(_chunk != NULL, "invariant"); |
|
251 return this->is_valid() ? _chunk->start_nanos() : invalid_time; |
|
252 } |
|
253 |
|
254 void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) { |
|
255 assert(_chunk != NULL, "invariant"); |
|
256 _chunk->set_last_checkpoint_offset(offset); |
|
257 } |
|
258 |
|
259 void JfrChunkWriter::set_last_metadata_offset(int64_t offset) { |
|
260 assert(_chunk != NULL, "invariant"); |
|
261 _chunk->set_last_metadata_offset(offset); |
|
262 } |
|
263 |
|
264 bool JfrChunkWriter::has_metadata() const { |
|
265 assert(_chunk != NULL, "invariant"); |
|
266 return _chunk->has_metadata(); |
|
267 } |
|
268 |
54 bool JfrChunkWriter::open() { |
269 bool JfrChunkWriter::open() { |
55 assert(_chunkstate != NULL, "invariant"); |
270 assert(_chunk != NULL, "invariant"); |
56 JfrChunkWriterBase::reset(open_chunk(_chunkstate->path())); |
271 JfrChunkWriterBase::reset(open_chunk(_chunk->path())); |
57 const bool is_open = this->has_valid_fd(); |
272 const bool is_open = this->has_valid_fd(); |
58 if (is_open) { |
273 if (is_open) { |
59 this->bytes("FLR", MAGIC_LEN); |
274 assert(0 == this->current_offset(), "invariant"); |
60 this->be_write((u2)JFR_VERSION_MAJOR); |
275 _chunk->reset(); |
61 this->be_write((u2)JFR_VERSION_MINOR); |
276 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 } |
277 } |
74 return is_open; |
278 return is_open; |
75 } |
279 } |
76 |
280 |
77 size_t JfrChunkWriter::close(int64_t metadata_offset) { |
281 int64_t JfrChunkWriter::close() { |
78 write_header(metadata_offset); |
282 assert(this->has_valid_fd(), "invariant"); |
79 this->flush(); |
283 const int64_t size_written = flush_chunk(false); |
80 this->close_fd(); |
284 this->close_fd(); |
81 return (size_t)size_written(); |
285 assert(!this->is_valid(), "invariant"); |
82 } |
286 return size_written; |
83 |
287 } |
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->last_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::last_checkpoint_offset() const { |
|
109 return _chunkstate->last_checkpoint_offset(); |
|
110 } |
|
111 |
|
112 void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) { |
|
113 _chunkstate->set_last_checkpoint_offset(offset); |
|
114 } |
|
115 |
|
116 void JfrChunkWriter::time_stamp_chunk_now() { |
|
117 _chunkstate->update_time_to_now(); |
|
118 } |
|