diff --git a/src/os/bsd/vm/osThread_bsd.hpp b/src/os/bsd/vm/osThread_bsd.hpp
--- a/src/os/bsd/vm/osThread_bsd.hpp
+++ b/src/os/bsd/vm/osThread_bsd.hpp
@@ -62,10 +62,8 @@
   sigset_t  caller_sigmask() const       { return _caller_sigmask; }
   void    set_caller_sigmask(sigset_t sigmask)  { _caller_sigmask = sigmask; }
 
-#ifndef PRODUCT
-  // Used for debugging, return a unique integer for each thread.
+  // unique integer for each thread
   intptr_t thread_identifier() const   { return (intptr_t)_pthread_id; }
-#endif
 
 #ifdef ASSERT
   // We expect no reposition failures so kill vm if we get one.
diff --git a/src/os/linux/vm/osThread_linux.hpp b/src/os/linux/vm/osThread_linux.hpp
--- a/src/os/linux/vm/osThread_linux.hpp
+++ b/src/os/linux/vm/osThread_linux.hpp
@@ -51,10 +51,9 @@
   sigset_t  caller_sigmask() const       { return _caller_sigmask; }
   void    set_caller_sigmask(sigset_t sigmask)  { _caller_sigmask = sigmask; }
 
-#ifndef PRODUCT
-  // Used for debugging, return a unique integer for each thread.
+  // unique integer for each thread
   int thread_identifier() const   { return _thread_id; }
-#endif
+
 #ifdef ASSERT
   // We expect no reposition failures so kill vm if we get one.
   //
diff --git a/src/os/windows/vm/osThread_windows.hpp b/src/os/windows/vm/osThread_windows.hpp
--- a/src/os/windows/vm/osThread_windows.hpp
+++ b/src/os/windows/vm/osThread_windows.hpp
@@ -43,10 +43,9 @@
   HANDLE interrupt_event() const                   { return _interrupt_event; }
   void set_interrupt_event(HANDLE interrupt_event) { _interrupt_event = interrupt_event; }
 
-#ifndef PRODUCT
-  // Used for debugging, return a unique integer for each thread.
+  // unique integer for each thread
   int thread_identifier() const                    { return _thread_id; }
-#endif
+
 #ifdef ASSERT
   // We expect no reposition failures so kill vm if we get one
   //
diff --git a/src/share/vm/classfile/vmSymbols.hpp b/src/share/vm/classfile/vmSymbols.hpp
--- a/src/share/vm/classfile/vmSymbols.hpp
+++ b/src/share/vm/classfile/vmSymbols.hpp
@@ -642,6 +642,12 @@
   template(addThreadDumpForMonitors_signature,         "(Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;[I)V") \
   template(addThreadDumpForSynchronizers_signature,    "(Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;)V")   \
                                                                                                                   \
+  template(sun_evtracing_TraceReaderThread,            "sun/evtracing/TraceReaderThread")                         \
+  template(TraceReaderThread_constructor_signature,    "(Lsun/evtracing/TraceBufferQueue;Lsun/evtracing/TraceBufferQueue;)V") \
+  template(sun_evtracing_TraceBufferQueue,             "sun/evtracing/TraceBufferQueue")                          \
+  template(TraceBufferQueue_from_handle_method_name,   "fromHandle")                                              \
+  template(TraceBufferQueue_from_handle_method_signature, "(J)Lsun/evtracing/TraceBufferQueue;")                  \
+                                                                                                                  \
   /* JVMTI/java.lang.instrument support and VM Attach mechanism */                                                \
   template(jdk_internal_module_Modules,                "jdk/internal/module/Modules")                             \
   template(jdk_internal_vm_VMSupport,                  "jdk/internal/vm/VMSupport")                               \
