1 /*
2 * Copyright (c) 2012, 2019, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "precompiled.hpp"
26 #include "jfr/recorder/repository/jfrChunk.hpp"
27 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
28 #include "jfr/utilities/jfrTime.hpp"
29 #include "runtime/mutexLocker.hpp"
30 #include "runtime/os.inline.hpp"
31
32 static const int64_t MAGIC_OFFSET = 0;
33 static const int64_t MAGIC_LEN = 4;
34 static const int64_t VERSION_OFFSET = MAGIC_LEN;
35 static const int64_t SIZE_OFFSET = 8;
36 static const int64_t SLOT_SIZE = 8;
37 static const int64_t CHECKPOINT_OFFSET = SIZE_OFFSET + SLOT_SIZE;
38 static const int64_t METADATA_OFFSET = CHECKPOINT_OFFSET + SLOT_SIZE;
39 static const int64_t START_NANOS_OFFSET = METADATA_OFFSET + SLOT_SIZE;
40 static const int64_t DURATION_NANOS_OFFSET = START_NANOS_OFFSET + SLOT_SIZE;
41 static const int64_t START_TICKS_OFFSET = DURATION_NANOS_OFFSET + SLOT_SIZE;
42 static const int64_t CPU_FREQUENCY_OFFSET = START_TICKS_OFFSET + SLOT_SIZE;
43 static const int64_t GENERATION_OFFSET = CPU_FREQUENCY_OFFSET + SLOT_SIZE;
44 static const int64_t FLAG_OFFSET = GENERATION_OFFSET + 2;
45 static const int64_t HEADER_SIZE = FLAG_OFFSET + 2;
46
open_chunk(const char * path)47 static fio_fd open_chunk(const char* path) {
48 return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd;
49 }
50
51 #ifdef ASSERT
assert_writer_position(JfrChunkWriter * writer,int64_t offset)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:
write_magic()63 void write_magic() {
64 _writer->bytes(_chunk->magic(), MAGIC_LEN);
65 }
66
write_version()67 void write_version() {
68 _writer->be_write(_chunk->major_version());
69 _writer->be_write(_chunk->minor_version());
70 }
71
write_size(int64_t size)72 void write_size(int64_t size) {
73 _writer->be_write(size);
74 }
75
write_checkpoint()76 void write_checkpoint() {
77 _writer->be_write(_chunk->last_checkpoint_offset());
78 }
79
write_metadata()80 void write_metadata() {
81 _writer->be_write(_chunk->last_metadata_offset());
82 }
83
write_time(bool finalize)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
write_cpu_frequency()96 void write_cpu_frequency() {
97 _writer->be_write(_chunk->cpu_frequency());
98 }
99
write_generation(bool finalize)100 void write_generation(bool finalize) {
101 _writer->be_write(finalize ? COMPLETE : _chunk->generation());
102 _writer->be_write(PAD);
103 }
104
write_next_generation()105 void write_next_generation() {
106 _writer->be_write(_chunk->next_generation());
107 _writer->be_write(PAD);
108 }
109
write_guard()110 void write_guard() {
111 _writer->be_write(GUARD);
112 _writer->be_write(PAD);
113 }
114
write_guard_flush()115 void write_guard_flush() {
116 write_guard();
117 _writer->flush();
118 }
119
write_flags()120 void write_flags() {
121 _writer->be_write(_chunk->flags());
122 }
123
write_size_to_generation(int64_t size,bool finalize)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
flush(int64_t size,bool finalize)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 write_flags();
139 _writer->seek(size); // implicit flush
140 }
141
initialize()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_flags();
150 DEBUG_ONLY(assert_writer_position(_writer, HEADER_SIZE);)
151 _writer->flush();
152 }
153
JfrChunkHeadWriter(JfrChunkWriter * writer,int64_t offset,bool guard=true)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
prepare_chunk_header_constant_pool(JfrChunkWriter & cw,int64_t event_offset,bool flushpoint)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
write_chunk_header_checkpoint(bool flushpoint)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_flags();
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
mark_chunk_final()214 void JfrChunkWriter::mark_chunk_final() {
215 assert(_chunk != NULL, "invariant");
216 _chunk->mark_final();
217 }
218
flush_chunk(bool flushpoint)219 int64_t JfrChunkWriter::flush_chunk(bool flushpoint) {
220 assert(_chunk != NULL, "invariant");
221 const int64_t sz_written = write_chunk_header_checkpoint(flushpoint);
222 assert(size_written() == sz_written, "invariant");
223 JfrChunkHeadWriter head(this, SIZE_OFFSET);
224 head.flush(sz_written, !flushpoint);
225 return sz_written;
226 }
227
JfrChunkWriter()228 JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunk(new JfrChunk()) {}
229
~JfrChunkWriter()230 JfrChunkWriter::~JfrChunkWriter() {
231 assert(_chunk != NULL, "invariant");
232 delete _chunk;
233 }
234
set_path(const char * path)235 void JfrChunkWriter::set_path(const char* path) {
236 assert(_chunk != NULL, "invariant");
237 _chunk->set_path(path);
238 }
239
set_time_stamp()240 void JfrChunkWriter::set_time_stamp() {
241 assert(_chunk != NULL, "invariant");
242 _chunk->set_time_stamp();
243 }
244
size_written() const245 int64_t JfrChunkWriter::size_written() const {
246 return this->is_valid() ? this->current_offset() : 0;
247 }
248
last_checkpoint_offset() const249 int64_t JfrChunkWriter::last_checkpoint_offset() const {
250 assert(_chunk != NULL, "invariant");
251 return _chunk->last_checkpoint_offset();
252 }
253
current_chunk_start_nanos() const254 int64_t JfrChunkWriter::current_chunk_start_nanos() const {
255 assert(_chunk != NULL, "invariant");
256 return this->is_valid() ? _chunk->start_nanos() : invalid_time;
257 }
258
set_last_checkpoint_offset(int64_t offset)259 void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) {
260 assert(_chunk != NULL, "invariant");
261 _chunk->set_last_checkpoint_offset(offset);
262 }
263
set_last_metadata_offset(int64_t offset)264 void JfrChunkWriter::set_last_metadata_offset(int64_t offset) {
265 assert(_chunk != NULL, "invariant");
266 _chunk->set_last_metadata_offset(offset);
267 }
268
has_metadata() const269 bool JfrChunkWriter::has_metadata() const {
270 assert(_chunk != NULL, "invariant");
271 return _chunk->has_metadata();
272 }
273
open()274 bool JfrChunkWriter::open() {
275 assert(_chunk != NULL, "invariant");
276 JfrChunkWriterBase::reset(open_chunk(_chunk->path()));
277 const bool is_open = this->has_valid_fd();
278 if (is_open) {
279 assert(0 == this->current_offset(), "invariant");
280 _chunk->reset();
281 JfrChunkHeadWriter head(this, HEADER_SIZE);
282 }
283 return is_open;
284 }
285
close()286 int64_t JfrChunkWriter::close() {
287 assert(this->has_valid_fd(), "invariant");
288 const int64_t size_written = flush_chunk(false);
289 this->close_fd();
290 assert(!this->is_valid(), "invariant");
291 return size_written;
292 }
293