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/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 static JfrStackTraceRepository* _instance = NULL;
  34 
  35 JfrStackTraceRepository::JfrStackTraceRepository() : _next_id(0), _entries(0) {
  36   memset(_table, 0, sizeof(_table));
  37 }
  38 
  39 JfrStackTraceRepository& JfrStackTraceRepository::instance() {
  40   return *_instance;
  41 }
  42 
  43 JfrStackTraceRepository* JfrStackTraceRepository::create() {
  44   assert(_instance == NULL, "invariant");
  45   _instance = new JfrStackTraceRepository();
  46   return _instance;
  47 }
  48 
  49 class JfrFrameType : public JfrSerializer {
  50  public:
  51   void serialize(JfrCheckpointWriter& writer) {
  52     writer.write_count(JfrStackFrame::NUM_FRAME_TYPES);
  53     writer.write_key(JfrStackFrame::FRAME_INTERPRETER);
  54     writer.write("Interpreted");
  55     writer.write_key(JfrStackFrame::FRAME_JIT);
  56     writer.write("JIT compiled");
  57     writer.write_key(JfrStackFrame::FRAME_INLINE);
  58     writer.write("Inlined");
  59     writer.write_key(JfrStackFrame::FRAME_NATIVE);
  60     writer.write("Native");
  61   }
  62 };
  63 
  64 bool JfrStackTraceRepository::initialize() {
  65   return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType());
  66 }
  67 
  68 void JfrStackTraceRepository::destroy() {
  69   assert(_instance != NULL, "invarinat");
  70   delete _instance;
  71   _instance = NULL;
  72 }
  73 
  74 size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) {
  75   MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
  76   assert(_entries > 0, "invariant");
  77   int count = 0;
  78   for (u4 i = 0; i < TABLE_SIZE; ++i) {
  79     JfrStackTrace* stacktrace = _table[i];
  80     while (stacktrace != NULL) {
  81       JfrStackTrace* next = const_cast<JfrStackTrace*>(stacktrace->next());
  82       if (stacktrace->should_write()) {
  83         stacktrace->write(sw);
  84         ++count;
  85       }
  86       if (clear) {
  87         delete stacktrace;
  88       }
  89       stacktrace = next;
  90     }
  91   }
  92   if (clear) {
  93     memset(_table, 0, sizeof(_table));
  94     _entries = 0;
  95   }
  96   return count;
  97 }
  98 
  99 size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
 100   return _entries > 0 ? write_impl(sw, clear) : 0;
 101 }
 102 
 103 traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) {
 104   assert(JfrStacktrace_lock->owned_by_self(), "invariant");
 105   const JfrStackTrace* const trace = lookup(hash, id);
 106   assert(trace != NULL, "invariant");
 107   assert(trace->hash() == hash, "invariant");
 108   assert(trace->id() == id, "invariant");
 109   trace->write(writer);
 110   return id;
 111 }
 112 
 113 void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) {
 114   JfrFrameType fct;
 115   writer.write_type(TYPE_FRAMETYPE);
 116   fct.serialize(writer);
 117 }
 118 
 119 size_t JfrStackTraceRepository::clear() {
 120   MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
 121   if (_entries == 0) {
 122     return 0;
 123   }
 124   for (u4 i = 0; i < TABLE_SIZE; ++i) {
 125     JfrStackTrace* stacktrace = _table[i];
 126     while (stacktrace != NULL) {
 127       JfrStackTrace* next = const_cast<JfrStackTrace*>(stacktrace->next());
 128       delete stacktrace;
 129       stacktrace = next;
 130     }
 131   }
 132   memset(_table, 0, sizeof(_table));
 133   const size_t processed = _entries;
 134   _entries = 0;
 135   return processed;
 136 }
 137 
 138 traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) {
 139   assert(thread == Thread::current(), "invariant");
 140   JfrThreadLocal* const tl = thread->jfr_thread_local();
 141   assert(tl != NULL, "invariant");
 142   if (tl->has_cached_stack_trace()) {
 143     return tl->cached_stack_trace_id();
 144   }
 145   if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) {
 146     return 0;
 147   }
 148   JfrStackFrame* frames = tl->stackframes();
 149   if (frames == NULL) {
 150     // pending oom
 151     return 0;
 152   }
 153   assert(frames != NULL, "invariant");
 154   assert(tl->stackframes() == frames, "invariant");
 155   return instance().record_for((JavaThread*)thread, skip, frames, tl->stackdepth());
 156 }
 157 
 158 traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames) {
 159   JfrStackTrace stacktrace(frames, max_frames);
 160   return stacktrace.record_safe(thread, skip) ? add(stacktrace) : 0;
 161 }
 162 
 163 traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) {
 164   traceid tid = instance().add_trace(stacktrace);
 165   if (tid == 0) {
 166     stacktrace.resolve_linenos();
 167     tid = instance().add_trace(stacktrace);
 168   }
 169   assert(tid != 0, "invariant");
 170   return tid;
 171 }
 172 
 173 void JfrStackTraceRepository::record_and_cache(JavaThread* thread, int skip /* 0 */) {
 174   assert(thread != NULL, "invariant");
 175   JfrThreadLocal* const tl = thread->jfr_thread_local();
 176   assert(tl != NULL, "invariant");
 177   assert(!tl->has_cached_stack_trace(), "invariant");
 178   JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth());
 179   stacktrace.record_safe(thread, skip);
 180   const unsigned int hash = stacktrace.hash();
 181   if (hash != 0) {
 182     tl->set_cached_stack_trace_id(instance().add(stacktrace), hash);
 183   }
 184 }
 185 
 186 traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) {
 187   MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
 188   const size_t index = stacktrace._hash % TABLE_SIZE;
 189   const JfrStackTrace* table_entry = _table[index];
 190 
 191   while (table_entry != NULL) {
 192     if (table_entry->equals(stacktrace)) {
 193       return table_entry->id();
 194     }
 195     table_entry = table_entry->next();
 196   }
 197 
 198   if (!stacktrace.have_lineno()) {
 199     return 0;
 200   }
 201 
 202   traceid id = ++_next_id;
 203   _table[index] = new JfrStackTrace(id, stacktrace, _table[index]);
 204   ++_entries;
 205   return id;
 206 }
 207 
 208 // invariant is that the entry to be resolved actually exists in the table
 209 const JfrStackTrace* JfrStackTraceRepository::lookup(unsigned int hash, traceid id) const {
 210   const size_t index = (hash % TABLE_SIZE);
 211   const JfrStackTrace* trace = _table[index];
 212   while (trace != NULL && trace->id() != id) {
 213     trace = trace->next();
 214   }
 215   assert(trace != NULL, "invariant");
 216   assert(trace->hash() == hash, "invariant");
 217   assert(trace->id() == id, "invariant");
 218   return trace;
 219 }