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