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 */ 24 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/jfrEmergencyDump.hpp" 32 #include "jfr/recorder/repository/jfrRepository.hpp" 33 #include "jfr/recorder/service/jfrPostBox.hpp" 34 #include "memory/resourceArea.hpp" 35 #include "runtime/mutex.hpp" 36 #include "runtime/arguments.hpp" 37 #include "runtime/thread.inline.hpp" 38 39 static JfrRepository* _instance = NULL; 40 41 JfrRepository& JfrRepository::instance() { 42 return *_instance; 43 } 44 45 static JfrChunkWriter* _chunkwriter = NULL; 46 47 static bool initialize_chunkwriter() { 48 assert(_chunkwriter == NULL, "invariant"); 49 _chunkwriter = new JfrChunkWriter(); 50 return _chunkwriter != NULL && _chunkwriter->initialize(); 51 } 52 53 JfrChunkWriter& JfrRepository::chunkwriter() { 54 return *_chunkwriter; 55 } 56 57 JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {} 58 59 bool JfrRepository::initialize() { 60 return initialize_chunkwriter(); 61 } 62 63 JfrRepository::~JfrRepository() { 64 if (_path != NULL) { 65 JfrCHeapObj::free(_path, strlen(_path) + 1); 66 _path = NULL; 67 } 68 69 if (_chunkwriter != NULL) { 70 delete _chunkwriter; 71 _chunkwriter = NULL; 72 } 73 } 74 75 JfrRepository* JfrRepository::create(JfrPostBox& post_box) { 76 assert(_instance == NULL, "invariant"); 77 _instance = new JfrRepository(post_box); 78 return _instance; 79 } 80 81 void JfrRepository::destroy() { 82 assert(_instance != NULL, "invariant"); 83 delete _instance; 84 _instance = NULL; 85 } 86 87 void JfrRepository::on_vm_error() { 88 assert(!JfrStream_lock->owned_by_self(), "invariant"); 89 if (_path == NULL) { 90 // completed already 91 return; 92 } 93 JfrEmergencyDump::on_vm_error(_path); 94 } 95 96 bool JfrRepository::set_path(const char* path) { 97 assert(path != NULL, "trying to set the repository path with a NULL string!"); 98 if (_path != NULL) { 99 // delete existing 100 JfrCHeapObj::free(_path, strlen(_path) + 1); 101 } 102 const size_t path_len = strlen(path); 103 _path = JfrCHeapObj::new_array<char>(path_len + 1); 104 if (_path == NULL) { 105 return false; 106 } 107 strncpy(_path, path, path_len + 1); 108 return true; 109 } 110 111 void JfrRepository::set_chunk_path(const char* path) { 112 assert(JfrStream_lock->owned_by_self(), "invariant"); 113 chunkwriter().set_chunk_path(path); 114 } 115 116 void JfrRepository::notify_on_new_chunk_path() { 117 if (Jfr::is_recording()) { 118 instance()._post_box.post(MSG_ROTATE); 119 } 120 } 121 122 /** 123 * Sets the file where data should be written. 124 * 125 * Recording Previous Current Action 126 * ============================================== 127 * true null null Ignore, keep recording in-memory 128 * true null file1 Start disk recording 129 * true file null Copy out metadata to disk and continue in-memory recording 130 * true file1 file2 Copy out metadata and start with new File (file2) 131 * false * null Ignore, but start recording to memory 132 * false * file Ignore, but start recording to disk 133 */ 134 void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) { 135 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 136 ResourceMark rm(jt); 137 const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt); 138 { 139 MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); 140 if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) { 141 // new output is NULL and current output is NULL 142 return; 143 } 144 instance().set_chunk_path(canonical_chunk_path); 145 } 146 notify_on_new_chunk_path(); 147 } 148 149 void JfrRepository::set_path(jstring location, JavaThread* jt) { 150 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 151 ResourceMark rm(jt); 152 const char* const path = JfrJavaSupport::c_str(location, jt); 153 if (path != NULL) { 154 instance().set_path(path); 155 } 156 } 157 158 bool JfrRepository::open_chunk(bool vm_error /* false */) { 159 assert(JfrStream_lock->owned_by_self(), "invariant"); 160 if (vm_error) { 161 ResourceMark rm; 162 _chunkwriter->set_chunk_path(JfrEmergencyDump::build_dump_path(_path)); 163 } 164 return _chunkwriter->open(); 165 } 166 167 size_t JfrRepository::close_chunk(jlong metadata_offset) { 168 return _chunkwriter->close(metadata_offset); 169 }