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