--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunk.cpp Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/dcmd/jfrDcmds.hpp"
+#include "jfr/recorder/jfrRecorder.hpp"
+#include "jfr/recorder/repository/jfrChunk.hpp"
+#include "jfr/recorder/repository/jfrChunkWriter.hpp"
+#include "jfr/utilities/jfrTimeConverter.hpp"
+#include "logging/log.hpp"
+#include "runtime/os.inline.hpp"
+#include "runtime/thread.inline.hpp"
+
+static const u1 GUARD = 0xff;
+
+static jlong nanos_now() {
+ return os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC;
+}
+
+static jlong ticks_now() {
+ return JfrTicks::now();
+}
+
+JfrChunk::JfrChunk() :
+ _path(NULL),
+ _start_ticks(0),
+ _previous_start_ticks(invalid_time),
+ _start_nanos(0),
+ _previous_start_nanos(invalid_time),
+ _last_update_nanos(0),
+ _last_checkpoint_offset(0),
+ _last_metadata_offset(0),
+ _generation(1) {}
+
+JfrChunk::~JfrChunk() {
+ reset();
+}
+
+void JfrChunk::reset() {
+ if (_path != NULL) {
+ JfrCHeapObj::free(_path, strlen(_path) + 1);
+ _path = NULL;
+ }
+ _last_checkpoint_offset = _last_metadata_offset = 0;
+ _generation = 1;
+}
+
+void JfrChunk::set_last_checkpoint_offset(int64_t offset) {
+ _last_checkpoint_offset = offset;
+}
+
+int64_t JfrChunk::last_checkpoint_offset() const {
+ return _last_checkpoint_offset;
+}
+
+int64_t JfrChunk::start_ticks() const {
+ assert(_start_ticks != 0, "invariant");
+ return _start_ticks;
+}
+
+int64_t JfrChunk::start_nanos() const {
+ assert(_start_nanos != 0, "invariant");
+ return _start_nanos;
+}
+
+int64_t JfrChunk::previous_start_ticks() const {
+ assert(_previous_start_ticks != invalid_time, "invariant");
+ return _previous_start_ticks;
+}
+
+int64_t JfrChunk::previous_start_nanos() const {
+ assert(_previous_start_nanos != invalid_time, "invariant");
+ return _previous_start_nanos;
+}
+
+void JfrChunk::update_start_ticks() {
+ _start_ticks = ticks_now();
+}
+
+void JfrChunk::update_start_nanos() {
+ _start_nanos = _last_update_nanos = nanos_now();
+}
+
+void JfrChunk::update() {
+ _last_update_nanos = nanos_now();
+}
+
+void JfrChunk::save_current_and_update_start_ticks() {
+ _previous_start_ticks = _start_ticks;
+ update_start_ticks();
+}
+
+void JfrChunk::save_current_and_update_start_nanos() {
+ _previous_start_nanos = _start_nanos;
+ update_start_nanos();
+}
+
+void JfrChunk::update_time_to_now() {
+ save_current_and_update_start_nanos();
+ save_current_and_update_start_ticks();
+}
+
+int64_t JfrChunk::last_chunk_duration() const {
+ assert(_previous_start_nanos != invalid_time, "invariant");
+ return _start_nanos - _previous_start_nanos;
+}
+
+static char* copy_path(const char* path) {
+ assert(path != NULL, "invariant");
+ const size_t path_len = strlen(path);
+ char* new_path = JfrCHeapObj::new_array<char>(path_len + 1);
+ strncpy(new_path, path, path_len + 1);
+ return new_path;
+}
+
+void JfrChunk::set_path(const char* path) {
+ if (_path != NULL) {
+ JfrCHeapObj::free(_path, strlen(_path) + 1);
+ _path = NULL;
+ }
+ if (path != NULL) {
+ _path = copy_path(path);
+ }
+}
+
+const char* JfrChunk::path() const {
+ return _path;
+}
+
+bool JfrChunk::is_started() const {
+ return _start_nanos != 0;
+}
+
+bool JfrChunk::is_finished() const {
+ return 0 == _generation;
+}
+
+bool JfrChunk::is_initial_flush() const {
+ return 0 == _last_metadata_offset;
+}
+
+int64_t JfrChunk::duration() const {
+ assert(_last_update_nanos >= _start_nanos, "invariant");
+ return _last_update_nanos - _start_nanos;
+}
+
+int64_t JfrChunk::last_metadata_offset() const {
+ return _last_metadata_offset;
+}
+
+void JfrChunk::set_last_metadata_offset(int64_t offset) {
+ if (0 == offset) {
+ return;
+ }
+ assert(offset > _last_metadata_offset, "invariant");
+ _last_metadata_offset = offset;
+}
+
+bool JfrChunk::has_metadata() const {
+ return 0 != _last_metadata_offset;
+}
+
+u1 JfrChunk::generation() const {
+ assert(_generation > 0, "invariant");
+ const u1 this_generation = _generation++;
+ if (GUARD == _generation) {
+ _generation = 1;
+ }
+ return this_generation;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
+#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
+
+#include "jni.h"
+#include "jfr/utilities/jfrAllocation.hpp"
+#include "jfr/utilities/jfrTypes.hpp"
+
+class JfrChunk : public JfrCHeapObj {
+ friend class JfrChunkWriter;
+ friend class JfrChunkHeadWriter;
+ private:
+ char* _path;
+ int64_t _start_ticks;
+ int64_t _previous_start_ticks;
+ int64_t _start_nanos;
+ int64_t _previous_start_nanos;
+ int64_t _last_update_nanos;
+ int64_t _last_checkpoint_offset;
+ int64_t _last_metadata_offset;
+ mutable u1 _generation;
+
+ JfrChunk();
+ ~JfrChunk();
+
+ void update_start_ticks();
+ void update_start_nanos();
+ void save_current_and_update_start_ticks();
+ void save_current_and_update_start_nanos();
+
+ void reset();
+ int64_t last_checkpoint_offset() const;
+ void set_last_checkpoint_offset(int64_t offset);
+
+ int64_t last_metadata_offset() const;
+ void set_last_metadata_offset(int64_t offset);
+ bool has_metadata() const;
+
+ int64_t start_ticks() const;
+ int64_t start_nanos() const;
+
+ int64_t previous_start_ticks() const;
+ int64_t previous_start_nanos() const;
+ int64_t last_chunk_duration() const;
+
+ void update_time_to_now();
+ void set_path(const char* path);
+ const char* path() const;
+
+ void update();
+
+ bool is_started() const;
+ bool is_finished() const;
+ bool is_initial_flush() const;
+
+ int64_t duration() const;
+ u1 generation() const;
+};
+
+#endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
+#include "runtime/thread.inline.hpp"
+
+static bool thread_inclusion_predicate(Thread* t) {
+ assert(t != NULL, "invariant");
+ return !(t->jfr_thread_local()->is_excluded() || t->jfr_thread_local()->is_dead());
+}
+
+static bool java_thread_inclusion_predicate(JavaThread* jt) {
+ assert(jt != NULL, "invariant");
+ return thread_inclusion_predicate(jt) && jt->thread_state() != _thread_new;
+}
+
+static JavaThread* next_java_thread(JavaThreadIteratorWithHandle& iter) {
+ JavaThread* next = iter.next();
+ while (next != NULL && !java_thread_inclusion_predicate(next)) {
+ next = iter.next();
+ }
+ return next;
+}
+
+static NonJavaThread* next_non_java_thread(NonJavaThread::Iterator& iter) {
+ NonJavaThread* next = NULL;
+ while (!iter.end()) {
+ next = iter.current();
+ iter.step();
+ assert(next != NULL, "invariant");
+ if (!thread_inclusion_predicate(next)) {
+ continue;
+ }
+ }
+ return next;
+}
+
+JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter() : _iter(), _next(next_java_thread(_iter)) {}
+
+JavaThread* JfrJavaThreadIteratorAdapter::next() {
+ assert(has_next(), "invariant");
+ Type* const temp = _next;
+ assert(java_thread_inclusion_predicate(temp), "invariant");
+ _next = next_java_thread(_iter);
+ assert(temp != _next, "invariant");
+ return temp;
+}
+
+JfrNonJavaThreadIteratorAdapter::JfrNonJavaThreadIteratorAdapter() : _iter(), _next(next_non_java_thread(_iter)) {}
+
+bool JfrNonJavaThreadIteratorAdapter::has_next() const {
+ return _next != NULL;
+}
+
+NonJavaThread* JfrNonJavaThreadIteratorAdapter::next() {
+ assert(has_next(), "invariant");
+ Type* const temp = _next;
+ assert(thread_inclusion_predicate(temp), "invariant");
+ _next = next_non_java_thread(_iter);
+ assert(temp != _next, "invariant");
+ return temp;
+}
+
+// explicit instantiations
+template class JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj>;
+template class JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj>;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
+#define SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
+
+template <typename Adapter, typename AP = StackObj>
+class JfrThreadIterator : public AP {
+ private:
+ Adapter _adapter;
+ public:
+ JfrThreadIterator() : _adapter() {}
+ typename Adapter::Type* next() {
+ assert(has_next(), "invariant");
+ return _adapter.next();
+ }
+ bool has_next() const {
+ return _adapter.has_next();
+ }
+};
+
+class JfrJavaThreadIteratorAdapter {
+ private:
+ JavaThreadIteratorWithHandle _iter;
+ JavaThread* _next;
+ public:
+ typedef JavaThread Type;
+ JfrJavaThreadIteratorAdapter();
+ bool has_next() const {
+ return _next != NULL;
+ }
+ Type* next();
+};
+
+class JfrNonJavaThreadIteratorAdapter {
+ private:
+ NonJavaThread::Iterator _iter;
+ NonJavaThread* _next;
+ public:
+ typedef NonJavaThread Type;
+ JfrNonJavaThreadIteratorAdapter();
+ bool has_next() const;
+ Type* next();
+};
+
+typedef JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj> JfrJavaThreadIterator;
+typedef JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj> JfrNonJavaThreadIterator;
+
+#endif // SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantLookup.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,42 @@
+package jdk.jfr.consumer;
+
+import jdk.jfr.internal.Type;
+
+final class ConstantLookup {
+
+ private final Type type;
+ private ConstantMap current;
+ private ConstantMap previous = ConstantMap.EMPTY;
+
+ ConstantLookup(ConstantMap current, Type type) {
+ this.current = current;
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public ConstantMap getLatestPool() {
+ return current;
+ }
+
+ public void newPool() {
+ // previous = current;
+ current = new ConstantMap(current.factory, current.name);
+ // previous = new ConstantMap(); // disable cache
+ }
+
+ public Object getPreviousResolved(long key) {
+ return previous.getResolved(key);
+ }
+
+ public Object getCurrentResolved(long key) {
+ return current.getResolved(key);
+ }
+
+ public Object getCurrent(long key) {
+ return current.get(key);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import jdk.jfr.EventType;
+
+final class EventDirectoryStream implements EventStream {
+
+ public final static class EventConsumer {
+ final private String eventName;
+ final Consumer<RecordedEvent> action;
+
+ EventConsumer(String eventName, Consumer<RecordedEvent> eventConsumer) {
+ this.eventName = eventName;
+ this.action = eventConsumer;
+ }
+
+ public void offer(RecordedEvent event) {
+ action.accept(event);
+ }
+
+ public boolean accepts(EventType eventType) {
+ return (eventName == null || eventType.getName().equals(eventName));
+ }
+ }
+
+ private final EventRunner eventRunner;
+ private Thread thread;
+ private boolean started;
+
+ public EventDirectoryStream(AccessControlContext acc) throws IOException {
+ eventRunner = new EventRunner(acc);
+ }
+
+ public void close() {
+ synchronized (eventRunner) {
+ eventRunner.close();
+ }
+ }
+
+ public synchronized void onFlush(Runnable action) {
+ Objects.requireNonNull(action);
+ synchronized (eventRunner) {
+ this.eventRunner.addFlush(action);
+ }
+ }
+
+ void start(long startNanos) {
+ synchronized (eventRunner) {
+ if (started) {
+ throw new IllegalStateException("Event stream can only be started once");
+ }
+ started = true;
+ eventRunner.setStartNanos(startNanos);
+ }
+ eventRunner.run();
+ }
+
+ @Override
+ public void start() {
+ start(Instant.now().toEpochMilli() * 1000*1000L);
+ }
+
+ @Override
+ public void startAsync() {
+ startAsync(Instant.now().toEpochMilli() * 1000*1000L);
+ }
+
+ void startAsync(long startNanos) {
+ synchronized (eventRunner) {
+ eventRunner.setStartNanos(startNanos);
+ thread = new Thread(eventRunner);
+ thread.setDaemon(true);
+ thread.start();
+ }
+ }
+
+ public void addEventConsumer(EventConsumer action) {
+ Objects.requireNonNull(action);
+ synchronized (eventRunner) {
+ eventRunner.add(action);
+ }
+ }
+
+
+
+ @Override
+ public void onEvent(Consumer<RecordedEvent> action) {
+ Objects.requireNonNull(action);
+ synchronized (eventRunner) {
+ eventRunner.add(new EventConsumer(null, action));
+ }
+ }
+
+ @Override
+ public void onEvent(String eventName, Consumer<RecordedEvent> action) {
+ Objects.requireNonNull(eventName);
+ Objects.requireNonNull(action);
+ synchronized (eventRunner) {
+ eventRunner.add(new EventConsumer(eventName, action));
+ }
+ }
+
+ @Override
+ public void onClose(Runnable action) {
+ Objects.requireNonNull(action);
+ synchronized (eventRunner) {
+ eventRunner.addCloseAction(action);
+ }
+ }
+
+ @Override
+ public boolean remove(Object action) {
+ Objects.requireNonNull(action);
+ synchronized (eventRunner) {
+ return eventRunner.remove(action);
+ }
+ }
+
+ @Override
+ public void awaitTermination(Duration timeout) {
+ Objects.requireNonNull(timeout);
+ Thread t = null;
+ synchronized (eventRunner) {
+ t = thread;
+ }
+ if (t != null && t != Thread.currentThread()) {
+ try {
+ t.join(timeout.toMillis());
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+
+ @Override
+ public void awaitTermination() {
+ awaitTermination(Duration.ofMillis(0));
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventFileStream.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Implementation of an event stream that operates against a recording file.
+ *
+ */
+final class EventFileStream implements EventStream {
+
+ public EventFileStream(Path path) {
+ Objects.requireNonNull(path);
+ }
+
+ @Override
+ public void onEvent(Consumer<RecordedEvent> action) {
+ Objects.requireNonNull(action);
+ notImplemented();
+ }
+
+ public void onEvent(EventFilter filter, Consumer<RecordedEvent> action) {
+ Objects.requireNonNull(filter);
+ Objects.requireNonNull(action);
+ notImplemented();
+ }
+
+ @Override
+ public void onEvent(String eventName, Consumer<RecordedEvent> action) {
+ Objects.requireNonNull(eventName);
+ Objects.requireNonNull(action);
+ notImplemented();
+ }
+
+ @Override
+ public void onFlush(Runnable action) {
+ Objects.requireNonNull(action);
+ notImplemented();
+ }
+
+ @Override
+ public void onClose(Runnable action) {
+ Objects.requireNonNull(action);
+ notImplemented();
+ }
+
+ @Override
+ public void close() {
+ notImplemented();
+ }
+
+ @Override
+ public boolean remove(Object action) {
+ Objects.requireNonNull(action);
+ notImplemented();
+ return false;
+ }
+
+ @Override
+ public void start() {
+ notImplemented();
+ }
+
+ @Override
+ public void startAsync() {
+ notImplemented();
+ }
+
+ @Override
+ public void awaitTermination(Duration timeout) {
+ Objects.requireNonNull(timeout);
+ }
+
+ @Override
+ public void awaitTermination() {
+ notImplemented();
+ }
+
+ private static void notImplemented() {
+ throw new UnsupportedOperationException("Streaming for files not yet implemenetd");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventFilter.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+
+final class EventFilter {
+ private final String[] eventNames;
+ private final Duration threshold;
+ private final String[] fields;
+
+ private EventFilter(String[] eventNames, Duration threshold, String[] fields) {
+ this.eventNames = eventNames;
+ this.threshold = threshold;
+ this.fields = fields;
+ }
+
+ public static EventFilter eventTypes(String... eventNames) {
+ return new EventFilter(eventNames.clone(), null, new String[0]);
+ }
+
+ public EventFilter threshold(Duration threshold) {
+ return new EventFilter(eventNames, threshold, fields);
+ }
+
+ public EventFilter fields(String... fieldNames) {
+ return new EventFilter(eventNames, threshold, fieldNames);
+ }
+
+ public EventFilter start(Instant instant) {
+ return this;
+ }
+
+ public EventFilter end(Instant instant) {
+ return this;
+ }
+
+ public EventFilter threads(Thread... t) {
+ return this;
+ }
+
+ public EventFilter threadIds(long... threadId) {
+ return this;
+ }
+
+ public EventFilter threadNames(String... threadName) {
+ return this;
+ }
+
+ public EventFilter threadFilters(String... filter) {
+ return this;
+ }
+
+ List<String> getFields() {
+ return Arrays.asList(fields);
+ }
+
+ List<String> getEventNames() {
+ return Arrays.asList(fields);
+ }
+
+ Duration getThreshold() {
+ return threshold;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventRunner.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import jdk.jfr.consumer.EventDirectoryStream.EventConsumer;
+import jdk.jfr.internal.JVM;
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+
+class EventRunner implements Runnable {
+ private final static VarHandle closedHandle;
+ private final static VarHandle consumersHandle;
+ private final static VarHandle dispatcherHandle;
+ private final static VarHandle flushActionsHandle;
+ private final static VarHandle closeActionsHandle;
+ static {
+ try {
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ closedHandle = l.findVarHandle(EventRunner.class, "closed", boolean.class);
+ consumersHandle = l.findVarHandle(EventRunner.class, "consumers", EventConsumer[].class);
+ dispatcherHandle = l.findVarHandle(EventRunner.class, "dispatcher", LongMap.class);
+ flushActionsHandle = l.findVarHandle(EventRunner.class, "flushActions", Runnable[].class);
+ closeActionsHandle = l.findVarHandle(EventRunner.class, "closeActions", Runnable[].class);
+ } catch (ReflectiveOperationException e) {
+ throw new InternalError(e);
+ }
+ }
+ // set by VarHandle
+ private boolean closed;
+ // set by VarHandle
+ private EventConsumer[] consumers = new EventConsumer[0];
+ // set by VarHandle
+ private LongMap<EventConsumer[]> dispatcher = new LongMap<>();
+ // set by VarHandle
+ private Runnable[] flushActions = new Runnable[0];
+ // set by VarHandle
+ private Runnable[] closeActions = new Runnable[0];
+
+ private final static JVM jvm = JVM.getJVM();
+ private final static EventConsumer[] NO_CONSUMERS = new EventConsumer[0];
+ private final AccessControlContext accessControlContext;
+ private EventSetLocation location;
+ private EventSet eventSet;
+ private InternalEventFilter eventFilter = InternalEventFilter.ACCEPT_ALL;
+ private int eventSetIndex;
+ private int eventArrayIndex;
+ private RecordedEvent[] currentEventArray = new RecordedEvent[0];
+ private volatile long startNanos;
+
+ public EventRunner(AccessControlContext acc) throws IOException {
+ this.accessControlContext = acc;
+ }
+
+ public void run() {
+ doPriviliged(() -> execute());
+ }
+
+ void doPriviliged(Runnable r) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ r.run();
+ return null;
+ }
+ }, accessControlContext);
+ }
+
+ private void execute() {
+ jvm.exclude(Thread.currentThread());
+ try {
+ process();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "Unexpectedexception iterating consumer.");
+ } finally {
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "Execution of stream ended.");
+ }
+ }
+
+ private void process() throws Exception, IOException {
+ this.location = EventSetLocation.current();
+ this.eventSet = location.acquire(startNanos, null); // use timestamp from
+ if (eventSet == null) {
+ return;
+ }
+ while (!closed) {
+ processSegment();
+ Runnable[] fas = this.flushActions;
+ for (int i = 0; i < fas.length; i++) {
+ fas[i].run();
+ }
+ do {
+ if (closed) {
+ return;
+ }
+ currentEventArray = eventSet.readEvents(eventSetIndex);
+ if (currentEventArray == EventSet.END_OF_SET) {
+ eventSet = eventSet.next(eventFilter);
+ if (eventSet == null || closed) {
+ return;
+ }
+ eventSetIndex = 0;
+ continue;
+ }
+ if (currentEventArray == null) {
+ return; // no more events
+ }
+ eventSetIndex++;
+ } while (currentEventArray.length == 0);
+ eventArrayIndex = 0;
+ }
+ }
+
+ private void processSegment() {
+ while (eventArrayIndex < currentEventArray.length) {
+ RecordedEvent e = currentEventArray[eventArrayIndex++];
+ if (e == null) {
+ return;
+ }
+ EventConsumer[] consumerDispatch = dispatcher.get(e.getEventType().getId());
+ if (consumerDispatch == null) {
+ consumerDispatch = NO_CONSUMERS;
+ for (EventConsumer ec : consumers.clone()) {
+ if (ec.accepts(e.getEventType())) {
+ consumerDispatch = merge(consumerDispatch, ec);
+ }
+ }
+ dispatcher.put(e.getEventType().getId(), consumerDispatch);
+ }
+ for (int i = 0; i < consumerDispatch.length; i++) {
+ consumerDispatch[i].offer(e);
+ }
+ }
+ }
+
+ static EventConsumer[] merge(EventConsumer[] current, EventConsumer add) {
+ EventConsumer[] array = new EventConsumer[current.length + 1];
+ System.arraycopy(current, 0, array, 0, current.length);
+ array[current.length] = add;
+ return array;
+ }
+
+ public void add(EventConsumer e) {
+ consumersHandle.setVolatile(this, merge(consumers, e));
+ dispatcherHandle.setVolatile(this, new LongMap<>()); // will reset
+ }
+
+ private static Runnable[] removeAction(Runnable[] array, Object action) {
+ if (array.length == 0) {
+ return null;
+ }
+ boolean remove = false;
+ List<Runnable> list = new ArrayList<>();
+ for (int i = 0; i < array.length; i++) {
+ if (array[i] != action) {
+ list.add(array[i]);
+ } else {
+ remove = true;
+ }
+ }
+ if (remove) {
+ return list.toArray(new Runnable[list.size()]);
+ }
+ return null;
+ }
+
+ private static Runnable[] addAction(Runnable[] array, Runnable action) {
+ ArrayList<Runnable> a = new ArrayList<>();
+ a.addAll(Arrays.asList(array));
+ a.add(action);
+ return a.toArray(new Runnable[0]);
+ }
+
+ public boolean remove(Object action) {
+ boolean remove = false;
+ Runnable[] updatedFlushActions = removeAction(flushActions, action);
+ if (updatedFlushActions != null) {
+ flushActionsHandle.setVolatile(this, updatedFlushActions);
+ remove = true;
+ }
+ Runnable[] updatedCloseActions = removeAction(closeActions, action);
+ if (updatedCloseActions != null) {
+ closeActionsHandle.setVolatile(this, updatedCloseActions);
+ remove = true;
+ }
+
+ boolean removeConsumer = false;
+ List<EventConsumer> list = new ArrayList<>();
+ for (int i = 0; i < consumers.length; i++) {
+ if (consumers[i].action != action) {
+ list.add(consumers[i]);
+ } else {
+ removeConsumer = true;
+ remove = true;
+ }
+ }
+ if (removeConsumer) {
+ EventConsumer[] array = list.toArray(new EventConsumer[list.size()]);
+ consumersHandle.setVolatile(this, array);
+ dispatcherHandle.setVolatile(this, new LongMap<>()); // will reset dispatch
+ }
+ return remove;
+ }
+
+ public void addFlush(Runnable action) {
+ flushActionsHandle.setVolatile(this, addAction(flushActions, action));
+ }
+
+ public void close() {
+ closedHandle.setVolatile(this, true);
+ // TODO: Data races here, must fix
+ if (eventSet != null) {
+ eventSet.release(null);
+ }
+ if (location != null) {
+ location.release();
+ }
+
+ Runnable[] cas = this.closeActions;
+ for (int i = 0; i < cas.length; i++) {
+ cas[i].run();
+ }
+ }
+
+ public void addCloseAction(Runnable action) {
+ closeActionsHandle.setVolatile(this, addAction(closeActions, action));
+ }
+
+ public void setStartNanos(long startNanos) {
+ this.startNanos = startNanos;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventSet.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Cache that represents all discovered events in a chunk.
+ *
+ */
+final class EventSet {
+
+ public static final RecordedEvent[] END_OF_SET = new RecordedEvent[0];
+ private static final AtomicInteger idCounter = new AtomicInteger(-1);
+
+ private volatile Object[][] segments = new Object[1000][];
+ private volatile boolean closed;
+ private final long startTimeNanos;
+ private final EventSetLocation location;
+ private final Path path;
+ private final int id;
+
+ // Guarded by lock
+ private boolean awaitNewEvents;
+ private RecordingInput input;
+ private ChunkParser chunkParser;
+ private int referenceCount;
+ private final ReentrantLock lock = new ReentrantLock();
+ private final Set<InternalEventFilter> filters = new HashSet<>();
+ private InternalEventFilter globalFilter = InternalEventFilter.ACCEPT_ALL;
+ private boolean dirtyFilter = true;
+
+ public void release(InternalEventFilter eventFilter) {
+ try {
+ lock.lock();
+ filters.remove(eventFilter);
+ updateGlobalFilter();
+ referenceCount--;
+ if (referenceCount == 0) {
+ closed = true;
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ // TODO: Flie locked by other process?
+ }
+ chunkParser = null;
+ input = null;
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public EventSet(EventSetLocation location, EventSet previousEventSet, Path p) throws IOException {
+ this.location = location;
+ this.path = p;
+ this.startTimeNanos = readStartTime(p);
+ this.id = idCounter.incrementAndGet();
+ }
+
+ private long readStartTime(Path p) throws IOException {
+ try (RecordingInput in = new RecordingInput(p.toFile(), 100)) {
+ ChunkHeader c = new ChunkHeader(in);
+ return c.getStartNanos();
+ }
+ }
+
+ Path getPath() {
+ return path;
+ }
+
+ // TODO: Use binary search, must use lock
+ public int findIndex(Instant timestamp) {
+ int index = 0;
+ for (int i = 0; i < segments.length; i++) {
+ RecordedEvent[] events = (RecordedEvent[]) segments[i];
+ if (events == null || events.length == 0) {
+ return Math.max(index - 1, 0);
+ }
+ RecordedEvent e = events[0]; // May not be sorted.
+ if (timestamp.isAfter(e.getEndTime())) {
+ return Math.max(index - 1, 0);
+ }
+ }
+ return segments.length;
+ }
+
+ public void addFilter(InternalEventFilter filter) {
+ try {
+ lock.lock();
+ filters.add(filter);
+ updateGlobalFilter();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ // held with lock
+ private void updateGlobalFilter() {
+ globalFilter = InternalEventFilter.merge(filters);
+ dirtyFilter = true;
+ }
+
+ public RecordedEvent[] readEvents(int index) throws Exception {
+ while (!closed) {
+
+ RecordedEvent[] events = (RecordedEvent[]) segments[index];
+ if (events != null) {
+ return events;
+ }
+ if (lock.tryLock(250, TimeUnit.MILLISECONDS)) {
+ try {
+ addSegment(index);
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+ return null;
+ }
+
+ // held with lock
+ private void addSegment(int index) throws IOException {
+ if (chunkParser == null) {
+ chunkParser = new ChunkParser(new RecordingInput(path.toFile()));
+ }
+ if (dirtyFilter) {
+ chunkParser.setParserFilter(globalFilter);
+ }
+ if (segments[index] != null) {
+ return;
+ }
+ if (index == segments.length - 2) {
+ segments = Arrays.copyOf(segments, segments.length * 2);
+ }
+ RecordedEvent[] segment = new RecordedEvent[10];
+ int i = 0;
+ while (true) {
+ RecordedEvent e = chunkParser.readStreamingEvent(awaitNewEvents);
+ if (e == null) {
+ // wait for new event with next call to readStreamingEvent()
+ awaitNewEvents = true;
+ break;
+ }
+ awaitNewEvents = false;
+ if (i == segment.length) {
+ segment = Arrays.copyOf(segment, segment.length * 2);
+ }
+ segment[i++] = e;
+ }
+
+ // no events found
+ if (i == 0) {
+ if (chunkParser.isChunkFinished()) {
+ segments[index] = END_OF_SET;
+ return;
+ }
+ }
+ // at least 2 events, sort them
+ if (i > 1) {
+ Arrays.sort(segment, 0, i, (e1, e2) -> Long.compare(e1.endTime, e2.endTime));
+ }
+ segments[index] = segment;
+ if (chunkParser.isChunkFinished()) {
+ segments[index + 1] = END_OF_SET;
+ }
+ }
+
+ public long getStartTimeNanos() {
+ return startTimeNanos;
+ }
+
+ public EventSet next(InternalEventFilter filter) throws IOException {
+ EventSet next = location.acquire(startTimeNanos + 1, this);
+ if (next == null) {
+ // closed
+ return null;
+ }
+ next.addFilter(filter);
+ release(filter);
+ return next;
+ }
+
+ public void acquire() {
+ try {
+ lock.lock();
+ referenceCount++;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public String toString() {
+ return "Chunk:" + id + " (" + path + ")";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventSetLocation.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.Repository;
+import jdk.jfr.internal.consumer.ChunkHeader;
+
+/**
+ * This class corresponds to a disk repository.
+ * <p>
+ * Main purpose is to act as a cache if multiple {@code EventStream} want to
+ * access the same repository. An {@code EventSetLocation} should be released
+ * when it is no longer being used.
+ *
+ */
+final class EventSetLocation {
+ private static Map<Path, EventSetLocation> locations = new HashMap<>();
+
+ private final SortedMap<Long, EventSet> eventSets = new TreeMap<>();
+ private final Map<Path, Long> lastPaths = new HashMap<>();
+
+ final Path path;
+ private int count = 0;
+ private volatile boolean closed;
+
+ private EventSetLocation(Path path) {
+ this.path = path;
+ }
+
+ public static EventSetLocation get(Path absolutPath) {
+ synchronized (locations) {
+ EventSetLocation esl = locations.get(absolutPath);
+ if (esl == null) {
+ esl = new EventSetLocation(absolutPath);
+ locations.put(absolutPath, esl);
+ }
+ esl.count++;
+ return esl;
+ }
+ }
+
+ public static EventSetLocation current() throws IOException {
+ Repository.getRepository().ensureRepository();
+ return get(Repository.getRepository().getRepositoryPath().toPath());
+ }
+
+ public void release() {
+ synchronized (locations) {
+ count--;
+ if (count == 0) {
+ locations.remove(path);
+ closed = true;
+ }
+ }
+ }
+
+ public synchronized EventSet acquire(long startTimeNanos, EventSet previousEventSet) {
+ synchronized (eventSets) {
+ while (!closed) {
+ SortedMap<Long, EventSet> after = eventSets.tailMap(startTimeNanos);
+ if (!after.isEmpty()) {
+ EventSet es = after.get(after.firstKey());
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE, "Acquired " + startTimeNanos + ", got " + es);
+ es.acquire();
+ return es;
+ }
+ try {
+ updateEventSets(previousEventSet);
+ } catch (IOException e) {
+ e.printStackTrace();
+ // This can happen if a chunk is being removed
+ // between the file was discovered and an instance
+ // of an EventSet was constructed. Just ignore,
+ // and retry later.
+ }
+ try {
+ eventSets.wait(1000);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ return null;
+ }
+
+ private void updateEventSets(EventSet previousEventSet) throws IOException {
+ List<Path> added = new ArrayList<>();
+ Set<Path> current = new HashSet<>();
+ try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(path, "*.jfr")) {
+ for (Path p : dirStream) {
+ if (!lastPaths.containsKey(p)) {
+ added.add(p);
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "New file found: " + p.toAbsolutePath());
+ }
+ current.add(p);
+ }
+ }
+ List<Path> removed = new ArrayList<>();
+ for (Path p : lastPaths.keySet()) {
+ if (!current.contains(p)) {
+ removed.add(p);
+ }
+ }
+
+ for (Path remove : removed) {
+ Long time = lastPaths.get(remove);
+ eventSets.remove(time);
+ lastPaths.remove(remove);
+ }
+ Collections.sort(added, (p1,p2) -> p1.compareTo(p2));
+ for (Path p : added) {
+ // Only add files that have a complete header
+ // as the JVM may be in progress writing the file
+ long size = Files.size(p);
+ if (size >= ChunkHeader.HEADER_SIZE) {
+ EventSet es = new EventSet(this, previousEventSet, p);
+ long startTime = es.getStartTimeNanos();
+ if (startTime == 0) {
+ String errorMsg = "Chunk header should always contain a valid start time";
+ System.err.println(errorMsg);
+ throw new InternalError(errorMsg);
+ }
+ EventSet previous = eventSets.get(startTime);
+ if (previous != null) {
+ String errorMsg = "Found chunk " + p + " with the same start time " + startTime + " as previous chunk " + previous.getPath();
+ System.err.println(errorMsg);
+ throw new InternalError(errorMsg);
+ }
+ eventSets.put(startTime, es);
+ lastPaths.put(p, startTime);
+ previousEventSet = es;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.function.Consumer;
+
+/**
+ * Represents a stream of event that actions can be performed up on.
+ */
+public interface EventStream extends AutoCloseable {
+
+ /**
+ * Creates a stream starting from the next written event in a disk
+ * repository.
+ *
+ * @param directory location of the disk repository, not {@code null}
+ * @return an event stream, not {@code null}
+ */
+ public static EventStream openRepository(Path directory) throws IOException {
+ throw new UnsupportedOperationException("Not yet implemented");
+// AccessControlContext acc = AccessController.getContext();
+// return new EventDirectoryStream(acc);
+ }
+
+ /**
+ * Creates an event stream starting from the first event in a file.
+ *
+ * @param file location of the file, not {@code null}
+ * @return an event stream, not {@code null}
+ */
+ public static EventStream openFile(Path file) throws IOException {
+ throw new UnsupportedOperationException("Not yet implemented");
+// return new EventFileStream(file);
+ }
+
+ /**
+ * Performs an action on all events in the stream.
+ *
+ * @param action an action to be performed on each {@code RecordedEvent},
+ * not {@code null}
+ */
+ void onEvent(Consumer<RecordedEvent> action);
+
+ /**
+ * Performs an action on all events in the stream with a specified name.
+ *
+ * @param eventName the name of the event, not {@code null}
+ * @param action an action to be performed on each {@code RecordedEvent}
+ * that matches the event name, not {@code null}
+ */
+ void onEvent(String eventName, Consumer<RecordedEvent> action);
+
+ /**
+ * Performs an action when the event stream has been flushed.
+ *
+ * @param action an action to be performed after stream has been flushed,
+ * not {@code null}
+ */
+ void onFlush(Runnable action);
+
+ /**
+ * Performs an action when the event stream is closed.
+ *
+ * @param action an action to be performed after the stream has been closed,
+ * not {@code null}
+ */
+ void onClose(Runnable action);
+
+ /**
+ * Releases all resources associated with this event stream.
+ */
+ void close();
+
+ /**
+ * Removes an action from the stream.
+ * <p>
+ * If the action has been added multiple times, all instance of it will be
+ * removed.
+ *
+ * @param action the action to remove, not {@code null}
+ * @return {@code true} if the action was removed, {@code false} otherwise
+ *
+ * @see #onClose(Runnable)
+ * @see #onFlush(Runnable)
+ * @see #onEvent(Consumer)
+ * @see #onEvent(String, Consumer)
+ */
+ boolean remove(Object action);
+
+ /**
+ * Starts processing events in the stream.
+ * <p>
+ * All actions will performed on this stream will happen in the current
+ * thread.
+ *
+ * @throws IllegalStateException if the stream is already started or if it
+ * has been closed
+ */
+ void start();
+
+ /**
+ * Starts processing events in the stream asynchronously.
+ * <p>
+ * All actions on this stream will be performed in a separate thread.
+ *
+ * @throws IllegalStateException if the stream is already started or if it
+ * has been closed
+ */
+ void startAsync();
+
+ /**
+ * Blocks the current thread until the stream is finished, closed, or it
+ * times out.
+ *
+ * @param timeout the maximum time to wait, not {@code null}
+ *
+ * @see #start()
+ * @see #startAsync()
+ */
+ void awaitTermination(Duration timeout);
+
+ /**
+ * Blocks the current thread until the stream is finished or closed.
+ *
+ * @see #start()
+ * @see #startAsync()
+ */
+ void awaitTermination();
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/InternalEventFilter.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public final class InternalEventFilter {
+ static final InternalEventFilter ACCEPT_ALL = new InternalEventFilter();
+ private final Map<String, Long> thresholds = new HashMap<>();
+ private boolean acceptAll;
+
+ public static InternalEventFilter merge(Collection<InternalEventFilter> filters) {
+ for (InternalEventFilter ef : filters) {
+ if (ef.getAcceptAll()) {
+ return ACCEPT_ALL;
+ }
+ }
+ if (filters.size() == 1) {
+ return filters.iterator().next();
+ }
+
+ Set<String> eventNames = new HashSet<>();
+ for (InternalEventFilter ef : filters) {
+ eventNames.addAll(ef.thresholds.keySet());
+ }
+ InternalEventFilter result = new InternalEventFilter();
+ for (String eventName : eventNames) {
+ for (InternalEventFilter ef : filters) {
+ Long l = ef.thresholds.get(eventName);
+ if (l != null) {
+ result.setThreshold(eventName, l.longValue());
+ }
+ }
+ }
+ return result;
+ }
+
+ private boolean getAcceptAll() {
+ return acceptAll;
+ }
+
+ public void setAcceptAll() {
+ acceptAll = true;
+ }
+
+ public void setThreshold(String eventName, long nanos) {
+ Long l = thresholds.get(eventName);
+ if (l != null) {
+ l = Math.min(l, nanos);
+ } else {
+ l = nanos;
+ }
+ thresholds.put(eventName, l);
+ }
+
+ public long getThreshold(String eventName) {
+ if (acceptAll) {
+ return 0;
+ }
+ Long l = thresholds.get(eventName);
+ if (l != null) {
+ return l;
+ }
+ return -1;
+ }
+ public String toString() {
+ if (acceptAll) {
+ return "ACCEPT ALL";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String key : thresholds.keySet().toArray(new String[0])) {
+ Long value = thresholds.get(key);
+ sb.append(key);
+ sb.append(" = ");
+ sb.append(value.longValue() / 1_000_000);
+ sb.append(" ms");
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/Parser.java Fri May 17 16:02:27 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.io.IOException;
-
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Base class for parsing data from a {@link RecordingInput}.
- */
-abstract class Parser {
- /**
- * Parses data from a {@link RecordingInput} and return an object.
- *
- * @param input input to read from
- * @return an object
- * @throws IOException if operation couldn't be completed due to I/O
- * problems
- */
- abstract Object parse(RecordingInput input) throws IOException;
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.time.Duration;
+import java.util.function.Consumer;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.Event;
+import jdk.jfr.EventSettings;
+import jdk.jfr.EventType;
+import jdk.jfr.Recording;
+import jdk.jfr.internal.PlatformRecording;
+import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.Utils;
+
+/**
+ * An event stream produces events from a file, directory or a running JVM (Java
+ * Virtual Machine).
+ */
+public class RecordingStream implements AutoCloseable, EventStream {
+
+ private final Recording recording;
+ private final EventDirectoryStream stream;
+
+ /**
+ * Creates an event stream for this JVM (Java Virtual Machine).
+ * <p>
+ * The following example shows how to create a recording stream that prints
+ * CPU usage and information about garbage collections.
+ *
+ * <pre>
+ * <code>
+ * try (RecordingStream r = new RecordingStream()) {
+ * r.enable("jdk.GarbageCollection");
+ * r.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
+ * r.onEvent(System.out::println);
+ * r.start();
+ * }
+ * </code>
+ * </pre>
+ *
+ * @throws IllegalStateException if Flight Recorder can't be created (for
+ * example, if the Java Virtual Machine (JVM) lacks Flight Recorder
+ * support, or if the file repository can't be created or accessed)
+ *
+ * @throws SecurityException if a security manager exists and the caller
+ * does not have
+ * {@code FlightRecorderPermission("accessFlightRecorder")}
+ */
+ public RecordingStream() {
+ Utils.checkAccessFlightRecorder();
+ AccessControlContext acc = AccessController.getContext();
+ this.recording = new Recording();
+ this.recording.setFlushInterval(Duration.ofMillis(1000));
+ try {
+ this.stream = new EventDirectoryStream(acc);
+ } catch (IOException ioe) {
+ throw new IllegalStateException(ioe.getMessage());
+ }
+ }
+
+ /**
+ * Creates a recording stream using settings from a configuration.
+ * <p>
+ * The following example shows how to create a recording stream that uses a
+ * predefined configuration.
+ *
+ * <pre>
+ * <code>
+ * Configuration c = Configuration.getConfiguration("default");
+ * try (RecordingStream r = new RecordingStream(c)) {
+ * r.onEvent(System.out::println);
+ * r.start();
+ * }
+ * </code>
+ * </pre>
+ *
+ * @param configuration configuration that contains the settings to be use,
+ * not {@code null}
+ *
+ * @throws IllegalStateException if Flight Recorder can't be created (for
+ * example, if the Java Virtual Machine (JVM) lacks Flight Recorder
+ * support, or if the file repository can't be created or accessed)
+ *
+ * @throws SecurityException if a security manager is used and
+ * FlightRecorderPermission "accessFlightRecorder" is not set.
+ *
+ * @see Configuration
+ */
+ public RecordingStream(Configuration configuration) {
+ this();
+ recording.setSettings(configuration.getSettings());
+ }
+
+ /**
+ * Enables the event with the specified name.
+ * <p>
+ * If multiple events have the same name (for example, the same class is
+ * loaded in different class loaders), then all events that match the name
+ * are enabled. To enable a specific class, use the {@link #enable(Class)}
+ * method or a {@code String} representation of the event type ID.
+ *
+ * @param name the settings for the event, not {@code null}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ *
+ * @see EventType
+ */
+ public EventSettings enable(String name) {
+ return recording.enable(name);
+ }
+
+ /**
+ * Enables event.
+ *
+ * @param eventClass the event to enable, not {@code null}
+ *
+ * @throws IllegalArgumentException if {@code eventClass} is an abstract
+ * class or not a subclass of {@link Event}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ */
+ public EventSettings enable(Class<? extends Event> eventClass) {
+ return recording.enable(eventClass);
+ }
+
+ /**
+ * Disables event with the specified name.
+ * <p>
+ * If multiple events with same name (for example, the same class is loaded
+ * in different class loaders), then all events that match the name is
+ * disabled. To disable a specific class, use the {@link #disable(Class)}
+ * method or a {@code String} representation of the event type ID.
+ *
+ * @param name the settings for the event, not {@code null}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ *
+ */
+ public EventSettings disable(String name) {
+ return recording.disable(name);
+ }
+
+ /**
+ * Disables event.
+ *
+ * @param eventClass the event to enable, not {@code null}
+ *
+ * @throws IllegalArgumentException if {@code eventClass} is an abstract
+ * class or not a subclass of {@link Event}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ *
+ */
+ public EventSettings disable(Class<? extends Event> eventClass) {
+ return recording.disable(eventClass);
+ }
+ /**
+ * Determines how far back data is kept for the stream if the stream can't
+ * keep up.
+ * <p>
+ * To control the amount of recording data stored on disk, the maximum
+ * length of time to retain the data can be specified. Data stored on disk
+ * that is older than the specified length of time is removed by the Java
+ * Virtual Machine (JVM).
+ * <p>
+ * If neither maximum limit or the maximum age is set, the size of the
+ * recording may grow indefinitely if events are on
+ *
+ * @param maxAge the length of time that data is kept, or {@code null} if
+ * infinite
+ *
+ * @throws IllegalArgumentException if <code>maxAge</code> is negative
+ *
+ * @throws IllegalStateException if the recording is in the {@code CLOSED}
+ * state
+ */
+ public void setMaxAge(Duration maxAge) {
+ recording.setMaxAge(maxAge);
+ }
+
+ /**
+ * Determines how much data is kept in the disk repository if the stream
+ * can't keep up.
+ * <p>
+ * To control the amount of recording data that is stored on disk, the
+ * maximum amount of data to retain can be specified. When the maximum limit
+ * is exceeded, the Java Virtual Machine (JVM) removes the oldest chunk to
+ * make room for a more recent chunk.
+ * <p>
+ * If neither maximum limit or the maximum age is set, the size of the
+ * recording may grow indefinitely.
+ *
+ * @param maxSize the amount of data to retain, {@code 0} if infinite
+ *
+ * @throws IllegalArgumentException if <code>maxSize</code> is negative
+ *
+ * @throws IllegalStateException if the recording is in {@code CLOSED} state
+ */
+ public void setMaxSize(long maxSize) {
+ recording.setMaxSize(maxSize);
+ }
+
+ @Override
+ public void onEvent(String eventName, Consumer<RecordedEvent> action) {
+ stream.onEvent(eventName, action);
+ }
+
+ @Override
+ public void onEvent(Consumer<RecordedEvent> action) {
+ stream.onEvent(action);
+ }
+
+ @Override
+ public void onFlush(Runnable action) {
+ stream.onFlush(action);
+ }
+
+ @Override
+ public void onClose(Runnable action) {
+ stream.onClose(action);
+ }
+
+ @Override
+ public void close() {
+ recording.close();
+ stream.close();
+ }
+
+ @Override
+ public boolean remove(Object action) {
+ return stream.remove(action);
+ }
+
+ @Override
+ public void start() {
+ PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
+ long startNanos = pr.start();
+ stream.start(startNanos);
+ }
+
+ @Override
+ public void startAsync() {
+ PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
+ long startNanos = pr.start();
+ stream.startAsync(startNanos);
+ }
+
+ @Override
+ public void awaitTermination(Duration timeout) {
+ stream.awaitTermination(timeout);
+ }
+
+ /**
+ * Determines how often events are made available for streaming.
+ *
+ * @param interval the interval at which events are made available to the
+ * stream
+ *
+ * @throws IllegalArgumentException if <code>interval</code> is negative
+ *
+ * @throws IllegalStateException if the stream is closed
+ */
+ public void setInterval(Duration duration) {
+ recording.setFlushInterval(duration);
+ }
+
+ @Override
+ public void awaitTermination() {
+ stream.awaitTermination();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/StringParser.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringEncoding;
+
+final class StringParser extends Parser {
+ private final static Charset UTF8 = Charset.forName("UTF-8");
+ private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
+
+
+ final static class CharsetParser extends Parser {
+ private final Charset charset;
+ private int lastSize;
+ private byte[] buffer = new byte[16];
+ private String lastString;
+
+ CharsetParser(Charset charset) {
+ this.charset = charset;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ ensureSize(size);
+ if (lastSize == size) {
+ boolean equalsLastString = true;
+ for (int i = 0; i < size; i++) {
+ // TODO: No need to read byte per byte
+ byte b = input.readByte();
+ if (buffer[i] != b) {
+ equalsLastString = false;
+ buffer[i] = b;
+ }
+ }
+ if (equalsLastString) {
+ return lastString;
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ buffer[i] = input.readByte();
+ }
+ }
+ lastString = new String(buffer, 0, size, charset);
+ lastSize = size;
+ return lastString;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ input.skipBytes(size);
+ }
+
+ private void ensureSize(int size) {
+ if (buffer.length < size) {
+ buffer = new byte[size];
+ }
+ }
+ }
+
+ final static class CharArrayParser extends Parser {
+ private char[] buffer = new char[16];
+ private int lastSize = -1;
+ private String lastString = null;
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ ensureSize(size);
+ if (lastSize == size) {
+ boolean equalsLastString = true;
+ for (int i = 0; i < size; i++) {
+ char c = input.readChar();
+ if (buffer[i] != c) {
+ equalsLastString = false;
+ buffer[i] = c;
+ }
+ }
+ if (equalsLastString) {
+ return lastString;
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ buffer[i] = input.readChar();
+ }
+ }
+ lastString = new String(buffer, 0, size);
+ lastSize = size;
+ return lastString;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ for (int i = 0; i < size; i++) {
+ input.readChar();
+ }
+ }
+
+ private void ensureSize(int size) {
+ if (buffer.length < size) {
+ buffer = new char[size];
+ }
+ }
+ }
+
+ private final ConstantLookup stringLookup;
+ private final CharArrayParser charArrayParser = new CharArrayParser();
+ private final CharsetParser utf8parser = new CharsetParser(UTF8);
+ private final CharsetParser latin1parser = new CharsetParser(LATIN1);
+ private final boolean event;
+
+ public StringParser(ConstantLookup stringLookup, boolean event) {
+ this.stringLookup = stringLookup;
+ this.event = event;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ byte encoding = input.readByte();
+ if (encoding == StringEncoding.STRING_ENCODING_CONSTANT_POOL) {
+ long key = input.readLong();
+ if (event) {
+ return stringLookup.getCurrentResolved(key);
+ } else {
+ return stringLookup.getCurrent(key);
+ }
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_NULL) {
+ return null;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_EMPTY_STRING) {
+ return "";
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_CHAR_ARRAY) {
+ return charArrayParser.parse(input);
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_UTF8_BYTE_ARRAY) {
+ return utf8parser.parse(input);
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_LATIN1_BYTE_ARRAY) {
+ return latin1parser.parse(input);
+ }
+ throw new IOException("Unknown string encoding " + encoding);
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ byte encoding = input.readByte();
+ if (encoding == StringEncoding.STRING_ENCODING_CONSTANT_POOL) {
+ input.readLong();
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_EMPTY_STRING) {
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_NULL) {
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_CHAR_ARRAY) {
+ charArrayParser.skip(input);
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_UTF8_BYTE_ARRAY) {
+ utf8parser.skip(input);
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_LATIN1_BYTE_ARRAY) {
+ latin1parser.skip(input);
+ return;
+ }
+ throw new IOException("Unknown string encoding " + encoding);
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/UseCasesStream.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.time.Duration;
+import java.util.ArrayDeque;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+
+class UseCasesStream {
+
+ //
+ // Use case: Out-of-the-Box Experience
+ //
+ // - Simple things should be simple
+ // - Pique interest, i.e. a one-liner on Stack Overflow
+ // - Few lines of code as possible
+ // - Should be easier than alternative technologies, like JMX and JVM TI
+ //
+ // - Non-goals: Corner-cases, advanced configuration, releasing resources
+ //
+ public static void outOfTheBox() throws Exception {
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.enable("jdk.ExceptionThrown");
+ rs.onEvent(e -> System.out.println(e.getString("message")));
+ rs.start();
+ }
+
+ // EventStream.start("jdk.JavaMonitorEnter", "threshold", "20 ms",
+ // "stackTrace", "false")
+ // .addConsumer(System.out::println);
+ //
+ // EventStream.start("jdk.CPULoad", "period", "1 s")
+ // .addConsumer(e -> System.out.println(100 *
+ // e.getDouble("totalMachine") + " %"));
+ //
+ // EventStream.start("jdk.GarbageCollection")
+ // .addConsumer(e -> System.out.println("GC: " + e.getStartTime() + "
+ // maxPauseTime=" + e.getDuration("maxPauseTime").toMillis() + " ms"));
+
+ Thread.sleep(100_000);
+ }
+
+ // Use case: Event Forwarding
+ //
+ // - Forward arbitrary event to frameworks such as RxJava, JSON/XML and
+ // Kafka
+ // - Handle flooding
+ // - Performant
+ // - Graceful shutdown
+ // - Non-goals: Filter events
+ //
+ public static void eventForwarding() throws InterruptedException, IOException, ParseException {
+ // KafkaProducer producer = new KafkaProducer<String, String>();
+ try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("default"))) {
+ rs.setMaxAge(Duration.ofMinutes(5));
+ rs.setMaxSize(1000_000_000L);
+ // es.setParallel(true);
+ // es.setReuse(true);
+ // es.consume(e -> producer.send(new ProducerRecord<String,
+ // String>("topic",
+ // e.getString("key"), e.getString("value"))));
+ rs.start();
+ }
+ // Write primitive values to XML
+ try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("deafult"))) {
+ try (PrintWriter p = new PrintWriter(new FileWriter("recording.xml"))) {
+ // es.setParallel(false);
+ // es.setReuse(true);
+ p.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
+ p.println("<events>");
+ rs.onEvent(e -> {
+ EventType type = e.getEventType();
+ p.println(" <event type=\"" + type.getName() + "\" start=\"" + e.getStartTime() + "\" end=\"" + e.getEndTime() + "\">");
+ for (ValueDescriptor field : e.getEventType().getFields()) {
+ Object value = e.getValue(field.getName());
+ if (value instanceof Number || field.getTypeName().equals("java.lang.String")) {
+ p.println(" <value field=\"" + field.getName() + "\">" + value + "</value>");
+ }
+ }
+ });
+ rs.start();
+ p.println("</events>");
+ }
+ }
+ }
+
+ // Use case: Repository Access
+ //
+ // - Read the disk repository from another process, for example a side car
+ // in
+ // Docker container
+ // - Be able to configure flush interval from command line or jcmd.
+ // - Graceful shutdown
+ //
+ public static void repositoryAccess() throws IOException, InterruptedException {
+ Path repository = Paths.get("c:\\repository").toAbsolutePath();
+ String command = new String();
+ command += "java -XX:StartFlightRecording:flush=2s";
+ command += "-XX:FlightRecorderOption:repository=" + repository + " Application";
+ Process myProcess = Runtime.getRuntime().exec(command);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(System.out::println);
+ rs.startAsync();
+ Thread.sleep(10_000);
+ myProcess.destroy();
+ Thread.sleep(10_000);
+ }
+ }
+
+ // Use: Tooling
+ //
+ // - Monitor a stream of data for a very long time
+ // - Predictable interval, i.e. once every second
+ // - Notification with minimal delay
+ // - Events with the same period should arrive together
+ // - Consume events in chronological order
+ // - Low overhead
+ //
+ public static void tooling() throws IOException, ParseException {
+ ArrayDeque<Double> measurements = new ArrayDeque<>();
+ try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("profile"))) {
+ rs.setInterval(Duration.ofSeconds(1));
+ rs.setMaxAge(Duration.ofMinutes(1));
+ // rs.setOrdered(true);
+ // rs.setReuse(false);
+ // rs.setParallel(true);
+ rs.onEvent("jdk.CPULoad", e -> {
+ double d = e.getDouble("totalMachine");
+ measurements.addFirst(d);
+ if (measurements.size() > 60) {
+ measurements.removeLast();
+ }
+ // repaint();
+ });
+ rs.start();
+ }
+ }
+
+ // Use case: Low Impact
+ //
+ // - Support event subscriptions in a low latency environment (minimal GC
+ // pauses)
+ // - Filter out relevant events to minimize disk overhead and allocation
+ // pressure
+ // - Avoid impact from other recordings
+ // - Avoid Heisenberg effects, in particular self-recursion
+ //
+ // Non-goals: one-liner
+ //
+ public static void lowImpact() throws InterruptedException, IOException, ParseException {
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.enable("jdk.JavaMonitorEnter#threshold").withThreshold(Duration.ofMillis(10));
+ rs.enable("jdk.ExceptionThrow#enabled");
+ // ep.setReuse(true);
+ rs.onEvent("jdk.JavaMonitorEnter", System.out::println);
+ rs.onEvent("jdk.ExceptionThrow", System.out::println);
+ rs.start();
+ ;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/FilePurger.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,47 @@
+package jdk.jfr.internal;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import jdk.jfr.internal.SecuritySupport.SafePath;
+
+// This class keeps track of files that can't be deleted
+// so they can a later staged be removed.
+final class FilePurger {
+
+ private final static Set<SafePath> paths = new LinkedHashSet<>();
+
+ public synchronized static void add(SafePath p) {
+ paths.add(p);
+ if (paths.size() > 1000) {
+ removeOldest();
+ }
+ }
+
+ public synchronized static void purge() {
+ if (paths.isEmpty()) {
+ return;
+ }
+
+ for (SafePath p : new ArrayList<>(paths)) {
+ if (delete(p)) {
+ paths.remove(p);
+ }
+ }
+ }
+
+ private static void removeOldest() {
+ SafePath oldest = paths.iterator().next();
+ paths.remove(oldest);
+ }
+
+ private static boolean delete(SafePath p) {
+ try {
+ SecuritySupport.delete(p);
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/Parser.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+
+/**
+ * Base class for parsing data from a {@link RecordingInput}.
+ */
+public abstract class Parser {
+ /**
+ * Parses data from a {@link RecordingInput} and return an object.
+ *
+ * @param input input to read from
+ * @return an object
+ * @throws IOException if operation couldn't be completed due to I/O
+ * problems
+ */
+ public abstract Object parse(RecordingInput input) throws IOException;
+
+ /**
+ * Skips data that would usually be by parsed the {@code #parse(RecordingInput)} method.
+ *
+ * @param input input to read from
+ * @throws IOException if operation couldn't be completed due to I/O
+ * problems
+ */
+ public abstract void skip(RecordingInput input) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringEncoding.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,11 @@
+package jdk.jfr.internal.consumer;
+
+public final class StringEncoding {
+ public final static byte STRING_ENCODING_NULL = (byte) 0;
+ public final static byte STRING_ENCODING_EMPTY_STRING = (byte) 1;
+ public final static byte STRING_ENCODING_CONSTANT_POOL = (byte) 2;
+ public final static byte STRING_ENCODING_UTF8_BYTE_ARRAY = (byte) 3;
+ public final static byte STRING_ENCODING_CHAR_ARRAY = (byte) 4;
+ public final static byte STRING_ENCODING_LATIN1_BYTE_ARRAY = (byte) 5;
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/PerfectHashMap.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,340 @@
+package jdk.jfr.internal.util;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class PerfectHashMap<V> {
+ private static final long COLLISION_SHIFT = 63;
+ private static final long COLLISION_BIT = 1L << COLLISION_SHIFT;
+ private static final long COLLISION_MASK = COLLISION_BIT - 1;
+ private static final int MAX_REMAP_ATTEMPTS = 100000;
+ private static final int MAX_ATTEMPS_BEFORE_RESIZE = 100;
+
+ static final long W = 64L;
+ static class LinkedValue<V> {
+ final V value;
+ long next;
+
+ LinkedValue(V value) {
+ this.value = value;
+ this.next = 0;
+ }
+ }
+
+ private UniversalHashFamily hashFamily = new UniversalHashFamily();
+ private PrimitiveHashMap<LinkedValue<V>> loadMap;
+ private Object[] valueTable;
+ private long[] routeTable;
+ private long shift;
+ private long shiftMask;
+ private int tableLengthMask;
+ private long primaryHashFunction = 0;
+ private int collisions = 0;
+ private int retries = 0;
+ private int sizeFactor = 1;
+ private boolean minimal;
+
+ public V get(long key) {
+ LinkedValue<V> v = loadMap.get(key);
+ return v != null ? v.value : null;
+ }
+
+ public V put(long key, V value) {
+ LinkedValue<V> existing = loadMap.put(key, new LinkedValue<V>(value));
+ return existing != null ? existing.value : null;
+ }
+
+ public void forEach(BiConsumer<? super Long, ? super V> action) {
+ //loadMap.forEach(PerfectHashMap<V>::callback);
+ }
+
+ public final void forEach(Consumer<? super V> action) {
+ //loadMap.forEach(action);
+ }
+
+ public final long[] keys() {
+ return loadMap.keys();
+ }
+
+ static class Log2 {
+ private static final int MAX_SIZE_EXPONENT = 32;
+
+ static long log2base10(long exponent) {
+ return 1L << exponent;
+ }
+
+ static int log2(int value) {
+ int i = 0;
+ int lastMultiple = 0;
+ while (i < MAX_SIZE_EXPONENT) {
+ int multiple = (int)log2base10(i);
+ if ((value & multiple) != 0) {
+ lastMultiple = i;
+ }
+ ++i;
+ }
+ return ((int)log2base10(lastMultiple) ^ value) != 0 ? lastMultiple + 1 : lastMultiple;
+ }
+ }
+
+ static final int tableExponent(int cap) {
+ return Log2.log2(cap);
+ }
+
+ PerfectHashMap() {
+ this(false, 101);
+ }
+
+ PerfectHashMap(int size) {
+ this(false, size);
+ }
+
+ PerfectHashMap(boolean minimal, int size) {
+ this.minimal = minimal;
+ this.loadMap = new PrimitiveHashMap<>(size);
+ this.primaryHashFunction = hashFamily.getRandomHashFunction();
+ }
+
+ @SuppressWarnings("unchecked")
+ public V getPerfect(long key) {
+ int routeIdx = getIndex(key, primaryHashFunction);
+ assert(routeIdx >= 0);
+ assert(routeIdx < routeTable.length);
+ long element = routeTable[routeIdx];
+ int valueIdx = element < 0 ? getIndex(key, -element - 1) : (int)element;
+ assert(valueIdx >= 0);
+ assert(valueIdx < valueTable.length);
+ return (V)valueTable[valueIdx];
+ }
+
+ private long getRandomHashFunction() {
+ return hashFamily.getRandomHashFunction();
+ }
+ private int getIndex(long key, long hashFunction) {
+ final int idx = UniversalHashFamily.getIndex(key, hashFunction, shift, shiftMask);
+ assert(idx >= 0);
+ assert(idx < routeTable.length);
+ return idx;
+ }
+ private static boolean isColliding(long entry) {
+ return entry < 0;
+ }
+ private boolean isNonColliding(long entry) {
+ return entry > 0;
+ }
+ private static long setColliding(long entry) {
+ return entry | COLLISION_BIT;
+ }
+ private static long read(long entry) {
+ return entry & COLLISION_MASK;
+ }
+
+ private int nextValueTableSlot(int lastIdx) {
+ assert(lastIdx < valueTable.length);
+ int i = lastIdx;
+ for (; i < valueTable.length; ++i) {
+ if (valueTable[i] == null) {
+ break;
+ }
+ }
+ return i;
+ }
+
+ private int valueTableStore(V value, int lastIdx) {
+ if (lastIdx > valueTable.length) {
+ lastIdx = 0;
+ }
+ assert(lastIdx < valueTable.length);
+ final int idx = nextValueTableSlot(lastIdx);
+ assert(idx < valueTable.length);
+ assert(valueTable[idx] == null);
+ valueTable[idx] = value;
+ return idx;
+ }
+
+
+ private void routeNonCollisions() {
+ int lastIdx = 0;
+ for (int i = 0; i < routeTable.length; ++i) {
+ if (isNonColliding(routeTable[i])) {
+ lastIdx = valueTableStore(loadMap.get(routeTable[i]).value, lastIdx);
+ routeTable[i] = lastIdx++;
+ }
+ }
+ }
+
+ private void rollback(int idx, int length, long hashFunction) {
+ assert(isColliding(routeTable[idx]));
+ long key = read(routeTable[idx]);
+ LinkedValue<V> v = loadMap.get(key); // boxing
+ for (int i = 0; i < length; ++i) {
+ final int valueIdx = getIndex(key, hashFunction);
+ assert(valueIdx >= 0);
+ assert(valueIdx < valueTable.length);
+ assert(valueTable[valueIdx] != null);
+ valueTable[valueIdx] = null;
+ key = v.next;
+ v = loadMap.get(v.next); // no boxing
+ }
+ }
+
+ private boolean remap(int idx, long hashFunction) {
+ assert(isColliding(routeTable[idx]));
+ int completed = 0;
+ long key = read(routeTable[idx]);
+ LinkedValue<V> v = loadMap.get(key);
+ while (key != 0) {
+ final int valueIdx = getIndex(key, hashFunction);
+ assert(valueIdx >= 0);
+ assert(valueIdx < valueTable.length);
+ if (valueTable[valueIdx] == null) {
+ valueTable[valueIdx] = v.value;
+ ++completed;
+ key = v.next;
+ v = loadMap.get(v.next);
+ continue;
+ }
+ rollback(idx, completed, hashFunction);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean routeCollisions(int idx) {
+ assert(isColliding(routeTable[idx]));
+ boolean success = false;
+ int attempts = 0;
+ long randomHashFunction = 0;
+ do {
+ randomHashFunction = getRandomHashFunction();
+ success = remap(idx, randomHashFunction);
+ if (++attempts == MAX_REMAP_ATTEMPTS) {
+ System.out.println("Failed number of attempts - restart: " + attempts);
+ return false;
+ }
+ } while (!success);
+ System.out.println("Number of remap attempts: " + attempts);
+ routeTable[idx] = -1 - randomHashFunction;
+ assert(-routeTable[idx] - 1 == randomHashFunction);
+ return true;
+ }
+
+
+ private boolean routeCollisions() {
+ for (int i = 0; i < routeTable.length; ++i) {
+ if (isColliding(routeTable[i])) {
+ if (!routeCollisions(i)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static void clearLongTable(long[] table) {
+ Arrays.fill(table, 0);
+ for (int i = 0; i < table.length; ++i) {
+ assert(table[i] == 0);
+ }
+ }
+
+ private static <T extends Object> void clearReferenceTable(T[] table) {
+ Arrays.fill(table, null);
+ for (int i = 0; i < table.length; ++i) {
+ assert(table[i] == null);
+ }
+ }
+
+ private void unlinkChains() {
+ for (long key : loadMap.keys()) {
+ loadMap.get(key).next = 0;
+ }
+ }
+
+ private void routeTableStore(long key, LinkedValue<V> value, int idx) {
+ assert(idx >= 0);
+ assert(idx < routeTable.length);
+ long existing = read(routeTable[idx]);
+ if (existing == 0) {
+ routeTable[idx] = key;
+ return;
+ }
+ ++collisions;
+ routeTable[idx] = setColliding(existing);
+ LinkedValue<V> existingValue = loadMap.get(existing);
+ value.next = existingValue.next;
+ existingValue.next = key;
+ }
+
+ private void mapKeys() {
+ for (long key : loadMap.keys()) {
+ routeTableStore(key, loadMap.get(key), getIndex(key, primaryHashFunction));
+ }
+ }
+
+ private void validate() {
+ for (long key : loadMap.keys()) {
+ long element = routeTable[getIndex(key, primaryHashFunction)];
+ int valueIdx = element < 0 ? getIndex(key, -element - 1) : (int)element;
+ assert(valueIdx >= 0);
+ assert(loadMap.get(key) == valueTable[valueIdx]);
+ }
+ }
+
+ private void reset() {
+ collisions = 0;
+ clearLongTable(routeTable);
+ clearReferenceTable(valueTable);
+ }
+
+ private int dimensionTableSize() {
+ int size = loadMap.size() * sizeFactor;
+ return (int)Log2.log2base10(Log2.log2(size));
+ }
+
+ @SuppressWarnings({"rawtypes","unchecked"})
+ private void allocateTables() {
+ int size = dimensionTableSize();
+ this.tableLengthMask = size - 1;
+ this.shift = W - tableExponent(size);
+ this.shiftMask = Log2.log2base10(shift) - 1;
+ routeTable = new long[size];
+ valueTable = (V[])new Object[size];
+ collisions = 0;
+ retries = 0;
+ }
+
+ public void build() {
+ start:
+ while (true) {
+ allocateTables();
+ System.out.println("Table size " + routeTable.length);
+ mapKeys();
+ if (collisions > 0) {
+ if (!routeCollisions()) {
+ unlinkChains();
+ if (++retries <= MAX_ATTEMPS_BEFORE_RESIZE) {
+ reset();
+ } else {
+ sizeFactor *= 2;
+ }
+ continue start;
+ }
+ }
+ routeNonCollisions();
+ return;
+ }
+ }
+
+ public void rebuild() {
+ sizeFactor = 1;
+ build();
+ }
+ public int size() {
+ return loadMap.size();
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/PrimitiveHashMap.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,341 @@
+package jdk.jfr.internal.util;
+
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.ConcurrentModificationException;
+import java.util.Random;
+
+public class PrimitiveHashMap<V> {
+
+ static final long W = 64L;
+ static final long A = 4633630788178346939L;
+ final Random rand = new Random();
+
+ private static int getIndex(long key, long hashFunction, long shift, long mask) {
+ return (int)(((A * key) + (hashFunction & mask)) >>> shift);
+ }
+ private long getRandomHashFunction() {
+ return rand.nextLong();
+ }
+ /**
+ * The maximum capacity, used if a higher value is implicitly specified
+ * by either of the constructors with arguments.
+ */
+ static final int MAX_SIZE_EXPONENT = 30;
+ static final int MAXIMUM_CAPACITY = 1 << MAX_SIZE_EXPONENT;
+
+ static final int DEFAULT_SIZE_EXPONENT = 4;
+ static final int DEFAULT_INITIAL_CAPACITY = 1 << DEFAULT_SIZE_EXPONENT; // aka 16
+ /**
+ * The load factor used when none specified in constructor.
+ */
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
+ static class Log2 {
+
+ static long log2base10(long exponent) {
+ return 1L << exponent;
+ }
+ static int log2(int value) {
+ int i = 0;
+ int lastMultiple = 0;
+ while (i < MAX_SIZE_EXPONENT) {
+ int multiple = (int)log2base10(i);
+ if ((value & multiple) != 0) {
+ lastMultiple = i;
+ }
+ ++i;
+ }
+ return ((int)log2base10(lastMultiple) ^ value) != 0 ? lastMultiple + 1 : lastMultiple;
+ }
+ }
+ /**
+ * Returns a power of two size for the given target capacity.
+ */
+ static final int tableSizeFor(int cap) {
+ int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+ static final int tableExponent(int cap) {
+ return Log2.log2(cap);
+ }
+
+ static class Node<V> {
+ final long key;
+ V value;
+
+ Node(long key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public final long getKey() { return key; }
+ public final V getValue() { return value; }
+ public final String toString() { return key + "=" + value; }
+ }
+
+ private Node<V>[] table;
+ private int size;
+ private int threshold;
+ private long shift;
+ private long shiftMask;
+ private int tableLengthMask;
+ int modCount;
+ private final float loadFactor;
+ long h1 = 0;
+ long h2 = 0;
+
+ public PrimitiveHashMap(int initialCapacity, float loadFactor) {
+ if (initialCapacity < 0) {
+ throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
+ }
+ if (initialCapacity > MAXIMUM_CAPACITY) {
+ initialCapacity = MAXIMUM_CAPACITY;
+ }
+ if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
+ throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
+ }
+ this.loadFactor = loadFactor;
+ this.threshold = tableSizeFor(initialCapacity);
+ h1 = getRandomHashFunction();
+ h2 = getRandomHashFunction();
+ resize();
+ }
+
+ public PrimitiveHashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ public PrimitiveHashMap() {
+ this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ public final void forEach(BiConsumer<? super Long, ? super V> action) {
+ Node<V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < table.length; ++i) {
+ if (table[i] == null) continue;
+ action.accept(table[i].getKey(), table[i].getValue());
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ public final void forEach(Consumer<? super V> action) {
+ Node<V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < table.length; ++i) {
+ if (table[i] == null) continue;
+ action.accept(table[i].getValue());
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ public final long[] keys() {
+ long[] keys = new long[size];
+ int j = 0;
+ for (int i = 0; i < table.length; ++i) {
+ if (table[i] == null) continue;
+ keys[j++] = table[i].getKey();
+ }
+ assert(j == size);
+ assert(keys.length == size);
+ return keys;
+ }
+
+ public Collection<V> values () {
+ final PrimitiveHashMap<V> thisMap = this;
+ return new AbstractCollection<V>() {
+ private PrimitiveHashMap<V> map = thisMap;
+ public Iterator<V> iterator() {
+ return new Iterator<V>() {
+ private int i = 0;
+ private long [] k = keys();
+ public boolean hasNext() {
+ return i < k.length;
+ }
+ public V next() {
+ assert(i < k.length);
+ return map.get(k[i++]);
+ }
+ };
+ }
+ public int size() {
+ return map.size();
+ }
+ public boolean isEmpty() {
+ return size() != 0;
+ }
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+ public boolean contains(Object v) {
+ for (V value : map.values()) {
+ if (v == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+ private int doubleHash(long key, int i) {
+ int h1_idx = getIndex(key, h1, shift, shiftMask);
+ assert(h1_idx < table.length);
+ int h2_idx = 0;
+ if (i != 0) {
+ h2_idx = getIndex(key, h2, shift, shiftMask);
+ h2_idx |= 1;
+ assert((h2_idx & 1) == 1);
+ }
+ assert(h2_idx < table.length);
+ final int idx = (h1_idx + (i * h2_idx)) & tableLengthMask;
+ assert(idx >= 0);
+ assert(idx < table.length);
+ return idx;
+ }
+
+ /**
+ * Initializes or doubles table size. If null, allocates in
+ * accord with initial capacity target held in field threshold.
+ * Otherwise, because we are using power-of-two expansion, the
+ * elements from each bin must either stay at same index, or move
+ * with a power of two offset in the new table.
+ *
+ * @return the table
+ */
+ final Node<V>[] resize() {
+ Node<V>[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node<V>[] newTab = (Node<V>[])new Node[newCap];
+ table = newTab;
+ tableLengthMask = newCap - 1;
+ this.shift = W - tableExponent(newCap);
+ this.shiftMask = Log2.log2base10(shift) - 1;
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node<V> e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ reinsert(e);
+ }
+ }
+ }
+ return newTab;
+ }
+
+ // used by table resize
+ private void reinsert(Node<V> e) {
+ assert(size < table.length);
+ for (int i = 0; i < table.length; ++i) {
+ int idx = doubleHash(e.getKey(), i);
+ assert(idx >= 0);
+ assert(idx < table.length);
+ if (table[idx] == null) {
+ table[idx] = e;
+ return;
+ }
+ assert(table[idx].key != e.getKey());
+ }
+ }
+
+ public V put(long key, V value) {
+ Node<V> existing = insert(key, value);
+ return existing != null ? existing.value : null;
+ }
+
+ private Node<V> insert(long key, V value) {
+ return insert(new Node<V>(key, value), key);
+ }
+
+ private Node<V> insert(Node<V> e, final long key) {
+ assert(size < table.length);
+ assert(e.getKey() == key);
+ Node<V> existing = null;
+ for (int i = 0; i < table.length; ++i) {
+ int idx = doubleHash(key, i);
+ assert(idx >= 0);
+ assert(idx < table.length);
+ if (table[idx] == null) {
+ table[idx] = e;
+ ++size;
+ break;
+ } else {
+ if (table[idx].key == key) {
+ existing = table[idx];
+ table[idx] = e;
+ break;
+ }
+ }
+ }
+ if (size > threshold) {
+ resize();
+ }
+ return existing;
+ }
+
+ private Node<V> find(long key) {
+ Node<V> result = null;
+ for (int i = 0; i < table.length; ++i) {
+ int idx = doubleHash(key, i);
+ assert(idx >= 0);
+ assert(idx < table.length);
+ result = table[idx];
+ if (result == null || result.key == key) {
+ break;
+ }
+ }
+ return result;
+ }
+
+
+ public V get(long key) {
+ Node<V> existing = find(key);
+ return existing != null ? existing.value : null;
+ }
+
+ public boolean containsKey(long key) {
+ return find(key) != null;
+ }
+ public int size() {
+ return this.size;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/UniversalHashFamily.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,27 @@
+package jdk.jfr.internal.util;
+
+import java.util.Random;
+
+public class UniversalHashFamily {
+ final Random rand = new Random();
+
+ private static long getA(long hashFunction) {
+ return hashFunction | 1;
+ }
+
+ private static long getB(long hashFunction, long mask) {
+ return hashFunction & mask;
+ }
+
+ private static long getHash(long key, long hashFunction, long mask) {
+ return (getA(hashFunction) * key) + (hashFunction & mask);
+ }
+
+ public static int getIndex(long key, long hashFunction, long shift, long mask) {
+ return (int)(getHash(key, hashFunction, mask) >>> shift);
+ }
+
+ public long getRandomHashFunction() {
+ return rand.nextLong() & Long.MAX_VALUE;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestAwaitTermination.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test RecordingStream::awaitTermination(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestAwaitTermination
+ */
+public class TestAwaitTermination {
+
+ public static void main(String... args) throws Exception {
+ testAwaitClose();
+ testAwaitTimeOut();
+ }
+
+ private static void testAwaitClose() throws InterruptedException, ExecutionException {
+ try (RecordingStream r = new RecordingStream()) {
+ r.startAsync();
+ var c = CompletableFuture.runAsync(() -> {
+ r.awaitTermination();
+ });
+ r.close();
+ c.get();
+ }
+ }
+
+ private static void testAwaitTimeOut() throws InterruptedException, ExecutionException {
+ try (RecordingStream r = new RecordingStream()) {
+ r.startAsync();
+ var c = CompletableFuture.runAsync(() -> {
+ r.awaitTermination(Duration.ofMillis(10));
+ });
+ c.get();
+ r.close();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestClose.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicLong;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::close()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestClose
+ */
+public class TestClose {
+
+ private static class CloseEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testCloseUnstarted();
+ testCloseStarted();
+ testCloseTwice();
+ testCloseStreaming();
+ testCloseMySelf();
+ }
+
+ private static void testCloseMySelf() throws Exception {
+ CountDownLatch l1 = new CountDownLatch(1);
+ CountDownLatch l2 = new CountDownLatch(1);
+ RecordingStream r = new RecordingStream();
+ r.onEvent(e -> {
+ try {
+ l1.await();
+ r.close();
+ l2.countDown();
+ } catch (InterruptedException ie) {
+ throw new Error(ie);
+ }
+ });
+ r.startAsync();
+ CloseEvent c = new CloseEvent();
+ c.commit();
+ l1.countDown();
+ l2.await();
+ }
+
+ private static void testCloseStreaming() throws Exception {
+ CountDownLatch streaming = new CountDownLatch(1);
+ RecordingStream r = new RecordingStream();
+ AtomicLong count = new AtomicLong();
+ r.onEvent(e -> {
+ if (count.incrementAndGet() == 100) {
+ streaming.countDown();
+ }
+ });
+ r.startAsync();
+ var streamingLoop = CompletableFuture.runAsync(() -> {
+ while (true) {
+ CloseEvent c = new CloseEvent();
+ c.commit();
+ }
+ });
+ streaming.await();
+ r.close();
+ streamingLoop.cancel(true);
+ }
+
+ private static void testCloseStarted() {
+ RecordingStream r = new RecordingStream();
+ r.startAsync();
+ r.close();
+ }
+
+ private static void testCloseUnstarted() {
+ RecordingStream r = new RecordingStream();
+ r.close();
+ }
+
+ private static void testCloseTwice() {
+ RecordingStream r = new RecordingStream();
+ r.startAsync();
+ r.close();
+ r.close();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestConstructor.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.Enabled;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::RecordingStream()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestConstructor
+ */
+public class TestConstructor {
+
+ public static void main(String... args) throws Exception {
+ testEmpty();
+ testConfiguration();
+ }
+
+ private static void testConfiguration() throws Exception {
+ CountDownLatch jvmInformation = new CountDownLatch(1);
+ Configuration c = Configuration.getConfiguration("default");
+ if (!c.getSettings().containsKey(EventNames.JVMInformation + "#" + Enabled.NAME)) {
+ throw new Exception("Expected default configuration to contain enabled " + EventNames.JVMInformation);
+ }
+ RecordingStream r = new RecordingStream(c);
+ r.onEvent("jdk.JVMInformation", e -> {
+ jvmInformation.countDown();
+ });
+ r.startAsync();
+ jvmInformation.await();
+ r.close();
+ }
+
+ private static void testEmpty() {
+ RecordingStream r = new RecordingStream();
+ r.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestDisable.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::disable(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestConstructor
+ */
+public class TestDisable {
+
+ private static class DisabledEvent extends Event {
+ }
+
+ private static class EnabledEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch twoEvent = new CountDownLatch(2);
+ AtomicBoolean fail = new AtomicBoolean(false);
+ try(RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ if (e.getEventType().getName().equals(DisabledEvent.class.getName())) {
+ fail.set(true);
+ }
+ twoEvent.countDown();
+ });
+ r.disable(DisabledEvent.class.getName());
+ r.startAsync();
+ EnabledEvent e1 = new EnabledEvent();
+ e1.commit();
+ DisabledEvent d1 = new DisabledEvent();
+ d1.commit();
+ EnabledEvent e2 = new EnabledEvent();
+ e2.commit();
+ twoEvent.await();
+ if (fail.get()) {
+ throw new Exception("Should not receive a disabled event");
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestEnable.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::enable(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestEnable
+ */
+public class TestEnable {
+
+ @Enabled(false)
+ private static class EnabledEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch event = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ event.countDown();
+ });
+ r.enable(EnabledEvent.class.getName());
+ r.startAsync();
+ EnabledEvent e = new EnabledEvent();
+ e.commit();
+ event.await();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestMaxAge.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::setMaxAge(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestMaxAge
+ */
+public class TestMaxAge {
+
+ public static void main(String... args) throws Exception {
+ Duration testDuration = Duration.ofMillis(1234567);
+ try (RecordingStream r = new RecordingStream()) {
+ r.setMaxAge(testDuration);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ Duration d = e.getDuration("maxAge");
+ System.out.println(d.toMillis());
+ if (testDuration.equals(d)) {
+ r.close();
+ return;
+ }
+ System.out.println("Max age not set, was " + d.toMillis() + " ms , but expected " + testDuration.toMillis() + " ms");
+ });
+ r.start();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onClose(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestMaxAge
+ */
+public class TestOnClose {
+
+ private static class CloseEvent extends Event {
+ }
+ public static void main(String... args) throws Exception {
+ testOnCloseNull();
+ testOnClosedUnstarted();
+ testOnClosedStarted();
+ }
+
+ private static void testOnCloseNull() {
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.onClose(null);
+ throw new AssertionError("Expected NullPointerException from onClose(null");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ }
+
+ private static void testOnClosedStarted() throws InterruptedException {
+ AtomicBoolean onClose = new AtomicBoolean(false);
+ CountDownLatch event = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ event.countDown();
+ });
+ r.onClose(() -> {
+ onClose.set(true);
+ });
+ r.startAsync();
+ CloseEvent c = new CloseEvent();
+ c.commit();
+ event.await();
+ }
+ if (!onClose.get()) {
+ throw new AssertionError("OnClose was not called");
+ }
+ }
+
+ private static void testOnClosedUnstarted() {
+ AtomicBoolean onClose = new AtomicBoolean(false);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onClose(() -> {
+ onClose.set(true);
+ });
+ }
+ if (!onClose.get()) {
+ throw new AssertionError("OnClose was not called");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnEvent.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onEvent(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnEvent
+ */
+public class TestOnEvent {
+
+ @Name("A")
+ static class EventA extends Event {
+ }
+
+ @Name("A")
+ static class EventAlsoA extends Event {
+ }
+
+ @Name("C")
+ static class EventC extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testOnEventNull();
+ testOnEvent();
+ testNamedEvent();
+ testTwoEventWithSameName();
+ }
+
+ private static void testOnEventNull() {
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.onEvent(null);
+ throw new AssertionError("Expected NullPointerException from onEvent(null)");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ try {
+ rs.onEvent("A", null);
+ throw new AssertionError("Expected NullPointerException from onEvent(\"A\", null)");
+
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ try {
+ String s = null;
+ rs.onEvent(s, null);
+ throw new AssertionError("Expected NullPointerException from onEvent(null, null)");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ }
+
+ private static void testTwoEventWithSameName() throws Exception {
+ CountDownLatch eventA = new CountDownLatch(2);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent("A", e -> {
+ System.out.println("testTwoEventWithSameName" + e);
+ eventA.countDown();
+ });
+ r.startAsync();
+ EventA a1 = new EventA();
+ a1.commit();
+ EventAlsoA a2 = new EventAlsoA();
+ a2.commit();
+ eventA.await();
+ }
+ }
+
+ private static void testNamedEvent() throws Exception {
+ try (RecordingStream r = new RecordingStream()) {
+ CountDownLatch eventA = new CountDownLatch(1);
+ CountDownLatch eventC = new CountDownLatch(1);
+ r.onEvent("A", e -> {
+ System.out.println("TestNamedEvent:" + e);
+ if (e.getEventType().getName().equals("A")) {
+ eventA.countDown();
+ }
+ });
+ r.onEvent("C", e -> {
+ System.out.println("TestNamedEvent:" + e);
+ if (e.getEventType().getName().equals("C")) {
+ eventC.countDown();
+ }
+ });
+
+ r.startAsync();
+ EventA a = new EventA();
+ a.commit();
+ EventC c = new EventC();
+ c.commit();
+ eventA.await();
+ eventC.await();
+ }
+ }
+
+ private static void testOnEvent() throws Exception {
+ try (RecordingStream r = new RecordingStream()) {
+ CountDownLatch event = new CountDownLatch(1);
+ r.onEvent(e -> {
+ event.countDown();
+ });
+ r.startAsync();
+ EventA a = new EventA();
+ a.commit();
+ event.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnFlush.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onFlush(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnFlush
+ */
+public class TestOnFlush {
+
+ static class OneEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testOnFLushNull();
+ testOneEvent();
+ testNoEvent();
+ }
+
+ private static void testOnFLushNull() {
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.onFlush(null);
+ throw new AssertionError("Expected NullPointerException from onFlush(null");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ }
+
+ private static void testNoEvent() throws Exception {
+ CountDownLatch flush = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onFlush(() -> {
+ flush.countDown();
+ });
+ r.startAsync();
+ flush.await();
+ }
+ }
+
+ private static void testOneEvent() throws InterruptedException {
+ CountDownLatch flush = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ // ignore event
+ });
+ r.onFlush(() -> {
+ flush.countDown();
+ });
+ r.startAsync();
+ OneEvent e = new OneEvent();
+ e.commit();
+ flush.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestRemove.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStrream::remove(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestRemove
+ */
+public class TestRemove {
+
+ static class RemoveEvent extends Event {
+
+ }
+
+ public static void main(String... args) throws Exception {
+ testRemoveNull();
+ testRemoveOnFlush();
+ testRemoveOnClose();
+ testRemoveOnEvent();
+ }
+
+ private static void testRemoveNull() {
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.remove(null);
+ throw new AssertionError("Expected NullPointerException from remove(null");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ }
+
+ private static void testRemoveOnEvent() throws Exception {
+ try (RecordingStream rs = new RecordingStream()) {
+ AtomicInteger counter = new AtomicInteger(0);
+ CountDownLatch events = new CountDownLatch(2);
+ Consumer<RecordedEvent> c1 = e -> {
+ counter.incrementAndGet();
+ };
+
+ Consumer<RecordedEvent> c2 = e -> {
+ events.countDown();
+ };
+ rs.onEvent(c1);
+ rs.onEvent(c2);
+
+ rs.remove(c1);
+ rs.startAsync();
+ RemoveEvent r1 = new RemoveEvent();
+ r1.commit();
+ RemoveEvent r2 = new RemoveEvent();
+ r2.commit();
+ events.await();
+ if (counter.get() > 0) {
+ throw new AssertionError("OnEvent handler not removed!");
+ }
+ }
+ }
+
+ private static void testRemoveOnClose() {
+ try (RecordingStream rs = new RecordingStream()) {
+ AtomicBoolean onClose = new AtomicBoolean(false);
+ Runnable r = () -> {
+ onClose.set(true);
+ };
+ rs.onClose(r);
+ rs.remove(r);
+ rs.close();
+ if (onClose.get()) {
+ throw new AssertionError("onClose handler not removed!");
+ }
+ }
+ }
+
+ private static void testRemoveOnFlush() throws Exception {
+ try (RecordingStream rs = new RecordingStream()) {
+ AtomicInteger flushCount = new AtomicInteger(2);
+ AtomicBoolean removeExecuted = new AtomicBoolean(false);
+ Runnable onFlush1 = () -> {
+ removeExecuted.set(true);
+ };
+ Runnable onFlush2 = () -> {
+ flushCount.incrementAndGet();
+ };
+
+ rs.onFlush(onFlush1);
+ rs.onFlush(onFlush2);
+ rs.remove(onFlush1);
+ rs.startAsync();
+ while (flushCount.get() < 2) {
+ RemoveEvent r = new RemoveEvent();
+ r.commit();
+ Thread.sleep(100);
+ }
+
+ if (removeExecuted.get()) {
+ throw new AssertionError("onFlush handler not removed!");
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetFlushInterval.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStrream::setFlushInterval
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetFlushInterval
+ */
+public class TestSetFlushInterval {
+
+ public static void main(String... args) throws Exception {
+ Duration expectedDuration = Duration.ofMillis(1001);
+ try (RecordingStream r = new RecordingStream()) {
+ r.setInterval(expectedDuration);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ Duration duration = e.getDuration("flushInterval");
+ if (expectedDuration.equals(duration)) {
+ System.out.println("Closing recording");
+ r.close();
+ return;
+ }
+ System.out.println("Flush interval not set, was " + duration+ ", but expected " + expectedDuration);
+ });
+ r.start();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetMaxAge.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStrream::setMaxAge
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxAge
+ */
+public class TestSetMaxAge {
+
+ public static void main(String... args) throws Exception {
+ Duration expecteddAge = Duration.ofMillis(123456789);
+ try (RecordingStream r = new RecordingStream()) {
+ r.setMaxAge(expecteddAge);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ Duration age = e.getDuration("maxAge");
+ if (expecteddAge.equals(age)) {
+ r.close();
+ return;
+ }
+ System.out.println("Max age not set, was " + age + ", but expected " + expecteddAge);
+ });
+ r.start();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetMaxSize.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.recordingstream;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+* @test
+* @summary Tests RecordingStrream::setMaxSize
+* @key jfr
+* @requires vm.hasJFR
+* @library /test/lib
+* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxSize
+*/
+public class TestSetMaxSize {
+
+ public static void main(String... args) throws Exception {
+ long testSize = 123456789;
+ try (RecordingStream r = new RecordingStream()) {
+ r.setMaxSize(123456789);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ long size= e.getLong("maxSize");
+ if (size == testSize) {
+ r.close();
+ return;
+ }
+ System.out.println("Max size not set, was " + size + ", but expected " + testSize);
+ });
+ r.start();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestStart.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::start()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStart
+ */
+public class TestStart {
+ static class StartEvent extends Event {
+ }
+ static class EventProducer extends Thread {
+ private boolean killed = false;
+ public void run() {
+ while (true) {
+ StartEvent s = new StartEvent();
+ s.commit();
+ synchronized (this) {
+ try {
+ wait(10);
+ if (killed) {
+ return; // end thread
+ }
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ public void kill() {
+ synchronized (this) {
+ this.killed = true;
+ this.notifyAll();
+ }
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ testStart();
+ testStartOnEvent();
+ testStartTwice();
+ testStartClosed();
+ }
+
+ private static void testStartTwice() throws Exception {
+ CountDownLatch started = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ EventProducer t = new EventProducer();
+ t.start();
+ CompletableFuture.runAsync(() -> {
+ rs.start();
+ });
+ rs.onEvent(e -> {
+ if (started.getCount() > 0) {
+ started.countDown();
+ }
+ });
+ started.await();
+ t.kill();
+ try {
+ rs.start();
+ throw new AssertionError("Expected IllegalStateException if started twice");
+ } catch (IllegalStateException ise) {
+ // OK, as expected
+ }
+ }
+ }
+
+ static void testStart() throws Exception {
+ CountDownLatch started = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ started.countDown();
+ });
+ EventProducer t = new EventProducer();
+ t.start();
+ CompletableFuture.runAsync(() -> {
+ rs.start();
+ });
+ started.await();
+ t.kill();
+ }
+ }
+
+ static void testStartOnEvent() throws Exception {
+ AtomicBoolean ISE = new AtomicBoolean(false);
+ CountDownLatch startedTwice = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ try {
+ rs.start(); // must not deadlock
+ } catch (IllegalStateException ise) {
+ if (!ISE.get()) {
+ startedTwice.countDown();
+ ISE.set(true);
+ }
+ }
+ });
+ EventProducer t = new EventProducer();
+ t.start();
+ CompletableFuture.runAsync(() -> {
+ rs.start();
+ });
+ startedTwice.await();
+ t.kill();
+ if (!ISE.get()) {
+ throw new AssertionError("Expected IllegalStateException");
+ }
+ }
+ }
+
+ static void testStartClosed() {
+ RecordingStream rs = new RecordingStream();
+ rs.close();
+ try {
+ rs.start();
+ throw new AssertionError("Expected IllegalStateException");
+ } catch (IllegalStateException ise) {
+ // OK, as expected.
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestStartAsync.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::startAsync()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStartAsync
+ */
+public class TestStartAsync {
+ static class StartEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testStart();
+ testStartTwice();
+ testStartClosed();
+ }
+
+ private static void testStartTwice() throws Exception {
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.startAsync();
+ try {
+ rs.startAsync();
+ throw new AssertionError("Expected IllegalStateException if started twice");
+ } catch (IllegalStateException ise) {
+ // OK, as expected
+ }
+ }
+ }
+
+ static void testStart() throws Exception {
+ CountDownLatch started = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ started.countDown();
+ });
+ rs.startAsync();
+ StartEvent e = new StartEvent();
+ e.commit();
+ started.await();
+ }
+ }
+
+ static void testStartClosed() {
+ RecordingStream rs = new RecordingStream();
+ rs.close();
+ try {
+ rs.startAsync();
+ throw new AssertionError("Expected IllegalStateException");
+ } catch (IllegalStateException ise) {
+ // OK, as expected.
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEmptyChunks.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to iterate over chunk without normal events
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEmptyChunks
+ */
+public class TestEmptyChunks {
+ static class EndEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch end = new CountDownLatch(1);
+ try (RecordingStream es = new RecordingStream()) {
+ es.onEvent(EndEvent.class.getName(), e -> {
+ end.countDown();
+ });
+ es.startAsync();
+ Recording r1 = new Recording();
+ r1.start();
+ System.out.println("Chunk 1 started");
+ Recording r2 = new Recording();
+ r2.start();
+ System.out.println("Chunk 2 started");
+ Recording r3 = new Recording();
+ r3.start();
+ System.out.println("Chunk 3 started");
+ r2.stop();
+ System.out.println("Chunk 4 started");
+ r3.stop();
+ System.out.println("Chunk 5 started");
+ EndEvent e = new EndEvent();
+ e.commit();
+ end.await();
+ r1.stop();
+ System.out.println("Chunk 5 ended");
+ r1.close();
+ r2.close();
+ r3.close();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEnableEvents.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents from specified event
+ * settings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEnableEvents
+ */
+public class TestEnableEvents {
+
+ @Enabled(false)
+ static class HorseEvent extends Event {
+ }
+
+ @Enabled(false)
+ static class ElephantEvent extends Event {
+ }
+
+ @Enabled(false)
+ static class TigerEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch elephantLatch = new CountDownLatch(1);
+ CountDownLatch tigerLatch = new CountDownLatch(1);
+ CountDownLatch horseLatch = new CountDownLatch(1);
+
+ FlightRecorder.addPeriodicEvent(ElephantEvent.class, () -> {
+ HorseEvent ze = new HorseEvent();
+ ze.commit();
+ });
+
+ try (RecordingStream s = new RecordingStream()) {
+ s.enable(HorseEvent.class.getName()).withPeriod(Duration.ofMillis(50));
+ s.enable(TigerEvent.class.getName());
+ s.enable(ElephantEvent.class.getName());
+ s.onEvent(TigerEvent.class.getName(), e -> {
+ System.out.println("Event: " + e.getEventType().getName());
+ System.out.println("Found tiger!");
+ tigerLatch.countDown();
+ });
+ s.onEvent(HorseEvent.class.getName(), e -> {
+ System.out.println("Found horse!");
+ horseLatch.countDown();
+ });
+ s.onEvent(ElephantEvent.class.getName(), e -> {
+ System.out.println("Found elelphant!");
+ elephantLatch.countDown();
+ });
+ s.startAsync();
+ TigerEvent te = new TigerEvent();
+ te.commit();
+ ElephantEvent ee = new ElephantEvent();
+ ee.commit();
+ elephantLatch.await();
+ horseLatch.await();
+ tigerLatch.await();
+ }
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEventRegistration.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Registered;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to register new metadata in a chunk
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEventRegistration
+ */
+public class TestEventRegistration {
+ @Registered(false)
+ static class StreamEvent1 extends Event {
+ }
+
+ @Registered(false)
+ static class StreamEvent2 extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+
+ CountDownLatch s1Latch = new CountDownLatch(1);
+ CountDownLatch s2Latch = new CountDownLatch(1);
+ try (RecordingStream es = new RecordingStream()) {
+ es.onEvent(StreamEvent1.class.getName(), e -> {
+ s1Latch.countDown();
+ });
+ es.onEvent(StreamEvent2.class.getName(), e -> {
+ s2Latch.countDown();
+ });
+ es.startAsync();
+ System.out.println("Registering " + StreamEvent1.class.getName());
+ FlightRecorder.register(StreamEvent1.class);
+ StreamEvent1 s1 = new StreamEvent1();
+ s1.commit();
+ System.out.println(StreamEvent1.class.getName() + " commited");
+ System.out.println("Awaiting latch for " + StreamEvent1.class.getName());
+ s1Latch.await();
+ System.out.println();
+ System.out.println("Registering " + StreamEvent2.class.getName());
+ FlightRecorder.register(StreamEvent2.class);
+ StreamEvent2 s2 = new StreamEvent2();
+ s2.commit();
+ System.out.println(StreamEvent2.class.getName() + " commited");
+ System.out.println("Awaiting latch for " + StreamEvent2.class.getName());
+ s2Latch.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to iterate over chunk with normal events
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
+ */
+public class TestFilledChunks {
+
+ static class FillEvent extends Event {
+ String message;
+ int value;
+ int id;
+ }
+
+ static class EndEvent extends Event {
+ }
+
+ // Will generate about 100 MB of data, or 8-9 chunks
+ private static final int EVENT_COUNT = 5_000_000;
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch end = new CountDownLatch(1);
+ AtomicInteger idCounter = new AtomicInteger();
+ try (RecordingStream es = new RecordingStream()) {
+ es.onEvent(EndEvent.class.getName(), e -> end.countDown());
+ es.onEvent(FillEvent.class.getName(), e -> {
+ idCounter.incrementAndGet();
+// if (id != expected) {
+// throw new Error("Expected id " + expected + ", but got " + id);
+// }
+ });
+ es.startAsync();
+ long seed = System.currentTimeMillis();
+ System.out.println("Random seed: " + seed);
+ Random r = new Random(seed);
+ for (int i = 1; i < EVENT_COUNT; i++) {
+ FillEvent f = new FillEvent();
+ f.message = i %2 == 0 ? "ko" : "kak";
+ f.value = r.nextInt(10000);
+ f.id = i;
+ f.commit();
+ if (i % 1_000_000 == 0) {
+ System.out.println("Emitted " + i + " events");
+ }
+ }
+ System.out.println("Awaiting end event");
+ Thread.sleep(1_000);
+ for (int i = 1; i < EVENT_COUNT; i++) {
+ EndEvent e = new EndEvent();
+ e.commit();
+ }
+ end.await();
+
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFiltering.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to filter a stream for an event
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFiltering
+ */
+public class TestFiltering {
+
+ static class SnakeEvent extends Event {
+ int id;
+ }
+
+ static class EelEvent extends Event {
+ int id;
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch l = new CountDownLatch(1);
+ String eventName = SnakeEvent.class.getName();
+ AtomicInteger idCounter = new AtomicInteger(-1);
+ try (RecordingStream e = new RecordingStream()) {
+ e.onEvent(eventName, event -> {
+ if (!event.getEventType().getName().equals(eventName)) {
+ throw new InternalError("Unexpected event " + e);
+ }
+ if (event.getInt("id") != idCounter.incrementAndGet()) {
+ throw new InternalError("Incorrect id");
+ }
+ if (idCounter.get() == 99) {
+ l.countDown();
+ }
+ });
+ e.startAsync();
+ for (int i = 0; i < 100; i++) {
+ SnakeEvent se = new SnakeEvent();
+ se.id = i;
+ se.commit();
+
+ EelEvent ee = new EelEvent();
+ ee.id = i;
+ ee.commit();
+ }
+ l.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFromFile.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.nio.file.Path;
+import java.util.concurrent.atomic.AtomicLong;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents from a file
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFromFile
+ */
+public class TestFromFile {
+
+ static class SnakeEvent extends Event {
+ int id;
+ }
+
+ public static void main(String... args) throws Exception {
+// Path path = Paths.get("./using-file.jfr");
+// try (Recording r1 = new Recording()) {
+// r1.start();
+// emitSnakeEvent(1);
+// emitSnakeEvent(2);
+// emitSnakeEvent(3);
+// // Force a chunk rotation
+// try (Recording r2 = new Recording()) {
+// r2.start();
+// emitSnakeEvent(4);
+// emitSnakeEvent(5);
+// emitSnakeEvent(6);
+// r2.stop();
+// }
+// r1.stop();
+// r1.dump(path);
+//
+// testIterator(path);
+// testConsumer(path);
+// }
+ }
+
+ static void testConsumer(Path path) throws Exception {
+ AtomicLong counter = new AtomicLong();
+ try (EventStream es = EventStream.openFile(path)) {
+ es.onEvent(e -> {
+ counter.incrementAndGet();
+ });
+ es.startAsync();
+ if (counter.get() != 6) {
+ throw new Exception("Expected 6 event, but got " + counter.get());
+ }
+ es.awaitTermination();
+ }
+ }
+
+ static void emitSnakeEvent(int id) {
+ SnakeEvent e = new SnakeEvent();
+ e.id = id;
+ e.commit();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRecordingBefore.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to start a stream when there are
+ * already chunk in the repository
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestRecordingBefore
+ */
+public class TestRecordingBefore {
+
+ static class SnakeEvent extends Event {
+ int id;
+ }
+
+ public static void main(String... args) throws Exception {
+
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ emitSnakeEvent(1);
+ emitSnakeEvent(2);
+ emitSnakeEvent(3);
+ // Force a chunk rotation
+ try (Recording r2 = new Recording()) {
+ r2.start();
+ emitSnakeEvent(4);
+ emitSnakeEvent(5);
+ emitSnakeEvent(6);
+ r2.stop();
+ }
+ r1.stop();
+ // Two chunks should now exist in the repository
+ AtomicBoolean fail = new AtomicBoolean(false);
+ CountDownLatch lastEvent = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ long id = e.getLong("id");
+ if (id < 7) {
+ System.out.println("Found unexpected id " + id);
+ fail.set(true);
+ }
+ if (id == 9) {
+ lastEvent.countDown();
+ }
+ });
+ rs.startAsync();
+ emitSnakeEvent(7);
+ emitSnakeEvent(8);
+ emitSnakeEvent(9);
+ lastEvent.await();
+ if (fail.get()) {
+ throw new Exception("Found events from a previous recording");
+ }
+ }
+ }
+ }
+
+ static void emitSnakeEvent(int id) {
+ SnakeEvent e = new SnakeEvent();
+ e.id = id;
+ e.commit();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRemovedChunks.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests that a stream can gracefully handle chunk being removed
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
+ */
+public class TestRemovedChunks {
+ private final static CountDownLatch parkLatch = new CountDownLatch(1);
+ private final static CountDownLatch removalLatch = new CountDownLatch(1);
+ private final static CountDownLatch IfeelFineLatch = new CountDownLatch(1);
+
+ static class DataEvent extends Event {
+ double double1;
+ double double2;
+ double double3;
+ double double4;
+ double double5;
+ }
+
+ static class ParkStream extends Event {
+ }
+
+ static class IFeelFine extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+
+ try (RecordingStream s = new RecordingStream()) {
+ s.setMaxSize(20_000_000);
+ s.onEvent(ParkStream.class.getName(), e -> {
+ parkLatch.countDown();
+ await(removalLatch);
+
+ });
+ s.onEvent(IFeelFine.class.getName(), e -> {
+ IfeelFineLatch.countDown();
+ });
+ s.startAsync();
+ emitData(15_000_000);
+ ParkStream ps = new ParkStream();
+ ps.commit();
+ await(parkLatch);
+ // Try to force removal of chunk that is being streamed
+ emitData(50_000_000);
+ removalLatch.countDown();
+ IFeelFine i = new IFeelFine();
+ i.commit();
+ await(IfeelFineLatch);
+ }
+
+ }
+
+ private static void await(CountDownLatch latch) throws Error {
+ try {
+ latch.await();
+ } catch (InterruptedException e1) {
+ throw new Error("Latch interupted");
+ }
+ }
+
+ private static void emitData(int amount) throws InterruptedException {
+ int count = 0;
+ while (amount > 0) {
+ DataEvent de = new DataEvent();
+ // 5 doubles are 40 bytes bytes
+ // and event size, event type, thread,
+ // start time, duration and stack trace about 15 bytes
+ de.double1 = 0.0;
+ de.double2 = 1.0;
+ de.double3 = 2.0;
+ de.double4 = 3.0;
+ de.double5 = 4.0;
+ de.commit();
+ amount -= 55;
+ count++;
+ //
+ if (count % 100_000 == 0) {
+ Thread.sleep(10);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryProperty.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.VirtualMachine;
+
+import jdk.jfr.Recording;
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.PidJcmdExecutor;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to access JFR repository from a system
+ * property
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @modules jdk.attach
+ * jdk.jfr
+ * @run main/othervm -Djdk.attach.allowAttachSelf=true jdk.jfr.api.consumer.streaming.TestRepositoryProperty
+ */
+public class TestRepositoryProperty {
+
+ private final static String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
+
+ public static void main(String... args) throws Exception {
+ testBeforeInitialization();
+ testAfterInitialization();
+ testFromAgent();
+ testAfterChange();
+ }
+
+ private static void testFromAgent() throws AttachNotSupportedException, IOException {
+ String pidText = String.valueOf(ProcessHandle.current().pid());
+ VirtualMachine vm = VirtualMachine.attach(pidText);
+ Properties p = vm.getSystemProperties();
+ String location = (String) p.get(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (location == null) {
+ throw new AssertionError("Could not find repository path in agent properties");
+ }
+ Path path = Path.of(location);
+ if (!Files.isDirectory(path)) {
+ throw new AssertionError("Repository path doesn't point to directory");
+ }
+ }
+
+ private static void testAfterChange() {
+ Path newRepository = Path.of(".").toAbsolutePath();
+
+ String cmd = "JFR.configure repository=" + newRepository.toString();
+ CommandExecutor executor = new PidJcmdExecutor();
+ executor.execute(cmd);
+ String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (newRepository.toString().equals(location)) {
+ throw new AssertionError("Repository path not updated after it has been changed");
+ }
+ }
+
+ private static void testAfterInitialization() {
+ try (Recording r = new Recording()) {
+ r.start();
+ String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (location == null) {
+ throw new AssertionError("Repository path should exit before JFR is initialized");
+ }
+ System.out.println("repository=" + location);
+ Path p = Path.of(location);
+ if (!Files.isDirectory(p)) {
+ throw new AssertionError("Repository path doesn't point to directory");
+ }
+ }
+
+ }
+
+ private static void testBeforeInitialization() {
+ String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (location != null) {
+ throw new AssertionError("Repository path should exit before JFR is initialized");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Name;
+import jdk.jfr.Period;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents of ongoing
+ * recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace
+ * jdk.jfr.api.consumer.streaming.TestStartMultiChunk
+ */
+public class TestStartMultiChunk {
+
+ @Period("10 s")
+ @Name("Zebra")
+ static class ZebraEvent extends Event {
+ }
+
+ @Name("Cat")
+ static class CatEvent extends Event {
+ }
+
+ @Name("Dog")
+ static class DogEvent extends Event {
+ }
+
+ @Name("Mouse")
+ static class MouseEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch dogLatch = new CountDownLatch(1);
+ CountDownLatch catLatch = new CountDownLatch(1);
+ CountDownLatch mouseLatch = new CountDownLatch(1);
+ CountDownLatch zebraLatch = new CountDownLatch(3);
+
+ FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
+ ZebraEvent ze = new ZebraEvent();
+ ze.commit();
+ System.out.println("Zebra emitted");
+ });
+
+ try (RecordingStream s = new RecordingStream()) {
+ s.onEvent("Cat", e -> {
+ System.out.println("Found cat!");
+ catLatch.countDown();
+ });
+ s.onEvent("Dog", e -> {
+ System.out.println("Found dog!");
+ dogLatch.countDown();
+ });
+ s.onEvent("Zebra", e -> {
+ System.out.println("Found zebra!");
+ zebraLatch.countDown();
+ });
+ s.onEvent("Mouse", e -> {
+ System.out.println("Found mouse!");
+ mouseLatch.countDown();
+ });
+ s.startAsync();
+ System.out.println("Stream recoding started");
+
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ System.out.println("r1.start()");
+ MouseEvent me = new MouseEvent();
+ me.commit();
+ System.out.println("Mouse emitted");
+ mouseLatch.await();
+ try (Recording r2 = new Recording()) { // force chunk rotation
+ // in stream
+ r2.start();
+ System.out.println("r2.start()");
+ DogEvent de = new DogEvent();
+ de.commit();
+ System.out.println("Dog emitted");
+ dogLatch.await();
+ CatEvent ce = new CatEvent();
+ ce.commit();
+ System.out.println("Cat emitted");
+ catLatch.await();
+ zebraLatch.await();
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartSingleChunk.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Period;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents of ongoing
+ * recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace
+ * jdk.jfr.api.consumer.streaming.TestStartSingleChunk
+ */
+public class TestStartSingleChunk {
+
+ @Period("500 ms")
+ static class ElkEvent extends Event {
+ }
+
+ static class FrogEvent extends Event {
+ }
+
+ static class LionEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch frogLatch = new CountDownLatch(1);
+ CountDownLatch lionLatch = new CountDownLatch(1);
+ CountDownLatch elkLatch = new CountDownLatch(3);
+
+ FlightRecorder.addPeriodicEvent(ElkEvent.class, () -> {
+ ElkEvent ee = new ElkEvent();
+ ee.commit();
+ });
+ try (RecordingStream s = new RecordingStream()) {
+ s.onEvent(ElkEvent.class.getName(), e -> {
+ System.out.println("Found elk!");
+ elkLatch.countDown();
+ });
+ s.onEvent(LionEvent.class.getName(), e -> {
+ System.out.println("Found lion!");
+ lionLatch.countDown();
+ });
+ s.onEvent(FrogEvent.class.getName(), e -> {
+ System.out.println("Found frog!");
+ frogLatch.countDown();
+ });
+ s.startAsync();
+ FrogEvent fe = new FrogEvent();
+ fe.commit();
+
+ LionEvent le = new LionEvent();
+ le.commit();
+
+ frogLatch.await();
+ lionLatch.await();
+ elkLatch.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/UseCasesStream.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.time.Duration;
+import java.util.ArrayDeque;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.consumer.RecordingStream;
+
+class UseCasesStream {
+
+ //
+ // Use case: Out-of-the-Box Experience
+ //
+ // - Simple things should be simple
+ // - Pique interest, i.e. a one-liner on Stack Overflow
+ // - Few lines of code as possible
+ // - Should be easier than alternative technologies, like JMX and JVM TI
+ //
+ // - Non-goals: Corner-cases, advanced configuration, releasing resources
+ //
+ public static void outOfTheBox() throws InterruptedException {
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.enable("jdk.ExceptionThrown");
+ rs.onEvent(e -> System.out.println(e.getString("message")));
+ rs.start();
+ }
+
+ // EventStream.start("jdk.JavaMonitorEnter", "threshold", "20 ms",
+ // "stackTrace", "false")
+ // .addConsumer(System.out::println);
+ //
+ // EventStream.start("jdk.CPULoad", "period", "1 s")
+ // .addConsumer(e -> System.out.println(100 *
+ // e.getDouble("totalMachine") + " %"));
+ //
+ // EventStream.start("jdk.GarbageCollection")
+ // .addConsumer(e -> System.out.println("GC: " + e.getStartTime() + "
+ // maxPauseTime=" + e.getDuration("maxPauseTime").toMillis() + " ms"));
+
+ Thread.sleep(100_000);
+ }
+
+ // Use case: Event Forwarding
+ //
+ // - Forward arbitrary event to frameworks such as RxJava, JSON/XML and
+ // Kafka
+ // - Handle flooding
+ // - Performant
+ // - Graceful shutdown
+ // - Non-goals: Filter events
+ //
+ public static void eventForwarding() throws InterruptedException, IOException, ParseException {
+ // KafkaProducer producer = new KafkaProducer<String, String>();
+ try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("default"))) {
+ rs.setMaxAge(Duration.ofMinutes(5));
+ rs.setMaxSize(1000_000_000L);
+ // es.setParallel(true);
+ // es.setReuse(true);
+ // es.consume(e -> producer.send(new ProducerRecord<String,
+ // String>("topic",
+ // e.getString("key"), e.getString("value"))));
+ rs.start();
+ }
+ // Write primitive values to XML
+ try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("deafult"))) {
+ try (PrintWriter p = new PrintWriter(new FileWriter("recording.xml"))) {
+ // es.setParallel(false);
+ // es.setReuse(true);
+ p.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
+ p.println("<events>");
+ rs.onEvent(e -> {
+ EventType type = e.getEventType();
+ p.println(" <event type=\"" + type.getName() + "\" start=\"" + e.getStartTime() + "\" end=\"" + e.getEndTime() + "\">");
+ for (ValueDescriptor field : e.getEventType().getFields()) {
+ Object value = e.getValue(field.getName());
+ if (value instanceof Number || field.getTypeName().equals("java.lang.String")) {
+ p.println(" <value field=\"" + field.getName() + "\">" + value + "</value>");
+ }
+ }
+ });
+ rs.start();
+ p.println("</events>");
+ }
+ }
+ }
+
+ // Use case: Repository Access
+ //
+ // - Read the disk repository from another process, for example a side car
+ // in
+ // Docker container
+ // - Be able to configure flush interval from command line or jcmd.
+ // - Graceful shutdown
+ //
+ public static void repositoryAccess() throws IOException, InterruptedException {
+ Path repository = Paths.get("c:\\repository").toAbsolutePath();
+ String command = new String();
+ command += "java -XX:StartFlightRecording:flush=2s";
+ command += "-XX:FlightRecorderOption:repository=" + repository + " Application";
+ Process myProcess = Runtime.getRuntime().exec(command);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(System.out::println);
+ rs.startAsync();
+ Thread.sleep(10_000);
+ myProcess.destroy();
+ Thread.sleep(10_000);
+ }
+ }
+
+ // Use: Tooling
+ //
+ // - Monitor a stream of data for a very long time
+ // - Predictable interval, i.e. once every second
+ // - Notification with minimal delay
+ // - Events with the same period should arrive together
+ // - Consume events in chronological order
+ // - Low overhead
+ //
+ public static void tooling() throws IOException, ParseException {
+ ArrayDeque<Double> measurements = new ArrayDeque<>();
+ try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("profile"))) {
+ rs.setInterval(Duration.ofSeconds(1));
+ rs.setMaxAge(Duration.ofMinutes(1));
+ // rs.setOrdered(true);
+ // rs.setReuse(false);
+ // rs.setParallel(true);
+ rs.onEvent("jdk.CPULoad", e -> {
+ double d = e.getDouble("totalMachine");
+ measurements.addFirst(d);
+ if (measurements.size() > 60) {
+ measurements.removeLast();
+ }
+ // repaint();
+ });
+ rs.start();
+ }
+ }
+
+ // Use case: Low Impact
+ //
+ // - Support event subscriptions in a low latency environment (minimal GC
+ // pauses)
+ // - Filter out relevant events to minimize disk overhead and allocation
+ // pressure
+ // - Avoid impact from other recordings
+ // - Avoid Heisenberg effects, in particular self-recursion
+ //
+ // Non-goals: one-liner
+ //
+ public static void lowImpact() throws InterruptedException, IOException, ParseException {
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.enable("jdk.JavaMonitorEnter#threshold").withThreshold(Duration.ofMillis(10));
+ rs.enable("jdk.ExceptionThrow#enabled");
+ // ep.setReuse(true);
+ rs.onEvent("jdk.JavaMonitorEnter", System.out::println);
+ rs.onEvent("jdk.ExceptionThrow", System.out::println);
+ rs.start();
+ ;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/TestFlush.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.event.runtime;
+
+import java.util.concurrent.CountDownLatch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Period;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.jfr.consumer.RecordedEvent;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Verifies at the metalevel that stream contents are written to ongoing recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace jdk.jfr.event.runtime.TestFlush
+ */
+public class TestFlush {
+ private static boolean flushEventAck = false;
+
+ @Period("2 s")
+ static class ZebraEvent extends Event {
+ }
+ static class CatEvent extends Event {
+ }
+ static class DogEvent extends Event {
+ }
+ static class MouseEvent extends Event {
+ }
+
+ public static void main(String... args) throws InterruptedException {
+ CountDownLatch dogLatch = new CountDownLatch(1);
+ CountDownLatch catLatch = new CountDownLatch(1);
+ CountDownLatch mouseLatch = new CountDownLatch(1);
+ CountDownLatch zebraLatch = new CountDownLatch(3);
+
+ FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
+ ZebraEvent ze = new ZebraEvent();
+ ze.commit();
+ });
+
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.enable(EventNames.Flush);
+ rs.enable(EventNames.FlushStorage);
+ rs.enable(EventNames.FlushStacktrace);
+ rs.enable(EventNames.FlushStringPool);
+ rs.enable(EventNames.FlushMetadata);
+ rs.enable(EventNames.FlushTypeSet);
+ rs.onEvent(e -> {
+ switch (e.getEventType().getName()) {
+ case EventNames.Flush:
+ flushEventAck = true;
+ case EventNames.FlushStorage:
+ case EventNames.FlushStacktrace:
+ case EventNames.FlushStringPool:
+ case EventNames.FlushMetadata:
+ case EventNames.FlushTypeSet:
+ validateFlushEvent(e);
+ return;
+ }
+ if (e.getEventType().getName().equals(CatEvent.class.getName())) {
+ System.out.println("Found cat!");
+ catLatch.countDown();
+ return;
+ }
+ if (e.getEventType().getName().equals(DogEvent.class.getName())) {
+ System.out.println("Found dog!");
+ dogLatch.countDown();
+ return;
+ }
+ if (e.getEventType().getName().equals(ZebraEvent.class.getName())) {
+ System.out.println("Found zebra!");
+ zebraLatch.countDown();
+ return;
+ }
+ if (e.getEventType().getName().equals(MouseEvent.class.getName())) {
+ System.out.println("Found mouse!");
+ mouseLatch.countDown();
+ return;
+ }
+ System.out.println("Unexpected event: " + e.getEventType().getName());
+ });
+
+ rs.startAsync();
+
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ MouseEvent me = new MouseEvent();
+ me.commit();
+ System.out.println("Mouse emitted");
+ mouseLatch.await();
+ try (Recording r2 = new Recording()) { // force chunk rotation in stream
+ r2.start();
+ DogEvent de = new DogEvent();
+ de.commit();
+ System.out.println("Dog emitted");
+ dogLatch.await();
+ CatEvent ce = new CatEvent();
+ ce.commit();
+ System.out.println("Cat emitted");
+ catLatch.await();
+ zebraLatch.await();
+ acknowledgeFlushEvent();
+ }
+ }
+ }
+ }
+
+ private static void printEvent(RecordedEvent re) {
+ System.out.println(re.getEventType().getName());
+ System.out.println(re.getStartTime().toEpochMilli());
+ System.out.println(re.getEndTime().toEpochMilli());
+ }
+
+ private static void printFlushEvent(RecordedEvent re) {
+ printEvent(re);
+ System.out.println("flushID: " + (long) re.getValue("flushId"));
+ System.out.println("elements: " + (long) re.getValue("elements"));
+ System.out.println("size: " + (long) re.getValue("size"));
+ }
+
+ private static void validateFlushEvent(RecordedEvent re) {
+ printFlushEvent(re);
+ Asserts.assertTrue(re.getEventType().getName().contains("Flush"), "invalid Event type");
+ Asserts.assertGT((long) re.getValue("flushId"), 0L, "Invalid flush ID");
+ Asserts.assertGT((long) re.getValue("elements"), 0L, "No elements");
+ Asserts.assertGT((long) re.getValue("size"), 0L, "Empty size");
+ }
+
+ private static void acknowledgeFlushEvent() {
+ Asserts.assertTrue(flushEventAck, "No Flush event");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/jvm/TestThreadExclusion.java Fri May 17 18:03:14 2019 +0200
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.jvm;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.JVM;
+import jdk.jfr.Recording;
+
+import jdk.test.lib.jfr.EventNames;
+import jdk.test.lib.jfr.Events;
+
+import static jdk.test.lib.Asserts.assertTrue;
+
+/**
+ * @test
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @modules jdk.jfr/jdk.jfr.internal
+ * @run main/othervm jdk.jfr.jvm.TestThreadExclusion
+ */
+
+/**
+ * Starts and stops a number of threads in order.
+ * Verifies that events are in the same order.
+ */
+public class TestThreadExclusion {
+ private final static String EVENT_NAME_THREAD_START = EventNames.ThreadStart;
+ private final static String EVENT_NAME_THREAD_END = EventNames.ThreadEnd;
+ private static final String THREAD_NAME_PREFIX = "TestThread-";
+ private static JVM jvm;
+
+ public static void main(String[] args) throws Throwable {
+ // Test Java Thread Start event
+ Recording recording = new Recording();
+ recording.enable(EVENT_NAME_THREAD_START).withThreshold(Duration.ofMillis(0));
+ recording.enable(EVENT_NAME_THREAD_END).withThreshold(Duration.ofMillis(0));
+ recording.start();
+ LatchedThread[] threads = startThreads();
+ long[] javaThreadIds = getJavaThreadIds(threads);
+ stopThreads(threads);
+ recording.stop();
+ List<RecordedEvent> events = Events.fromRecording(recording);
+ verifyThreadExclusion(events, javaThreadIds);
+ }
+
+ private static void verifyThreadExclusion(List<RecordedEvent> events, long[] javaThreadIds) throws Exception {
+ for (RecordedEvent event : events) {
+ System.out.println("Event:" + event);
+ final long eventJavaThreadId = event.getThread().getJavaThreadId();
+ for (int i = 0; i < javaThreadIds.length; ++i) {
+ if (eventJavaThreadId == javaThreadIds[i]) {
+ throw new Exception("Event " + event.getEventType().getName() + " has a thread id " + eventJavaThreadId + " that should have been excluded");
+ }
+ }
+ }
+ }
+
+ private static LatchedThread[] startThreads() {
+ LatchedThread threads[] = new LatchedThread[10];
+ ThreadGroup threadGroup = new ThreadGroup("TestThreadGroup");
+ jvm = JVM.getJVM();
+ for (int i = 0; i < threads.length; i++) {
+ threads[i] = new LatchedThread(threadGroup, THREAD_NAME_PREFIX + i);
+ jvm.exclude(threads[i]);
+ threads[i].startThread();
+ System.out.println("Started thread id=" + threads[i].getId());
+ }
+ return threads;
+ }
+
+ private static long[] getJavaThreadIds(LatchedThread[] threads) {
+ long[] javaThreadIds = new long[threads.length];
+ for (int i = 0; i < threads.length; ++i) {
+ javaThreadIds[i] = threads[i].getId();
+ }
+ return javaThreadIds;
+ }
+
+ private static void stopThreads(LatchedThread[] threads) {
+ for (LatchedThread thread : threads) {
+ assertTrue(jvm.isExcluded(thread), "Thread " + thread + "should be excluded");
+ thread.stopThread();
+ while (thread.isAlive()) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private static class LatchedThread extends Thread {
+ private final CountDownLatch start = new CountDownLatch(1);
+ private final CountDownLatch stop = new CountDownLatch(1);
+
+ public LatchedThread(ThreadGroup threadGroup, String name) {
+ super(threadGroup, name);
+ }
+
+ public void run() {
+ start.countDown();
+ try {
+ stop.await();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void startThread() {
+ this.start();
+ try {
+ start.await();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void stopThread() {
+ stop.countDown();
+ }
+ }
+}