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