1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp Mon Aug 12 18:30:40 2019 +0300 1.3 @@ -0,0 +1,464 @@ 1.4 +/* 1.5 + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 + * or visit www.oracle.com if you need additional information or have any 1.24 + * questions. 1.25 + * 1.26 + */ 1.27 + 1.28 +#include "precompiled.hpp" 1.29 +#include "jfr/metadata/jfrSerializer.hpp" 1.30 +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" 1.31 +#include "jfr/recorder/repository/jfrChunkWriter.hpp" 1.32 +#include "jfr/recorder/service/jfrOptionSet.hpp" 1.33 +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" 1.34 +#include "jfr/utilities/jfrTypes.hpp" 1.35 +#include "memory/allocation.inline.hpp" 1.36 +#include "runtime/mutexLocker.hpp" 1.37 +#include "runtime/os.hpp" 1.38 +#include "runtime/safepoint.hpp" 1.39 +#include "runtime/task.hpp" 1.40 +#include "runtime/vframe.hpp" 1.41 + 1.42 +class vframeStreamSamples : public vframeStreamCommon { 1.43 + public: 1.44 + // constructor that starts with sender of frame fr (top_frame) 1.45 + vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub); 1.46 + void samples_next(); 1.47 + void stop() {} 1.48 +}; 1.49 + 1.50 +vframeStreamSamples::vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub) : vframeStreamCommon(jt) { 1.51 + _stop_at_java_call_stub = stop_at_java_call_stub; 1.52 + _frame = fr; 1.53 + 1.54 + // We must always have a valid frame to start filling 1.55 + bool filled_in = fill_from_frame(); 1.56 + assert(filled_in, "invariant"); 1.57 +} 1.58 + 1.59 +// Solaris SPARC Compiler1 needs an additional check on the grandparent 1.60 +// of the top_frame when the parent of the top_frame is interpreted and 1.61 +// the grandparent is compiled. However, in this method we do not know 1.62 +// the relationship of the current _frame relative to the top_frame so 1.63 +// we implement a more broad sanity check. When the previous callee is 1.64 +// interpreted and the current sender is compiled, we verify that the 1.65 +// current sender is also walkable. If it is not walkable, then we mark 1.66 +// the current vframeStream as at the end. 1.67 +void vframeStreamSamples::samples_next() { 1.68 + // handle frames with inlining 1.69 + if (_mode == compiled_mode && 1.70 + vframeStreamCommon::fill_in_compiled_inlined_sender()) { 1.71 + return; 1.72 + } 1.73 + 1.74 + // handle general case 1.75 + int loop_count = 0; 1.76 + int loop_max = MaxJavaStackTraceDepth * 2; 1.77 + do { 1.78 + loop_count++; 1.79 + // By the time we get here we should never see unsafe but better safe then segv'd 1.80 + if (loop_count > loop_max || !_frame.safe_for_sender(_thread)) { 1.81 + _mode = at_end_mode; 1.82 + return; 1.83 + } 1.84 + _frame = _frame.sender(&_reg_map); 1.85 + } while (!fill_from_frame()); 1.86 +} 1.87 + 1.88 +static JfrStackTraceRepository* _instance = NULL; 1.89 + 1.90 +JfrStackTraceRepository& JfrStackTraceRepository::instance() { 1.91 + return *_instance; 1.92 +} 1.93 + 1.94 +JfrStackTraceRepository* JfrStackTraceRepository::create() { 1.95 + assert(_instance == NULL, "invariant"); 1.96 + _instance = new JfrStackTraceRepository(); 1.97 + return _instance; 1.98 +} 1.99 + 1.100 +void JfrStackTraceRepository::destroy() { 1.101 + assert(_instance != NULL, "invarinat"); 1.102 + delete _instance; 1.103 + _instance = NULL; 1.104 +} 1.105 + 1.106 +JfrStackTraceRepository::JfrStackTraceRepository() : _next_id(0), _entries(0) { 1.107 + memset(_table, 0, sizeof(_table)); 1.108 +} 1.109 +class JfrFrameType : public JfrSerializer { 1.110 + public: 1.111 + void serialize(JfrCheckpointWriter& writer) { 1.112 + writer.write_count(JfrStackFrame::NUM_FRAME_TYPES); 1.113 + writer.write_key(JfrStackFrame::FRAME_INTERPRETER); 1.114 + writer.write("Interpreted"); 1.115 + writer.write_key(JfrStackFrame::FRAME_JIT); 1.116 + writer.write("JIT compiled"); 1.117 + writer.write_key(JfrStackFrame::FRAME_INLINE); 1.118 + writer.write("Inlined"); 1.119 + writer.write_key(JfrStackFrame::FRAME_NATIVE); 1.120 + writer.write("Native"); 1.121 + } 1.122 +}; 1.123 + 1.124 +bool JfrStackTraceRepository::initialize() { 1.125 + return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType()); 1.126 +} 1.127 + 1.128 +size_t JfrStackTraceRepository::clear() { 1.129 + MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); 1.130 + if (_entries == 0) { 1.131 + return 0; 1.132 + } 1.133 + for (u4 i = 0; i < TABLE_SIZE; ++i) { 1.134 + JfrStackTraceRepository::StackTrace* stacktrace = _table[i]; 1.135 + while (stacktrace != NULL) { 1.136 + JfrStackTraceRepository::StackTrace* next = stacktrace->next(); 1.137 + delete stacktrace; 1.138 + stacktrace = next; 1.139 + } 1.140 + } 1.141 + memset(_table, 0, sizeof(_table)); 1.142 + const size_t processed = _entries; 1.143 + _entries = 0; 1.144 + return processed; 1.145 +} 1.146 + 1.147 +traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) { 1.148 + MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); 1.149 + const size_t index = stacktrace._hash % TABLE_SIZE; 1.150 + const StackTrace* table_entry = _table[index]; 1.151 + 1.152 + while (table_entry != NULL) { 1.153 + if (table_entry->equals(stacktrace)) { 1.154 + return table_entry->id(); 1.155 + } 1.156 + table_entry = table_entry->next(); 1.157 + } 1.158 + 1.159 + if (!stacktrace.have_lineno()) { 1.160 + return 0; 1.161 + } 1.162 + 1.163 + traceid id = ++_next_id; 1.164 + _table[index] = new StackTrace(id, stacktrace, _table[index]); 1.165 + ++_entries; 1.166 + return id; 1.167 +} 1.168 + 1.169 +traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) { 1.170 + return instance().add_trace(stacktrace); 1.171 +} 1.172 + 1.173 +traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) { 1.174 + assert(thread == Thread::current(), "invariant"); 1.175 + JfrThreadLocal* const tl = thread->jfr_thread_local(); 1.176 + assert(tl != NULL, "invariant"); 1.177 + if (tl->has_cached_stack_trace()) { 1.178 + return tl->cached_stack_trace_id(); 1.179 + } 1.180 + if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) { 1.181 + return 0; 1.182 + } 1.183 + JfrStackFrame* frames = tl->stackframes(); 1.184 + if (frames == NULL) { 1.185 + // pending oom 1.186 + return 0; 1.187 + } 1.188 + assert(frames != NULL, "invariant"); 1.189 + assert(tl->stackframes() == frames, "invariant"); 1.190 + return instance().record_for((JavaThread*)thread, skip,frames, tl->stackdepth()); 1.191 +} 1.192 + 1.193 +traceid JfrStackTraceRepository::record(Thread* thread, int skip, unsigned int* hash) { 1.194 + assert(thread == Thread::current(), "invariant"); 1.195 + JfrThreadLocal* const tl = thread->jfr_thread_local(); 1.196 + assert(tl != NULL, "invariant"); 1.197 + 1.198 + if (tl->has_cached_stack_trace()) { 1.199 + *hash = tl->cached_stack_trace_hash(); 1.200 + return tl->cached_stack_trace_id(); 1.201 + } 1.202 + if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) { 1.203 + return 0; 1.204 + } 1.205 + JfrStackFrame* frames = tl->stackframes(); 1.206 + if (frames == NULL) { 1.207 + // pending oom 1.208 + return 0; 1.209 + } 1.210 + assert(frames != NULL, "invariant"); 1.211 + assert(tl->stackframes() == frames, "invariant"); 1.212 + return instance().record_for((JavaThread*)thread, skip, frames, tl->stackdepth(), hash); 1.213 +} 1.214 + 1.215 +traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames) { 1.216 + JfrStackTrace stacktrace(frames, max_frames); 1.217 + if (!stacktrace.record_safe(thread, skip)) { 1.218 + return 0; 1.219 + } 1.220 + traceid tid = add(stacktrace); 1.221 + if (tid == 0) { 1.222 + stacktrace.resolve_linenos(); 1.223 + tid = add(stacktrace); 1.224 + } 1.225 + return tid; 1.226 +} 1.227 + 1.228 +traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames, unsigned int* hash) { 1.229 + assert(hash != NULL && *hash == 0, "invariant"); 1.230 + JfrStackTrace stacktrace(frames, max_frames); 1.231 + if (!stacktrace.record_safe(thread, skip, true)) { 1.232 + return 0; 1.233 + } 1.234 + traceid tid = add(stacktrace); 1.235 + if (tid == 0) { 1.236 + stacktrace.resolve_linenos(); 1.237 + tid = add(stacktrace); 1.238 + } 1.239 + *hash = stacktrace._hash; 1.240 + return tid; 1.241 +} 1.242 + 1.243 +size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) { 1.244 + MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); 1.245 + assert(_entries > 0, "invariant"); 1.246 + int count = 0; 1.247 + for (u4 i = 0; i < TABLE_SIZE; ++i) { 1.248 + JfrStackTraceRepository::StackTrace* stacktrace = _table[i]; 1.249 + while (stacktrace != NULL) { 1.250 + JfrStackTraceRepository::StackTrace* next = stacktrace->next(); 1.251 + if (stacktrace->should_write()) { 1.252 + stacktrace->write(sw); 1.253 + ++count; 1.254 + } 1.255 + if (clear) { 1.256 + delete stacktrace; 1.257 + } 1.258 + stacktrace = next; 1.259 + } 1.260 + } 1.261 + if (clear) { 1.262 + memset(_table, 0, sizeof(_table)); 1.263 + _entries = 0; 1.264 + } 1.265 + return count; 1.266 +} 1.267 + 1.268 +size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) { 1.269 + return _entries > 0 ? write_impl(sw, clear) : 0; 1.270 +} 1.271 + 1.272 +traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) { 1.273 + assert(JfrStacktrace_lock->owned_by_self(), "invariant"); 1.274 + const StackTrace* const trace = resolve_entry(hash, id); 1.275 + assert(trace != NULL, "invariant"); 1.276 + assert(trace->hash() == hash, "invariant"); 1.277 + assert(trace->id() == id, "invariant"); 1.278 + trace->write(writer); 1.279 + return id; 1.280 +} 1.281 + 1.282 +JfrStackTraceRepository::StackTrace::StackTrace(traceid id, const JfrStackTrace& trace, JfrStackTraceRepository::StackTrace* next) : 1.283 + _next(next), 1.284 + _frames(NULL), 1.285 + _id(id), 1.286 + _nr_of_frames(trace._nr_of_frames), 1.287 + _hash(trace._hash), 1.288 + _reached_root(trace._reached_root), 1.289 + _written(false) { 1.290 + if (_nr_of_frames > 0) { 1.291 + _frames = NEW_C_HEAP_ARRAY(JfrStackFrame, _nr_of_frames, mtTracing); 1.292 + memcpy(_frames, trace._frames, _nr_of_frames * sizeof(JfrStackFrame)); 1.293 + } 1.294 +} 1.295 + 1.296 +JfrStackTraceRepository::StackTrace::~StackTrace() { 1.297 + if (_frames != NULL) { 1.298 + FREE_C_HEAP_ARRAY(JfrStackFrame, _frames, mtTracing); 1.299 + } 1.300 +} 1.301 + 1.302 +bool JfrStackTraceRepository::StackTrace::equals(const JfrStackTrace& rhs) const { 1.303 + if (_reached_root != rhs._reached_root || _nr_of_frames != rhs._nr_of_frames || _hash != rhs._hash) { 1.304 + return false; 1.305 + } 1.306 + for (u4 i = 0; i < _nr_of_frames; ++i) { 1.307 + if (!_frames[i].equals(rhs._frames[i])) { 1.308 + return false; 1.309 + } 1.310 + } 1.311 + return true; 1.312 +} 1.313 + 1.314 +template <typename Writer> 1.315 +static void write_stacktrace(Writer& w, traceid id, bool reached_root, u4 nr_of_frames, const JfrStackFrame* frames) { 1.316 + w.write((u8)id); 1.317 + w.write((u1)!reached_root); 1.318 + w.write(nr_of_frames); 1.319 + for (u4 i = 0; i < nr_of_frames; ++i) { 1.320 + frames[i].write(w); 1.321 + } 1.322 +} 1.323 + 1.324 +void JfrStackTraceRepository::StackTrace::write(JfrChunkWriter& sw) const { 1.325 + assert(!_written, "invariant"); 1.326 + write_stacktrace(sw, _id, _reached_root, _nr_of_frames, _frames); 1.327 + _written = true; 1.328 +} 1.329 + 1.330 +void JfrStackTraceRepository::StackTrace::write(JfrCheckpointWriter& cpw) const { 1.331 + write_stacktrace(cpw, _id, _reached_root, _nr_of_frames, _frames); 1.332 +} 1.333 + 1.334 +// JfrStackFrame 1.335 + 1.336 +bool JfrStackFrame::equals(const JfrStackFrame& rhs) const { 1.337 + return _methodid == rhs._methodid && _bci == rhs._bci && _type == rhs._type; 1.338 +} 1.339 + 1.340 +template <typename Writer> 1.341 +static void write_frame(Writer& w, traceid methodid, int line, int bci, u1 type) { 1.342 + w.write((u8)methodid); 1.343 + w.write((u4)line); 1.344 + w.write((u4)bci); 1.345 + w.write((u8)type); 1.346 +} 1.347 + 1.348 +void JfrStackFrame::write(JfrChunkWriter& cw) const { 1.349 + write_frame(cw, _methodid, _line, _bci, _type); 1.350 +} 1.351 + 1.352 +void JfrStackFrame::write(JfrCheckpointWriter& cpw) const { 1.353 + write_frame(cpw, _methodid, _line, _bci, _type); 1.354 +} 1.355 + 1.356 +// invariant is that the entry to be resolved actually exists in the table 1.357 +const JfrStackTraceRepository::StackTrace* JfrStackTraceRepository::resolve_entry(unsigned int hash, traceid id) const { 1.358 + const size_t index = (hash % TABLE_SIZE); 1.359 + const StackTrace* trace = _table[index]; 1.360 + while (trace != NULL && trace->id() != id) { 1.361 + trace = trace->next(); 1.362 + } 1.363 + assert(trace != NULL, "invariant"); 1.364 + assert(trace->hash() == hash, "invariant"); 1.365 + assert(trace->id() == id, "invariant"); 1.366 + return trace; 1.367 +} 1.368 + 1.369 +void JfrStackFrame::resolve_lineno() { 1.370 + assert(_method, "no method pointer"); 1.371 + assert(_line == 0, "already have linenumber"); 1.372 + _line = _method->line_number_from_bci(_bci); 1.373 + _method = NULL; 1.374 +} 1.375 + 1.376 +void JfrStackTrace::set_frame(u4 frame_pos, JfrStackFrame& frame) { 1.377 + assert(frame_pos < _max_frames, "illegal frame_pos"); 1.378 + _frames[frame_pos] = frame; 1.379 +} 1.380 + 1.381 +void JfrStackTrace::resolve_linenos() { 1.382 + for(unsigned int i = 0; i < _nr_of_frames; i++) { 1.383 + _frames[i].resolve_lineno(); 1.384 + } 1.385 + _lineno = true; 1.386 +} 1.387 + 1.388 +bool JfrStackTrace::record_safe(JavaThread* thread, int skip, bool leakp /* false */) { 1.389 + assert(SafepointSynchronize::safepoint_safe(thread, thread->thread_state()) 1.390 + || thread == Thread::current(), "Thread stack needs to be walkable"); 1.391 + vframeStream vfs(thread); 1.392 + u4 count = 0; 1.393 + _reached_root = true; 1.394 + for(int i = 0; i < skip; i++) { 1.395 + if (vfs.at_end()) { 1.396 + break; 1.397 + } 1.398 + vfs.next(); 1.399 + } 1.400 + 1.401 + while (!vfs.at_end()) { 1.402 + if (count >= _max_frames) { 1.403 + _reached_root = false; 1.404 + break; 1.405 + } 1.406 + const Method* method = vfs.method(); 1.407 + const traceid mid = JfrTraceId::use(method, leakp); 1.408 + int type = vfs.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT; 1.409 + int bci = 0; 1.410 + if (method->is_native()) { 1.411 + type = JfrStackFrame::FRAME_NATIVE; 1.412 + } else { 1.413 + bci = vfs.bci(); 1.414 + } 1.415 + // Can we determine if it's inlined? 1.416 + _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type); 1.417 + _frames[count] = JfrStackFrame(mid, bci, type, method); 1.418 + vfs.next(); 1.419 + count++; 1.420 + } 1.421 + 1.422 + _nr_of_frames = count; 1.423 + return true; 1.424 +} 1.425 + 1.426 +bool JfrStackTrace::record_thread(JavaThread& thread, frame& frame) { 1.427 + vframeStreamSamples st(&thread, frame, false); 1.428 + u4 count = 0; 1.429 + _reached_root = true; 1.430 + 1.431 + while (!st.at_end()) { 1.432 + if (count >= _max_frames) { 1.433 + _reached_root = false; 1.434 + break; 1.435 + } 1.436 + const Method* method = st.method(); 1.437 + if (!method->is_valid_method()) { 1.438 + // we throw away everything we've gathered in this sample since 1.439 + // none of it is safe 1.440 + return false; 1.441 + } 1.442 + const traceid mid = JfrTraceId::use(method); 1.443 + int type = st.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT; 1.444 + int bci = 0; 1.445 + if (method->is_native()) { 1.446 + type = JfrStackFrame::FRAME_NATIVE; 1.447 + } else { 1.448 + bci = st.bci(); 1.449 + } 1.450 + const int lineno = method->line_number_from_bci(bci); 1.451 + // Can we determine if it's inlined? 1.452 + _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type); 1.453 + _frames[count] = JfrStackFrame(mid, bci, type, lineno); 1.454 + st.samples_next(); 1.455 + count++; 1.456 + } 1.457 + 1.458 + _lineno = true; 1.459 + _nr_of_frames = count; 1.460 + return true; 1.461 +} 1.462 + 1.463 +void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) { 1.464 + JfrFrameType fct; 1.465 + writer.write_type(TYPE_FRAMETYPE); 1.466 + fct.serialize(writer); 1.467 +}