1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/vm/jfr/recorder/repository/jfrRepository.cpp Mon Aug 12 18:30:40 2019 +0300 1.3 @@ -0,0 +1,476 @@ 1.4 +/* 1.5 + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 + * or visit www.oracle.com if you need additional information or have any 1.24 + * questions. 1.25 + * 1.26 + */ 1.27 + 1.28 +#include "precompiled.hpp" 1.29 +#include "jfr/jfr.hpp" 1.30 +#include "jfr/jni/jfrJavaSupport.hpp" 1.31 +#include "jfr/recorder/jfrRecorder.hpp" 1.32 +#include "jfr/recorder/repository/jfrChunkState.hpp" 1.33 +#include "jfr/recorder/repository/jfrChunkWriter.hpp" 1.34 +#include "jfr/recorder/repository/jfrRepository.hpp" 1.35 +#include "jfr/recorder/service/jfrPostBox.hpp" 1.36 +#include "memory/resourceArea.hpp" 1.37 +#include "runtime/mutex.hpp" 1.38 +#include "runtime/arguments.hpp" 1.39 +#include "runtime/os.hpp" 1.40 +#include "runtime/thread.inline.hpp" 1.41 + 1.42 +static JfrRepository* _instance = NULL; 1.43 + 1.44 +JfrRepository& JfrRepository::instance() { 1.45 + return *_instance; 1.46 +} 1.47 + 1.48 +static JfrChunkWriter* _chunkwriter = NULL; 1.49 + 1.50 +static bool initialize_chunkwriter() { 1.51 + assert(_chunkwriter == NULL, "invariant"); 1.52 + _chunkwriter = new JfrChunkWriter(); 1.53 + return _chunkwriter != NULL && _chunkwriter->initialize(); 1.54 +} 1.55 + 1.56 +JfrChunkWriter& JfrRepository::chunkwriter() { 1.57 + return *_chunkwriter; 1.58 +} 1.59 + 1.60 +JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {} 1.61 + 1.62 +bool JfrRepository::initialize() { 1.63 + return initialize_chunkwriter(); 1.64 +} 1.65 + 1.66 +JfrRepository::~JfrRepository() { 1.67 + if (_path != NULL) { 1.68 + JfrCHeapObj::free(_path, strlen(_path) + 1); 1.69 + _path = NULL; 1.70 + } 1.71 + 1.72 + if (_chunkwriter != NULL) { 1.73 + delete _chunkwriter; 1.74 + _chunkwriter = NULL; 1.75 + } 1.76 +} 1.77 + 1.78 +JfrRepository* JfrRepository::create(JfrPostBox& post_box) { 1.79 + assert(_instance == NULL, "invariant"); 1.80 + _instance = new JfrRepository(post_box); 1.81 + return _instance; 1.82 +} 1.83 + 1.84 +void JfrRepository::destroy() { 1.85 + assert(_instance != NULL, "invariant"); 1.86 + delete _instance; 1.87 + _instance = NULL; 1.88 +} 1.89 + 1.90 +static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr"; 1.91 +static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr"; 1.92 +static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr"; 1.93 +static const char chunk_file_jfr_ext[] = ".jfr"; 1.94 +static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS" 1.95 + 1.96 +static fio_fd open_exclusivly(const char* path) { 1.97 + return os::open(path, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); 1.98 +} 1.99 + 1.100 +static fio_fd open_existing(const char* path) { 1.101 + return os::open(path, O_RDWR, S_IREAD | S_IWRITE); 1.102 +} 1.103 + 1.104 +static int file_sort(const char** const file1, const char** file2) { 1.105 + assert(NULL != *file1 && NULL != *file2, "invariant"); 1.106 + int cmp = strncmp(*file1, *file2, iso8601_len); 1.107 + if (0 == cmp) { 1.108 + const char* const dot1 = strchr(*file1, '.'); 1.109 + assert(NULL != dot1, "invariant"); 1.110 + const char* const dot2 = strchr(*file2, '.'); 1.111 + assert(NULL != dot2, "invariant"); 1.112 + ptrdiff_t file1_len = dot1 - *file1; 1.113 + ptrdiff_t file2_len = dot2 - *file2; 1.114 + if (file1_len < file2_len) { 1.115 + return -1; 1.116 + } 1.117 + if (file1_len > file2_len) { 1.118 + return 1; 1.119 + } 1.120 + assert(file1_len == file2_len, "invariant"); 1.121 + cmp = strncmp(*file1, *file2, file1_len); 1.122 + } 1.123 + assert(cmp != 0, "invariant"); 1.124 + return cmp; 1.125 +} 1.126 + 1.127 +static void iso8601_to_date_time(char* iso8601_str) { 1.128 + assert(iso8601_str != NULL, "invariant"); 1.129 + assert(strlen(iso8601_str) == iso8601_len, "invariant"); 1.130 + // "YYYY-MM-DDTHH:MM:SS" 1.131 + for (size_t i = 0; i < iso8601_len; ++i) { 1.132 + switch(iso8601_str[i]) { 1.133 + case 'T' : 1.134 + case '-' : 1.135 + case ':' : 1.136 + iso8601_str[i] = '_'; 1.137 + break; 1.138 + } 1.139 + } 1.140 + // "YYYY_MM_DD_HH_MM_SS" 1.141 +} 1.142 + 1.143 +static void date_time(char* buffer, size_t buffer_len) { 1.144 + assert(buffer != NULL, "invariant"); 1.145 + assert(buffer_len >= iso8601_len, "buffer too small"); 1.146 + os::iso8601_time(buffer, buffer_len); 1.147 + assert(strlen(buffer) >= iso8601_len + 1, "invariant"); 1.148 + // "YYYY-MM-DDTHH:MM:SS" 1.149 + buffer[iso8601_len] = '\0'; 1.150 + iso8601_to_date_time(buffer); 1.151 +} 1.152 + 1.153 +static jlong file_size(fio_fd fd) { 1.154 + assert(fd != invalid_fd, "invariant"); 1.155 + const jlong current_offset = os::current_file_offset(fd); 1.156 + const jlong size = os::lseek(fd, 0, SEEK_END); 1.157 + os::seek_to_file_offset(fd, current_offset); 1.158 + return size; 1.159 +} 1.160 + 1.161 +class RepositoryIterator : public StackObj { 1.162 + private: 1.163 + const char* const _repo; 1.164 + const size_t _repository_len; 1.165 + GrowableArray<const char*>* _files; 1.166 + const char* const fully_qualified(const char* entry) const; 1.167 + mutable int _iterator; 1.168 + 1.169 + public: 1.170 + RepositoryIterator(const char* repository, size_t repository_len); 1.171 + ~RepositoryIterator() {} 1.172 + debug_only(void print_repository_files() const;) 1.173 + const char* const filter(const char* entry) const; 1.174 + bool has_next() const; 1.175 + const char* const next() const; 1.176 +}; 1.177 + 1.178 +const char* const RepositoryIterator::fully_qualified(const char* entry) const { 1.179 + assert(NULL != entry, "invariant"); 1.180 + char* file_path_entry = NULL; 1.181 + // only use files that have content, not placeholders 1.182 + const char* const file_separator = os::file_separator(); 1.183 + if (NULL != file_separator) { 1.184 + const size_t entry_len = strlen(entry); 1.185 + const size_t file_separator_length = strlen(file_separator); 1.186 + const size_t file_path_entry_length = _repository_len + file_separator_length + entry_len; 1.187 + file_path_entry = NEW_RESOURCE_ARRAY_RETURN_NULL(char, file_path_entry_length + 1); 1.188 + if (NULL == file_path_entry) { 1.189 + return NULL; 1.190 + } 1.191 + int position = 0; 1.192 + position += jio_snprintf(&file_path_entry[position], _repository_len + 1, "%s", _repo); 1.193 + position += jio_snprintf(&file_path_entry[position], file_separator_length + 1, "%s", os::file_separator()); 1.194 + position += jio_snprintf(&file_path_entry[position], entry_len + 1, "%s", entry); 1.195 + file_path_entry[position] = '\0'; 1.196 + assert((size_t)position == file_path_entry_length, "invariant"); 1.197 + assert(strlen(file_path_entry) == (size_t)position, "invariant"); 1.198 + } 1.199 + return file_path_entry; 1.200 +} 1.201 + 1.202 +const char* const RepositoryIterator::filter(const char* entry) const { 1.203 + if (entry == NULL) { 1.204 + return NULL; 1.205 + } 1.206 + const size_t entry_len = strlen(entry); 1.207 + if (entry_len <= 2) { 1.208 + // for "." and ".." 1.209 + return NULL; 1.210 + } 1.211 + char* entry_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, entry_len + 1); 1.212 + if (entry_name == NULL) { 1.213 + return NULL; 1.214 + } 1.215 + strncpy(entry_name, entry, entry_len); 1.216 + entry_name[entry_len] = '\0'; 1.217 + const char* const fully_qualified_path_entry = fully_qualified(entry_name); 1.218 + if (NULL == fully_qualified_path_entry) { 1.219 + return NULL; 1.220 + } 1.221 + const fio_fd entry_fd = open_existing(fully_qualified_path_entry); 1.222 + if (invalid_fd == entry_fd) { 1.223 + return NULL; 1.224 + } 1.225 + const jlong entry_size = file_size(entry_fd); 1.226 + os::close(entry_fd); 1.227 + if (0 == entry_size) { 1.228 + return NULL; 1.229 + } 1.230 + return entry_name; 1.231 +} 1.232 + 1.233 +RepositoryIterator::RepositoryIterator(const char* repository, size_t repository_len) : 1.234 + _repo(repository), 1.235 + _repository_len(repository_len), 1.236 + _files(NULL), 1.237 + _iterator(0) { 1.238 + if (NULL != _repo) { 1.239 + assert(strlen(_repo) == _repository_len, "invariant"); 1.240 + _files = new GrowableArray<const char*>(10); 1.241 + DIR* dirp = os::opendir(_repo); 1.242 + if (dirp == NULL) { 1.243 + if (true) tty->print_cr("Unable to open repository %s", _repo); 1.244 + return; 1.245 + } 1.246 + struct dirent* dentry; 1.247 + while ((dentry = os::readdir(dirp)) != NULL) { 1.248 + const char* const entry_path = filter(dentry->d_name); 1.249 + if (NULL != entry_path) { 1.250 + _files->append(entry_path); 1.251 + } 1.252 + } 1.253 + os::closedir(dirp); 1.254 + if (_files->length() > 1) { 1.255 + _files->sort(file_sort); 1.256 + } 1.257 + } 1.258 +} 1.259 + 1.260 +#ifdef ASSERT 1.261 +void RepositoryIterator::print_repository_files() const { 1.262 + while (has_next()) { 1.263 + if (true) tty->print_cr( "%s", next()); 1.264 + } 1.265 +} 1.266 +#endif 1.267 +bool RepositoryIterator::has_next() const { 1.268 + return (_files != NULL && _iterator < _files->length()); 1.269 +} 1.270 + 1.271 +const char* const RepositoryIterator::next() const { 1.272 + return _iterator >= _files->length() ? NULL : fully_qualified(_files->at(_iterator++)); 1.273 +} 1.274 + 1.275 +static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& iterator) { 1.276 + assert(emergency_fd != invalid_fd, "invariant"); 1.277 + const size_t size_of_file_copy_block = 1 * M; // 1 mb 1.278 + jbyte* const file_copy_block = NEW_RESOURCE_ARRAY_RETURN_NULL(jbyte, size_of_file_copy_block); 1.279 + if (file_copy_block == NULL) { 1.280 + return; 1.281 + } 1.282 + jlong bytes_written_total = 0; 1.283 + while (iterator.has_next()) { 1.284 + fio_fd current_fd = invalid_fd; 1.285 + const char* const fqn = iterator.next(); 1.286 + if (fqn != NULL) { 1.287 + current_fd = open_existing(fqn); 1.288 + if (current_fd != invalid_fd) { 1.289 + const jlong current_filesize = file_size(current_fd); 1.290 + assert(current_filesize > 0, "invariant"); 1.291 + jlong bytes_read = 0; 1.292 + jlong bytes_written = 0; 1.293 + while (bytes_read < current_filesize) { 1.294 + bytes_read += (jlong)os::read_at(current_fd, file_copy_block, size_of_file_copy_block, bytes_read); 1.295 + assert(bytes_read - bytes_written <= (jlong)size_of_file_copy_block, "invariant"); 1.296 + bytes_written += (jlong)os::write(emergency_fd, file_copy_block, bytes_read - bytes_written); 1.297 + assert(bytes_read == bytes_written, "invariant"); 1.298 + } 1.299 + os::close(current_fd); 1.300 + bytes_written_total += bytes_written; 1.301 + } 1.302 + } 1.303 + } 1.304 +} 1.305 + 1.306 +static const char* create_emergency_dump_path() { 1.307 + assert(JfrStream_lock->owned_by_self(), "invariant"); 1.308 + char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN); 1.309 + if (NULL == buffer) { 1.310 + return NULL; 1.311 + } 1.312 + const char* const cwd = os::get_current_directory(buffer, O_BUFLEN); 1.313 + if (NULL == cwd) { 1.314 + return NULL; 1.315 + } 1.316 + size_t pos = strlen(cwd); 1.317 + const int fsep_len = jio_snprintf(&buffer[pos], O_BUFLEN - pos, "%s", os::file_separator()); 1.318 + const char* filename_fmt = NULL; 1.319 + // fetch specific error cause 1.320 + switch (JfrJavaSupport::cause()) { 1.321 + case JfrJavaSupport::OUT_OF_MEMORY: 1.322 + filename_fmt = vm_oom_filename_fmt; 1.323 + break; 1.324 + case JfrJavaSupport::STACK_OVERFLOW: 1.325 + filename_fmt = vm_soe_filename_fmt; 1.326 + break; 1.327 + default: 1.328 + filename_fmt = vm_error_filename_fmt; 1.329 + } 1.330 + char* emergency_dump_path = NULL; 1.331 + pos += fsep_len; 1.332 + if (Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), &buffer[pos], O_BUFLEN - pos)) { 1.333 + const size_t emergency_filename_length = strlen(buffer); 1.334 + emergency_dump_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, emergency_filename_length + 1); 1.335 + if (NULL == emergency_dump_path) { 1.336 + return NULL; 1.337 + } 1.338 + strncpy(emergency_dump_path, buffer, emergency_filename_length); 1.339 + emergency_dump_path[emergency_filename_length] = '\0'; 1.340 + } 1.341 + return emergency_dump_path; 1.342 +} 1.343 + 1.344 +// Caller needs ResourceMark 1.345 +static const char* create_emergency_chunk_path(const char* repository_base, size_t repository_len) { 1.346 + assert(repository_base != NULL, "invariant"); 1.347 + assert(JfrStream_lock->owned_by_self(), "invariant"); 1.348 + // date time 1.349 + char date_time_buffer[32] = {0}; 1.350 + date_time(date_time_buffer, sizeof(date_time_buffer)); 1.351 + size_t date_time_len = strlen(date_time_buffer); 1.352 + size_t chunkname_max_len = repository_len // repository_base 1.353 + + 1 // "/" 1.354 + + date_time_len // date_time 1.355 + + strlen(chunk_file_jfr_ext) // .jfr 1.356 + + 1; 1.357 + char* chunk_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, chunkname_max_len); 1.358 + if (chunk_path == NULL) { 1.359 + return NULL; 1.360 + } 1.361 + // append the individual substrings 1.362 + jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_base, os::file_separator(), date_time_buffer, chunk_file_jfr_ext); 1.363 + return chunk_path; 1.364 +} 1.365 + 1.366 +static fio_fd emergency_dump_file() { 1.367 + assert(JfrStream_lock->owned_by_self(), "invariant"); 1.368 + ResourceMark rm; 1.369 + const char* const emergency_dump_path = create_emergency_dump_path(); 1.370 + if (emergency_dump_path == NULL) { 1.371 + return invalid_fd; 1.372 + } 1.373 + const fio_fd fd = open_exclusivly(emergency_dump_path); 1.374 + if (fd != invalid_fd) { 1.375 + if (LogJFR) tty->print_cr( // For user, should not be "jfr, system" 1.376 + "Attempting to recover JFR data, emergency jfr file: %s", emergency_dump_path); 1.377 + } 1.378 + return fd; 1.379 +} 1.380 + 1.381 +static const char* emergency_path(const char* repository, size_t repository_len) { 1.382 + return repository == NULL ? create_emergency_dump_path() : create_emergency_chunk_path(repository, repository_len); 1.383 +} 1.384 + 1.385 +void JfrRepository::on_vm_error() { 1.386 + assert(!JfrStream_lock->owned_by_self(), "invariant"); 1.387 + const char* path = _path; 1.388 + if (path == NULL) { 1.389 + // completed already 1.390 + return; 1.391 + } 1.392 + ResourceMark rm; 1.393 + MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); 1.394 + const fio_fd emergency_fd = emergency_dump_file(); 1.395 + if (emergency_fd != invalid_fd) { 1.396 + RepositoryIterator iterator(path, strlen(path)); 1.397 + write_emergency_file(emergency_fd, iterator); 1.398 + os::close(emergency_fd); 1.399 + } 1.400 +} 1.401 + 1.402 +bool JfrRepository::set_path(const char* path) { 1.403 + assert(path != NULL, "trying to set the repository path with a NULL string!"); 1.404 + if (_path != NULL) { 1.405 + // delete existing 1.406 + JfrCHeapObj::free(_path, strlen(_path) + 1); 1.407 + } 1.408 + const size_t path_len = strlen(path); 1.409 + _path = JfrCHeapObj::new_array<char>(path_len + 1); 1.410 + if (_path == NULL) { 1.411 + return false; 1.412 + } 1.413 + strncpy(_path, path, path_len); 1.414 + _path[path_len] = '\0'; 1.415 + return true; 1.416 +} 1.417 + 1.418 +void JfrRepository::set_chunk_path(const char* path) { 1.419 + assert(JfrStream_lock->owned_by_self(), "invariant"); 1.420 + chunkwriter().set_chunk_path(path); 1.421 +} 1.422 + 1.423 +void JfrRepository::notify_on_new_chunk_path() { 1.424 + if (Jfr::is_recording()) { 1.425 + instance()._post_box.post(MSG_ROTATE); 1.426 + } 1.427 +} 1.428 + 1.429 +/** 1.430 +* Sets the file where data should be written. 1.431 +* 1.432 +* Recording Previous Current Action 1.433 +* ============================================== 1.434 +* true null null Ignore, keep recording in-memory 1.435 +* true null file1 Start disk recording 1.436 +* true file null Copy out metadata to disk and continue in-memory recording 1.437 +* true file1 file2 Copy out metadata and start with new File (file2) 1.438 +* false * null Ignore, but start recording to memory 1.439 +* false * file Ignore, but start recording to disk 1.440 +*/ 1.441 +void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) { 1.442 + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 1.443 + ResourceMark rm(jt); 1.444 + const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt); 1.445 + { 1.446 + MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); 1.447 + if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) { 1.448 + // new output is NULL and current output is NULL 1.449 + return; 1.450 + } 1.451 + instance().set_chunk_path(canonical_chunk_path); 1.452 + } 1.453 + notify_on_new_chunk_path(); 1.454 +} 1.455 + 1.456 +void JfrRepository::set_path(jstring location, JavaThread* jt) { 1.457 + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 1.458 + ResourceMark rm(jt); 1.459 + const char* const path = JfrJavaSupport::c_str(location, jt); 1.460 + if (path != NULL) { 1.461 + instance().set_path(path); 1.462 + } 1.463 +} 1.464 + 1.465 +bool JfrRepository::open_chunk(bool vm_error /* false */) { 1.466 + assert(JfrStream_lock->owned_by_self(), "invariant"); 1.467 + if (vm_error) { 1.468 + ResourceMark rm; 1.469 + const char* repository_path = _path; 1.470 + const size_t repository_path_len = repository_path != NULL ? strlen(repository_path) : 0; 1.471 + const char* const path = emergency_path(repository_path, repository_path_len); 1.472 + _chunkwriter->set_chunk_path(path); 1.473 + } 1.474 + return _chunkwriter->open(); 1.475 +} 1.476 + 1.477 +size_t JfrRepository::close_chunk(jlong metadata_offset) { 1.478 + return _chunkwriter->close(metadata_offset); 1.479 +}