diff --git a/src/share/vm/evtrace/traceBuffer.hpp b/src/share/vm/evtrace/traceBuffer.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceBuffer.hpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEBUFFER_HPP
+#define SHARE_VM_EVTRACE_TRACEBUFFER_HPP
+
+#include "memory/allocation.hpp"
+
+class Thread;
+
+class TraceBuffer: public CHeapObj<mtEventTracing> {
+public:
+  TraceBuffer(size_t capacity);
+  virtual ~TraceBuffer() { }
+
+  void reset();
+  bool reserve(size_t nbytes);
+  size_t filled_size() const;
+
+  void* operator new(size_t size, size_t capacity) throw();
+
+  TraceBuffer  *queue_next;
+  u1           *top;
+  const u1     *end;
+  Thread       *owner;
+  s8            owner_id;
+  u1            data[0]; // follows here
+};
+
+#include "evtrace/traceBuffer.inline.hpp"
+
+#endif /* SHARE_VM_EVTRACE_TRACEBUFFER_HPP */
diff --git a/src/share/vm/evtrace/traceBuffer.inline.hpp b/src/share/vm/evtrace/traceBuffer.inline.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceBuffer.inline.hpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEBUFFER_INLINE_HPP
+#define SHARE_VM_EVTRACE_TRACEBUFFER_INLINE_HPP
+
+#include "runtime/thread.hpp"
+
+inline TraceBuffer::TraceBuffer(size_t capacity)
+  : end(data + capacity),
+    queue_next(NULL)
+{
+  reset();
+}
+
+inline void TraceBuffer::reset() {
+  top = data;
+  owner = NULL;
+  owner_id = 0;
+  DEBUG_ONLY(for (uint8_t *p = data; p != end; p++) *p = 0;)
+}
+
+inline bool TraceBuffer::reserve(size_t nbytes) {
+  assert(top >= data, "top before data area");
+  assert(top <= end, "top after data area");
+  assert(owner != NULL, "has no owner");
+  assert(Thread::current() == owner, "current thread is not the owner");
+
+  u1 *new_top = top + nbytes;
+  if (new_top > end) {
+    return false;
+  }
+  top = new_top;
+  return true;
+}
+
+inline void* TraceBuffer::operator new(size_t size, size_t capacity) throw () {
+  return CHeapObj<mtEventTracing>::operator new(size + capacity, CALLER_PC);
+}
+
+inline size_t TraceBuffer::filled_size() const {
+  return (size_t) (top - data);
+}
+
+#endif /* SHARE_VM_EVTRACE_TRACEBUFFER_INLINE_HPP */
diff --git a/src/share/vm/evtrace/traceBufferQueue.cpp b/src/share/vm/evtrace/traceBufferQueue.cpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceBufferQueue.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evtrace/traceBufferQueue.hpp"
+
+#include "evtrace/traceBuffer.hpp"
+#include "runtime/spinLocker.hpp"
+
+TraceBufferQueue::TraceBufferQueue()
+: _head(NULL),
+  _tail(NULL),
+  _mtx(0),
+  _enqueue_ops(0),
+  _dequeue_ops(0)
+{
+}
+
+TraceBufferQueue::~TraceBufferQueue() {
+}
+
+size_t TraceBufferQueue::count() {
+  SpinLocker sl(&_mtx, "TraceBufferQueue - count");
+  return (_enqueue_ops - _dequeue_ops);
+}
+
+TraceBuffer * TraceBufferQueue::try_dequeue() {
+  TraceBuffer *buffer = NULL;
+
+  {
+    SpinLocker sl(&_mtx, "TraceBufferQueue - try_dequeue");
+    if (_head != NULL) {
+      buffer = _head;
+      _head = _head->queue_next;
+      if (_head == NULL) {
+        _tail = NULL;
+      }
+      _dequeue_ops++;
+    }
+  }
+
+  if (buffer != NULL) {
+    buffer->queue_next = NULL;
+  }
+  return buffer;
+}
+
+void TraceBufferQueue::enqueue(TraceBuffer * const buffer) {
+  assert(buffer != NULL, "sanity");
+  assert(buffer->queue_next == NULL, "queue linkage");
+
+  SpinLocker sl(&_mtx, "TraceBufferQueue - enqueue");
+  if (_tail != NULL) {
+    _tail->queue_next = buffer;
+  } else {
+    _head = buffer;
+  }
+  _tail = buffer;
+  _enqueue_ops++;
+}
diff --git a/src/share/vm/evtrace/traceBufferQueue.hpp b/src/share/vm/evtrace/traceBufferQueue.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceBufferQueue.hpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEBUFFERQUEUE_HPP
+#define SHARE_VM_EVTRACE_TRACEBUFFERQUEUE_HPP
+
+#include "memory/allocation.hpp"
+
+class TraceBuffer;
+
+class TraceBufferQueue: public CHeapObj<mtEventTracing> {
+private:
+  volatile jlong _enqueue_ops;
+  volatile jlong _dequeue_ops;
+  volatile int _mtx;
+  TraceBuffer * volatile _head;
+  TraceBuffer * volatile _tail;
+
+public:
+  TraceBufferQueue();
+  virtual ~TraceBufferQueue();
+
+  bool is_empty() { return (_head == NULL); }
+  size_t count();
+
+  void enqueue(TraceBuffer *buffer);
+  TraceBuffer * try_dequeue();
+
+  jlong fill_mark()   { return _dequeue_ops; }
+  bool has_dequeued_at_mark(jlong mark) { return (_dequeue_ops >= mark); }
+
+  jlong enqueue_ops() { return _enqueue_ops; }
+  jlong dequeue_ops() { return _dequeue_ops; }
+};
+
+#endif /* SHARE_VM_EVTRACE_TRACEBUFFERQUEUE_HPP */
diff --git a/src/share/vm/evtrace/traceEvents.cpp b/src/share/vm/evtrace/traceEvents.cpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceEvents.cpp
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evtrace/traceEvents.hpp"
+
+#include "evtrace/traceWriter.hpp"
+#include "evtrace/traceManager.hpp"
+#include "evtrace/traceMetadata.hpp"
+
+#include "runtime/vframe.hpp"
+#include "oops/oop.inline.hpp"
+
+void TraceEvents::initialize() {
+  assert(_event_max <= UINT8_MAX, "event type does not fit");
+  assert(_monitor_enter_wait_max <= UINT8_MAX, "monitor wait code does not fit");
+  assert(_monitor_entered_flags_max <= UINT8_MAX, "monitor entered flags do not fit");
+  assert(_stack_frame_kind_max <= UINT8_MAX, "stack frame types do not fit");
+}
+
+inline bool TraceEvents::can_write() {
+  assert(Thread::current()->is_tracing_active(), "must be active");
+  return TraceManager::is_initialized();
+}
+
+inline TraceTypes::timestamp TraceEvents::time_now() {
+  return TraceMetadata::time_now();
+}
+
+inline TraceTypes::thread_id TraceEvents::thread_id_for(Thread* t) {
+  return TraceMetadata::thread_id(t);
+}
+
+inline TraceTypes::nativemonitor_id TraceEvents::nativemonitor_id_for(Monitor* m) {
+  return TraceMetadata::nativemonitor_id(m);
+}
+
+void TraceEvents::write_thread_start() {
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  Thread *t = Thread::current();
+  assert(t != NULL, "thread not attached");
+
+  ResourceMark rm;
+  const char *name = NULL;
+  if (t->is_Java_thread()) {
+    name = ((JavaThread *) t)->get_thread_name();
+  } else if (t->is_Named_thread()) {
+    name = ((NamedThread *) t)->name();
+  }
+
+  const size_t name_nbytes = TraceWriter::nbytes_for_utf8str(name, 64);
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + name_nbytes);
+  wr.put_event_type(event_thread_start);
+  wr.put_timestamp(time_now());
+  wr.put_utf8str(name, name_nbytes);
+}
+
+void TraceEvents::write_thread_name_change(Thread *t) {
+  assert(t != NULL, "null thread");
+
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  ResourceMark rm;
+  const char *name = NULL;
+  if (t->is_Java_thread()) {
+    name = ((JavaThread *) t)->get_thread_name();
+  }
+
+  const size_t name_nbytes = TraceWriter::nbytes_for_utf8str(name, 64);
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + sizeof(thread_id) + name_nbytes);
+  wr.put_event_type(event_thread_name_change);
+  wr.put_timestamp(time_now());
+  wr.put_thread_id(thread_id_for(t));
+  wr.put_utf8str(name, name_nbytes);
+}
+
+void TraceEvents::write_thread_exit() {
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp));
+  wr.put_event_type(event_thread_exit);
+  wr.put_timestamp(time_now());
+}
+
+void TraceEvents::write_stack_metadata_extended(stack_id id, const CompositeTraceStack &ts) {
+  assert(can_write(), "caller's responsibility");
+
+  // NOTE: this is a separate event to maintain compatibility.
+
+  // TODO: with heavy inlining, stack traces can become so large
+  // that they no longer fit in a single trace buffer
+
+  TraceStackVframeIterator it(ts);
+  size_t count = 0;
+  size_t frames_size = 0;
+
+  // make methods known
+  while (it.has_next()) {
+    it.next();
+    frames_size += sizeof(u1);
+    assert (!it.is_Java_frame(), "sanity");
+    // TODO: store which symbols we have defined before
+    //       currently, we always define all native symbols of a new stack
+    write_native_symbol_metadata(it.native_C_pc());
+    frames_size += sizeof(native_address);
+    count++;
+  }
+  it.reset();
+
+  TraceWriter wr(sizeof(event_type) + sizeof(stack_id) + sizeof(u1) + sizeof(u2) + frames_size);
+  wr.put_event_type(event_stack_metadata_extended);
+  wr.put_stack_id(id);
+  wr.put_u1(ts.is_truncated() ? 0 : 1);
+  assert(count == (u2) count, "must fit");
+  wr.put_u2(count);
+  while (it.has_next()) {
+    it.next();
+    wr.put_u1(stack_frame_kind_native);
+    wr.put_native_address((native_address) it.native_C_pc());
+  }
+}
+
+TraceTypes::stack_id TraceEvents::retrieve_native_stack_id_or_write_metadata(Thread *t) {
+  class StatsUpdate : StackObj {
+  public:
+    bool     truncated;
+    intptr_t frames;
+    StatsUpdate() : truncated(false), frames(0) {}
+    ~StatsUpdate() {
+      TraceManager::update_stack_trace_stats(truncated, frames);
+    }
+  };
+
+  assert(can_write(), "caller's responsibility");
+
+  if (!EnableEventTracingStackTraces) {
+    return (TraceTypes::stack_id) 0;
+  }
+
+  StatsUpdate stats;
+
+  TraceStackBuilder sb;
+
+  frame fr = os::current_frame();
+  // this is adapted from print_native_stack() in debug.cpp
+  int skip = 1; // skip this method
+  while (!sb.is_full()) {
+    // Compiled code may use EBP register on x86 so it looks like
+    // non-walkable C frame. Use frame.sender() for java frames.
+    if (t != NULL && t->is_Java_thread()) {
+      // Catch very first native frame by using stack address.
+      // For JavaThread stack_base and stack_size should be set.
+      if (!t->on_local_stack((address)(fr.real_fp() + 1))) {
+        break;
+      }
+      if (fr.cb() != NULL) {
+        // stop on first non-native frame
+        break;
+      }
+    } else {
+      // is_first_C_frame() does only simple checks for frame pointer,
+      // it will pass if java compiled code has a pointer in EBP.
+      if (os::is_first_C_frame(&fr)) break;
+    }
+
+    if (skip == 0) {
+      sb.add_frame(&fr);
+    } else {
+      skip--;
+    }
+    fr = os::get_sender_for_C_frame(&fr);
+  }
+
+  if (sb.count() == 0) {
+    return (TraceTypes::stack_id) 0;
+  }
+
+  if (!fr.is_first_frame() && sb.is_full()) {
+    sb.set_truncated();
+  }
+
+  CompositeTraceStack cps(sb);
+
+  stats.frames = cps.count();
+  stats.truncated = cps.is_truncated();
+
+  bool is_known;
+  const CachedTraceStack *cached = TraceManager::metadata()->get_or_try_add_stack(cps, is_known, 0);
+  TraceTypes::stack_id id;
+  if (cached != NULL) {
+    id = cached->id();
+  } else {
+    // insert failed (full): allocate a one-time id (wasteful)
+    id = TraceManager::metadata()->next_stack_id();
+  }
+  if (!is_known) {
+    write_stack_metadata_extended(id, cps);
+  }
+  return id;
+}
+
+void TraceEvents::write_native_symbol_metadata(address pc) {
+  assert(can_write(), "caller's responsibility");
+
+  char desc[128];
+  TraceManager::metadata()->resolve_native_C_pc(pc, desc, sizeof(desc));
+  const size_t desc_length = TraceWriter::nbytes_for_utf8str(desc, 128);
+
+  TraceWriter wr(sizeof(event_type) + sizeof(native_address) + desc_length);
+  wr.put_event_type(event_native_symbol_metadata);
+  wr.put_native_address((native_address) pc);
+  wr.put_utf8str(desc, desc_length);
+}
+
+void TraceEvents::write_vm_end() {
+  assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread()
+      && TraceManager::is_initialized() && TraceManager::is_reclaiming(),
+      "may only be called by VM thread at safepoint while reclaiming");
+
+  assert(!Thread::current()->is_tracing_active(), "invariant");
+  Thread::current()->toggle_tracing_active(); // TraceLocker would deadlock because we are reclaiming
+  {
+    TraceWriter wr(sizeof(event_type) + sizeof(timestamp));
+    wr.put_event_type(event_vm_end);
+    wr.put_timestamp(time_now());
+  }
+  Thread::current()->toggle_tracing_active();
+}
+
+TraceTypes::seq_num TraceEvents::generate_native_monitor_seq_num_and_make_known(Monitor *m) {
+  assert(can_write(), "caller's responsibility");
+
+  seq_num seq = m->next_trace_seq();
+  if (seq != 1) { // info event already written
+    while (m->trace_id() == 0) {
+      // concurrent initialization: wait until the other thread has set the id
+    }
+    return seq;
+  }
+
+  TraceEvents::write_native_monitor_info(m);
+  return m->next_trace_seq();
+}
+
+void TraceEvents::write_native_monitor_info(Monitor *m) {
+  assert(can_write(), "caller's responsibility");
+
+  assert(m->trace_id() == 0, "must be uninitialized");
+  m->set_trace_id(TraceManager::metadata()->next_nativemonitor_id());
+
+  const size_t name_nbytes = TraceWriter::nbytes_for_utf8str(m->name(), 64);
+
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + sizeof(seq_num) + sizeof(nativemonitor_id) + name_nbytes);
+  wr.put_event_type(event_native_monitor_info);
+  wr.put_timestamp(time_now());
+  wr.put_seq_num(1);
+  wr.put_nativemonitor_id(nativemonitor_id_for(m));
+  wr.put_utf8str(m->name(), name_nbytes);
+}
+
+void TraceEvents::write_native_monitor_contended_lock(Monitor *m, monitor_enter_wait wait) {
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  seq_num seq = generate_native_monitor_seq_num_and_make_known(m);
+
+  stack_id stack = retrieve_native_stack_id_or_write_metadata(Thread::current());
+
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + sizeof(seq_num) + sizeof(nativemonitor_id) + sizeof(stack_id) + sizeof(u1));
+  wr.put_event_type(event_native_monitor_contended_lock);
+  wr.put_timestamp(time_now());
+  wr.put_seq_num(seq);
+  wr.put_nativemonitor_id(nativemonitor_id_for(m));
+  wr.put_stack_id(stack);
+  wr.put_u1(wait);
+}
+
+void TraceEvents::write_native_monitor_contended_locked(Monitor *m) {
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  seq_num seq = generate_native_monitor_seq_num_and_make_known(m);
+
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + sizeof(seq_num) + sizeof(nativemonitor_id));
+  wr.put_event_type(event_native_monitor_contended_locked);
+  wr.put_timestamp(time_now());
+  wr.put_seq_num(seq);
+  wr.put_nativemonitor_id(nativemonitor_id_for(m));
+}
+
+void TraceEvents::write_native_monitor_contended_unlocked(Monitor *m) {
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  seq_num seq = generate_native_monitor_seq_num_and_make_known(m);
+
+  stack_id stack = retrieve_native_stack_id_or_write_metadata(Thread::current());
+
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + sizeof(seq_num) + sizeof(nativemonitor_id) + sizeof(stack_id));
+  wr.put_event_type(event_native_monitor_contended_unlocked);
+  wr.put_timestamp(time_now());
+  wr.put_seq_num(seq);
+  wr.put_nativemonitor_id(nativemonitor_id_for(m));
+  wr.put_stack_id(stack);
+}
+
+void TraceEvents::write_native_monitor_destroy(Monitor *m) {
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  seq_num seq = generate_native_monitor_seq_num_and_make_known(m);
+
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + sizeof(seq_num) + sizeof(nativemonitor_id));
+  wr.put_event_type(event_native_monitor_destroy);
+  wr.put_timestamp(time_now());
+  wr.put_seq_num(seq);
+  wr.put_nativemonitor_id(nativemonitor_id_for(m));
+}
+
+void TraceEvents::write_marker(const char *label) {
+  TraceLocker tl;
+  if (!can_write())
+    return;
+
+  const size_t label_nbytes = TraceWriter::nbytes_for_utf8str(label, 64);
+
+  TraceWriter wr(sizeof(event_type) + sizeof(timestamp) + label_nbytes);
+  wr.put_event_type(event_marker);
+  wr.put_timestamp(time_now());
+  wr.put_utf8str(label, label_nbytes);
+}
diff --git a/src/share/vm/evtrace/traceEvents.hpp b/src/share/vm/evtrace/traceEvents.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceEvents.hpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEEVENTS_HPP
+#define SHARE_VM_EVTRACE_TRACEEVENTS_HPP
+
+#include "evtrace/traceManager.hpp"
+#include "evtrace/traceMetadata.hpp"
+
+#include "memory/allocation.hpp"
+#include "runtime/thread.hpp"
+
+class TraceWriter;
+
+class TraceEvents: private TraceTypes // for mere convenience
+{
+public:
+  static void initialize();
+
+  static void write_thread_start();
+  static void write_thread_name_change(Thread *t);
+  static void write_thread_exit();
+
+  static void write_native_monitor_contended_lock(Monitor *m, monitor_enter_wait wait);
+  static void write_native_monitor_contended_locked(Monitor *m);
+  static void write_native_monitor_contended_unlocked(Monitor *m);
+  static void write_native_monitor_destroy(Monitor *m);
+
+  static void write_vm_end();
+
+  static void write_marker(const char *label);
+
+private:
+  TraceEvents() { }
+
+  static bool can_write();
+
+  static seq_num  generate_native_monitor_seq_num_and_make_known(Monitor *m);
+  static void     write_native_monitor_info(Monitor *m);
+
+  static void      write_stack_metadata_extended(stack_id id, const CompositeTraceStack &ts);
+  static stack_id  retrieve_native_stack_id_or_write_metadata(Thread *t);
+  static void      write_native_symbol_metadata(address pc);
+
+  static timestamp        time_now();
+  static thread_id        thread_id_for(Thread *t);
+  static nativemonitor_id nativemonitor_id_for(Monitor *m);
+};
+
+class TraceEventsNativeMonitorContendedLock : public StackObj {
+private:
+  Monitor *_monitor;
+  TraceTypes::monitor_enter_wait _wait;
+  bool _contended;
+
+public:
+  TraceEventsNativeMonitorContendedLock(Monitor *m, TraceTypes::monitor_enter_wait wait)
+  : _monitor(m), _wait(wait), _contended(false)
+  {
+  }
+
+  ~TraceEventsNativeMonitorContendedLock() {
+    if (_contended) {
+      TraceEvents::write_native_monitor_contended_locked(_monitor);
+    }
+  }
+
+  void contended_lock() {
+    if (!_contended && TraceManager::should_trace_native_monitor_events()) {
+      TraceEvents::write_native_monitor_contended_lock(_monitor, _wait);
+      _contended = true;
+    }
+  }
+};
+
+class TraceEventNativeMonitorContendedUnlocked : public StackObj {
+private:
+  bool     _written;
+  Monitor *_monitor;
+
+public:
+  TraceEventNativeMonitorContendedUnlocked(Monitor *m)
+  : _written(false), _monitor(m)
+  {
+  }
+
+  // NOTE: we used to preallocate a sequence number and delay writing the event
+  // until the lock is released and this object is destroyed, but doing so
+  // causes a race with deinitialization.
+
+  void enable() {
+    if (!_written && TraceManager::should_trace_native_monitor_events()) {
+      TraceEvents::write_native_monitor_contended_unlocked(_monitor);
+      _written = true;
+    }
+  }
+};
+
+// for convenience, so this is the only file to include for writing events
+#include "evtrace/traceWriter.hpp"
+
+#endif /* SHARE_VM_EVTRACE_TRACEEVENTS_HPP */
diff --git a/src/share/vm/evtrace/traceJavaBridge.cpp b/src/share/vm/evtrace/traceJavaBridge.cpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceJavaBridge.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evtrace/traceJavaBridge.hpp"
+
+#include "evtrace/traceBuffer.hpp"
+#include "evtrace/traceBufferQueue.hpp"
+#include "evtrace/traceEvents.hpp"
+#include "evtrace/traceWriter.hpp"
+#include "evtrace/traceReaderThread.hpp"
+
+#include "prims/jni.h"
+#include "runtime/interfaceSupport.hpp"
+#include "runtime/javaCalls.hpp"
+#include "oops/oop.inline.hpp"
+
+#define TEVT_ENTRY(result_type, header) \
+  JVM_ENTRY(result_type, header)
+
+#define TEVT_END JVM_END
+
+TEVT_ENTRY(jobject, TEvT_DequeueBuffer(JNIEnv *env, jobject self, jlong queue_handle, const jboolean should_block))
+  TraceBufferQueue *queue = (TraceBufferQueue *) queue_handle;
+
+  TraceBuffer *buffer;
+
+  if (thread->is_TraceReader_thread()) {
+    // we do this to avoid interrupting the thread at a bad time
+    ((TraceReaderThread *) thread)->set_is_polling_queue(true);
+  }
+  jlong delay = 4;
+  for (;;) {
+    buffer = queue->try_dequeue();
+    if (buffer != NULL || !should_block || !TraceManager::is_initialized()) {
+      break;
+    }
+
+    os::sleep(thread, delay, true);
+    if (delay <= 64) {
+      delay = 2 * delay;
+    }
+  }
+  if (thread->is_TraceReader_thread()) {
+    ((TraceReaderThread *) thread)->set_is_polling_queue(false);
+  }
+
+  jobject bufobj = NULL;
+  if (buffer != NULL) {
+    ThreadToNativeFromVM ttn(thread);
+
+    jlong handle = (jlong) ((uintptr_t) buffer); // sign extension paranoia
+    size_t length = buffer->filled_size();
+    jobject bytebuf = env->NewDirectByteBuffer(buffer->data, length);
+    jclass clazz = env->FindClass("sun/evtracing/TraceBuffer");
+    jmethodID method = env->GetMethodID(clazz, "<init>", "(JLjava/nio/ByteBuffer;J)V");
+    assert(method != NULL, "must be successful");
+    bufobj = env->NewObject(clazz, method, handle, bytebuf, buffer->owner_id);
+    assert(bufobj != NULL, "must succeed");
+  }
+  return bufobj;
+TEVT_END
+
+TEVT_ENTRY(void, TEvT_EnqueueBuffer(JNIEnv *env, jobject self, jlong queue_handle, jlong buffer_handle))
+  TraceBufferQueue *queue = (TraceBufferQueue *) queue_handle;
+  queue->enqueue((TraceBuffer *) buffer_handle);
+TEVT_END
+
+TEVT_ENTRY(void, TEvT_ResetAndEnqueueBuffer(JNIEnv *env, jobject self, jlong queue_handle, jlong buffer_handle))
+  TraceBuffer *buffer = (TraceBuffer *) buffer_handle;
+  buffer->reset();
+  TraceBufferQueue *queue = (TraceBufferQueue *) queue_handle;
+  queue->enqueue((TraceBuffer *) buffer_handle);
+TEVT_END
+
+TEVT_ENTRY(void, TEvT_FreeBuffer(JNIEnv *env, jobject self, jlong buffer_handle))
+  TraceBuffer *buffer = (TraceBuffer *) buffer_handle;
+  TraceManager::free_buffer(buffer);
+TEVT_END
+
+TEVT_ENTRY(jlong, TEvT_QueueCount(JNIEnv *env, jobject self, jlong queue_handle))
+  TraceBufferQueue *queue = (TraceBufferQueue *) queue_handle;
+  return (jlong) queue->count();
+TEVT_END
+
+TEVT_ENTRY(void, TEvT_WriteGroupEvent(JNIEnv *env, jobject self, jlong park_global_seq_begin_ref, jobject source))
+  // ignore
+TEVT_END
+
+TEVT_ENTRY(jstring, TEvT_GetConfiguration(JNIEnv *env, jobject self))
+  jstring config = NULL;
+  if (EventTracingConfiguration != NULL && EventTracingConfiguration[0] != '\0') {
+    ThreadToNativeFromVM ttn(thread);
+    config = env->NewStringUTF(EventTracingConfiguration);
+  }
+  return config;
+TEVT_END
+
+TEVT_ENTRY(void, TEvT_PutNativeStatistics(JNIEnv *env, jobject self, jobject map))
+  JavaTraceStatistics jts(env, thread, instanceHandle(instanceOop(JNIHandles::resolve_non_null(map))), CHECK);
+  TraceManager::write_stats(&jts);
+TEVT_END
+
+JavaTraceStatistics::JavaTraceStatistics(JNIEnv* env, JavaThread* thread, instanceHandle map, TRAPS)
+: _env(env),
+  THREAD(THREAD),
+  _hashmap_obj(map)
+{
+}
+
+void JavaTraceStatistics::add_entry(const char* name, jdouble value) {
+  assert(name != NULL, "must not be NULL");
+
+  // assume utf8 string to save native transition required by java_lang_String::create_from_platform_dependent_str
+  Handle name_str = java_lang_String::create_from_str(name, CHECK);
+  oop value_oop = java_lang_boxing_object::create(T_DOUBLE, (jvalue*) &value, CHECK);
+  Handle value_handle(THREAD, value_oop);
+
+  JavaValue result(T_OBJECT);
+  JavaCalls::call_virtual(&result,
+                          _hashmap_obj,
+                          _hashmap_obj->klass(),
+                          vmSymbols::put_name(),
+                          vmSymbols::object_object_object_signature(),
+                          name_str,
+                          value_handle,
+                          CHECK);
+}
+
+TEVT_ENTRY(void, TEvT_ResetNativeStatistics(JNIEnv *env, jobject self))
+  TraceManager::reset_stats();
+TEVT_END
+
+TEVT_ENTRY(void, TEvT_ResetNativeMetadata(JNIEnv *env, jobject self))
+  // ignore
+TEVT_END
+
+TEVT_ENTRY(void, TEvT_ReclaimBuffers(JNIEnv *env, jobject self, jboolean wait_until_processed))
+  TraceManager::reclaim_buffers_in_safepoint(wait_until_processed);
+TEVT_END
+
+#define CC (char*)  /*cast a literal from (const char*)*/
+#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
+
+static JNINativeMethod tevtmethods[] = {
+  {CC"dequeueBuffer",             CC"(JZ)Lsun/evtracing/TraceBuffer;",   FN_PTR(TEvT_DequeueBuffer)},
+  {CC"enqueueBuffer",             CC"(JJ)V",                             FN_PTR(TEvT_EnqueueBuffer)},
+  {CC"resetAndEnqueueBuffer",     CC"(JJ)V",                             FN_PTR(TEvT_ResetAndEnqueueBuffer)},
+  {CC"freeBuffer",                CC"(J)V",                              FN_PTR(TEvT_FreeBuffer)},
+  {CC"queueCount",                CC"(J)J",                              FN_PTR(TEvT_QueueCount)   },
+  {CC"writeGroupEvent",           CC"(JLjava/lang/Object;)V",            FN_PTR(TEvT_WriteGroupEvent)},
+  {CC"getConfiguration",          CC"()Ljava/lang/String;",              FN_PTR(TEvT_GetConfiguration)},
+  {CC"putNativeStatistics",       CC"(Ljava/util/Map;)V",                FN_PTR(TEvT_PutNativeStatistics)},
+  {CC"resetNativeStatistics",     CC"()V",                               FN_PTR(TEvT_ResetNativeStatistics)},
+  {CC"resetNativeMetadata",       CC"()V",                               FN_PTR(TEvT_ResetNativeMetadata)},
+  {CC"reclaimBuffers",            CC"(Z)V",                              FN_PTR(TEvT_ReclaimBuffers)},
+};
+
+JVM_ENTRY(jboolean, JVM_RegisterEventTracingMethods(JNIEnv* env, jclass tevtclass))
+  if (EnableEventTracing) {
+    ThreadToNativeFromVM ttnfv(thread);
+    int ok = env->RegisterNatives(tevtclass, tevtmethods, sizeof(tevtmethods) / sizeof(JNINativeMethod));
+    guarantee(ok == 0, "register event tracing natives");
+  }
+  return EnableEventTracing;
+JVM_END
diff --git a/src/share/vm/evtrace/traceJavaBridge.hpp b/src/share/vm/evtrace/traceJavaBridge.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceJavaBridge.hpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEJAVABRIDGE_HPP
+#define SHARE_VM_EVTRACE_TRACEJAVABRIDGE_HPP
+
+#include "evtrace/traceManager.hpp"
+
+class JavaTraceStatistics: public TraceStatistics {
+private:
+  JNIEnv* _env;
+  TRAPS;
+  instanceHandle _hashmap_obj;
+
+public:
+  JavaTraceStatistics(JNIEnv* env, JavaThread* thread, instanceHandle map_obj, TRAPS);
+
+  virtual void add_entry(const char* name, jdouble value);
+};
+
+#endif /* SHARE_VM_EVTRACE_TRACEJAVABRIDGE_HPP */
diff --git a/src/share/vm/evtrace/traceManager.cpp b/src/share/vm/evtrace/traceManager.cpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceManager.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evtrace/traceManager.hpp"
+
+#include "evtrace/traceBuffer.hpp"
+#include "evtrace/traceBufferQueue.hpp"
+#include "evtrace/traceReaderThread.hpp"
+#include "evtrace/traceEvents.hpp"
+
+#include "runtime/javaCalls.hpp"
+#include "runtime/thread.hpp"
+
+volatile bool      TraceManager::_is_initialized = false;
+volatile bool      TraceManager::_is_reclaiming  = false;
+volatile intptr_t  TraceManager::_tracing_aux_threads = 0;
+TraceReaderThread *TraceManager::_thread         = NULL;
+Monitor           *TraceManager::_thread_mtx     = NULL;
+volatile bool      TraceManager::_thread_running = false;
+TraceBufferQueue  *TraceManager::_flush_queue    = NULL;
+TraceMetadata     *TraceManager::_metadata       = NULL;
+DEBUG_ONLY(int     TraceManager::_current_safepoint_counter);
+
+// statistics
+volatile intptr_t TraceManager::_buffer_count             = 0xdead;
+volatile intptr_t TraceManager::_max_buffer_count         = 0xdead;
+volatile intptr_t TraceManager::_allocated_buffer_count   = 0xdead;
+volatile intptr_t TraceManager::_submitted_trace_bytes    = 0xdead;
+intptr_t          TraceManager::_reclaimed_trace_bytes    = 0xdead;
+volatile intptr_t TraceManager::_reclaim_time_nanos       = 0xdead;
+intptr_t          TraceManager::_stored_flush_enqueue_ops = 0xdead;
+intptr_t          TraceManager::_stored_flush_dequeue_ops = 0xdead;
+volatile intptr_t TraceManager::_total_stack_traces       = 0xdead;
+volatile intptr_t TraceManager::_truncated_stack_traces   = 0xdead;
+volatile intptr_t TraceManager::_total_stack_frames       = 0xdead;
+
+void TraceManager::initialize() {
+  assert(!_is_initialized, "already initialized");
+  assert(!_is_reclaiming, "must be unset");
+  assert(_flush_queue == NULL, "flush queue exists");
+  assert(_thread == NULL, "thread exists");
+  assert(!_thread_running, "thread running");
+  assert(_metadata == NULL, "metadata exists");
+
+  // nothing should be allocated before this point
+  // NOTE: requires -XX:NativeMemoryTracking=summary or =detail
+  assert(MallocMemorySummary::as_snapshot()->by_type(mtEventTracing)->malloc_size() == 0, "premature event tracing allocations");
+
+  if (!EnableEventTracing) {
+    // so we only need to check one flag
+    EnableEventTracingNativeMonitorEvents = false;
+    return;
+  }
+
+  guarantee(EventTracingStackDepthLimit <= TRACE_STACK_MAX_FRAMES, "stack depth limit is too high");
+  if (EventTracingStackDepthLimit == 0) {
+    EnableEventTracingStackTraces = false;
+  }
+
+  TraceEvents::initialize();
+
+  _metadata = new TraceMetadata();
+
+  _thread_mtx = new Monitor(Mutex::nonleaf + 1, "TraceManager::_thread_mtx");
+
+  _buffer_count = 0;
+  _flush_queue = new TraceBufferQueue();
+
+  _is_initialized = true;
+
+  reset_stats();
+}
+
+Handle TraceManager::create_queue_object(TraceBufferQueue *q, instanceKlassHandle klass, TRAPS) {
+  JavaValue result(T_OBJECT);
+  result.set_jobject(NULL);
+  if (q != NULL) {
+    JavaCallArguments args;
+    args.push_long((jlong) q);
+    JavaCalls::call_static(&result,
+                           klass,
+                           vmSymbols::TraceBufferQueue_from_handle_method_name(),
+                           vmSymbols::TraceBufferQueue_from_handle_method_signature(),
+                           &args,
+                           CHECK_NH);
+  }
+  return Handle(THREAD, (oop) result.get_jobject());
+}
+
+void TraceManager::start_threads(TRAPS) {
+  assert(_thread == NULL && !_thread_running, "trace reader thread exists");
+
+  Klass* qk = SystemDictionary::resolve_or_fail(vmSymbols::sun_evtracing_TraceBufferQueue(), true, CHECK);
+  instanceKlassHandle q_klass(THREAD, qk);
+  q_klass->initialize(CHECK);
+
+  Handle flushq = create_queue_object(_flush_queue, q_klass, CHECK);
+
+  _thread = TraceReaderThread::start(flushq(), Handle(NULL), THREAD);
+  _thread_running = true;
+}
+
+class TraceManager::ReclaimTraceBuffersClosure : public ThreadClosure {
+  bool  _deinitialize;
+  jlong _reclaimed_bytes;
+
+public:
+  ReclaimTraceBuffersClosure(bool deinit)
+  : _deinitialize(deinit), _reclaimed_bytes(0)
+  {
+  }
+
+  ~ReclaimTraceBuffersClosure() { }
+
+  virtual void do_thread(Thread *t) {
+    assert(t != NULL, "null thread");
+
+    int counter = 0;
+    while (t->is_tracing_active()) {
+      // spin until the thread backs off or finishes writing an event
+      counter++;
+      if ((counter & 0xfff) == 0) {
+        os::naked_yield();
+      } else {
+        SpinPause();
+      }
+    }
+    OrderAccess::loadload();
+
+    TraceBuffer *buffer = t->trace_buffer();
+    if (buffer != NULL && (_deinitialize || buffer->filled_size() > 0)) {
+      TraceManager::pre_submit_buffer(buffer);
+      _reclaimed_bytes += buffer->filled_size();
+      TraceManager::_flush_queue->enqueue(buffer);
+
+      buffer = NULL;
+      t->set_trace_buffer(buffer);
+    }
+  }
+
+  jlong reclaimed_bytes() { return _reclaimed_bytes; }
+};
+
+class TraceManager::VM_ReclaimTraceBuffers : public VM_Operation {
+  bool  _deinitialize;
+  bool  _do_maintenance;
+  jlong _queue_fill_mark;
+
+public:
+  VM_ReclaimTraceBuffers(bool deinit, bool maintenance) : _deinitialize(deinit), _do_maintenance(maintenance) {}
+  VMOp_Type type() const { return VMOp_ReclaimTraceBuffers; }
+  void doit();
+
+  jlong queue_fill_mark() { return _queue_fill_mark; }
+};
+
+void TraceManager::VM_ReclaimTraceBuffers::doit() {
+  assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "sanity");
+
+  _is_reclaiming = true;
+  OrderAccess::storeload(); // ensure is_reclaiming is stored before
+                            // reading trace_active of individual threads
+
+  TraceManager::ReclaimTraceBuffersClosure cl(_deinitialize);
+  Threads::threads_do(&cl);
+
+  // from this point, no Java threads and no other threads
+  // on the threads list (GC, compiler, etc.) are tracing
+
+  if (_do_maintenance || _deinitialize) {
+    // for maintenance and deinitialization, we must synchronize with threads
+    // which are tracing, but which are not on the threads list
+    // (we cannot reclaim their buffers, but they are not allowed to record events
+    // for which reclamation is strictly necessary, such as Java metadata)
+    int counter = 0;
+    while (_tracing_aux_threads != 0) {
+      counter++;
+      if ((counter & 0xfff) == 0) {
+        os::naked_yield();
+      } else {
+        SpinPause();
+      }
+    }
+  }
+
+  if (_do_maintenance) {
+    _metadata->do_maintenance();
+  }
+
+  if (_deinitialize) {
+    TraceEvents::write_vm_end();
+    cl.do_thread(Thread::current()); // reclaim own buffer
+
+    TraceManager::_is_initialized = false;
+  }
+
+  OrderAccess::storestore();
+  _is_reclaiming = false;
+
+  _queue_fill_mark = TraceManager::_flush_queue->fill_mark();
+  TraceManager::_reclaimed_trace_bytes += cl.reclaimed_bytes();
+
+  if (_thread != NULL && _thread->is_polling_queue()) {
+    // reader thread is sleeping in poll loop, interrupt
+
+    // we don't want to interrupt the thread elsewhere, for example during
+    // NIO channel operations, when an interrupt shuts down the channel
+    Thread::interrupt(_thread);
+  }
+}
+
+void TraceManager::reclaim_buffers_in_safepoint(bool wait_until_processed) {
+  assert_initialized();
+
+  jlong reclaim_start = os::javaTimeNanos();
+
+  jlong fill_mark = do_reclaim_buffers_in_safepoint(false);
+
+  if (wait_until_processed) {
+    // NOTE: this doesn't actually wait until the last buffer has been
+    //       *processed*, only until the reader thread has *dequeued*
+    //       the last reclaimed buffer (usually when the second-to-last
+    //       buffer has been processed)
+    while (!_flush_queue->has_dequeued_at_mark(fill_mark)) {
+      os::sleep(Thread::current(), 1, true);
+    }
+  }
+
+  if (EnableEventTracingDiagnostics) {
+    // FIXME: counter value depends on whether callers set wait_until_processed
+    jlong duration = os::javaTimeNanos() - reclaim_start;
+    Atomic::add_ptr(duration, &_reclaim_time_nanos);
+  }
+}
+
+jlong TraceManager::do_reclaim_buffers_in_safepoint(bool deinit) {
+  VM_ReclaimTraceBuffers op(deinit, false);
+  VMThread::execute(&op);
+
+  return op.queue_fill_mark();
+}
+
+void TraceManager::do_work_before_safepoint_end() {
+  assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "sanity");
+
+  if (is_initialized()) {
+    VM_ReclaimTraceBuffers(false, true).doit(); // don't bother with VM op
+  }
+}
+
+void TraceManager::thread_is_exiting(TraceReaderThread* t) {
+  guarantee(!is_initialized(), "thread exiting prematurely");
+  guarantee(t == _thread, "unexpected thread");
+  guarantee(_thread_running, "not running?");
+
+  MonitorLockerEx ml(_thread_mtx);
+  _thread_running = false;
+  ml.notify_all();
+}
+
+void TraceManager::finish_and_destroy(TRAPS) {
+  assert_initialized();
+  assert(_thread != NULL, "thread not initialized");
+
+  jlong reclaim_start = os::javaTimeNanos();
+
+  // This just sets a flag that the reader thread only checks once queue
+  // operations fail after the reclaim op
+  _thread->shutdown(CHECK);
+
+  _thread_mtx->lock();
+
+  do_reclaim_buffers_in_safepoint(true);
+
+  while(_thread_running) {
+    _thread_mtx->wait(); // wait for trace reader thread to call back
+  }
+  _thread_mtx->unlock();
+  delete _thread_mtx;
+  _thread_mtx = NULL;
+  _thread = NULL; // NOTE: thread deletes itself on exit
+
+  if (EnableEventTracingDiagnostics) {
+    _reclaim_time_nanos += (os::javaTimeNanos() - reclaim_start);
+  }
+
+  // We keep these around to provide statistics to agents after deinitialization (during shutdown)
+  // FIXME: we should fill a single TraceStatistics object instead of all those variables
+  _stored_flush_enqueue_ops = _flush_queue->enqueue_ops() - _stored_flush_enqueue_ops;
+  _stored_flush_dequeue_ops = _flush_queue->dequeue_ops() - _stored_flush_dequeue_ops;
+
+  assert(_flush_queue->is_empty(), "flush queue not empty");
+  delete _flush_queue;
+  _flush_queue = NULL;
+
+  delete _metadata;
+  _metadata = NULL;
+
+  // all event tracing memory must be deallocated here
+  // NOTE: requires -XX:NativeMemoryTracking=summary or =detail
+  assert(MallocMemorySummary::as_snapshot()->by_type(mtEventTracing)->malloc_size() == 0, "memory leak");
+}
+
+void TraceManager::write_stats(TraceStatistics *stats) {
+  assert(!is_initialized() || _flush_queue != NULL, "sanity");
+
+  jlong flush_enqueue_ops, flush_dequeue_ops;
+  flush_enqueue_ops = is_initialized() ? _flush_queue->enqueue_ops() - _stored_flush_enqueue_ops : _stored_flush_enqueue_ops;
+  flush_dequeue_ops = is_initialized() ? _flush_queue->dequeue_ops() - _stored_flush_dequeue_ops : _stored_flush_dequeue_ops;
+  stats->add_entry("flush_enqueue_ops", flush_enqueue_ops);
+  stats->add_entry("flush_dequeue_ops", flush_dequeue_ops);
+
+  stats->add_entry("mean_buffer_capacity", EventTracingBufferCapacity);
+  stats->add_entry("reclaimed_trace_bytes", _reclaimed_trace_bytes);
+
+  if (EnableEventTracingDiagnostics) {
+    stats->add_entry("submitted_trace_bytes", _submitted_trace_bytes);
+    stats->add_entry("reclaim_time_nanos", _reclaim_time_nanos);
+    stats->add_entry("stack_cache_lookups", _metadata->stack_cache_lookups());
+    stats->add_entry("stack_cache_lookup_misses", _metadata->stack_cache_lookup_misses());
+    stats->add_entry("stack_cache_lookup_collisions", _metadata->stack_cache_lookup_collisions());
+    stats->add_entry("stack_cache_probes", _metadata->stack_cache_probes());
+    stats->add_entry("stack_cache_probe_collisions", _metadata->stack_cache_probe_collisions());
+    stats->add_entry("stack_cache_maintenance_millis", _metadata->stack_cache_maintenance_millis());
+    stats->add_entry("total_stack_traces", _total_stack_traces);
+    stats->add_entry("truncated_stack_traces", _truncated_stack_traces);
+    stats->add_entry("total_stack_frames", _total_stack_frames);
+    stats->add_entry("buffer_count", _buffer_count);
+    stats->add_entry("max_buffer_count", _max_buffer_count);
+    stats->add_entry("allocated_buffer_count", _allocated_buffer_count);
+  }
+}
+
+void TraceManager::reset_stats() {
+  assert(!is_initialized() || _flush_queue != NULL, "sanity");
+
+  if (is_initialized()) {
+    _stored_flush_enqueue_ops = _flush_queue->enqueue_ops();
+    _stored_flush_dequeue_ops = _flush_queue->dequeue_ops();
+  }
+
+  _reclaimed_trace_bytes = 0;
+
+  if (EnableEventTracingDiagnostics) {
+    _submitted_trace_bytes = 0;
+    _reclaim_time_nanos = 0;
+    _total_stack_traces = 0;
+    _truncated_stack_traces = 0;
+    _total_stack_frames = 0;
+    _max_buffer_count = _buffer_count;
+    _allocated_buffer_count = 0;
+
+    _metadata->reset_stack_cache_stats();
+  }
+}
diff --git a/src/share/vm/evtrace/traceManager.hpp b/src/share/vm/evtrace/traceManager.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceManager.hpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEMANAGER_HPP
+#define SHARE_VM_EVTRACE_TRACEMANAGER_HPP
+
+#include "runtime/handles.hpp"
+#include "utilities/exceptions.hpp"
+
+class TraceBuffer;
+class TraceBufferQueue;
+class TraceReaderThread;
+class TraceMetadata;
+class TraceLocker;
+
+class TraceStatistics {
+public:
+  virtual ~TraceStatistics() {}
+
+  virtual void add_entry(const char* name, jdouble value) = 0;
+};
+
+class TraceManager {
+  friend class TraceLocker;
+
+private:
+  static void assert_initialized();
+
+  static volatile bool      _is_initialized;
+  static volatile bool      _is_reclaiming;
+  static volatile intptr_t  _tracing_aux_threads;  // threads that are currently tracing, but are not on the threads list
+  static TraceReaderThread *_thread;
+  static volatile bool      _thread_running;
+  static Monitor           *_thread_mtx;
+  static TraceBufferQueue  *_flush_queue;
+  static TraceMetadata     *_metadata;
+  DEBUG_ONLY(static int     _current_safepoint_counter);
+
+  // statistics
+  static volatile intptr_t _buffer_count, _max_buffer_count, _allocated_buffer_count;
+  static volatile intptr_t _submitted_trace_bytes;
+  static intptr_t          _reclaimed_trace_bytes;
+  static volatile intptr_t _reclaim_time_nanos;
+  static intptr_t          _stored_flush_dequeue_ops, _stored_flush_enqueue_ops;
+  static volatile intptr_t _total_stack_traces, _truncated_stack_traces, _total_stack_frames;
+
+  static Handle create_queue_object(TraceBufferQueue *q, instanceKlassHandle klass, TRAPS);
+
+  static jlong do_reclaim_buffers_in_safepoint(bool deinit);
+  static void pre_submit_buffer(TraceBuffer *buffer);
+
+  static TraceBuffer *allocate_buffer();
+
+public:
+  class VM_ReclaimTraceBuffers;
+  class ReclaimTraceBuffersClosure;
+
+  static void initialize();
+  static bool is_initialized();
+  static bool is_reclaiming();
+  static void start_threads(TRAPS);
+  static void reclaim_buffers_in_safepoint(bool wait_until_processed);
+  static void finish_and_destroy(TRAPS);
+
+  static bool should_trace_native_monitor_events();
+
+  static TraceBuffer * request_buffer();
+  static void submit_buffer(TraceBuffer *buffer);
+  static void free_buffer(TraceBuffer *buffer);
+
+  static TraceMetadata * metadata();
+  static void reset_metadata();
+
+  static void do_work_before_safepoint_end();
+
+  static void thread_is_exiting(TraceReaderThread *t);
+
+  // statistics
+  static void write_stats(TraceStatistics *stats);
+  static void reset_stats();
+  static void update_stack_trace_stats(bool truncated, intptr_t total_frames);
+};
+
+#include "evtrace/traceManager.inline.hpp"
+
+#endif /* SHARE_VM_EVTRACE_TRACEMANAGER_HPP */
diff --git a/src/share/vm/evtrace/traceManager.inline.hpp b/src/share/vm/evtrace/traceManager.inline.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceManager.inline.hpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEMANAGER_INLINE_HPP
+#define SHARE_VM_EVTRACE_TRACEMANAGER_INLINE_HPP
+
+#include "evtrace/traceBuffer.hpp"
+#include "evtrace/traceBufferQueue.hpp"
+#include "evtrace/traceMetadata.hpp"
+
+inline bool TraceManager::is_initialized() {
+  assert(Thread::current()->is_VM_thread() || !SafepointSynchronize::is_at_safepoint() || Thread::current()->is_tracing_active(),
+         "requires a deinitialization-safe context");
+  return _is_initialized;
+}
+
+inline bool TraceManager::is_reclaiming() {
+  return _is_reclaiming;
+}
+
+inline void TraceManager::assert_initialized() {
+  assert(is_initialized(), "not initialized");
+}
+
+inline TraceBuffer *TraceManager::request_buffer() {
+  assert_initialized();
+
+  // tradeoff: small buffer sizes reduce the time it takes to process a
+  // buffer and make it available again, but cause more queue operations
+  // (and possible contentions). large buffers are wasteful for short-lived
+  // threads, but cause less queue contention and reclaim operations
+  // waiting for buffers to be processed can take significantly longer.
+  size_t capacity = EventTracingBufferCapacity;
+  if (EnableEventTracingRandomizedBufferCapacity) {
+    // use random buffer sizes to avoid that threads which do similar work
+    // submit and request buffers all at once
+    capacity = round_to(capacity / 2 + ((size_t) os::random()) % capacity, 128);
+  }
+  TraceBuffer *buffer = new (capacity) TraceBuffer(capacity);
+  if (EnableEventTracingDiagnostics) {
+    Atomic::inc_ptr(&_allocated_buffer_count);
+    jlong count = Atomic::add_ptr(1, &_buffer_count);
+    jlong max;
+    do {
+      max = _max_buffer_count;
+    } while (count > max && Atomic::cmpxchg(count, &_max_buffer_count, max) != max);
+  }
+  return buffer;
+}
+
+inline void TraceManager::free_buffer(TraceBuffer *buffer) {
+  // may be called after tracing has been de-initialized
+
+  assert(buffer != NULL, "sanity");
+  delete buffer;
+
+  if (EnableEventTracingDiagnostics && is_initialized()) {
+    Atomic::dec_ptr(&_buffer_count);
+  }
+}
+
+inline void TraceManager::pre_submit_buffer(TraceBuffer *buffer) {
+  assert(buffer != NULL, "no buffer given");
+  assert(buffer->owner_id == 0, "must not be set at this point");
+  buffer->owner_id = _metadata->thread_id(buffer->owner);
+  buffer->owner = NULL;
+}
+
+inline void TraceManager::submit_buffer(TraceBuffer *buffer) {
+  assert_initialized();
+  assert(buffer != NULL, "buffer is NULL");
+
+  TraceManager::pre_submit_buffer(buffer);
+  size_t bytes = buffer->filled_size();
+  _flush_queue->enqueue(buffer);
+
+  if (EnableEventTracingDiagnostics) {
+    Atomic::add(bytes, &_submitted_trace_bytes);
+  }
+}
+
+inline TraceMetadata *TraceManager::metadata() {
+  assert_initialized();
+  return _metadata;
+}
+
+inline void TraceManager::update_stack_trace_stats(bool truncated, intptr_t total_frames) {
+  assert_initialized();
+  if (EnableEventTracingDiagnostics) {
+    assert(total_frames >= 0, "sanity");
+    Atomic::inc_ptr(&_total_stack_traces);
+    if (truncated) {
+      Atomic::inc_ptr(&_truncated_stack_traces);
+    }
+    Atomic::add_ptr(total_frames, &_total_stack_frames);
+  }
+}
+
+inline bool TraceManager::should_trace_native_monitor_events() {
+  return EnableEventTracingNativeMonitorEvents;
+}
+
+#endif /* SHARE_VM_EVTRACE_TRACEMANAGER_INLINE_HPP */
diff --git a/src/share/vm/evtrace/traceMetadata.cpp b/src/share/vm/evtrace/traceMetadata.cpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceMetadata.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evtrace/traceMetadata.hpp"
+
+#include "runtime/spinLocker.hpp"
+#include "utilities/decoder.hpp"
+
+TraceMetadata::TraceMetadata() {
+  _stack_cache = new TraceStackCache(this);
+  _last_stack_id = 0;
+  _last_nativemonitor_id = 0;
+  _decoder_mtx = 0;
+  _decoder = NULL;
+}
+
+TraceMetadata::~TraceMetadata() {
+  delete _stack_cache;
+  if (_decoder != NULL) {
+    delete _decoder;
+  }
+}
+
+void TraceMetadata::resolve_native_C_pc(address pc, char *dest, size_t capacity) {
+  assert(dest != NULL && capacity > 0, "invalid destination buffer");
+
+  dest[0] = '\0'; // in case we can't write anything
+  stringStream st(dest, capacity);
+
+  // The following is adapted from frame::print_C_frame().
+  //
+  // print_C_frame() calls static methods of the Decoder class, which uses a
+  // shared decoder that is protected by a lock. Tracing contention for that
+  // lock causes deadlocks or infinite recursion, so our version has its own
+  // decoder which is protected by a simple spinlock.
+
+  char buffer[O_BUFLEN];
+  int offset;
+  bool found = false;
+
+  // The following is adapted from os::dll_address_to_function_name()
+  Dl_info dlinfo;
+  if (dladdr((void*)pc, &dlinfo) != 0) {
+    SpinLocker sl(&_decoder_mtx, "tracing decoder");
+
+    if (_decoder == NULL) {
+      _decoder = Decoder::create_decoder();
+    }
+    assert(_decoder != NULL && !_decoder->has_error(), "sanity");
+    if (_decoder != NULL) {
+      // see if we have a matching symbol
+      if (dlinfo.dli_saddr != NULL && dlinfo.dli_sname != NULL) {
+        if (!_decoder->demangle(dlinfo.dli_sname, buffer, sizeof(buffer))) {
+          jio_snprintf(buffer, sizeof(buffer), "%s", dlinfo.dli_sname);
+        }
+        offset = pc - (address)dlinfo.dli_saddr;
+        found = true;
+      } else if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) {
+        // no matching symbol so try for just file info
+        found = _decoder->decode((address) (pc - (address)dlinfo.dli_fbase), buffer, sizeof(buffer), &offset, dlinfo.dli_fname, true);
+      }
+      if (found) {
+        st.print("%s+0x%x", buffer, offset);
+      }
+    }
+  }
+
+  if (!found) {
+    if (os::dll_address_to_library_name(pc, buffer, sizeof(buffer), &offset)) {
+      // skip directory names
+      const char *p1, *p2;
+      p1 = buffer;
+      int len = (int)strlen(os::file_separator());
+      while ((p2 = strstr(p1, os::file_separator())) != NULL) {
+        p1 = p2 + len;
+      }
+      st.print("%s+0x%x", p1, offset);
+    } else {
+      st.print(PTR_FORMAT, p2i(pc));
+    }
+  }
+}
+
+void TraceMetadata::do_maintenance() {
+  assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "must be done in VM thread at safepoint");
+
+  _stack_cache->do_maintenance();
+}
diff --git a/src/share/vm/evtrace/traceMetadata.hpp b/src/share/vm/evtrace/traceMetadata.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceMetadata.hpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEMETADATA_HPP
+#define SHARE_VM_EVTRACE_TRACEMETADATA_HPP
+
+#include "evtrace/traceStack.hpp"
+#include "evtrace/traceTypes.hpp"
+
+#include "memory/allocation.hpp"
+
+class AbstractDecoder;
+
+class TraceMetadata: public CHeapObj<mtEventTracing> {
+private:
+  TraceStackCache *_stack_cache;
+  volatile intptr_t _last_stack_id;
+  volatile intptr_t _last_nativemonitor_id;
+
+  volatile int     _decoder_mtx;
+  AbstractDecoder *_decoder;
+
+public:
+  TraceMetadata();
+  virtual ~TraceMetadata();
+
+  static TraceTypes::timestamp         time_now();
+  static TraceTypes::thread_id         thread_id(Thread *t);
+  static TraceTypes::nativemonitor_id  nativemonitor_id(Monitor *m);
+
+  void resolve_native_C_pc(address pc, char *buffer, size_t capacity);
+
+  const CachedTraceStack *      get_or_try_add_stack(CompositeTraceStack &ts, bool &known, TraceTypes::stack_id preallocated_id = 0);
+  TraceTypes::stack_id          next_stack_id();
+  TraceTypes::nativemonitor_id  next_nativemonitor_id();
+
+  void  do_maintenance();
+
+  intptr_t stack_cache_lookups()            { return _stack_cache->lookups();            }
+  intptr_t stack_cache_lookup_misses()      { return _stack_cache->lookup_misses();      }
+  intptr_t stack_cache_lookup_collisions()  { return _stack_cache->lookup_collisions();  }
+  intptr_t stack_cache_probes()             { return _stack_cache->probes();             }
+  intptr_t stack_cache_probe_collisions()   { return _stack_cache->probe_collisions();   }
+  jlong    stack_cache_maintenance_millis() { return _stack_cache->maintenance_millis(); }
+  void     reset_stack_cache_stats()        { _stack_cache->reset_stats();               }
+};
+
+#include "evtrace/traceMetadata.inline.hpp"
+
+#endif /* SHARE_VM_EVTRACE_TRACEMETADATA_HPP */
diff --git a/src/share/vm/evtrace/traceMetadata.inline.hpp b/src/share/vm/evtrace/traceMetadata.inline.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceMetadata.inline.hpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEMETADATA_INLINE_HPP
+#define SHARE_VM_EVTRACE_TRACEMETADATA_INLINE_HPP
+
+#include "runtime/thread.hpp"
+#include "runtime/objectMonitor.hpp"
+#include "runtime/objectMonitor.inline.hpp"
+#include "jvmtifiles/jvmtiEnv.hpp"
+
+inline TraceTypes::timestamp TraceMetadata::time_now() {
+  return (TraceTypes::timestamp) os::javaTimeNanos();
+}
+
+inline TraceTypes::thread_id TraceMetadata::thread_id(Thread *t) {
+  assert(t != NULL, "thread is NULL");
+  return t->trace_thread_id();
+}
+
+inline TraceTypes::nativemonitor_id TraceMetadata::nativemonitor_id(Monitor *m) {
+  assert(m != NULL, "native monitor is NULL");
+  return m->trace_id();
+}
+
+inline TraceTypes::stack_id TraceMetadata::next_stack_id() {
+  assert(EnableEventTracingStackTraces, "stack traces not enabled");
+  return (TraceTypes::stack_id) Atomic::add_ptr(1, &_last_stack_id);
+}
+
+inline TraceTypes::nativemonitor_id TraceMetadata::next_nativemonitor_id() {
+  return (TraceTypes::nativemonitor_id) Atomic::add_ptr(1, &_last_nativemonitor_id);
+}
+
+inline const CachedTraceStack * TraceMetadata::get_or_try_add_stack(CompositeTraceStack &ts, bool &known, TraceTypes::stack_id preallocated_id) {
+  return _stack_cache->get_or_try_add(ts, known, preallocated_id);
+}
+
+#endif /* SHARE_VM_EVTRACE_TRACEMETADATA_INLINE_HPP */
diff --git a/src/share/vm/evtrace/traceReaderThread.cpp b/src/share/vm/evtrace/traceReaderThread.cpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceReaderThread.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evtrace/traceReaderThread.hpp"
+
+#include "evtrace/traceManager.hpp"
+
+#include "runtime/javaCalls.hpp"
+
+TraceReaderThread::TraceReaderThread(oop obj)
+: JavaThread(trace_reader_thread_entry),
+  _object(obj),
+  _is_polling_queue(false)
+{
+}
+
+TraceReaderThread::~TraceReaderThread() {
+  TraceManager::thread_is_exiting(this);
+}
+
+void TraceReaderThread::trace_reader_thread_entry(JavaThread* thread, TRAPS) {
+  TraceReaderThread *self = (TraceReaderThread *) thread;
+  JavaValue result(T_VOID);
+  Klass* klass = SystemDictionary::resolve_or_fail(vmSymbols::sun_evtracing_TraceReaderThread(), true, CHECK);
+  JavaCalls::call_virtual(&result,
+                          self->_object,
+                          klass,
+                          vmSymbols::run_method_name(),
+                          vmSymbols::void_method_signature(),
+                          CHECK);
+}
+
+TraceReaderThread* TraceReaderThread::start(Handle flushq, Handle freeq, TRAPS) {
+  Klass* rt_klass = SystemDictionary::resolve_or_fail(vmSymbols::sun_evtracing_TraceReaderThread(), true, CHECK_NULL);
+  instanceKlassHandle rt_khandle(THREAD, rt_klass);
+  rt_khandle->initialize(CHECK_NULL);
+  instanceHandle rt_obj = rt_khandle->allocate_instance_handle(CHECK_NULL);
+
+  JavaValue result(T_VOID);
+  JavaCalls::call_special(&result,
+                          rt_obj,
+                          rt_khandle,
+                          vmSymbols::object_initializer_name(),
+                          vmSymbols::TraceReaderThread_constructor_signature(),
+                          flushq,
+                          freeq,
+                          CHECK_NULL);
+
+  Klass* t_klass = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_Thread(), true, CHECK_NULL);
+  instanceKlassHandle t_khandle(THREAD, t_klass);
+  instanceHandle t_obj = t_khandle->allocate_instance_handle(CHECK_NULL);
+
+  const char thread_name[] = "Trace Reader Thread";
+  Handle string = java_lang_String::create_from_str(thread_name, CHECK_NULL);
+
+  // Initialize thread_oop to put it into the system threadGroup
+  Handle thread_group(THREAD, Universe::system_thread_group());
+  JavaCalls::call_special(&result,
+                          t_obj,
+                          t_khandle,
+                          vmSymbols::object_initializer_name(),
+                          vmSymbols::threadgroup_string_void_signature(),
+                          thread_group,
+                          string,
+                          CHECK_NULL);
+
+  TraceReaderThread *trt = NULL;
+  {
+    MutexLocker mu(Threads_lock);
+    trt = new TraceReaderThread(rt_obj());
+
+    if (trt == NULL || trt->osthread() == NULL) {
+      vm_exit_during_initialization("java.lang.OutOfMemoryError", "unable to create new native thread");
+    }
+    java_lang_Thread::set_thread(t_obj(), trt);
+    java_lang_Thread::set_daemon(t_obj());
+
+    trt->set_threadObj(t_obj());
+    Threads::add(trt);
+    Thread::start(trt);
+  }
+  return trt;
+}
+
+void TraceReaderThread::shutdown(TRAPS) {
+  Klass* rt_klass = SystemDictionary::resolve_or_fail(vmSymbols::sun_evtracing_TraceReaderThread(), true, CHECK);
+  instanceKlassHandle rt_khandle(THREAD, rt_klass);
+
+  JavaValue result(T_VOID);
+  JavaCalls::call_virtual(&result,
+                          Handle(_object),
+                          rt_khandle,
+                          vmSymbols::shutdown_method_name(),
+                          vmSymbols::void_method_signature(),
+                          CHECK);
+}
+
+void TraceReaderThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
+  JavaThread::oops_do(f, cf);
+
+  f->do_oop(&_object);
+}
diff --git a/src/share/vm/evtrace/traceReaderThread.hpp b/src/share/vm/evtrace/traceReaderThread.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceReaderThread.hpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEREADERTHREAD_HPP
+#define SHARE_VM_EVTRACE_TRACEREADERTHREAD_HPP
+
+#include "runtime/thread.hpp"
+
+class TraceReaderThread: public JavaThread {
+private:
+  oop _object;
+  volatile bool _is_polling_queue;
+
+  TraceReaderThread(oop obj);
+
+  static void trace_reader_thread_entry(JavaThread* thread, TRAPS);
+
+public:
+  virtual ~TraceReaderThread();
+
+  virtual bool is_TraceReader_thread() const { return true; }
+
+  virtual void oops_do(OopClosure* f, CodeBlobClosure* cf);
+
+  static TraceReaderThread * start(Handle flushq, Handle freeq, TRAPS);
+
+  bool is_polling_queue() { return _is_polling_queue; }
+  void set_is_polling_queue(bool polling) {
+    assert(_is_polling_queue != polling, "flip only");
+    _is_polling_queue = polling;
+  }
+
+  void shutdown(TRAPS);
+};
+
+#endif /* SHARE_VM_EVTRACE_TRACEREADERTHREAD_HPP */
diff --git a/src/share/vm/evtrace/traceStack.cpp b/src/share/vm/evtrace/traceStack.cpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceStack.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evtrace/traceStack.hpp"
+
+#include "evtrace/traceEvents.hpp"
+#include "evtrace/traceMetadata.hpp"
+
+#include "runtime/vframe.hpp"
+#include "runtime/init.hpp"
+
+//
+// TraceStackBuilder
+//
+
+void TraceStackBuilder::add(const TraceStackFrame &f) {
+  assert(!is_full(), "already full");
+  _frames[_count] = f;
+  _hash ^= f.hash();
+  _count++;
+}
+
+void TraceStackBuilder::add_frame(const frame *fr) {
+  TraceStackFrame f;
+
+  assert(fr->cb() == NULL, "sanity");
+  f.kind = TraceStackFrame::frame_native_C;
+  f.native_C.pc = fr->pc();
+
+  add(f);
+}
+
+bool TraceStackBuilder::range_equals(size_t offset, const CachedTraceStack *cts, size_t cts_offset, size_t count) const {
+  for (size_t i = 0; i < count; i++) {
+    if (!frame_at(offset + i)->equals(*cts->frame_at(cts_offset + i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+//
+// CachedTraceStack
+//
+
+CachedTraceStack *CachedTraceStack::create(TraceTypes::stack_id id, const CompositeTraceStack &ts) {
+  assert (in_bytes(byte_offset_of(CachedTraceStack, _frames)) == sizeof(CachedTraceStack),
+      "variable-sized frame array must be last field");
+  return new (ts.count()) CachedTraceStack(id, ts);
+}
+
+void* CachedTraceStack::operator new(size_t size, size_t nframes) throw () {
+  return CHeapObj<mtEventTracing>::operator new(size + sizeof(_frames[0]) * nframes, CALLER_PC);
+}
+
+void CachedTraceStack::operator delete(void* p) {
+  CHeapObj<mtEventTracing>::operator delete(p);
+}
+
+CachedTraceStack::CachedTraceStack(TraceTypes::stack_id id, const CompositeTraceStack& ts)
+  : _id(id),
+    _count(ts.count()),
+    _hash(ts.hash()),
+    _truncated(ts.is_truncated()),
+    _valid(true)
+{
+  for (size_t i = 0; i < ts.count(); i++) {
+    _frames[i] = *ts.frame_at(i);
+  }
+}
+
+bool CachedTraceStack::range_equals(size_t offset, const CachedTraceStack *other, size_t other_offset, size_t count) const {
+  for (size_t i = 0; i < count; i++) {
+    if (!frame_at(offset + i)->equals(*other->frame_at(other_offset + i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+//
+// CompositeTraceStack
+//
+
+void CompositeTraceStack::set_bottom(const CachedTraceStack *bottom, size_t offset) {
+  _hash = _top.hash();
+  _count = _top.count();
+  _truncated = _top.is_truncated();
+
+  _bottom = bottom;
+  _bottom_offset = offset;
+  if (bottom != NULL) {
+    if (_top.count() < EventTracingStackDepthLimit) {
+      assert(!_top.is_truncated(), "missing frames between top and bottom");
+      _count += bottom->count() - offset;
+      _truncated = _truncated || bottom->is_truncated();
+      if (_count > EventTracingStackDepthLimit) {
+        _truncated = true;
+        _count = EventTracingStackDepthLimit;
+      }
+      _hash ^= bottom->hash();
+      // drop frames before offset from hash
+      for (size_t i = 0; i < offset; i++) {
+        _hash ^= bottom->frame_at(i)->hash();
+      }
+      // drop truncated frames from hash
+      for (size_t i = EventTracingStackDepthLimit - _top.count() + offset; i < _bottom->count(); i++) {
+        _hash ^= bottom->frame_at(i)->hash();
+      }
+    } else {
+      _truncated = _truncated || (bottom->count() > offset);
+    }
+  }
+
+#ifdef ASSERT
+  intptr_t h = 0;
+  for (size_t i = 0; i < count(); i++) {
+    h ^= frame_at(i)->hash();
+  }
+  assert(h == hash(), "hash mismatch");
+#endif
+}
+
+bool CompositeTraceStack::equals(const CachedTraceStack* cts) const {
+  if (hash() != cts->hash() || count() != cts->count() || is_truncated() != cts->is_truncated()) {
+    return false;
+  }
+  return _top.range_equals(0, cts, 0, _top.count())
+      && (_bottom == NULL || _bottom->range_equals(_bottom_offset, cts, _top.count(), count() - _top.count()));
+}
+
+bool CompositeTraceStack::equals(const CompositeTraceStack &other) const {
+  if (hash() != other.hash() || count() != other.count() || is_truncated() != other.is_truncated()) {
+    return false;
+  }
+  for (size_t i = 0; i < count(); i++) {
+    if (!frame_at(i)->equals(*other.frame_at(i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+//
+// TraceStackCache
+//
+
+TraceStackCache::TraceStackCache(TraceMetadata *tm)
+{
+  _metadata = tm;
+
+  _table = NULL;
+  _count = 0;
+
+  _lookup_counter = _lookup_miss_counter = _lookup_collision_counter = _probe_counter = _probe_collision_counter = 0;
+
+  _size = (1 << 12);
+  assert(is_power_of_2(_size), "sanity");
+  _table = (CachedTraceStack **) os::malloc(_size * sizeof(_table[0]), mtEventTracing, CURRENT_PC);
+  memset(_table, 0, _size * sizeof(_table[0]));
+}
+
+TraceStackCache::~TraceStackCache() {
+  for (size_t i = 0; i < _size; i++) {
+    CachedTraceStack *p = _table[i];
+    while (p != NULL) {
+      CachedTraceStack *next = p->cache_next();
+      delete p;
+      p = next;
+    }
+  }
+  os::free(_table);
+}
+
+void TraceStackCache::add_for_rehash(CachedTraceStack *cts) {
+  const size_t mask = (_size - 1);
+  intptr_t index = cts->hash() & mask;
+  cts->set_cache_next(_table[index]);
+  _table[index] = cts;
+  _count++;
+}
+
+inline void TraceStackCache::update_counters_after_lookup(bool present, jlong probes, jlong collisions) {
+  if (EnableEventTracingDiagnostics) {
+    Atomic::inc_ptr(&_lookup_counter);
+    Atomic::add_ptr(probes, &_probe_counter);
+    if (!present) {
+      Atomic::inc_ptr(&_lookup_miss_counter);
+    }
+    if (collisions > 0) {
+      Atomic::inc_ptr(&_lookup_collision_counter);
+      Atomic::add_ptr(collisions, &_probe_collision_counter);
+    }
+  }
+}
+
+const CachedTraceStack * TraceStackCache::get_or_try_add(const CompositeTraceStack &ts, bool &known, TraceTypes::stack_id preallocated_id) {
+  jlong probes = 0, collisions = 0;
+
+  CachedTraceStack *created = NULL;
+
+  const size_t mask = (_size - 1);
+  const size_t index = ts.hash() & mask;
+  // XXX: probably need barriers here on non-x86
+  for(;;) {
+    CachedTraceStack *head = _table[index];
+    if (head == NULL) {
+      probes++;
+    }
+    CachedTraceStack *p = head;
+    while (p != NULL) {
+      probes++;
+      if (ts.hash() == p->hash()) {
+        if (ts.equals(p)) {
+          delete created;
+          known = true;
+          update_counters_after_lookup(true, probes, collisions);
+          return p;
+        } else {
+          collisions++;
+        }
+      }
+      p = p->cache_next();
+    }
+    // not found
+    if (created == NULL) {
+      TraceTypes::stack_id id = preallocated_id;
+      if (id == 0) {
+        id = _metadata->next_stack_id();
+      }
+      created = CachedTraceStack::create(id, ts);
+    }
+    created->set_cache_next(head);
+    if (Atomic::cmpxchg_ptr(created, &_table[index], head) == head) {
+      Atomic::inc_ptr(&_count);
+      known = false;
+      update_counters_after_lookup(false, probes, collisions);
+      return created;
+    }
+    // head of collision chain changed: walk the entire chain again in the next
+    // next iteration to check whether the stack trace has been inserted by
+    // another thread (head is not enough, multiple threads may have inserted)
+  }
+}
+
+void TraceStackCache::do_maintenance() {
+  assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread()
+         && TraceManager::is_reclaiming(), "must be done in a safe state");
+
+  bool should_grow = (_count / (float) _size > 0.7f);
+  if (should_grow) {
+    if (EnableEventTracingDiagnostics) {
+      _maintenance_timer.start();
+    }
+
+    CachedTraceStack **old_table = _table;
+    size_t old_capacity = _size;
+
+    if (should_grow) {
+      _size <<= 1;
+      assert(is_power_of_2(_size), "sanity");
+    }
+    _count = 0;
+    _table = (CachedTraceStack **) os::malloc(_size * sizeof(_table[0]), mtEventTracing, CURRENT_PC);
+    memset(_table, 0, _size * sizeof(_table[0]));
+    for (size_t i = 0; i < old_capacity; i++) {
+      CachedTraceStack *p = old_table[i];
+      while (p != NULL) {
+        CachedTraceStack *next = p->cache_next();
+        if (p->is_valid()) {
+          add_for_rehash(p);
+        } else {
+          delete p;
+        }
+        p = next;
+      }
+    }
+    os::free(old_table);
+
+    if (EnableEventTracingDiagnostics) {
+      _maintenance_timer.stop();
+    }
+  }
+}
+
+void TraceStackCache::reset_stats() {
+  _lookup_counter = _lookup_miss_counter = _lookup_collision_counter = _probe_counter = _probe_collision_counter = 0;
+
+  // these are only used in a safepoint: we should be fine either way
+  _maintenance_timer.reset();
+}
diff --git a/src/share/vm/evtrace/traceStack.hpp b/src/share/vm/evtrace/traceStack.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceStack.hpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACESTACK_HPP
+#define SHARE_VM_EVTRACE_TRACESTACK_HPP
+
+#include "evtrace/traceTypes.hpp"
+
+#include "memory/allocation.hpp"
+#include "runtime/timer.hpp"
+
+class CachedTraceStack;
+class TraceMetadata;
+
+#define TRACE_STACK_MAX_FRAMES 128
+
+class CompositeTraceStack;
+
+struct TraceStackFrame {
+  enum {
+    frame_native_C,
+  } kind;
+
+  union {
+    struct {
+      address pc;
+    } native_C;
+  };
+
+  intptr_t hash() const;
+  bool equals(const TraceStackFrame& other) const;
+};
+
+class TraceStackBuilder: StackObj {
+private:
+  TraceStackFrame _frames[TRACE_STACK_MAX_FRAMES];
+  size_t   _count;
+  bool     _truncated;
+  intptr_t _hash;
+
+  void add(const TraceStackFrame &f);
+
+public:
+  TraceStackBuilder();
+
+  void add_frame(const frame *fr);
+  void set_truncated() { _truncated = true; }
+
+  const TraceStackFrame *frame_at(size_t index) const;
+
+  size_t   count() const        { return _count;     }
+  bool     is_full() const      { return (_count == EventTracingStackDepthLimit); }
+  bool     is_truncated() const { return _truncated; }
+  intptr_t hash() const         { return _hash;      }
+
+  bool     range_equals(size_t offset, const CachedTraceStack *cts, size_t cts_offset, size_t count) const;
+};
+
+class CachedTraceStack: CHeapObj<mtEventTracing> {
+public:
+  static CachedTraceStack *create(TraceTypes::stack_id id, const CompositeTraceStack &ts);
+
+  const TraceStackFrame *frame_at(size_t index) const;
+
+  TraceTypes::stack_id  id() const { return _id;        }
+  size_t   count() const           { return _count;     }
+  bool     is_truncated() const    { return _truncated; }
+  intptr_t hash() const            { return _hash;      }
+
+  bool     range_equals(size_t offset, const CachedTraceStack *other, size_t other_offset, size_t count) const;
+
+  bool     is_valid() const { return _valid; }
+  void     invalidate();
+
+  CachedTraceStack *cache_next();
+  void     set_cache_next(CachedTraceStack *next);
+
+  void operator delete(void* p);
+
+private:
+  void* operator new(size_t size, size_t nframes) throw ();
+  CachedTraceStack(TraceTypes::stack_id id, const CompositeTraceStack &ts);
+
+  CachedTraceStack * volatile _cache_next;
+
+  const TraceTypes::stack_id  _id;
+  const intptr_t              _hash;
+  const size_t                _count;
+  const bool                  _truncated;
+  bool                        _valid;
+  TraceStackFrame             _frames[0];
+};
+
+class CompositeTraceStack: StackObj {
+private:
+  const TraceStackBuilder &_top;
+  const CachedTraceStack  *_bottom;
+  size_t    _bottom_offset;
+  intptr_t  _hash;
+  size_t    _count;
+  bool      _truncated;
+
+public:
+  CompositeTraceStack(TraceStackBuilder &top);
+
+  void set_bottom(const CachedTraceStack *cts, size_t offset);
+
+  bool equals(const CachedTraceStack *cts) const;
+  bool equals(const CompositeTraceStack &other) const;
+
+  const TraceStackFrame *frame_at(size_t index) const;
+
+  intptr_t hash() const         { return _hash;      }
+  size_t   count() const        { return _count;     }
+  bool     is_truncated() const { return _truncated; }
+};
+
+class TraceStackVframeIterator: StackObj {
+private:
+  const CompositeTraceStack &_ts;
+  int      _index;
+  address  _native_C_pc;
+
+public:
+  TraceStackVframeIterator(const CompositeTraceStack &ts);
+
+  bool has_next();
+  void next();
+  void reset();
+
+  bool    is_Java_frame() const { return (_native_C_pc == NULL); }
+  address native_C_pc() const   { return _native_C_pc; }
+};
+
+class TraceStackCache: public CHeapObj<mtEventTracing> {
+private:
+  TraceMetadata *_metadata;
+
+  CachedTraceStack **_table;
+  volatile intptr_t  _count;
+  size_t             _size;
+
+  // statistics
+  elapsedTimer   _maintenance_timer;
+  volatile intptr_t _lookup_counter;
+  volatile intptr_t _lookup_miss_counter;
+  volatile intptr_t _lookup_collision_counter;
+  volatile intptr_t _probe_counter;
+  volatile intptr_t _probe_collision_counter;
+
+  void add_for_rehash(CachedTraceStack *cts);
+
+  void update_counters_after_lookup(bool present, jlong probes, jlong collisions);
+
+public:
+  TraceStackCache(TraceMetadata *tm);
+  virtual ~TraceStackCache();
+
+  const CachedTraceStack * get_or_try_add(const CompositeTraceStack &ts, bool &known, TraceTypes::stack_id preallocated_id = 0);
+
+  void do_maintenance();
+
+  intptr_t lookups()            { return _lookup_counter;                   }
+  intptr_t lookup_misses()      { return _lookup_miss_counter;              }
+  intptr_t lookup_collisions()  { return _lookup_collision_counter;         }
+  intptr_t probes()             { return _probe_counter;                    }
+  intptr_t probe_collisions()   { return _probe_collision_counter;          }
+  jlong    maintenance_millis() { return _maintenance_timer.milliseconds(); }
+  void     reset_stats();
+};
+
+#include "evtrace/traceStack.inline.hpp"
+
+#endif /* SHARE_VM_EVTRACE_TRACESTACK_HPP */
diff --git a/src/share/vm/evtrace/traceStack.inline.hpp b/src/share/vm/evtrace/traceStack.inline.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceStack.inline.hpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACESTACK_INLINE_HPP
+#define SHARE_VM_EVTRACE_TRACESTACK_INLINE_HPP
+
+#include "runtime/vframe.hpp"
+#include "services/memTracker.hpp"
+
+//
+// TraceStackFrame
+//
+
+inline intptr_t TraceStackFrame::hash() const {
+  assert(kind == frame_native_C, "sanity");
+  return 31 * (intptr_t) native_C.pc + (intptr_t) kind;
+}
+
+inline bool TraceStackFrame::equals(const TraceStackFrame &other) const {
+  assert(kind == frame_native_C, "sanity");
+  return (native_C.pc == other.native_C.pc);
+}
+
+//
+// TraceStackBuilder
+//
+
+inline TraceStackBuilder::TraceStackBuilder() {
+  assert(EventTracingStackDepthLimit <= TRACE_STACK_MAX_FRAMES, "stack depth limit too high");
+  _hash  = 0;
+  _count = 0;
+  _truncated = false;
+}
+
+inline const TraceStackFrame* TraceStackBuilder::frame_at(size_t index) const {
+  assert(index < _count, "range");
+  return &_frames[index];
+}
+
+//
+// CachedTraceStack
+//
+
+inline const TraceStackFrame *CachedTraceStack::frame_at(size_t index) const {
+  assert(index < _count, "range");
+  return &_frames[index];
+}
+
+inline void CachedTraceStack::invalidate() {
+  assert(_valid, "only once");
+  _valid = false;
+}
+
+inline CachedTraceStack *CachedTraceStack::cache_next() {
+  return _cache_next;
+}
+
+inline void CachedTraceStack::set_cache_next(CachedTraceStack *next) {
+  _cache_next = next;
+}
+
+//
+// CompositeTraceStack
+//
+
+inline CompositeTraceStack::CompositeTraceStack(TraceStackBuilder& top)
+: _top(top)
+{
+  set_bottom(NULL, 0);
+}
+
+inline const TraceStackFrame *CompositeTraceStack::frame_at(size_t index) const {
+  assert (index < count(), "range");
+  if (index < _top.count()) {
+    return _top.frame_at(index);
+  }
+  return _bottom->frame_at(index - _top.count() + _bottom_offset);
+}
+
+//
+// TraceStackVframeIterator
+//
+
+inline TraceStackVframeIterator::TraceStackVframeIterator(const CompositeTraceStack& ts)
+: _ts(ts)
+{
+  reset();
+}
+
+inline bool TraceStackVframeIterator::has_next() {
+  return (_index + 1 < (int)_ts.count());
+}
+
+inline void TraceStackVframeIterator::next() {
+  assert(has_next(), "at end");
+
+  _index++;
+  const TraceStackFrame *frame = _ts.frame_at(_index);
+  assert(frame->kind == TraceStackFrame::frame_native_C, "sanity");
+  _native_C_pc = frame->native_C.pc;
+}
+
+inline void TraceStackVframeIterator::reset() {
+  _index = -1;
+  _native_C_pc = NULL;
+}
+
+#endif /* SHARE_VM_EVTRACE_TRACESTACK_INLINE_HPP */
diff --git a/src/share/vm/evtrace/traceTypes.hpp b/src/share/vm/evtrace/traceTypes.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceTypes.hpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACETYPES_HPP
+#define SHARE_VM_EVTRACE_TRACETYPES_HPP
+
+#include "utilities/globalDefinitions.hpp"
+
+#define TRACE_TYPES_DO(fun)         \
+          fun(u1, event_type      ) \
+          fun(s8, timestamp       ) \
+          fun(s8, seq_num         ) \
+          fun(s8, thread_id       ) \
+          fun(s8, stack_id        ) \
+          fun(s8, nativemonitor_id) \
+          fun(s8, native_address  ) \
+
+// Scalar trace stream types
+class TraceTypes {
+public:
+
+#define TRACE_TYPE_DECLARE(primitive, name) \
+  typedef primitive name;
+TRACE_TYPES_DO(TRACE_TYPE_DECLARE)
+#undef TRACE_TYPE_DECLARE
+
+  enum monitor_enter_wait {
+    _monitor_enter_wait_min = -1,
+    enter_no_wait,
+    enter_after_wait_notify,
+    enter_after_wait_timeout,
+    enter_after_wait_other, // interrupt or spurious
+    _monitor_enter_wait_max,
+  };
+
+  enum monitor_entered_flags {
+    _monitor_entered_flags_min = -1,
+    entered_flags_none = 0,
+    entered_queued = (1 << 0),
+    entered_parked = (1 << 1),
+    _monitor_entered_flags_max,
+  };
+
+  enum stack_frame_kind {
+    _stack_frame_kind_min = -1,
+    stack_frame_kind_java,
+    stack_frame_kind_native,
+    _stack_frame_kind_max,
+  };
+
+  enum event {
+    _event_min = -1,
+    event_thread_start,
+    event_thread_name_change,
+    event_thread_state_change,
+    event_thread_interrupt,
+    event_thread_exit,
+    event_thread_park_begin,
+    event_thread_park_end,
+    event_thread_unpark,
+    event_monitor_inflate,
+    event_monitor_deflate,
+    event_monitor_contended_enter,
+    event_monitor_contended_entered,
+    event_monitor_contended_exited,
+    event_monitor_dummy,
+    event_class_metadata,
+    event_method_metadata,
+    event_stack_metadata,
+    event_identical_stacks_metadata,
+    event_class_loader_unload,
+    event_safepoint_begin,
+    event_safepoint_end,
+    event_vm_end,
+    event_metadata_reset,
+    event_group,
+    event_native_monitor_info,
+    event_native_monitor_contended_lock,
+    event_native_monitor_contended_locked,
+    event_native_monitor_contended_unlocked,
+    event_native_monitor_destroy,
+    event_native_monitor_dummy,
+    event_stack_metadata_extended,
+    event_native_symbol_metadata,
+    event_marker,
+    _event_max,
+  };
+
+protected:
+  TraceTypes() { }
+};
+
+#endif /* SHARE_VM_EVTRACE_TRACETYPES_HPP */
diff --git a/src/share/vm/evtrace/traceWriter.hpp b/src/share/vm/evtrace/traceWriter.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceWriter.hpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEWRITER_H
+#define SHARE_VM_EVTRACE_TRACEWRITER_H
+
+#include "memory/allocation.hpp"
+#include "gc/shared/gcLocker.hpp"
+
+// prevents interference (buffer reclaims, etc.) while writing events
+class TraceLocker: public StackObj {
+private:
+  bool _thread_terminating;
+  NoSafepointVerifier _nsv;
+
+public:
+  TraceLocker();
+  ~TraceLocker();
+};
+
+class TraceWriterBase: public StackObj {
+private:
+  u1  *_writer_top;
+
+  void assert_reserved(size_t nbytes);
+  void reserve(size_t nbytes);
+
+protected:
+  TraceWriterBase(size_t nbytes);
+  ~TraceWriterBase();
+
+public:
+  void put_u1(u1 v);
+  void put_u2(u2 v);
+  void put_u4(u4 v);
+  void put_u8(u8 v);
+  void put_s4(s4 v);
+  void put_s8(s8 v);
+  void put_data(void *data, size_t length);
+  void put_utf8str(const char *s, size_t bytes);
+  static size_t nbytes_for_utf8str(const char *s, size_t maxbytes);
+};
+
+class TraceWriter: public TraceWriterBase {
+public:
+  TraceWriter(size_t nbytes) : TraceWriterBase(nbytes) { }
+
+#define TRACE_TYPE_DEFINE_PUT_METHOD(primitive, name) \
+  void put_##name(TraceTypes::name val) { put_##primitive(val); }
+TRACE_TYPES_DO(TRACE_TYPE_DEFINE_PUT_METHOD)
+#undef TRACE_TYPE_DEFINE_PUT_METHOD
+};
+
+#include "evtrace/traceWriter.inline.hpp"
+
+#endif /* SHARE_VM_EVTRACE_TRACEWRITER_H */
diff --git a/src/share/vm/evtrace/traceWriter.inline.hpp b/src/share/vm/evtrace/traceWriter.inline.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/evtrace/traceWriter.inline.hpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_EVTRACE_TRACEWRITER_INLINE_HPP
+#define SHARE_VM_EVTRACE_TRACEWRITER_INLINE_HPP
+
+#include "evtrace/traceBuffer.hpp"
+#include "evtrace/traceManager.hpp"
+
+//
+// TraceLocker
+//
+
+inline TraceLocker::TraceLocker()
+: _nsv(false, false)
+{
+  assert(!Thread::current()->is_tracing_active(), "tracing must not be active");
+
+  // the VM thread cannot synchronize with terminating threads when they are
+  // no longer on the threads list, so we use a separate counter to indicate
+  // that threads are tracing. we still set the thread's "trace active" flag
+  // because it's used in various assertions.
+  _thread_terminating = (Thread::current()->is_Java_thread() && JavaThread::current()->is_terminated());
+
+  // NOTE: this code must work even when tracing is not currently initialized
+
+  bool repeat;
+  do {
+    Thread::current()->toggle_tracing_active();
+    OrderAccess::storeload();
+    if (_thread_terminating) {
+      Atomic::inc_ptr(&TraceManager::_tracing_aux_threads);
+    }
+
+    repeat = false;
+    if (TraceManager::is_reclaiming()) {
+      // unset and wait for the reclamation to finish
+      Thread::current()->toggle_tracing_active();
+      if (_thread_terminating) {
+        Atomic::dec_ptr(&TraceManager::_tracing_aux_threads);
+      }
+
+      int counter = 0;
+      while (TraceManager::is_reclaiming()) {
+        counter++;
+        if ((counter & 0xfff) == 0) {
+          os::naked_yield();
+        } else {
+          SpinPause();
+        }
+      }
+      repeat = true;
+    }
+  } while (repeat);
+
+  assert(Thread::current()->is_tracing_active(), "invariant");
+
+  if (!_thread_terminating && Thread::current()->is_Java_thread()) {
+    // avoid deadlocks: do not allow tracing in Java threads
+    // to remain active during a safepoint
+    _nsv.enable();
+  }
+}
+
+inline TraceLocker::~TraceLocker() {
+  assert(Thread::current()->is_tracing_active(), "tracing must still be active");
+  assert(_thread_terminating == (Thread::current()->is_Java_thread() && JavaThread::current()->is_terminated()), "sanity");
+
+  OrderAccess::storestore();
+  Thread::current()->toggle_tracing_active();
+
+  if (_thread_terminating) {
+    Atomic::dec_ptr(&TraceManager::_tracing_aux_threads);
+  }
+}
+
+//
+// TraceWriterBase
+//
+
+inline TraceWriterBase::TraceWriterBase(size_t nbytes)
+: _writer_top(NULL)
+{
+  assert(Thread::current()->is_tracing_active(), "tracing must be active");
+
+  reserve(nbytes);
+}
+
+inline TraceWriterBase::~TraceWriterBase() {
+  assert(Thread::current()->is_tracing_active(), "tracing must (still) be active");
+  assert(_writer_top == NULL || Thread::current()->trace_buffer() == NULL
+      || _writer_top == Thread::current()->trace_buffer()->top,
+      "must have used up reserved space");
+}
+
+inline void TraceWriterBase::assert_reserved(size_t nbytes) {
+  assert(_writer_top != NULL, "no space reserved");
+  assert(Thread::current()->trace_buffer() != NULL, "thread has no trace buffer");
+  assert(_writer_top + nbytes <= Thread::current()->trace_buffer()->top, "not enough space reserved");
+}
+
+inline void TraceWriterBase::reserve(size_t nbytes) {
+  if (!TraceManager::is_initialized()) {
+    assert(_writer_top == NULL, "have pointer into buffer, but tracing is not initialized");
+    return;
+  }
+
+  assert(_writer_top == NULL || Thread::current()->trace_buffer() == NULL
+      || _writer_top == Thread::current()->trace_buffer()->top,
+      "must finish writing before reserving more space");
+
+  // NOTE: our No_Safepoint_Verifier ensures that no safepoint can happen
+  // between submitting/requesting a buffer and setting it as the thread's
+  // trace buffer
+
+  TraceBuffer *buf = Thread::current()->trace_buffer();
+  if (buf != NULL) {
+    _writer_top = buf->top;
+    if (!buf->reserve(nbytes)) {
+      // does not fit
+      _writer_top = NULL;
+      TraceManager::submit_buffer(Thread::current()->trace_buffer());
+      Thread::current()->set_trace_buffer(NULL);
+      buf = NULL;
+    }
+  }
+  if (buf == NULL && TraceManager::is_initialized()) {
+    buf = TraceManager::request_buffer();
+    buf->owner = Thread::current();
+    _writer_top = buf->top;
+    guarantee(buf->reserve(nbytes), "newly requested buffer does not fit the event");
+    Thread::current()->set_trace_buffer(buf);
+  }
+}
+
+inline void TraceWriterBase::put_u1(u1 v) {
+  assert_reserved(1);
+  *_writer_top++ = v;
+}
+
+inline void TraceWriterBase::put_u2(u2 v) {
+  assert_reserved(2);
+  *(u2 *) _writer_top = v;
+  _writer_top += 2;
+}
+
+inline void TraceWriterBase::put_u4(u4 v) {
+  assert_reserved(4);
+  *(u4 *) _writer_top = v;
+  _writer_top += 4;
+}
+
+inline void TraceWriterBase::put_u8(u8 v) {
+  assert_reserved(8);
+  *(u8 *) _writer_top = v;
+  _writer_top += 8;
+}
+
+inline void TraceWriterBase::put_s4(s4 v) {
+  assert_reserved(4);
+  *(s4 *) _writer_top = v;
+  _writer_top += 4;
+}
+
+inline void TraceWriterBase::put_s8(s8 v) {
+  assert_reserved(8);
+  *(s8 *) _writer_top = v;
+  _writer_top += 8;
+}
+
+inline void TraceWriterBase::put_data(void *data, size_t length) {
+  assert_reserved(length);
+  memcpy(_writer_top, data, length);
+  _writer_top += length;
+}
+
+inline void TraceWriterBase::put_utf8str(const char *s, size_t bytes) {
+  assert_reserved(bytes);
+
+  assert(bytes > 2, "invalid length");
+  bytes -= 2;
+  assert(bytes == (size_t) ((s2) bytes), "string length must fit in u2");
+  *(s2 *) _writer_top = (s2) bytes;
+  _writer_top += 2;
+  if (s != NULL) {
+    memcpy(_writer_top, s, bytes);
+    _writer_top += bytes;
+  }
+}
+
+inline size_t TraceWriterBase::nbytes_for_utf8str(const char* s, size_t maxbytes) {
+  assert(s != NULL, "null string");
+
+  size_t nbytes = 0;
+  if (s != NULL) {
+    jchar c;
+    const char *end = s + maxbytes;
+    for (;;) {
+      const char *q = s;
+      s = UTF8::next(s, &c);
+      if (c == 0 || s >= end) {
+        break;
+      }
+      nbytes += (size_t) (s - q);
+    }
+    assert(nbytes == (u2) nbytes, "string length must fit in u2");
+  }
+  nbytes += 2; // for u2 with length
+  return nbytes;
+}
+
+#endif /* SHARE_VM_EVTRACE_TRACEWRITER_INLINE_HPP */
diff --git a/src/share/vm/gc/shared/gcLocker.hpp b/src/share/vm/gc/shared/gcLocker.hpp
--- a/src/share/vm/gc/shared/gcLocker.hpp
+++ b/src/share/vm/gc/shared/gcLocker.hpp
@@ -200,24 +200,38 @@
   Thread *_thread;
  public:
 #ifdef ASSERT
-  NoSafepointVerifier(bool activated = true, bool verifygc = true ) :
+  NoSafepointVerifier(bool activate = true, bool verifygc = true ) :
     NoGCVerifier(verifygc),
-    _activated(activated) {
+    _activated(false) {
     _thread = Thread::current();
-    if (_activated) {
-      _thread->_allow_allocation_count++;
-      _thread->_allow_safepoint_count++;
+    if (activate) {
+      enable();
     }
   }
 
+  void enable() {
+    assert(!_activated, "expected");
+    _thread->_allow_allocation_count++;
+    _thread->_allow_safepoint_count++;
+    _activated = true;
+  }
+
+  void disable() {
+    assert(_activated, "expected");
+    _thread->_allow_allocation_count--;
+    _thread->_allow_safepoint_count--;
+    _activated = false;
+  }
+
   ~NoSafepointVerifier() {
     if (_activated) {
-      _thread->_allow_allocation_count--;
-      _thread->_allow_safepoint_count--;
+      disable();
     }
   }
 #else
-  NoSafepointVerifier(bool activated = true, bool verifygc = true) : NoGCVerifier(verifygc){}
+  NoSafepointVerifier(bool activate = true, bool verifygc = true) : NoGCVerifier(verifygc){}
+  void enable() {}
+  void disable() {}
   ~NoSafepointVerifier() {}
 #endif
 };
diff --git a/src/share/vm/gc/shared/vmGCOperations.cpp b/src/share/vm/gc/shared/vmGCOperations.cpp
--- a/src/share/vm/gc/shared/vmGCOperations.cpp
+++ b/src/share/vm/gc/shared/vmGCOperations.cpp
@@ -38,6 +38,7 @@
 #include "utilities/dtrace.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/preserveException.hpp"
+#include "evtrace/traceEvents.hpp"
 #if INCLUDE_ALL_GCS
 #include "gc/g1/g1CollectedHeap.inline.hpp"
 #include "gc/g1/g1Policy.hpp"
@@ -85,6 +86,10 @@
   assert(((_gc_cause != GCCause::_no_gc) &&
           (_gc_cause != GCCause::_no_cause_specified)), "Illegal GCCause");
 
+  if (EnableEventTracing) {
+    TraceEvents::write_marker("GC operation begin");
+  }
+
   // To be able to handle a GC the VM initialization needs to be completed.
   if (!is_init_completed()) {
     vm_exit_during_initialization(
@@ -115,6 +120,10 @@
     Heap_lock->notify_all();
   }
   Heap_lock->unlock();
+
+  if (EnableEventTracing) {
+    TraceEvents::write_marker("GC operation end");
+  }
 }
 
 bool VM_GC_HeapInspection::skip_operation() const {
diff --git a/src/share/vm/memory/allocation.hpp b/src/share/vm/memory/allocation.hpp
--- a/src/share/vm/memory/allocation.hpp
+++ b/src/share/vm/memory/allocation.hpp
@@ -145,8 +145,9 @@
   mtLogging           = 0x0F,  // memory for logging
   mtArguments         = 0x10,  // memory for argument processing
   mtModule            = 0x11,  // memory for module processing
-  mtNone              = 0x12,  // undefined
-  mt_number_of_types  = 0x13   // number of memory types (mtDontTrack
+  mtEventTracing      = 0x12,  // memory used for event tracing
+  mtNone              = 0x13,  // undefined
+  mt_number_of_types  = 0x14   // number of memory types (mtDontTrack
                                  // is not included as validate type)
 };
 
diff --git a/src/share/vm/prims/jni.cpp b/src/share/vm/prims/jni.cpp
--- a/src/share/vm/prims/jni.cpp
+++ b/src/share/vm/prims/jni.cpp
@@ -78,6 +78,7 @@
 #include "services/runtimeService.hpp"
 #include "trace/traceMacros.hpp"
 #include "trace/tracing.hpp"
+#include "evtrace/traceEvents.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/events.hpp"
@@ -3999,6 +4000,10 @@
     // Tracks the time application was running before GC
     RuntimeService::record_application_start();
 
+    if (EnableEventTracing) {
+      TraceEvents::write_thread_start();
+    }
+
     // Notify JVMTI
     if (JvmtiExport::should_post_thread_life()) {
        JvmtiExport::post_thread_start(thread);
@@ -4219,6 +4224,10 @@
   java_lang_Thread::set_thread_status(thread->threadObj(),
               java_lang_Thread::RUNNABLE);
 
+  if (EnableEventTracing) {
+    TraceEvents::write_thread_start();
+  }
+
   // Notify the debugger
   if (JvmtiExport::should_post_thread_life()) {
     JvmtiExport::post_thread_start(thread);
@@ -4283,6 +4292,10 @@
   // middel of a safepoint operation
   ThreadStateTransition::transition_from_native(thread, _thread_in_vm);
 
+  if (EnableEventTracing) {
+    TraceEvents::write_thread_exit();
+  }
+
   // XXX: Note that JavaThread::exit() call below removes the guards on the
   // stack pages set up via enable_stack_{red,yellow}_zone() calls
   // above in jni_AttachCurrentThread. Unfortunately, while the setting
diff --git a/src/share/vm/prims/jvm.cpp b/src/share/vm/prims/jvm.cpp
--- a/src/share/vm/prims/jvm.cpp
+++ b/src/share/vm/prims/jvm.cpp
@@ -73,6 +73,7 @@
 #include "services/management.hpp"
 #include "services/threadService.hpp"
 #include "trace/tracing.hpp"
+#include "evtrace/traceEvents.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/dtrace.hpp"
@@ -3170,6 +3171,9 @@
   ResourceMark rm(THREAD);
   oop java_thread = JNIHandles::resolve_non_null(jthread);
   JavaThread* thr = java_lang_Thread::thread(java_thread);
+  if (EnableEventTracing) {
+    TraceEvents::write_thread_name_change(thr);
+  }
   // Thread naming only supported for the current thread, doesn't work for
   // target threads.
   if (Thread::current() == thr && !thr->has_attached_via_jni()) {
diff --git a/src/share/vm/prims/nativeLookup.cpp b/src/share/vm/prims/nativeLookup.cpp
--- a/src/share/vm/prims/nativeLookup.cpp
+++ b/src/share/vm/prims/nativeLookup.cpp
@@ -113,6 +113,7 @@
   void JNICALL JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass unsafecls);
   void JNICALL JVM_RegisterPerfMethods(JNIEnv *env, jclass perfclass);
   void JNICALL JVM_RegisterWhiteBoxMethods(JNIEnv *env, jclass wbclass);
+  void JNICALL JVM_RegisterEventTracingMethods(JNIEnv *env, jclass evtclass);
 #if INCLUDE_JVMCI
   jobject  JNICALL JVM_GetJVMCIRuntime(JNIEnv *env, jclass c);
   void     JNICALL JVM_RegisterJVMCINatives(JNIEnv *env, jclass compilerToVMClass);
@@ -130,6 +131,7 @@
 #if INCLUDE_JVMCI
   { CC"Java_jdk_vm_ci_runtime_JVMCI_initializeRuntime",            NULL, FN_PTR(JVM_GetJVMCIRuntime)             },
   { CC"Java_jdk_vm_ci_hotspot_CompilerToVM_registerNatives",       NULL, FN_PTR(JVM_RegisterJVMCINatives)        },
+  { CC"Java_sun_evtracing_EventTracing_registerNatives",           NULL, FN_PTR(JVM_RegisterEventTracingMethods) },
 #endif
 #if INCLUDE_TRACE
   { CC"Java_jdk_jfr_internal_JVM_registerNatives",                 NULL, TRACE_REGISTER_NATIVES                  },
diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp
--- a/src/share/vm/runtime/globals.hpp
+++ b/src/share/vm/runtime/globals.hpp
@@ -4049,8 +4049,31 @@
   diagnostic(bool, CompilerDirectivesPrint, false,                          \
              "Print compiler directives on installation.")                  \
   diagnostic(int,  CompilerDirectivesLimit, 50,                             \
-             "Limit on number of compiler directives.")
-
+             "Limit on number of compiler directives.")                     \
+                                                                            \
+  product(bool, EnableEventTracing, false,                                  \
+          "Enable event tracing")                                           \
+                                                                            \
+  product(bool, EnableEventTracingNativeMonitorEvents, true,                \
+          "Enable tracing of native (VM-internal) monitor events")          \
+                                                                            \
+  product(bool, EnableEventTracingDiagnostics, false,                       \
+          "Enable extra diagnostic counters and timers for event tracing")  \
+                                                                            \
+  product(ccstr, EventTracingConfiguration, NULL,                           \
+          "Configuration string for event tracing")                         \
+                                                                            \
+  product(uintx, EventTracingBufferCapacity, 16384,                         \
+          "Capacity (in bytes) of event tracing buffers")                   \
+                                                                            \
+  product(bool, EnableEventTracingRandomizedBufferCapacity, true,           \
+          "Enable randomization of event tracing buffer sizes (+/- 50%)")   \
+                                                                            \
+  product(uintx, EventTracingStackDepthLimit, 128,                          \
+          "Maximum number of raw (no inlining) stack frames to walk")       \
+                                                                            \
+  product(bool, EnableEventTracingStackTraces, true,                        \
+          "Enable stack traces for trace events")
 
 /*
  *  Macros for factoring of globals
diff --git a/src/share/vm/runtime/java.cpp b/src/share/vm/runtime/java.cpp
--- a/src/share/vm/runtime/java.cpp
+++ b/src/share/vm/runtime/java.cpp
@@ -67,6 +67,8 @@
 #include "services/memTracker.hpp"
 #include "trace/traceMacros.hpp"
 #include "trace/tracing.hpp"
+#include "evtrace/traceEvents.hpp"
+#include "evtrace/traceManager.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/histogram.hpp"
@@ -446,6 +448,13 @@
     os::infinite_sleep();
   }
 
+  if (EnableEventTracing)
+  {
+    HandleMark hm;
+    EXCEPTION_MARK;
+    TraceManager::finish_and_destroy(CHECK);
+  }
+
   EventThreadEnd event;
   if (event.should_commit()) {
     event.set_thread(THREAD_TRACE_ID(thread));
diff --git a/src/share/vm/runtime/mutex.cpp b/src/share/vm/runtime/mutex.cpp
--- a/src/share/vm/runtime/mutex.cpp
+++ b/src/share/vm/runtime/mutex.cpp
@@ -30,6 +30,7 @@
 #include "runtime/osThread.hpp"
 #include "runtime/thread.inline.hpp"
 #include "utilities/events.hpp"
+#include "evtrace/traceEvents.hpp"
 #include "utilities/macros.hpp"
 
 // o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o
@@ -440,7 +441,7 @@
 // Note that ILock and IWait do *not* access _owner.
 // _owner is a higher-level logical concept.
 
-void Monitor::ILock(Thread * Self) {
+void Monitor::ILock(Thread * Self, TraceTypes::monitor_enter_wait after_wait) {
   assert(_OnDeck != Self->_MutexEvent, "invariant");
 
   if (TryFast()) {
@@ -462,6 +463,8 @@
   ESelf->reset();
   OrderAccess::fence();
 
+  TraceEventsNativeMonitorContendedLock events(this, after_wait);
+
   // Optional optimization ... try barging on the inner lock
   if ((NativeMonitorFlags & 32) && CASPTR (&_OnDeck, NULL, UNS(Self)) == 0) {
     goto OnDeck_LOOP;
@@ -475,6 +478,7 @@
   // CONSIDER: use Self->OnDeck instead of m->OnDeck.
   // Deschedule Self so that others may run.
   while (_OnDeck != ESelf) {
+    events.contended_lock();
     ParkCommon(ESelf, 0);
   }
 
@@ -489,6 +493,7 @@
     // preemptively drain the cxq into the EntryList.
     // The best place and time to perform queue operations -- lock metadata --
     // is _before having acquired the outer lock, while waiting for the lock to drop.
+    events.contended_lock();
     ParkCommon(ESelf, 0);
   }
 
@@ -513,6 +518,13 @@
 
 void Monitor::IUnlock(bool RelaxAssert) {
   assert(ILocked(), "invariant");
+
+  TraceEventNativeMonitorContendedUnlocked event(this);
+  if (((_LockWord.FullWord & ~_LBIT) | UNS(_EntryList)) != 0) {
+    // there are queued threads -- we are definitely writing a trace event
+    event.enable();
+  }
+
   // Conceptually we need a MEMBAR #storestore|#loadstore barrier or fence immediately
   // before the store that releases the lock.  Crucially, all the stores and loads in the
   // critical section must be globally visible before the store of 0 into the lock-word
@@ -560,6 +572,8 @@
     return;
   }
 
+  event.enable();
+
  Succession:
   // Slow-path exit - this thread must ensure succession and progress.
   // OnDeck serves as lock to protect cxq and EntryList.
@@ -829,13 +843,17 @@
     // ESelf was previously on the WaitSet but we just unlinked it above
     // because of a timeout.  ESelf is not resident on any list and is not OnDeck
     assert(_OnDeck != ESelf, "invariant");
-    ILock(Self);
+    ILock(Self, TraceTypes::enter_after_wait_timeout);
   } else {
     // A prior notify() operation moved ESelf from the WaitSet to the cxq.
     // ESelf is now on the cxq, EntryList or at the OnDeck position.
     // The following fragment is extracted from Monitor::ILock()
+
+    TraceEventsNativeMonitorContendedLock events(this, TraceTypes::enter_after_wait_notify);
     for (;;) {
       if (_OnDeck == ESelf && TrySpin(Self)) break;
+
+      events.contended_lock();
       ParkCommon(ESelf, 0);
     }
     assert(_OnDeck == ESelf, "invariant");
@@ -927,10 +945,10 @@
     // Horrible dictu - we suffer through a state transition
     assert(rank() > Mutex::special, "Potential deadlock with special or lesser rank mutex");
     ThreadBlockInVM tbivm((JavaThread *) Self);
-    ILock(Self);
+    ILock(Self, TraceTypes::enter_no_wait);
   } else {
     // Mirabile dictu
-    ILock(Self);
+    ILock(Self, TraceTypes::enter_no_wait);
   }
   goto Exeunt;
 }
@@ -949,7 +967,7 @@
   assert(_safepoint_check_required != Monitor::_safepoint_check_always,
          "This lock should always have a safepoint check: %s", name());
   assert(_owner != Self, "invariant");
-  ILock(Self);
+  ILock(Self, TraceTypes::enter_no_wait);
   assert(_owner == NULL, "invariant");
   set_owner(Self);
 }
@@ -1134,7 +1152,7 @@
       assert(ILocked(), "invariant");
       IUnlock(true);
       jt->java_suspend_self();
-      ILock(Self);
+      ILock(Self, TraceTypes::enter_after_wait_other);
       assert(ILocked(), "invariant");
     }
   }
@@ -1158,6 +1176,10 @@
          "_owner(" INTPTR_FORMAT ")|_LockWord(" INTPTR_FORMAT ")|_EntryList(" INTPTR_FORMAT ")|_WaitSet("
          INTPTR_FORMAT ")|_OnDeck(" INTPTR_FORMAT ") != 0", owner, lockword, entrylist, waitset, ondeck);
 #endif
+
+  if (TraceManager::should_trace_native_monitor_events()) {
+    TraceEvents::write_native_monitor_destroy(this);
+  }
 }
 
 void Monitor::ClearMonitor(Monitor * m, const char *name) {
@@ -1174,6 +1196,12 @@
   m->_OnDeck            = NULL;
   m->_WaitSet           = NULL;
   m->_WaitLock[0]       = 0;
+
+  m->_trace_id = 0;
+  m->_trace_current_seq = 0;
+  // event tracing: some monitors are created before event tracing is
+  // initialized, therefore we record the monitor's identity when we
+  // record its first event.
 }
 
 Monitor::Monitor() { ClearMonitor(this); }
diff --git a/src/share/vm/runtime/mutex.hpp b/src/share/vm/runtime/mutex.hpp
--- a/src/share/vm/runtime/mutex.hpp
+++ b/src/share/vm/runtime/mutex.hpp
@@ -28,6 +28,7 @@
 #include "memory/allocation.hpp"
 #include "runtime/os.hpp"
 #include "utilities/histogram.hpp"
+#include "evtrace/traceTypes.hpp"
 
 // The SplitWord construct allows us to colocate the contention queue
 // (cxq) with the lock-byte.  The queue elements are ParkEvents, which are
@@ -131,6 +132,9 @@
   int NotifyCount ;                      // diagnostic assist
   char _name[MONITOR_NAME_LEN];          // Name of mutex
 
+  volatile TraceTypes::nativemonitor_id _trace_id;
+  volatile TraceTypes::seq_num          _trace_current_seq;
+
   // Debugging fields for naming, deadlock detection, etc. (some only used in debug mode)
 #ifndef PRODUCT
   bool      _allow_vm_block;
@@ -184,7 +188,7 @@
    int  TryFast () ;
    int  AcquireOrPush (ParkEvent * ev) ;
    void IUnlock (bool RelaxAssert) ;
-   void ILock (Thread * Self) ;
+   void ILock (Thread * Self, TraceTypes::monitor_enter_wait after_wait) ;
    int  IWait (Thread * Self, jlong timo);
    int  ILocked () ;
 
@@ -225,6 +229,12 @@
   Thread* owner() const         { return _owner; }
   bool owned_by_self() const;
 
+  TraceTypes::nativemonitor_id trace_id() { return _trace_id; }
+  void set_trace_id(TraceTypes::nativemonitor_id id) {
+    _trace_id = id;
+  }
+  TraceTypes::seq_num next_trace_seq() { return Atomic::add_ptr(1, &_trace_current_seq); }
+
   // Support for JVM_RawMonitorEnter & JVM_RawMonitorExit. These can be called by
   // non-Java thread. (We should really have a RawMonitor abstraction)
   void jvm_raw_lock();
diff --git a/src/share/vm/runtime/safepoint.cpp b/src/share/vm/runtime/safepoint.cpp
--- a/src/share/vm/runtime/safepoint.cpp
+++ b/src/share/vm/runtime/safepoint.cpp
@@ -60,6 +60,7 @@
 #include "trace/traceMacros.hpp"
 #include "utilities/events.hpp"
 #include "utilities/macros.hpp"
+#include "evtrace/traceManager.hpp"
 #if INCLUDE_ALL_GCS
 #include "gc/cms/concurrentMarkSweepThread.hpp"
 #include "gc/g1/suspendibleThreadSet.hpp"
@@ -436,6 +437,11 @@
 
   assert(Threads_lock->owned_by_self(), "must hold Threads_lock");
   assert((_safepoint_counter & 0x1) == 1, "must be odd");
+
+  if (EnableEventTracing) {
+    TraceManager::do_work_before_safepoint_end();
+  }
+
   _safepoint_counter ++;
   // memory fence isn't required here since an odd _safepoint_counter
   // value can do no harm and a fence is issued below anyway.
diff --git a/src/share/vm/runtime/spinLocker.hpp b/src/share/vm/runtime/spinLocker.hpp
new file mode 100644
--- /dev/null
+++ b/src/share/vm/runtime/spinLocker.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved.
+ *
+ * This file is part of the Lock Contention Tracing Subsystem for the HotSpot
+ * Virtual Machine, which is developed at Christian Doppler Laboratory on
+ * Monitoring and Evolution of Very-Large-Scale Software Systems. Please
+ * contact us at <http://mevss.jku.at/> if you need additional information
+ * or have any questions.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SHARE_VM_RUNTIME_SPINLOCKER_HPP
+#define SHARE_VM_RUNTIME_SPINLOCKER_HPP
+
+#include "runtime/thread.hpp"
+
+class SpinLocker: public StackObj {
+private:
+  volatile int *_mtx;
+
+public:
+  SpinLocker(volatile int *mtx, const char *name)
+  : _mtx(mtx)
+  {
+    Thread::SpinAcquire(_mtx, name);
+  }
+
+  ~SpinLocker() {
+    Thread::SpinRelease(_mtx);
+  }
+};
+
+#endif /* SHARE_VM_RUNTIME_SPINLOCKER_HPP */
diff --git a/src/share/vm/runtime/thread.cpp b/src/share/vm/runtime/thread.cpp
--- a/src/share/vm/runtime/thread.cpp
+++ b/src/share/vm/runtime/thread.cpp
@@ -96,6 +96,7 @@
 #include "services/threadService.hpp"
 #include "trace/traceMacros.hpp"
 #include "trace/tracing.hpp"
+#include "evtrace/traceEvents.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/events.hpp"
@@ -244,6 +245,9 @@
   omFreeProvision = 32;
   omInUseList = NULL;
   omInUseCount = 0;
+  _trace_buffer = NULL;
+  _trace_thread_id = min_jlong;
+  _tracing_active = false;
 
 #ifdef ASSERT
   _visited_for_critical_count = false;
@@ -381,6 +385,19 @@
   // osthread() can be NULL, if creation of thread failed.
   if (osthread() != NULL) os::free_thread(osthread());
 
+  if (_trace_buffer != NULL) {
+    TraceLocker tl;
+    if (_trace_buffer != NULL) {
+      if (TraceManager::is_initialized()) {
+        TraceManager::submit_buffer(_trace_buffer);
+      } else { // too late, discard
+        TraceManager::free_buffer(_trace_buffer);
+      }
+      _trace_buffer = NULL;
+    }
+  }
+
+
   // clear Thread::current if thread is deleting itself.
   // Needed to ensure JNI correctly detects non-attached threads.
   if (this == Thread::current()) {
@@ -395,6 +412,14 @@
   ShouldNotReachHere();
 }
 
+jlong Thread::generate_trace_thread_id() {
+  if (is_Java_thread()) {
+    JavaThread *jt = (JavaThread *) this;
+    return java_lang_Thread::thread_id(jt->threadObj());
+  }
+  return -osthread()->thread_identifier();
+}
+
 #ifdef ASSERT
 // Private method to check for dangling thread pointer
 void check_for_dangling_thread_pointer(Thread *thread) {
@@ -1178,6 +1203,12 @@
 
 void NamedThread::initialize_named_thread() {
   set_native_thread_name(name());
+
+  if (EnableEventTracing) {
+    // TODO: record the end of non-Java threads
+    //       (though almost all of them live until the VM exits)
+    TraceEvents::write_thread_start();
+  }
 }
 
 void NamedThread::print_on(outputStream* st) const {
@@ -1682,6 +1713,10 @@
   assert(JavaThread::current() == this, "sanity check");
   assert(!Thread::current()->owns_locks(), "sanity check");
 
+  if (EnableEventTracing) {
+    TraceEvents::write_thread_start();
+  }
+
   DTRACE_THREAD_PROBE(start, this);
 
   // This operation might block. We call that after all safepoint checks for a new thread has
@@ -1725,6 +1760,10 @@
 
   DTRACE_THREAD_PROBE(stop, this);
 
+  if (EnableEventTracing) {
+    TraceEvents::write_thread_exit();
+  }
+
   this->exit(false);
   delete this;
 }
@@ -1928,6 +1967,11 @@
   }
 #endif // INCLUDE_ALL_GCS
 
+  // The thread id that is used for tracing is read from its Java object when it is first needed.
+  // That object may be garbage collected after this method. Since we might need to write trace
+  // events after that, we call this method to cache the id if that has not happened yet.
+  trace_thread_id();
+
   log_info(os, thread)("JavaThread %s (tid: " UINTX_FORMAT ").",
     exit_type == JavaThread::normal_exit ? "exiting" : "detaching",
     os::current_thread_id());
@@ -3620,6 +3664,9 @@
   // Initialize Java-Level synchronization subsystem
   ObjectMonitor::Initialize();
 
+  // Initialize event tracing
+  TraceManager::initialize();
+
   // Initialize global modules
   jint status = init_globals();
   if (status != JNI_OK) {
@@ -3804,6 +3851,10 @@
 
   BiasedLocking::init();
 
+  if (EnableEventTracing) {
+    TraceManager::start_threads(CHECK_0); // needs a fully functional Java environment
+  }
+
 #if INCLUDE_RTM_OPT
   RTMLockingCounters::init();
 #endif
diff --git a/src/share/vm/runtime/thread.hpp b/src/share/vm/runtime/thread.hpp
--- a/src/share/vm/runtime/thread.hpp
+++ b/src/share/vm/runtime/thread.hpp
@@ -85,6 +85,8 @@
 
 DEBUG_ONLY(class ResourceMark;)
 
+class TraceBuffer;
+
 class WorkerThread;
 
 // Class hierarchy
@@ -273,6 +275,10 @@
   jlong _allocated_bytes;                       // Cumulative number of bytes allocated on
                                                 // the Java heap
 
+  TraceBuffer *_trace_buffer;
+  jlong        _trace_thread_id;
+  volatile bool _tracing_active;                // Indicates when a thread is currently tracing
+
   mutable TRACE_DATA _trace_data;               // Thread-local data for tracing
 
   ThreadExt _ext;
@@ -326,6 +332,7 @@
   virtual bool is_Java_thread()     const            { return false; }
   virtual bool is_Compiler_thread() const            { return false; }
   virtual bool is_Code_cache_sweeper_thread() const  { return false; }
+  virtual bool is_TraceReader_thread() const         { return false; }
   virtual bool is_hidden_from_external_view() const  { return false; }
   virtual bool is_jvmti_agent_thread() const         { return false; }
   // True iff the thread can perform GC operations at a safepoint.
@@ -447,6 +454,23 @@
   void incr_allocated_bytes(jlong size) { _allocated_bytes += size; }
   inline jlong cooked_allocated_bytes();
 
+  TraceBuffer *trace_buffer()            { return _trace_buffer; }
+  void set_trace_buffer(TraceBuffer *b)  { _trace_buffer = b;    }
+  bool is_tracing_active()               { return _tracing_active; }
+  void toggle_tracing_active()           { _tracing_active = !_tracing_active; }
+private:
+  jlong generate_trace_thread_id();
+public:
+  jlong trace_thread_id() {
+    jlong id = _trace_thread_id;
+    if (id == min_jlong) {
+      id = generate_trace_thread_id();
+      // different threads must compute the same id, so races are not a concern
+      _trace_thread_id = id;
+    }
+    return id;
+  }
+
   TRACE_DEFINE_THREAD_TRACE_DATA_OFFSET;
   TRACE_DATA* trace_data() const        { return &_trace_data; }
 
diff --git a/src/share/vm/runtime/vm_operations.hpp b/src/share/vm/runtime/vm_operations.hpp
--- a/src/share/vm/runtime/vm_operations.hpp
+++ b/src/share/vm/runtime/vm_operations.hpp
@@ -106,6 +106,7 @@
   template(MarkActiveNMethods)                    \
   template(PrintCompileQueue)                     \
   template(PrintClassHierarchy)                   \
+  template(ReclaimTraceBuffers)                   \
 
 class VM_Operation: public CHeapObj<mtInternal> {
  public:
diff --git a/src/share/vm/services/diagnosticCommand.cpp b/src/share/vm/services/diagnosticCommand.cpp
--- a/src/share/vm/services/diagnosticCommand.cpp
+++ b/src/share/vm/services/diagnosticCommand.cpp
@@ -41,6 +41,7 @@
 #include "services/writeableFlags.hpp"
 #include "utilities/macros.hpp"
 #include "oops/objArrayOop.inline.hpp"
+#include "evtrace/traceEvents.hpp"
 
 void DCmdRegistrant::register_dcmds(){
   // Registration of the diagnostic commands
@@ -82,6 +83,7 @@
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeListDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TouchedMethodsDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<EventTracingMarkerDCmd>(full_export, true, false));
 
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesPrintDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompilerDirectivesAddDCmd>(full_export, true, false));
@@ -621,6 +623,28 @@
   }
 }
 
+EventTracingMarkerDCmd::EventTracingMarkerDCmd(outputStream* output, bool heap)
+  : DCmdWithParser(output, heap),
+    _label("label", "description for the marker", "STRING", true)
+{
+  _dcmdparser.add_dcmd_argument(&_label);
+}
+
+void EventTracingMarkerDCmd::execute(DCmdSource source, TRAPS) {
+  TraceEvents::write_marker(_label.value());
+}
+
+int EventTracingMarkerDCmd::num_arguments() {
+  ResourceMark rm;
+  EventTracingMarkerDCmd* dcmd = new EventTracingMarkerDCmd(NULL, false);
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
 // Enhanced JMX Agent support
 
 JMXStartRemoteDCmd::JMXStartRemoteDCmd(outputStream *output, bool heap_allocated) :
diff --git a/src/share/vm/services/diagnosticCommand.hpp b/src/share/vm/services/diagnosticCommand.hpp
--- a/src/share/vm/services/diagnosticCommand.hpp
+++ b/src/share/vm/services/diagnosticCommand.hpp
@@ -461,6 +461,27 @@
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+class EventTracingMarkerDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<char *> _label;
+public:
+  EventTracingMarkerDCmd(outputStream* output, bool heap);
+  static const char* name() { return "EventTracing.mark"; }
+  static const char* description() {
+    return "Mark the current point in time in the event trace.";
+  }
+  static const char* impact() {
+    return "Low: records a single trace event.";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "monitor", NULL};
+    return p;
+  }
+  static int num_arguments();
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
 // Enhanced JMX Agent support
 
 class JMXStartRemoteDCmd : public DCmdWithParser {
diff --git a/src/share/vm/utilities/decoder.cpp b/src/share/vm/utilities/decoder.cpp
--- a/src/share/vm/utilities/decoder.cpp
+++ b/src/share/vm/utilities/decoder.cpp
@@ -52,6 +52,9 @@
 
   if (_shared_decoder == NULL) {
     _shared_decoder = create_decoder();
+    if (_shared_decoder == NULL) {
+      _shared_decoder = &_do_nothing_decoder;
+    }
   }
   return _shared_decoder;
 }
@@ -59,6 +62,9 @@
 AbstractDecoder* Decoder::get_error_handler_instance() {
   if (_error_handler_decoder == NULL) {
     _error_handler_decoder = create_decoder();
+    if (_error_handler_decoder == NULL) {
+      _error_handler_decoder = &_do_nothing_decoder;
+    }
   }
   return _error_handler_decoder;
 }
@@ -76,11 +82,9 @@
   decoder = new (std::nothrow)ElfDecoder();
 #endif
 
-  if (decoder == NULL || decoder->has_error()) {
-    if (decoder != NULL) {
-      delete decoder;
-    }
-    decoder = &_do_nothing_decoder;
+  if (decoder != NULL && decoder->has_error()) {
+    delete decoder;
+    decoder = NULL;
   }
   return decoder;
 }
diff --git a/src/share/vm/utilities/decoder.hpp b/src/share/vm/utilities/decoder.hpp
--- a/src/share/vm/utilities/decoder.hpp
+++ b/src/share/vm/utilities/decoder.hpp
@@ -105,6 +105,8 @@
 
 class Decoder : AllStatic {
 public:
+  static AbstractDecoder* create_decoder();
+
   static bool decode(address pc, char* buf, int buflen, int* offset, const char* modulepath = NULL, bool demangle = true);
   static bool decode(address pc, char* buf, int buflen, int* offset, bool demangle) {
     return decode(pc, buf, buflen, offset, (const char*) NULL, demangle);
@@ -124,7 +126,6 @@
   // in this scenario.
   static AbstractDecoder* get_error_handler_instance();
 
-  static AbstractDecoder* create_decoder();
 private:
   static AbstractDecoder*     _shared_decoder;
   static AbstractDecoder*     _error_handler_decoder;
diff --git a/src/share/vm/utilities/elfFile.cpp b/src/share/vm/utilities/elfFile.cpp
--- a/src/share/vm/utilities/elfFile.cpp
+++ b/src/share/vm/utilities/elfFile.cpp
@@ -205,7 +205,8 @@
   ElfStringTable* string_table = get_string_table(string_table_index);
 
   if (string_table == NULL) {
-    m_status = NullDecoder::file_invalid;
+    // We used to set m_status to file_invalid here, but that is a bit extreme,
+    // since it prevents successful future lookups. Instead, just return false.
     return false;
   }
   if (offset) *offset = off;
