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 
 44 JfrStackTraceRepository& JfrStackTraceRepository::instance() {
 45   assert(_instance != NULL, "invariant");
 46   return *_instance;
 47 }
 48 
 49 static JfrStackTraceRepository& leak_profiler_instance() {
 50   assert(_leak_profiler_instance != NULL, "invariant");
 51   return *_leak_profiler_instance;
 52 }
 53 
 54 JfrStackTraceRepository::JfrStackTraceRepository() : _last_entries(0), _entries(0) {
 55   memset(_table, 0, sizeof(_table));
 56 }
 57 
 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:
 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 
 84 bool JfrStackTraceRepository::initialize() {
 85   return JfrSerializer::register_serializer(TYPE_FRAMETYPE, true, new JfrFrameType());
 86 }
 87 
 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 
 96 bool JfrStackTraceRepository::is_modified() const {
 97   return _last_entries != _entries;
 98 }
 99 
100 size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
101   if (_entries == 0) {
102     return 0;
103   }
104   MutexLocker 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 
129 size_t JfrStackTraceRepository::clear(JfrStackTraceRepository& repo) {
130   MutexLocker 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 
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() || tl->is_excluded()) {
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::cast(thread), skip, frames, tl->stackdepth());
167 }
168 
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 }
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 
183 traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) {
184   return add(instance(), stacktrace);
185 }
186 
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 
200 traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) {
201   MutexLocker 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
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 
235 void JfrStackTraceRepository::clear_leak_profiler() {
236   clear(leak_profiler_instance());
237 }
238 
239 size_t JfrStackTraceRepository::clear() {
240   clear_leak_profiler();
241   return clear(instance());
242 }