--- /dev/null 2016-10-25 08:46:44.038854975 +0200
+++ new/src/share/vm/evtrace/traceStack.cpp 2016-10-25 10:40:15.706781847 +0200
@@ -0,0 +1,412 @@
+/*
+ * 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 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 .
+ *
+ */
+
+#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;
+ f.is_compiled = fr->cb()->is_nmethod();
+ if (f.is_compiled) {
+ assert(fr->is_compiled_frame() || fr->is_native_frame(), "unsupported frame type");
+ f.compiled.pc = fr->pc();
+ f.compiled.nm = (nmethod *) fr->cb();
+ } else {
+ assert(fr->is_interpreted_frame(), "unsupported frame type");
+ f.interpreted.method = fr->interpreter_frame_method();
+ f.interpreted.bci = fr->interpreter_frame_bci();
+ }
+ 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::operator new(size + sizeof(_frames[0]) * nframes, CALLER_PC);
+}
+
+void CachedTraceStack::operator delete(void* p) {
+ CHeapObj::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::has_interpreted_method_from_classloader(const ClassLoaderData* loader) const {
+ assert(is_valid(), "sanity");
+ for (size_t i = 0; i < _count; i++) {
+ if (!_frames[i].is_compiled && _frames[i].interpreted.method->method_holder()->class_loader_data() == loader) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CachedTraceStack::has_nmethod(const nmethod *nm) const {
+ assert(is_valid(), "sanity");
+ for (size_t i = 0; i < _count; i++) {
+ if (_frames[i].is_compiled && _frames[i].compiled.nm == nm) {
+ return true;
+ }
+ }
+ return false;
+}
+
+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;
+ _has_invalid_stacks = false;
+
+ _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, mtEventTracing);
+}
+
+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)
+ }
+}
+
+class TraceStackCache::CachedTraceStackPredicate {
+public:
+ virtual bool test(CachedTraceStack *cts) = 0;
+};
+
+inline void TraceStackCache::purge_matches(TraceStackCache::CachedTraceStackPredicate *pr) {
+ if (EnableEventTracingDiagnostics) {
+ _purge_timer.start();
+ }
+
+ for (size_t i = 0; i < _size; i++) {
+ CachedTraceStack *p = _table[i];
+ while (p != NULL) {
+ if (p->is_valid() && pr->test(p)) {
+ p->invalidate();
+ _has_invalid_stacks = true;
+ }
+ p = p->cache_next();
+ }
+ }
+
+ if (EnableEventTracingDiagnostics) {
+ _purge_timer.stop();
+ }
+}
+
+class TraceStackCache::UnloadingClassPredicate : public TraceStackCache::CachedTraceStackPredicate {
+ const ClassLoaderData *_loader;
+public:
+ UnloadingClassPredicate(const ClassLoaderData *loader) : _loader(loader) { }
+ virtual bool test(CachedTraceStack *stack) {
+ return stack->has_interpreted_method_from_classloader(_loader);
+ }
+};
+
+void TraceStackCache::purge_unloading_classes(const ClassLoaderData* loader) {
+ assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "must be done in VM thread at safepoint");
+
+ // NOTE: only purges stack traces with *interpreted* frames that refer to
+ // unloading classes. nmethods with code from unloaded classes are unloaded
+ // in a later step, at which point we also unload the stack traces that
+ // contain those nmethods.
+ UnloadingClassPredicate pr(loader);
+ purge_matches(&pr);
+}
+
+class TraceStackCache::UnloadingNmethodPredicate : public TraceStackCache::CachedTraceStackPredicate {
+ const nmethod *_nmethod;
+public:
+ UnloadingNmethodPredicate(const nmethod *nm) : _nmethod(nm) { }
+ virtual bool test(CachedTraceStack *stack) {
+ return stack->has_nmethod(_nmethod);
+ }
+};
+
+void TraceStackCache::purge_unloading_nmethod(const nmethod *nm) {
+ UnloadingNmethodPredicate pr(nm);
+ purge_matches(&pr);
+}
+
+class TraceStackCache::AnyPredicate : public TraceStackCache::CachedTraceStackPredicate {
+public:
+ virtual bool test(CachedTraceStack *stack) {
+ return true;
+ }
+};
+
+void TraceStackCache::purge_all() {
+ AnyPredicate pr;
+ purge_matches(&pr);
+}
+
+void TraceStackCache::do_maintenance() {
+ assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "must be done in VM thread at safepoint");
+
+ bool should_grow = (_count / (float) _size > 0.7f);
+ if (should_grow || has_invalid_stacks()) {
+ 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, mtEventTracing);
+
+ if (EnableEventTracingDiagnostics) {
+ _maintenance_timer.stop();
+ }
+
+ _has_invalid_stacks = false;
+ }
+}
+
+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
+ _purge_timer.reset();
+ _maintenance_timer.reset();
+}