src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp
branchJEP-349-branch
changeset 57870 00860d9caf4d
child 57983 a57907813a83
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp	Sat Aug 24 14:30:27 2019 +0200
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
+#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
+#include "jfr/recorder/repository/jfrChunkWriter.hpp"
+#include "jfr/recorder/stacktrace/jfrStackTrace.hpp"
+#include "memory/allocation.inline.hpp"
+#include "runtime/vframe.inline.hpp"
+
+static void copy_frames(JfrStackFrame** lhs_frames, u4 length, const JfrStackFrame* rhs_frames) {
+  assert(lhs_frames != NULL, "invariant");
+  assert(rhs_frames != NULL, "invariant");
+  if (length > 0) {
+    *lhs_frames = NEW_C_HEAP_ARRAY(JfrStackFrame, length, mtTracing);
+    memcpy(*lhs_frames, rhs_frames, length * sizeof(JfrStackFrame));
+  }
+}
+
+JfrStackFrame::JfrStackFrame(const traceid& id, int bci, int type, InstanceKlass* k) :
+  _klass(k), _methodid(id), _line(0), _bci(bci), _type(type) {}
+
+JfrStackFrame::JfrStackFrame(const traceid& id, int bci, int type, int lineno) :
+  _klass(NULL), _methodid(id), _line(lineno), _bci(bci), _type(type) {}
+
+JfrStackTrace::JfrStackTrace(JfrStackFrame* frames, u4 max_frames) :
+  _next(NULL),
+  _frames(frames),
+  _id(0),
+  _hash(0),
+  _nr_of_frames(0),
+  _max_frames(max_frames),
+  _frames_ownership(false),
+  _reached_root(false),
+  _lineno(false),
+  _written(false) {}
+
+JfrStackTrace::JfrStackTrace(traceid id, const JfrStackTrace& trace, const JfrStackTrace* next) :
+  _next(next),
+  _frames(NULL),
+  _id(id),
+  _hash(trace._hash),
+  _nr_of_frames(trace._nr_of_frames),
+  _max_frames(trace._max_frames),
+  _frames_ownership(true),
+  _reached_root(trace._reached_root),
+  _lineno(trace._lineno),
+  _written(false) {
+  copy_frames(&_frames, trace._nr_of_frames, trace._frames);
+}
+
+JfrStackTrace::~JfrStackTrace() {
+  if (_frames_ownership && _frames != NULL) {
+    FREE_C_HEAP_ARRAY(JfrStackFrame, _frames);
+  }
+}
+
+void JfrStackTrace::operator=(const JfrStackTrace& trace) {
+  assert(_next == NULL, "invariant");
+  assert(_frames_ownership, "invariant");
+
+  if (_id == trace._id) {
+    assert(_hash == trace._hash, "invariant");
+    assert(_nr_of_frames == trace._nr_of_frames, "invariant");
+    return;
+  }
+  _next = NULL;
+  _id = trace._id;
+  _hash = trace._hash;
+  _nr_of_frames = trace._nr_of_frames;
+  _max_frames = trace._max_frames;
+  _reached_root = trace._reached_root;
+  _lineno = trace._lineno;
+  _written = false;
+  copy_frames(&_frames, trace._nr_of_frames, trace._frames);
+}
+
+template <typename Writer>
+static void write_stacktrace(Writer& w, traceid id, bool reached_root, u4 nr_of_frames, const JfrStackFrame* frames) {
+  w.write((u8)id);
+  w.write((u1)!reached_root);
+  w.write(nr_of_frames);
+  for (u4 i = 0; i < nr_of_frames; ++i) {
+    frames[i].write(w);
+  }
+}
+
+void JfrStackTrace::write(JfrChunkWriter& sw) const {
+  assert(!_written, "invariant");
+  write_stacktrace(sw, _id, _reached_root, _nr_of_frames, _frames);
+  _written = true;
+}
+
+void JfrStackTrace::write(JfrCheckpointWriter& cpw) const {
+  write_stacktrace(cpw, _id, _reached_root, _nr_of_frames, _frames);
+}
+
+bool JfrStackFrame::equals(const JfrStackFrame& rhs) const {
+  return _methodid == rhs._methodid && _bci == rhs._bci && _type == rhs._type;
+}
+
+bool JfrStackTrace::equals(const JfrStackTrace& rhs) const {
+  if (_reached_root != rhs._reached_root || _nr_of_frames != rhs._nr_of_frames || _hash != rhs._hash) {
+    return false;
+  }
+  for (u4 i = 0; i < _nr_of_frames; ++i) {
+    if (!_frames[i].equals(rhs._frames[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename Writer>
+static void write_frame(Writer& w, traceid methodid, int line, int bci, u1 type) {
+  w.write((u8)methodid);
+  w.write((u4)line);
+  w.write((u4)bci);
+  w.write((u8)type);
+}
+
+void JfrStackFrame::write(JfrChunkWriter& cw) const {
+  write_frame(cw, _methodid, _line, _bci, _type);
+}
+
+void JfrStackFrame::write(JfrCheckpointWriter& cpw) const {
+  write_frame(cpw, _methodid, _line, _bci, _type);
+}
+
+class vframeStreamSamples : public vframeStreamCommon {
+ public:
+  // constructor that starts with sender of frame fr (top_frame)
+  vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub) : vframeStreamCommon(jt) {
+    _stop_at_java_call_stub = stop_at_java_call_stub;
+    _frame = fr;
+
+    // We must always have a valid frame to start filling
+    bool filled_in = fill_from_frame();
+    assert(filled_in, "invariant");
+  }
+  void samples_next();
+  void stop() {}
+};
+
+// Solaris SPARC Compiler1 needs an additional check on the grandparent
+// of the top_frame when the parent of the top_frame is interpreted and
+// the grandparent is compiled. However, in this method we do not know
+// the relationship of the current _frame relative to the top_frame so
+// we implement a more broad sanity check. When the previous callee is
+// interpreted and the current sender is compiled, we verify that the
+// current sender is also walkable. If it is not walkable, then we mark
+// the current vframeStream as at the end.
+void vframeStreamSamples::samples_next() {
+  // handle frames with inlining
+  if (_mode == compiled_mode &&
+    vframeStreamCommon::fill_in_compiled_inlined_sender()) {
+    return;
+  }
+
+  // handle general case
+  u4 loop_count = 0;
+  u4 loop_max = MAX_STACK_DEPTH * 2;
+  do {
+    loop_count++;
+    // By the time we get here we should never see unsafe but better safe then segv'd
+    if (loop_count > loop_max || !_frame.safe_for_sender(_thread)) {
+      _mode = at_end_mode;
+      return;
+    }
+    _frame = _frame.sender(&_reg_map);
+  } while (!fill_from_frame());
+}
+
+bool JfrStackTrace::record_thread(JavaThread& thread, frame& frame) {
+  vframeStreamSamples st(&thread, frame, false);
+  u4 count = 0;
+  _reached_root = true;
+
+  while (!st.at_end()) {
+    if (count >= _max_frames) {
+      _reached_root = false;
+      break;
+    }
+    const Method* method = st.method();
+    if (!Method::is_valid_method(method)) {
+      // we throw away everything we've gathered in this sample since
+      // none of it is safe
+      return false;
+    }
+    const traceid mid = JfrTraceId::use(method);
+    int type = st.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
+    int bci = 0;
+    if (method->is_native()) {
+      type = JfrStackFrame::FRAME_NATIVE;
+    } else {
+      bci = st.bci();
+    }
+    const int lineno = method->line_number_from_bci(bci);
+    // Can we determine if it's inlined?
+    _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
+    _frames[count] = JfrStackFrame(mid, bci, type, method->constants()->pool_holder());
+    st.samples_next();
+    count++;
+  }
+
+  _lineno = true;
+  _nr_of_frames = count;
+  return true;
+}
+
+void JfrStackFrame::resolve_lineno() const {
+  assert(_klass, "no klass pointer");
+  assert(_line == 0, "already have linenumber");
+  const int id_num = _methodid & METHOD_ID_NUM_MASK;
+  const Method* const method = _klass->method_with_orig_idnum(id_num);
+  assert(method != NULL, "invariant");
+  _line = method->line_number_from_bci(_bci);
+}
+
+void JfrStackTrace::resolve_linenos() const {
+  for (unsigned int i = 0; i < _nr_of_frames; i++) {
+    _frames[i].resolve_lineno();
+  }
+  _lineno = true;
+}
+
+bool JfrStackTrace::record_safe(JavaThread* thread, int skip) {
+  assert(thread == Thread::current(), "Thread stack needs to be walkable");
+  vframeStream vfs(thread);
+  u4 count = 0;
+  _reached_root = true;
+  for (int i = 0; i < skip; i++) {
+    if (vfs.at_end()) {
+      break;
+    }
+    vfs.next();
+  }
+
+  while (!vfs.at_end()) {
+    if (count >= _max_frames) {
+      _reached_root = false;
+      break;
+    }
+    const Method* method = vfs.method();
+    const traceid mid = JfrTraceId::use(method);
+    int type = vfs.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
+    int bci = 0;
+    if (method->is_native()) {
+      type = JfrStackFrame::FRAME_NATIVE;
+    }
+    else {
+      bci = vfs.bci();
+    }
+    // Can we determine if it's inlined?
+    _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
+    _frames[count] = JfrStackFrame(mid, bci, type, method->constants()->pool_holder());
+    vfs.next();
+    count++;
+  }
+
+  _nr_of_frames = count;
+  return true;
+}
+