1 /* 2 * Copyright (c) 2011, 2020, 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/jfrChunkWriter.hpp" 30 #include "jfr/recorder/repository/jfrEmergencyDump.hpp" 31 #include "jfr/recorder/repository/jfrRepository.hpp" 32 #include "jfr/recorder/service/jfrPostBox.hpp" 33 #include "logging/log.hpp" 34 #include "memory/resourceArea.hpp" 35 #include "runtime/mutex.hpp" 36 #include "runtime/os.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 48 JfrChunkWriter& JfrRepository::chunkwriter() { 49 return *_chunkwriter; 50 } 51 52 JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {} 53 54 bool JfrRepository::initialize() { 55 assert(_chunkwriter == NULL, "invariant"); 56 _chunkwriter = new JfrChunkWriter(); 57 return _chunkwriter != NULL; 58 } 59 60 JfrRepository::~JfrRepository() { 61 if (_path != NULL) { 62 JfrCHeapObj::free(_path, strlen(_path) + 1); 63 _path = NULL; 64 } 65 66 if (_chunkwriter != NULL) { 67 delete _chunkwriter; 68 _chunkwriter = NULL; 69 } 70 } 71 72 JfrRepository* JfrRepository::create(JfrPostBox& post_box) { 73 assert(_instance == NULL, "invariant"); 74 _instance = new JfrRepository(post_box); 75 return _instance; 76 } 77 78 void JfrRepository::destroy() { 79 assert(_instance != NULL, "invariant"); 80 delete _instance; 81 _instance = NULL; 82 } 83 84 void JfrRepository::on_vm_error() { 85 if (_path == NULL) { 86 // completed already 87 return; 88 } 89 JfrEmergencyDump::on_vm_error(_path); 90 } 91 92 void JfrRepository::on_vm_error_report(outputStream* st) { 93 JfrEmergencyDump::on_vm_error_report(st, instance()._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::notify_on_new_chunk_path() { 112 if (Jfr::is_recording()) { 113 // rotations are synchronous, block until rotation completes 114 instance()._post_box.post(MSG_ROTATE); 115 } 116 } 117 118 void JfrRepository::set_chunk_path(const char* path) { 119 chunkwriter().set_path(path); 120 } 121 122 void JfrRepository::mark_chunk_final() { 123 chunkwriter().mark_chunk_final(); 124 } 125 126 jlong JfrRepository::current_chunk_start_nanos() { 127 return chunkwriter().current_chunk_start_nanos(); 128 } 129 130 /** 131 * Sets the file where data should be written. 132 * 133 * Recording Previous Current Action 134 * ============================================== 135 * true null null Ignore, keep recording in-memory 136 * true null file1 Start disk recording 137 * true file null Copy out metadata to disk and continue in-memory recording 138 * true file1 file2 Copy out metadata and start with new File (file2) 139 * false * null Ignore, but start recording to memory 140 * false * file Ignore, but start recording to disk 141 */ 142 void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) { 143 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 144 ResourceMark rm(jt); 145 const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt); 146 if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) { 147 // new output is NULL and current output is NULL 148 return; 149 } 150 instance().set_chunk_path(canonical_chunk_path); 151 notify_on_new_chunk_path(); 152 } 153 154 void JfrRepository::set_path(jstring location, JavaThread* jt) { 155 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 156 ResourceMark rm(jt); 157 const char* const path = JfrJavaSupport::c_str(location, jt); 158 if (path != NULL) { 159 instance().set_path(path); 160 } 161 } 162 163 bool JfrRepository::open_chunk(bool vm_error /* false */) { 164 if (vm_error) { 165 _chunkwriter->set_path(JfrEmergencyDump::chunk_path(_path)); 166 } 167 return _chunkwriter->open(); 168 } 169 170 size_t JfrRepository::close_chunk() { 171 return _chunkwriter->close(); 172 } 173 174 void JfrRepository::flush(JavaThread* jt) { 175 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 176 if (!Jfr::is_recording()) { 177 return; 178 } 179 if (!_chunkwriter->is_valid()) { 180 return; 181 } 182 instance()._post_box.post(MSG_FLUSHPOINT); 183 } 184 185 size_t JfrRepository::flush_chunk() { 186 return _chunkwriter->flush_chunk(true); 187 }