1 /*
2 * Copyright (c) 2011, 2021, 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/metadata/jfrSerializer.hpp"
27 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
28 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
29 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
30 #include "jfr/support/jfrThreadLocal.hpp"
31 #include "runtime/mutexLocker.hpp"
32
33 /*
34 * There are two separate repository instances.
35 * One instance is dedicated to stacktraces taken as part of the leak profiler subsystem.
36 * It is kept separate because at the point of insertion, it is unclear if a trace will be serialized,
37 * which is a decision postponed and taken during rotation.
38 */
39
40 static JfrStackTraceRepository* _instance = NULL;
41 static JfrStackTraceRepository* _leak_profiler_instance = NULL;
42 static traceid _next_id = 0;
43
instance()44 JfrStackTraceRepository& JfrStackTraceRepository::instance() {
45 assert(_instance != NULL, "invariant");
46 return *_instance;
47 }
48
leak_profiler_instance()49 static JfrStackTraceRepository& leak_profiler_instance() {
50 assert(_leak_profiler_instance != NULL, "invariant");
51 return *_leak_profiler_instance;
52 }
53
JfrStackTraceRepository()54 JfrStackTraceRepository::JfrStackTraceRepository() : _last_entries(0), _entries(0) {
55 memset(_table, 0, sizeof(_table));
56 }
57
create()58 JfrStackTraceRepository* JfrStackTraceRepository::create() {
59 assert(_instance == NULL, "invariant");
60 assert(_leak_profiler_instance == NULL, "invariant");
61 _leak_profiler_instance = new JfrStackTraceRepository();
62 if (_leak_profiler_instance == NULL) {
63 return NULL;
64 }
65 _instance = new JfrStackTraceRepository();
66 return _instance;
67 }
68
69 class JfrFrameType : public JfrSerializer {
70 public:
serialize(JfrCheckpointWriter & writer)71 void serialize(JfrCheckpointWriter& writer) {
72 writer.write_count(JfrStackFrame::NUM_FRAME_TYPES);
73 writer.write_key(JfrStackFrame::FRAME_INTERPRETER);
74 writer.write("Interpreted");
75 writer.write_key(JfrStackFrame::FRAME_JIT);
76 writer.write("JIT compiled");
77 writer.write_key(JfrStackFrame::FRAME_INLINE);
78 writer.write("Inlined");
79 writer.write_key(JfrStackFrame::FRAME_NATIVE);
80 writer.write("Native");
81 }
82 };
83
initialize()84 bool JfrStackTraceRepository::initialize() {
85 return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType());
86 }
87
destroy()88 void JfrStackTraceRepository::destroy() {
89 assert(_instance != NULL, "invarinat");
90 delete _instance;
91 _instance = NULL;
92 delete _leak_profiler_instance;
93 _leak_profiler_instance = NULL;
94 }
95
is_modified() const96 bool JfrStackTraceRepository::is_modified() const {
97 return _last_entries != _entries;
98 }
99
write(JfrChunkWriter & sw,bool clear)100 size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
101 if (_entries == 0) {
102 return 0;
103 }
104 MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
105 assert(_entries > 0, "invariant");
106 int count = 0;
107 for (u4 i = 0; i < TABLE_SIZE; ++i) {
108 JfrStackTrace* stacktrace = _table[i];
109 while (stacktrace != NULL) {
110 JfrStackTrace* next = const_cast<JfrStackTrace*>(stacktrace->next());
111 if (stacktrace->should_write()) {
112 stacktrace->write(sw);
113 ++count;
114 }
115 if (clear) {
116 delete stacktrace;
117 }
118 stacktrace = next;
119 }
120 }
121 if (clear) {
122 memset(_table, 0, sizeof(_table));
123 _entries = 0;
124 }
125 _last_entries = _entries;
126 return count;
127 }
128
clear(JfrStackTraceRepository & repo)129 size_t JfrStackTraceRepository::clear(JfrStackTraceRepository& repo) {
130 MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
131 if (repo._entries == 0) {
132 return 0;
133 }
134 for (u4 i = 0; i < TABLE_SIZE; ++i) {
135 JfrStackTrace* stacktrace = repo._table[i];
136 while (stacktrace != NULL) {
137 JfrStackTrace* next = const_cast<JfrStackTrace*>(stacktrace->next());
138 delete stacktrace;
139 stacktrace = next;
140 }
141 }
142 memset(repo._table, 0, sizeof(repo._table));
143 const size_t processed = repo._entries;
144 repo._entries = 0;
145 repo._last_entries = 0;
146 return processed;
147 }
148
record(Thread * thread,int skip)149 traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) {
150 assert(thread == Thread::current(), "invariant");
151 JfrThreadLocal* const tl = thread->jfr_thread_local();
152 assert(tl != NULL, "invariant");
153 if (tl->has_cached_stack_trace()) {
154 return tl->cached_stack_trace_id();
155 }
156 if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) {
157 return 0;
158 }
159 JfrStackFrame* frames = tl->stackframes();
160 if (frames == NULL) {
161 // pending oom
162 return 0;
163 }
164 assert(frames != NULL, "invariant");
165 assert(tl->stackframes() == frames, "invariant");
166 return instance().record_for((JavaThread*)thread, skip, frames, tl->stackdepth());
167 }
168
record_for(JavaThread * thread,int skip,JfrStackFrame * frames,u4 max_frames)169 traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames) {
170 JfrStackTrace stacktrace(frames, max_frames);
171 return stacktrace.record_safe(thread, skip) ? add(instance(), stacktrace) : 0;
172 }
add(JfrStackTraceRepository & repo,const JfrStackTrace & stacktrace)173 traceid JfrStackTraceRepository::add(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace) {
174 traceid tid = repo.add_trace(stacktrace);
175 if (tid == 0) {
176 stacktrace.resolve_linenos();
177 tid = repo.add_trace(stacktrace);
178 }
179 assert(tid != 0, "invariant");
180 return tid;
181 }
182
add(const JfrStackTrace & stacktrace)183 traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) {
184 return add(instance(), stacktrace);
185 }
186
record_for_leak_profiler(JavaThread * thread,int skip)187 void JfrStackTraceRepository::record_for_leak_profiler(JavaThread* thread, int skip /* 0 */) {
188 assert(thread != NULL, "invariant");
189 JfrThreadLocal* const tl = thread->jfr_thread_local();
190 assert(tl != NULL, "invariant");
191 assert(!tl->has_cached_stack_trace(), "invariant");
192 JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth());
193 stacktrace.record_safe(thread, skip);
194 const unsigned int hash = stacktrace.hash();
195 if (hash != 0) {
196 tl->set_cached_stack_trace_id(add(leak_profiler_instance(), stacktrace), hash);
197 }
198 }
199
add_trace(const JfrStackTrace & stacktrace)200 traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) {
201 MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
202 const size_t index = stacktrace._hash % TABLE_SIZE;
203 const JfrStackTrace* table_entry = _table[index];
204
205 while (table_entry != NULL) {
206 if (table_entry->equals(stacktrace)) {
207 return table_entry->id();
208 }
209 table_entry = table_entry->next();
210 }
211
212 if (!stacktrace.have_lineno()) {
213 return 0;
214 }
215
216 traceid id = ++_next_id;
217 _table[index] = new JfrStackTrace(id, stacktrace, _table[index]);
218 ++_entries;
219 return id;
220 }
221
222 // invariant is that the entry to be resolved actually exists in the table
lookup_for_leak_profiler(unsigned int hash,traceid id)223 const JfrStackTrace* JfrStackTraceRepository::lookup_for_leak_profiler(unsigned int hash, traceid id) {
224 const size_t index = (hash % TABLE_SIZE);
225 const JfrStackTrace* trace = leak_profiler_instance()._table[index];
226 while (trace != NULL && trace->id() != id) {
227 trace = trace->next();
228 }
229 assert(trace != NULL, "invariant");
230 assert(trace->hash() == hash, "invariant");
231 assert(trace->id() == id, "invariant");
232 return trace;
233 }
234
clear_leak_profiler()235 void JfrStackTraceRepository::clear_leak_profiler() {
236 clear(leak_profiler_instance());
237 }
238
clear()239 size_t JfrStackTraceRepository::clear() {
240 clear_leak_profiler();
241 return clear(instance());
242 }
243