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/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 bool JfrRepository::set_path(const char* path) { 93 assert(path != NULL, "trying to set the repository path with a NULL string!"); 94 if (_path != NULL) { 95 // delete existing 96 JfrCHeapObj::free(_path, strlen(_path) + 1); 97 } 98 const size_t path_len = strlen(path); 99 _path = JfrCHeapObj::new_array<char>(path_len + 1); 100 if (_path == NULL) { 101 return false; 102 } 103 strncpy(_path, path, path_len + 1); 104 return true; 105 } 106 107 void JfrRepository::notify_on_new_chunk_path() { 108 if (Jfr::is_recording()) { 109 // rotations are synchronous, block until rotation completes 110 instance()._post_box.post(MSG_ROTATE); 111 } 112 } 113 114 void JfrRepository::set_chunk_path(const char* path) { 115 chunkwriter().set_path(path); 116 } 117 118 void JfrRepository::mark_chunk_final() { 119 chunkwriter().mark_chunk_final(); 120 } 121 122 jlong JfrRepository::current_chunk_start_nanos() { 123 return chunkwriter().current_chunk_start_nanos(); 124 } 125 126 /** 127 * Sets the file where data should be written. 128 * 129 * Recording Previous Current Action 130 * ============================================== 131 * true null null Ignore, keep recording in-memory 132 * true null file1 Start disk recording 133 * true file null Copy out metadata to disk and continue in-memory recording 134 * true file1 file2 Copy out metadata and start with new File (file2) 135 * false * null Ignore, but start recording to memory 136 * false * file Ignore, but start recording to disk 137 */ 138 void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) { 139 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 140 ResourceMark rm(jt); 141 const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt); 142 if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) { 143 // new output is NULL and current output is NULL 144 return; 145 } 146 instance().set_chunk_path(canonical_chunk_path); 147 notify_on_new_chunk_path(); 148 } 149 150 void JfrRepository::set_path(jstring location, JavaThread* jt) { 151 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 152 ResourceMark rm(jt); 153 const char* const path = JfrJavaSupport::c_str(location, jt); 154 if (path != NULL) { 155 instance().set_path(path); 156 } 157 } 158 159 bool JfrRepository::open_chunk(bool vm_error /* false */) { 160 if (vm_error) { 161 ResourceMark rm; 162 _chunkwriter->set_path(JfrEmergencyDump::build_dump_path(_path)); 163 } 164 return _chunkwriter->open(); 165 } 166 167 size_t JfrRepository::close_chunk() { 168 return _chunkwriter->close(); 169 } 170 171 void JfrRepository::flush(JavaThread* jt) { 172 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); 173 if (!Jfr::is_recording()) { 174 return; 175 } 176 if (!_chunkwriter->is_valid()) { 177 return; 178 } 179 instance()._post_box.post(MSG_FLUSHPOINT); 180 } 181 182 size_t JfrRepository::flush_chunk() { 183 return _chunkwriter->flush_chunk(true); 184 }