1/*
2 * Copyright (c) 2016, 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 "classfile/javaClasses.inline.hpp"
27#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
28#include "jfr/recorder/service/jfrOptionSet.hpp"
29#include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
30#include "jfr/recorder/repository/jfrChunkWriter.hpp"
31#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
32#include "jfr/recorder/stringpool/jfrStringPool.hpp"
33#include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
34#include "jfr/utilities/jfrTypes.hpp"
35#include "logging/log.hpp"
36#include "runtime/atomic.hpp"
37#include "runtime/mutexLocker.hpp"
38#include "runtime/orderAccess.hpp"
39#include "runtime/safepoint.hpp"
40#include "runtime/thread.inline.hpp"
41
42typedef JfrStringPool::Buffer* BufferPtr;
43
44static JfrStringPool* _instance = NULL;
45
46JfrStringPool& JfrStringPool::instance() {
47 return *_instance;
48}
49
50JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) {
51 assert(_instance == NULL, "invariant");
52 _instance = new JfrStringPool(cw);
53 return _instance;
54}
55
56void JfrStringPool::destroy() {
57 assert(_instance != NULL, "invariant");
58 delete _instance;
59 _instance = NULL;
60}
61
62JfrStringPool::JfrStringPool(JfrChunkWriter& cw) : _free_list_mspace(NULL), _lock(NULL), _chunkwriter(cw) {}
63
64JfrStringPool::~JfrStringPool() {
65 if (_free_list_mspace != NULL) {
66 delete _free_list_mspace;
67 }
68 if (_lock != NULL) {
69 delete _lock;
70 }
71}
72
73static const size_t unlimited_mspace_size = 0;
74static const size_t string_pool_cache_count = 2;
75static const size_t string_pool_buffer_size = 512 * K;
76
77bool JfrStringPool::initialize() {
78 assert(_free_list_mspace == NULL, "invariant");
79 _free_list_mspace = new JfrStringPoolMspace(string_pool_buffer_size, unlimited_mspace_size, string_pool_cache_count, this);
80 if (_free_list_mspace == NULL || !_free_list_mspace->initialize()) {
81 return false;
82 }
83 assert(_lock == NULL, "invariant");
84 _lock = new Mutex(Monitor::leaf - 1, "Checkpoint mutex", Mutex::_allow_vm_block_flag, Monitor::_safepoint_check_never);
85 return _lock != NULL;
86}
87
88/*
89* If the buffer was a "lease" from the global system, release back.
90*
91* The buffer is effectively invalidated for the thread post-return,
92* and the caller should take means to ensure that it is not referenced any longer.
93*/
94static void release(BufferPtr buffer, Thread* thread) {
95 assert(buffer != NULL, "invariant");
96 assert(buffer->lease(), "invariant");
97 assert(buffer->acquired_by_self(), "invariant");
98 buffer->clear_lease();
99 buffer->release();
100}
101
102BufferPtr JfrStringPool::flush(BufferPtr old, size_t used, size_t requested, Thread* thread) {
103 assert(old != NULL, "invariant");
104 assert(old->lease(), "invariant");
105 if (0 == requested) {
106 // indicates a lease is being returned
107 release(old, thread);
108 return NULL;
109 }
110 // migration of in-flight information
111 BufferPtr const new_buffer = lease_buffer(thread, used + requested);
112 if (new_buffer != NULL) {
113 migrate_outstanding_writes(old, new_buffer, used, requested);
114 }
115 release(old, thread);
116 return new_buffer; // might be NULL
117}
118
119static const size_t lease_retry = 10;
120
121BufferPtr JfrStringPool::lease_buffer(Thread* thread, size_t size /* 0 */) {
122 BufferPtr buffer = mspace_get_free_lease_with_retry(size, instance()._free_list_mspace, lease_retry, thread);
123 if (buffer == NULL) {
124 buffer = mspace_allocate_transient_lease_to_free(size, instance()._free_list_mspace, thread);
125 }
126 assert(buffer->acquired_by_self(), "invariant");
127 assert(buffer->lease(), "invariant");
128 return buffer;
129}
130
131bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
132 assert(jt != NULL, "invariant");
133 const bool current_epoch = JfrTraceIdEpoch::epoch();
134 if (current_epoch == epoch) {
135 JfrStringPoolWriter writer(jt);
136 writer.write(id);
137 writer.write(string);
138 writer.inc_nof_strings();
139 }
140 return current_epoch;
141}
142
143template <template <typename> class Operation>
144class StringPoolOp {
145 public:
146 typedef JfrStringPoolBuffer Type;
147 private:
148 Operation<Type> _op;
149 Thread* _thread;
150 size_t _strings_processed;
151 public:
152 StringPoolOp() : _op(), _thread(Thread::current()), _strings_processed(0) {}
153 StringPoolOp(JfrChunkWriter& writer, Thread* thread) : _op(writer), _thread(thread), _strings_processed(0) {}
154 bool write(Type* buffer, const u1* data, size_t size) {
155 assert(buffer->acquired_by(_thread) || buffer->retired(), "invariant");
156 const uint64_t nof_strings_used = buffer->string_count();
157 assert(nof_strings_used > 0, "invariant");
158 buffer->set_string_top(buffer->string_top() + nof_strings_used);
159 // "size processed" for string pool buffers is the number of processed string elements
160 _strings_processed += nof_strings_used;
161 return _op.write(buffer, data, size);
162 }
163 size_t processed() { return _strings_processed; }
164};
165
166template <typename Type>
167class StringPoolDiscarderStub {
168 public:
169 bool write(Type* buffer, const u1* data, size_t size) {
170 // stub only, discard happens at higher level
171 return true;
172 }
173};
174
175typedef StringPoolOp<UnBufferedWriteToChunk> WriteOperation;
176typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
177typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
178typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
179typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
180typedef CompositeOperation<ExclusiveWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
181typedef CompositeOperation<ExclusiveDiscardOperation, StringPoolReleaseOperation> StringPoolDiscardOperation;
182
183size_t JfrStringPool::write() {
184 Thread* const thread = Thread::current();
185 WriteOperation wo(_chunkwriter, thread);
186 ExclusiveWriteOperation ewo(wo);
187 StringPoolReleaseOperation spro(_free_list_mspace, thread, false);
188 StringPoolWriteOperation spwo(&ewo, &spro);
189 assert(_free_list_mspace->is_full_empty(), "invariant");
190 process_free_list(spwo, _free_list_mspace);
191 return wo.processed();
192}
193
194size_t JfrStringPool::write_at_safepoint() {
195 assert(SafepointSynchronize::is_at_safepoint(), "invariant");
196 return write();
197}
198
199size_t JfrStringPool::clear() {
200 DiscardOperation discard_operation;
201 ExclusiveDiscardOperation edo(discard_operation);
202 StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
203 StringPoolDiscardOperation spdo(&edo, &spro);
204 assert(_free_list_mspace->is_full_empty(), "invariant");
205 process_free_list(spdo, _free_list_mspace);
206 return discard_operation.processed();
207}
208
209void JfrStringPool::register_full(BufferPtr t, Thread* thread) {
210 // nothing here at the moment
211 assert(t != NULL, "invariant");
212 assert(t->acquired_by(thread), "invariant");
213 assert(t->retired(), "invariant");
214}
215
216void JfrStringPool::lock() {
217 assert(!_lock->owned_by_self(), "invariant");
218 _lock->lock_without_safepoint_check();
219}
220
221void JfrStringPool::unlock() {
222 _lock->unlock();
223}
224
225#ifdef ASSERT
226bool JfrStringPool::is_locked() const {
227 return _lock->owned_by_self();
228}
229#endif
230