Thu, 14 Feb 2019 15:17:03 -0800
8218935: Make jfr strncpy uses GCC 8.x friendly
Reviewed-by: clanger
1 /*
2 * Copyright (c) 2011, 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 */
25 #include "precompiled.hpp"
26 #include "jfr/jfr.hpp"
27 #include "jfr/jni/jfrJavaSupport.hpp"
28 #include "jfr/recorder/jfrRecorder.hpp"
29 #include "jfr/recorder/repository/jfrChunkState.hpp"
30 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
31 #include "jfr/recorder/repository/jfrRepository.hpp"
32 #include "jfr/recorder/service/jfrPostBox.hpp"
33 #include "memory/resourceArea.hpp"
34 #include "runtime/mutex.hpp"
35 #include "runtime/arguments.hpp"
36 #include "runtime/os.hpp"
37 #include "runtime/thread.inline.hpp"
39 static JfrRepository* _instance = NULL;
41 JfrRepository& JfrRepository::instance() {
42 return *_instance;
43 }
45 static JfrChunkWriter* _chunkwriter = NULL;
47 static bool initialize_chunkwriter() {
48 assert(_chunkwriter == NULL, "invariant");
49 _chunkwriter = new JfrChunkWriter();
50 return _chunkwriter != NULL && _chunkwriter->initialize();
51 }
53 JfrChunkWriter& JfrRepository::chunkwriter() {
54 return *_chunkwriter;
55 }
57 JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {}
59 bool JfrRepository::initialize() {
60 return initialize_chunkwriter();
61 }
63 JfrRepository::~JfrRepository() {
64 if (_path != NULL) {
65 JfrCHeapObj::free(_path, strlen(_path) + 1);
66 _path = NULL;
67 }
69 if (_chunkwriter != NULL) {
70 delete _chunkwriter;
71 _chunkwriter = NULL;
72 }
73 }
75 JfrRepository* JfrRepository::create(JfrPostBox& post_box) {
76 assert(_instance == NULL, "invariant");
77 _instance = new JfrRepository(post_box);
78 return _instance;
79 }
81 void JfrRepository::destroy() {
82 assert(_instance != NULL, "invariant");
83 delete _instance;
84 _instance = NULL;
85 }
87 static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr";
88 static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr";
89 static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr";
90 static const char chunk_file_jfr_ext[] = ".jfr";
91 static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS"
93 static fio_fd open_exclusivly(const char* path) {
94 return os::open(path, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
95 }
97 static fio_fd open_existing(const char* path) {
98 return os::open(path, O_RDWR, S_IREAD | S_IWRITE);
99 }
101 static int file_sort(const char** const file1, const char** file2) {
102 assert(NULL != *file1 && NULL != *file2, "invariant");
103 int cmp = strncmp(*file1, *file2, iso8601_len);
104 if (0 == cmp) {
105 const char* const dot1 = strchr(*file1, '.');
106 assert(NULL != dot1, "invariant");
107 const char* const dot2 = strchr(*file2, '.');
108 assert(NULL != dot2, "invariant");
109 ptrdiff_t file1_len = dot1 - *file1;
110 ptrdiff_t file2_len = dot2 - *file2;
111 if (file1_len < file2_len) {
112 return -1;
113 }
114 if (file1_len > file2_len) {
115 return 1;
116 }
117 assert(file1_len == file2_len, "invariant");
118 cmp = strncmp(*file1, *file2, file1_len);
119 }
120 assert(cmp != 0, "invariant");
121 return cmp;
122 }
124 static void iso8601_to_date_time(char* iso8601_str) {
125 assert(iso8601_str != NULL, "invariant");
126 assert(strlen(iso8601_str) == iso8601_len, "invariant");
127 // "YYYY-MM-DDTHH:MM:SS"
128 for (size_t i = 0; i < iso8601_len; ++i) {
129 switch(iso8601_str[i]) {
130 case 'T' :
131 case '-' :
132 case ':' :
133 iso8601_str[i] = '_';
134 break;
135 }
136 }
137 // "YYYY_MM_DD_HH_MM_SS"
138 }
140 static void date_time(char* buffer, size_t buffer_len) {
141 assert(buffer != NULL, "invariant");
142 assert(buffer_len >= iso8601_len, "buffer too small");
143 os::iso8601_time(buffer, buffer_len);
144 assert(strlen(buffer) >= iso8601_len + 1, "invariant");
145 // "YYYY-MM-DDTHH:MM:SS"
146 buffer[iso8601_len] = '\0';
147 iso8601_to_date_time(buffer);
148 }
150 static jlong file_size(fio_fd fd) {
151 assert(fd != invalid_fd, "invariant");
152 const jlong current_offset = os::current_file_offset(fd);
153 const jlong size = os::lseek(fd, 0, SEEK_END);
154 os::seek_to_file_offset(fd, current_offset);
155 return size;
156 }
158 class RepositoryIterator : public StackObj {
159 private:
160 const char* const _repo;
161 const size_t _repository_len;
162 GrowableArray<const char*>* _files;
163 const char* const fully_qualified(const char* entry) const;
164 mutable int _iterator;
166 public:
167 RepositoryIterator(const char* repository, size_t repository_len);
168 ~RepositoryIterator() {}
169 debug_only(void print_repository_files() const;)
170 const char* const filter(const char* entry) const;
171 bool has_next() const;
172 const char* const next() const;
173 };
175 const char* const RepositoryIterator::fully_qualified(const char* entry) const {
176 assert(NULL != entry, "invariant");
177 char* file_path_entry = NULL;
178 // only use files that have content, not placeholders
179 const char* const file_separator = os::file_separator();
180 if (NULL != file_separator) {
181 const size_t entry_len = strlen(entry);
182 const size_t file_separator_length = strlen(file_separator);
183 const size_t file_path_entry_length = _repository_len + file_separator_length + entry_len;
184 file_path_entry = NEW_RESOURCE_ARRAY_RETURN_NULL(char, file_path_entry_length + 1);
185 if (NULL == file_path_entry) {
186 return NULL;
187 }
188 int position = 0;
189 position += jio_snprintf(&file_path_entry[position], _repository_len + 1, "%s", _repo);
190 position += jio_snprintf(&file_path_entry[position], file_separator_length + 1, "%s", os::file_separator());
191 position += jio_snprintf(&file_path_entry[position], entry_len + 1, "%s", entry);
192 file_path_entry[position] = '\0';
193 assert((size_t)position == file_path_entry_length, "invariant");
194 assert(strlen(file_path_entry) == (size_t)position, "invariant");
195 }
196 return file_path_entry;
197 }
199 const char* const RepositoryIterator::filter(const char* entry) const {
200 if (entry == NULL) {
201 return NULL;
202 }
203 const size_t entry_len = strlen(entry);
204 if (entry_len <= 2) {
205 // for "." and ".."
206 return NULL;
207 }
208 char* entry_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, entry_len + 1);
209 if (entry_name == NULL) {
210 return NULL;
211 }
212 strncpy(entry_name, entry, entry_len + 1);
213 const char* const fully_qualified_path_entry = fully_qualified(entry_name);
214 if (NULL == fully_qualified_path_entry) {
215 return NULL;
216 }
217 const fio_fd entry_fd = open_existing(fully_qualified_path_entry);
218 if (invalid_fd == entry_fd) {
219 return NULL;
220 }
221 const jlong entry_size = file_size(entry_fd);
222 os::close(entry_fd);
223 if (0 == entry_size) {
224 return NULL;
225 }
226 return entry_name;
227 }
229 RepositoryIterator::RepositoryIterator(const char* repository, size_t repository_len) :
230 _repo(repository),
231 _repository_len(repository_len),
232 _files(NULL),
233 _iterator(0) {
234 if (NULL != _repo) {
235 assert(strlen(_repo) == _repository_len, "invariant");
236 _files = new GrowableArray<const char*>(10);
237 DIR* dirp = os::opendir(_repo);
238 if (dirp == NULL) {
239 if (true) tty->print_cr("Unable to open repository %s", _repo);
240 return;
241 }
242 struct dirent* dentry;
243 while ((dentry = os::readdir(dirp)) != NULL) {
244 const char* const entry_path = filter(dentry->d_name);
245 if (NULL != entry_path) {
246 _files->append(entry_path);
247 }
248 }
249 os::closedir(dirp);
250 if (_files->length() > 1) {
251 _files->sort(file_sort);
252 }
253 }
254 }
256 #ifdef ASSERT
257 void RepositoryIterator::print_repository_files() const {
258 while (has_next()) {
259 if (true) tty->print_cr( "%s", next());
260 }
261 }
262 #endif
263 bool RepositoryIterator::has_next() const {
264 return (_files != NULL && _iterator < _files->length());
265 }
267 const char* const RepositoryIterator::next() const {
268 return _iterator >= _files->length() ? NULL : fully_qualified(_files->at(_iterator++));
269 }
271 static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& iterator) {
272 assert(emergency_fd != invalid_fd, "invariant");
273 const size_t size_of_file_copy_block = 1 * M; // 1 mb
274 jbyte* const file_copy_block = NEW_RESOURCE_ARRAY_RETURN_NULL(jbyte, size_of_file_copy_block);
275 if (file_copy_block == NULL) {
276 return;
277 }
278 jlong bytes_written_total = 0;
279 while (iterator.has_next()) {
280 fio_fd current_fd = invalid_fd;
281 const char* const fqn = iterator.next();
282 if (fqn != NULL) {
283 current_fd = open_existing(fqn);
284 if (current_fd != invalid_fd) {
285 const jlong current_filesize = file_size(current_fd);
286 assert(current_filesize > 0, "invariant");
287 jlong bytes_read = 0;
288 jlong bytes_written = 0;
289 while (bytes_read < current_filesize) {
290 bytes_read += (jlong)os::read_at(current_fd, file_copy_block, size_of_file_copy_block, bytes_read);
291 assert(bytes_read - bytes_written <= (jlong)size_of_file_copy_block, "invariant");
292 bytes_written += (jlong)os::write(emergency_fd, file_copy_block, bytes_read - bytes_written);
293 assert(bytes_read == bytes_written, "invariant");
294 }
295 os::close(current_fd);
296 bytes_written_total += bytes_written;
297 }
298 }
299 }
300 }
302 static const char* create_emergency_dump_path() {
303 assert(JfrStream_lock->owned_by_self(), "invariant");
304 char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN);
305 if (NULL == buffer) {
306 return NULL;
307 }
308 const char* const cwd = os::get_current_directory(buffer, O_BUFLEN);
309 if (NULL == cwd) {
310 return NULL;
311 }
312 size_t pos = strlen(cwd);
313 const int fsep_len = jio_snprintf(&buffer[pos], O_BUFLEN - pos, "%s", os::file_separator());
314 const char* filename_fmt = NULL;
315 // fetch specific error cause
316 switch (JfrJavaSupport::cause()) {
317 case JfrJavaSupport::OUT_OF_MEMORY:
318 filename_fmt = vm_oom_filename_fmt;
319 break;
320 case JfrJavaSupport::STACK_OVERFLOW:
321 filename_fmt = vm_soe_filename_fmt;
322 break;
323 default:
324 filename_fmt = vm_error_filename_fmt;
325 }
326 char* emergency_dump_path = NULL;
327 pos += fsep_len;
328 if (Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), &buffer[pos], O_BUFLEN - pos)) {
329 const size_t emergency_filename_length = strlen(buffer);
330 emergency_dump_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, emergency_filename_length + 1);
331 if (NULL == emergency_dump_path) {
332 return NULL;
333 }
334 strncpy(emergency_dump_path, buffer, emergency_filename_length + 1);
335 }
336 return emergency_dump_path;
337 }
339 // Caller needs ResourceMark
340 static const char* create_emergency_chunk_path(const char* repository_base, size_t repository_len) {
341 assert(repository_base != NULL, "invariant");
342 assert(JfrStream_lock->owned_by_self(), "invariant");
343 // date time
344 char date_time_buffer[32] = {0};
345 date_time(date_time_buffer, sizeof(date_time_buffer));
346 size_t date_time_len = strlen(date_time_buffer);
347 size_t chunkname_max_len = repository_len // repository_base
348 + 1 // "/"
349 + date_time_len // date_time
350 + strlen(chunk_file_jfr_ext) // .jfr
351 + 1;
352 char* chunk_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, chunkname_max_len);
353 if (chunk_path == NULL) {
354 return NULL;
355 }
356 // append the individual substrings
357 jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_base, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
358 return chunk_path;
359 }
361 static fio_fd emergency_dump_file() {
362 assert(JfrStream_lock->owned_by_self(), "invariant");
363 ResourceMark rm;
364 const char* const emergency_dump_path = create_emergency_dump_path();
365 if (emergency_dump_path == NULL) {
366 return invalid_fd;
367 }
368 const fio_fd fd = open_exclusivly(emergency_dump_path);
369 if (fd != invalid_fd) {
370 if (LogJFR) tty->print_cr( // For user, should not be "jfr, system"
371 "Attempting to recover JFR data, emergency jfr file: %s", emergency_dump_path);
372 }
373 return fd;
374 }
376 static const char* emergency_path(const char* repository, size_t repository_len) {
377 return repository == NULL ? create_emergency_dump_path() : create_emergency_chunk_path(repository, repository_len);
378 }
380 void JfrRepository::on_vm_error() {
381 assert(!JfrStream_lock->owned_by_self(), "invariant");
382 const char* path = _path;
383 if (path == NULL) {
384 // completed already
385 return;
386 }
387 ResourceMark rm;
388 MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
389 const fio_fd emergency_fd = emergency_dump_file();
390 if (emergency_fd != invalid_fd) {
391 RepositoryIterator iterator(path, strlen(path));
392 write_emergency_file(emergency_fd, iterator);
393 os::close(emergency_fd);
394 }
395 }
397 bool JfrRepository::set_path(const char* path) {
398 assert(path != NULL, "trying to set the repository path with a NULL string!");
399 if (_path != NULL) {
400 // delete existing
401 JfrCHeapObj::free(_path, strlen(_path) + 1);
402 }
403 const size_t path_len = strlen(path);
404 _path = JfrCHeapObj::new_array<char>(path_len + 1);
405 if (_path == NULL) {
406 return false;
407 }
408 strncpy(_path, path, path_len + 1);
409 return true;
410 }
412 void JfrRepository::set_chunk_path(const char* path) {
413 assert(JfrStream_lock->owned_by_self(), "invariant");
414 chunkwriter().set_chunk_path(path);
415 }
417 void JfrRepository::notify_on_new_chunk_path() {
418 if (Jfr::is_recording()) {
419 instance()._post_box.post(MSG_ROTATE);
420 }
421 }
423 /**
424 * Sets the file where data should be written.
425 *
426 * Recording Previous Current Action
427 * ==============================================
428 * true null null Ignore, keep recording in-memory
429 * true null file1 Start disk recording
430 * true file null Copy out metadata to disk and continue in-memory recording
431 * true file1 file2 Copy out metadata and start with new File (file2)
432 * false * null Ignore, but start recording to memory
433 * false * file Ignore, but start recording to disk
434 */
435 void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) {
436 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
437 ResourceMark rm(jt);
438 const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt);
439 {
440 MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
441 if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
442 // new output is NULL and current output is NULL
443 return;
444 }
445 instance().set_chunk_path(canonical_chunk_path);
446 }
447 notify_on_new_chunk_path();
448 }
450 void JfrRepository::set_path(jstring location, JavaThread* jt) {
451 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
452 ResourceMark rm(jt);
453 const char* const path = JfrJavaSupport::c_str(location, jt);
454 if (path != NULL) {
455 instance().set_path(path);
456 }
457 }
459 bool JfrRepository::open_chunk(bool vm_error /* false */) {
460 assert(JfrStream_lock->owned_by_self(), "invariant");
461 if (vm_error) {
462 ResourceMark rm;
463 const char* repository_path = _path;
464 const size_t repository_path_len = repository_path != NULL ? strlen(repository_path) : 0;
465 const char* const path = emergency_path(repository_path, repository_path_len);
466 _chunkwriter->set_chunk_path(path);
467 }
468 return _chunkwriter->open();
469 }
471 size_t JfrRepository::close_chunk(jlong metadata_offset) {
472 return _chunkwriter->close(metadata_offset);
473 }